Message ID | 20220712042258.293010-3-jstultz@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC,1/3] drm: drm_syncobj: Add note in DOC about absolute timeout values | expand |
Am 12.07.22 um 06:22 schrieb John Stultz: > An initial pass at a drm_syncobj API test. > > Currently covers trivial use of: > DRM_IOCTL_SYNCOBJ_CREATE > DRM_IOCTL_SYNCOBJ_DESTROY > DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD > DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE > DRM_IOCTL_SYNCOBJ_WAIT > DRM_IOCTL_SYNCOBJ_RESET > DRM_IOCTL_SYNCOBJ_SIGNAL > DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT > DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL > > And demonstrates how the userspace API can be used, along with > some fairly simple bad parameter checking. > > The patch includes a few helpers taken from libdrm, as at least > on the VM I was testing with, I didn't have a new enough libdrm > to support the *_TIMELINE_* ioctls. Ideally the ioctl-helper bits > can be dropped at a later time. > > Feedback would be appreciated! > > Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> > Cc: Maxime Ripard <mripard@kernel.org> > Cc: Thomas Zimmermann <tzimmermann@suse.de> > Cc: Jason Ekstrand <jason@jlekstrand.net> > Cc: Christian König <christian.koenig@amd.com> > Cc: Lionel Landwerlin <lionel.g.landwerlin@intel.com> > Cc: Chunming Zhou <david1.zhou@amd.com> > Cc: David Airlie <airlied@linux.ie> > Cc: Daniel Vetter <daniel@ffwll.ch> > Cc: Shuah Khan <shuah@kernel.org> > Cc: dri-devel@lists.freedesktop.org > Signed-off-by: John Stultz <jstultz@google.com> > --- > .../drivers/gpu/drm_syncobj/Makefile | 11 + > .../drivers/gpu/drm_syncobj/ioctl-helper.c | 85 ++++ > .../drivers/gpu/drm_syncobj/ioctl-helper.h | 74 ++++ > .../drivers/gpu/drm_syncobj/syncobj-test.c | 410 ++++++++++++++++++ DRM userspace selftests usually go either into libdrm or igt and not into the kernel source. If you want to make in kernel self tests they should go into drivers/gpu/drm/selftests/ Regards, Christian. > 4 files changed, 580 insertions(+) > create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile > create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c > create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h > create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c > > diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile b/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile > new file mode 100644 > index 000000000000..6576d9b2006c > --- /dev/null > +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile > @@ -0,0 +1,11 @@ > +# SPDX-License-Identifier: GPL-2.0 > +CFLAGS += -I/usr/include/libdrm/ > +LDFLAGS += -pthread -ldrm > + > +TEST_GEN_FILES= syncobj-test > + > +include ../../../lib.mk > + > +$(OUTPUT)/syncobj-test: syncobj-test.c ioctl-helper.c > +EXTRA_CLEAN = $(OUTPUT)/ioctl-helper.o > + > diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c > new file mode 100644 > index 000000000000..e5c59c9bed36 > --- /dev/null > +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c > @@ -0,0 +1,85 @@ > +// SPDX-License-Identifier: MIT > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdint.h> > +#include <string.h> > +#include <unistd.h> > +#include <time.h> > +#include <errno.h> > +#include <libdrm/drm.h> > +#include <xf86drm.h> > +#include "ioctl-helper.h" > + > +#ifndef DRM_CAP_SYNCOBJ_TIMELINE > +/* > + * The following is nabbed from libdrm's xf86drm.c as the > + * installed libdrm doesn't yet include these definitions > + * > + * > + * \author Rickard E. (Rik) Faith <faith@valinux.com> > + * \author Kevin E. Martin <martin@valinux.com> > + */ > +/* > + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. > + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. > + * All Rights Reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + */ > +int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, > + uint64_t *points, uint32_t handle_count) > +{ > + struct drm_syncobj_timeline_array args; > + int ret; > + > + memset(&args, 0, sizeof(args)); > + args.handles = (uintptr_t)handles; > + args.points = (uintptr_t)points; > + args.count_handles = handle_count; > + > + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args); > + return ret; > +} > + > +int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, > + unsigned int num_handles, > + int64_t timeout_nsec, unsigned int flags, > + uint32_t *first_signaled) > +{ > + struct drm_syncobj_timeline_wait args; > + int ret; > + > + memset(&args, 0, sizeof(args)); > + args.handles = (uintptr_t)handles; > + args.points = (uintptr_t)points; > + args.timeout_nsec = timeout_nsec; > + args.count_handles = num_handles; > + args.flags = flags; > + > + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args); > + if (ret < 0) > + return -errno; > + > + if (first_signaled) > + *first_signaled = args.first_signaled; > + return ret; > +} > + > +#endif > diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h > new file mode 100644 > index 000000000000..b0c1025034b5 > --- /dev/null > +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h > @@ -0,0 +1,74 @@ > +/* SPDX-License-Identifier: MIT */ > +#ifndef __IOCTL_HELPER_H__ > +#define __IOCTL_HELPER_H__ > + > +/* Bits pulled from libdrm's include/drm/drm.h */ > +#ifndef DRM_CAP_SYNCOBJ_TIMELINE > +/* > + * Header for the Direct Rendering Manager > + * > + * Author: Rickard E. (Rik) Faith <faith@valinux.com> > + * > + * Acknowledgments: > + * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg. > + */ > + > +/* > + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. > + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. > + * All rights reserved. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + */ > +struct drm_syncobj_timeline_wait { > + __u64 handles; > + /* wait on specific timeline point for every handles*/ > + __u64 points; > + /* absolute timeout */ > + __s64 timeout_nsec; > + __u32 count_handles; > + __u32 flags; > + __u32 first_signaled; /* only valid when not waiting all */ > + __u32 pad; > +}; > + > + > +#define DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED (1 << 0) > +struct drm_syncobj_timeline_array { > + __u64 handles; > + __u64 points; > + __u32 count_handles; > + __u32 flags; > +}; > + > +#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) > +#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) > +#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) > +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) > + > +int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, > + uint64_t *points, uint32_t handle_count); > +int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, > + unsigned int num_handles, > + int64_t timeout_nsec, unsigned int flags, > + uint32_t *first_signaled); > +#endif > +#endif /*__IOCTL_HELPER_H__*/ > + > diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c b/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c > new file mode 100644 > index 000000000000..21474b0d3b9e > --- /dev/null > +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c > @@ -0,0 +1,410 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * This test exercises basic syncobj ioctl interfaces from > + * userland via the libdrm helpers. > + * > + * Copyright (C) 2022, Google LLC. > + * > + * Currently covers trivial use of: > + * DRM_IOCTL_SYNCOBJ_CREATE > + * DRM_IOCTL_SYNCOBJ_DESTROY > + * DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD > + * DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE > + * DRM_IOCTL_SYNCOBJ_WAIT > + * DRM_IOCTL_SYNCOBJ_RESET > + * DRM_IOCTL_SYNCOBJ_SIGNAL > + * DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT > + * DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL > + * > + * TODO: Need coverage for the following ioctls: > + * DRM_IOCTL_SYNCOBJ_QUERY > + * DRM_IOCTL_SYNCOBJ_TRANSFER > + * As well as more complicated use of interface (like > + * signal/wait with multiple handles, etc), and sync_file > + * import/export. > + */ > +#include <dirent.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdint.h> > +#include <string.h> > +#include <unistd.h> > +#include <time.h> > +#include <poll.h> > +#include <sys/ioctl.h> > +#include <sys/mman.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <pthread.h> > +#include <linux/dma-buf.h> > +#include <libdrm/drm.h> > +#include <xf86drm.h> > + > +#include "ioctl-helper.h" > + > + > +#define NSEC_PER_SEC 1000000000ULL > +static uint64_t get_abs_timeout(uint64_t rel_nsec) > +{ > + struct timespec ts; > + uint64_t ns; > + > + clock_gettime(CLOCK_MONOTONIC, &ts); > + ns = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; > + ns += rel_nsec; > + return ns; > +} > + > +struct test_arg { > + int dev_fd; > + uint32_t handle; > + int handle_fd; > +}; > +#define TEST_TIMES 5 > + > +void *syncobj_signal_reset(void *arg) > +{ > + struct test_arg *d = (struct test_arg *)arg; > + int ret; > + int i; > + > + for (i = 0; i < TEST_TIMES; i++) { > + sleep(3); > + printf("%s: sending signal!\n", __func__); > + ret = drmSyncobjSignal(d->dev_fd, &d->handle, 1); > + if (ret) > + printf("Signal failed %i\n", ret); > + } > + return NULL; > +} > + > +static int syncobj_wait_reset(struct test_arg *d) > +{ > + uint64_t abs_timeout; > + int ret; > + int i; > + > + for (i = 0; i < TEST_TIMES; i++) { > + abs_timeout = get_abs_timeout(10*NSEC_PER_SEC); > + printf("%s calling drmSyncobjWait\n", __func__); > + ret = drmSyncobjWait(d->dev_fd, &d->handle, 1, abs_timeout, > + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, > + NULL); > + if (ret) { > + printf("Error: syncobjwait failed %i\n", ret); > + break; > + } > + printf("%s: drmSyncobjWait returned!\n", __func__); > + > + ret = drmSyncobjReset(d->dev_fd, &d->handle, 1); > + if (ret) { > + printf("Error: syncobjreset failed\n"); > + break; > + } > + } > + return ret; > +} > + > +void *syncobj_signal_timeline(void *arg) > +{ > + struct test_arg *d = (struct test_arg *)arg; > + uint64_t point = 0; > + int ret; > + > + for (point = 0; point <= (TEST_TIMES-1)*5; point++) { > + sleep(1); > + printf("%s: sending signal %lld!\n", __func__, point); > + ret = drmSyncobjTimelineSignal(d->dev_fd, &d->handle, &point, 1); > + if (ret) > + printf("Signal failed %i\n", ret); > + } > + return NULL; > +} > + > + > +int syncobj_timeline_wait(struct test_arg *d) > +{ > + uint64_t abs_timeout; > + uint64_t point; > + int ret; > + int i; > + > + for (i = 0; i < TEST_TIMES; i++) { > + abs_timeout = get_abs_timeout(10*NSEC_PER_SEC); > + > + point = i * 5; > + printf("%s: drmSyncobjTimelineWait waiting on %lld!\n", __func__, point); > + ret = drmSyncobjTimelineWait(d->dev_fd, &d->handle, &point, 1, > + abs_timeout, > + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, > + NULL); > + if (ret) { > + printf("Error: syncobjwait failed %i\n", ret); > + return ret; > + } > + printf("%s: drmSyncobjTimelineWait got %lld!\n", __func__, point); > + } > + return 0; > +} > + > + > +static int test_thread_signal_wait(int devfd, void *(*signal_fn)(void *), > + int (*wait_fn)(struct test_arg *)) > +{ > + uint32_t handle; > + struct test_arg d; > + pthread_t pth; > + int ret; > + > + ret = drmSyncobjCreate(devfd, 0, &handle); > + if (ret) { > + printf("Error: Couldn't create syncobj\n"); > + return ret; > + } > + > + d.dev_fd = devfd; > + d.handle = handle; > + > + pthread_create(&pth, 0, signal_fn, &d); > + ret = wait_fn(&d); > + pthread_join(pth, NULL); > + drmSyncobjDestroy(devfd, handle); > + > + return ret; > +} > + > +static int test_fork_signal_wait(int devfd, void *(*signal_fn)(void *), > + int (*wait_fn)(struct test_arg *)) > +{ > + uint32_t handle; > + struct test_arg p, c; > + pid_t id; > + int ret; > + > + ret = drmSyncobjCreate(devfd, 0, &handle); > + if (ret) { > + printf("Error: Couldn't create syncobj\n"); > + return ret; > + } > + > + p.dev_fd = devfd; > + p.handle = 0; > + p.handle_fd = 0; > + c = p; > + p.handle = handle; > + > + ret = drmSyncobjHandleToFD(devfd, handle, &c.handle_fd); > + if (ret) { > + printf("Error: Couldn't convert handle to fd\n"); > + goto out; > + } > + > + id = fork(); > + if (id == 0) { > + ret = drmSyncobjFDToHandle(c.dev_fd, c.handle_fd, &c.handle); > + if (ret) { > + printf("Error: Couldn't convert fd to handle\n"); > + exit(-1); > + } > + close(c.handle_fd); > + signal_fn((void *)&c); > + exit(0); > + } else { > + ret = wait_fn(&p); > + waitpid(id, 0, 0); > + } > + > +out: > + if (c.handle_fd) > + close(c.handle_fd); > + drmSyncobjDestroy(devfd, handle); > + > + return ret; > +} > + > + > +static int test_badparameters(int devfd) > +{ > + uint32_t handle1, handle2; > + int ret, fail = 0; > + > + /* create bad fd */ > + ret = drmSyncobjCreate(-1, 0, &handle1); > + if (!ret || errno != EBADF) { > + printf("drmSyncobjCreate - bad fd fails! (%i != EBADF)\n", errno); > + fail = 1; > + } > + /* destroy bad fd */ > + ret = drmSyncobjDestroy(-1, handle1); > + if (!ret || errno != EBADF) { > + printf("drmSyncobjDestroy - bad fd fails! (%i != EBADF)\n", errno); > + fail = 1; > + } > + > + /* TODO: Bad flags */ > + > + ret = drmSyncobjCreate(devfd, 0, &handle1); > + if (ret) { > + printf("drmSyncobjCreate - unexpected failure!\n"); > + fail = 1; > + } > + > + /* Destroy zeroed handle */ > + handle2 = 0; > + ret = drmSyncobjDestroy(devfd, handle2); > + if (!ret || errno != EINVAL) { > + printf("drmSyncobjDestroy - zero'ed handle! (%i != EINVAL)\n", errno); > + fail = 1; > + } > + /* Destroy invalid handle */ > + handle2 = -1; > + ret = drmSyncobjDestroy(devfd, handle2); > + if (!ret || errno != EINVAL) { > + printf("drmSyncobjDestroy - invalid handle! (%i != EINVAL)\n", errno); > + fail = 1; > + } > + > + /* invalid timeouts */ > + ret = drmSyncobjWait(devfd, &handle1, 1, 1000, > + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, > + NULL); > + if (!ret || errno != ETIME) { > + printf("drmSyncobjWait - invalid timeout (relative)! (%i != ETIME)\n", errno); > + fail = 1; > + } > + > + ret = drmSyncobjWait(devfd, &handle1, 1, -1, > + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, > + NULL); > + if (!ret || errno != ETIME) { > + printf("drmSyncobjWait - invalid timeout (-1)! (%i != ETIME)\n", errno); > + fail = 1; > + } > + > + ret = drmSyncobjDestroy(devfd, handle1); > + if (ret) { > + printf("drmSyncobjDestroy - unexpected failure!\n"); > + fail = 1; > + } > + > + > + return fail; > +} > + > + > +#define NAME_LEN 16 > +static int check_device(int fd) > +{ > + drm_version_t version = { 0 }; > + char name[NAME_LEN]; > + uint32_t handle; > + int ret; > + > + memset(name, 0, NAME_LEN); > + version.name_len = NAME_LEN; > + version.name = name; > + > + ret = ioctl(fd, DRM_IOCTL_VERSION, &version); > + if (ret) > + return -1; > + > + printf("%s name: %s\n", __func__, name); > + > + ret = drmSyncobjCreate(fd, 0, &handle); > + if (!ret) { > + drmSyncobjDestroy(fd, handle); > + printf("%s selected: %s\n", __func__, name); > + } > + return ret; > +} > + > +static int find_device(void) > +{ > + int i, fd; > + const char *drmstr = "/dev/dri/card"; > + > + fd = -1; > + for (i = 0; i < 16; i++) { > + char name[80]; > + > + snprintf(name, 80, "%s%u", drmstr, i); > + > + fd = open(name, O_RDWR); > + if (fd < 0) > + continue; > + > + if (check_device(fd)) { > + close(fd); > + fd = -1; > + continue; > + } else { > + break; > + } > + } > + return fd; > +} > + > +int main(int argc, char **argv) > +{ > + int devfd = find_device(); > + char *testname; > + int ret; > + > + if (devfd < 0) { > + printf("Error: Couldn't find supported drm device\n"); > + return devfd; > + } > + > + testname = "Bad parameters test"; > + printf("\n%s\n", testname); > + printf("===================\n"); > + ret = test_badparameters(devfd); > + if (ret) > + printf("%s: FAILED\n", testname); > + else > + printf("%s: PASSED\n", testname); > + > + > + testname = "Threaded reset test"; > + printf("\n%s\n", testname); > + printf("===================\n"); > + ret = test_thread_signal_wait(devfd, &syncobj_signal_reset, &syncobj_wait_reset); > + if (ret) > + printf("%s: FAILED\n", testname); > + else > + printf("%s: PASSED\n", testname); > + > + testname = "Threaded timeline test"; > + printf("\n%s\n", testname); > + printf("===================\n"); > + ret = test_thread_signal_wait(devfd, &syncobj_signal_timeline, &syncobj_timeline_wait); > + if (ret) > + printf("%s: FAILED\n", testname); > + else > + printf("%s: PASSED\n", testname); > + > + > + testname = "Forked reset test"; > + printf("\n%s\n", testname); > + printf("===================\n"); > + ret = test_fork_signal_wait(devfd, &syncobj_signal_reset, &syncobj_wait_reset); > + if (ret) > + printf("\n%s: FAILED\n", testname); > + else > + printf("\n%s: PASSED\n", testname); > + > + testname = "Forked timeline test"; > + printf("\n%s\n", testname); > + printf("===================\n"); > + ret = test_fork_signal_wait(devfd, &syncobj_signal_timeline, &syncobj_timeline_wait); > + if (ret) > + printf("\n%s: FAILED\n", testname); > + else > + printf("\n%s: PASSED\n", testname); > + > + > + close(devfd); > + return 0; > +}
On Tue, Jul 12, 2022 at 12:43 AM Christian König <christian.koenig@amd.com> wrote: > Am 12.07.22 um 06:22 schrieb John Stultz: > > An initial pass at a drm_syncobj API test. > > > > Currently covers trivial use of: > > DRM_IOCTL_SYNCOBJ_CREATE > > DRM_IOCTL_SYNCOBJ_DESTROY > > DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD > > DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE > > DRM_IOCTL_SYNCOBJ_WAIT > > DRM_IOCTL_SYNCOBJ_RESET > > DRM_IOCTL_SYNCOBJ_SIGNAL > > DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT > > DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL > > > > And demonstrates how the userspace API can be used, along with > > some fairly simple bad parameter checking. > > > > The patch includes a few helpers taken from libdrm, as at least > > on the VM I was testing with, I didn't have a new enough libdrm > > to support the *_TIMELINE_* ioctls. Ideally the ioctl-helper bits > > can be dropped at a later time. > > > > Feedback would be appreciated! > > DRM userspace selftests usually go either into libdrm or igt and not > into the kernel source. Appreciate the pointer, I'll rework and submit to one of those projects. thanks -john
On Tue, Jul 12, 2022 at 08:52:53AM -0700, John Stultz wrote: > On Tue, Jul 12, 2022 at 12:43 AM Christian König > <christian.koenig@amd.com> wrote: > > Am 12.07.22 um 06:22 schrieb John Stultz: > > > An initial pass at a drm_syncobj API test. > > > > > > Currently covers trivial use of: > > > DRM_IOCTL_SYNCOBJ_CREATE > > > DRM_IOCTL_SYNCOBJ_DESTROY > > > DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD > > > DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE > > > DRM_IOCTL_SYNCOBJ_WAIT > > > DRM_IOCTL_SYNCOBJ_RESET > > > DRM_IOCTL_SYNCOBJ_SIGNAL > > > DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT > > > DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL > > > > > > And demonstrates how the userspace API can be used, along with > > > some fairly simple bad parameter checking. > > > > > > The patch includes a few helpers taken from libdrm, as at least > > > on the VM I was testing with, I didn't have a new enough libdrm > > > to support the *_TIMELINE_* ioctls. Ideally the ioctl-helper bits > > > can be dropped at a later time. > > > > > > Feedback would be appreciated! > > > > DRM userspace selftests usually go either into libdrm or igt and not > > into the kernel source. > > Appreciate the pointer, I'll rework and submit to one of those projects. https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#testing-and-validation There should be already a ton of syncobj tests, so probably more just work needed to make them work on vgem so we can test them all without a suitable hw driver loaded. Cheers, Daniel
diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile b/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile new file mode 100644 index 000000000000..6576d9b2006c --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS += -I/usr/include/libdrm/ +LDFLAGS += -pthread -ldrm + +TEST_GEN_FILES= syncobj-test + +include ../../../lib.mk + +$(OUTPUT)/syncobj-test: syncobj-test.c ioctl-helper.c +EXTRA_CLEAN = $(OUTPUT)/ioctl-helper.o + diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c new file mode 100644 index 000000000000..e5c59c9bed36 --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> +#include <libdrm/drm.h> +#include <xf86drm.h> +#include "ioctl-helper.h" + +#ifndef DRM_CAP_SYNCOBJ_TIMELINE +/* + * The following is nabbed from libdrm's xf86drm.c as the + * installed libdrm doesn't yet include these definitions + * + * + * \author Rickard E. (Rik) Faith <faith@valinux.com> + * \author Kevin E. Martin <martin@valinux.com> + */ +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, + uint64_t *points, uint32_t handle_count) +{ + struct drm_syncobj_timeline_array args; + int ret; + + memset(&args, 0, sizeof(args)); + args.handles = (uintptr_t)handles; + args.points = (uintptr_t)points; + args.count_handles = handle_count; + + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args); + return ret; +} + +int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, + unsigned int num_handles, + int64_t timeout_nsec, unsigned int flags, + uint32_t *first_signaled) +{ + struct drm_syncobj_timeline_wait args; + int ret; + + memset(&args, 0, sizeof(args)); + args.handles = (uintptr_t)handles; + args.points = (uintptr_t)points; + args.timeout_nsec = timeout_nsec; + args.count_handles = num_handles; + args.flags = flags; + + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args); + if (ret < 0) + return -errno; + + if (first_signaled) + *first_signaled = args.first_signaled; + return ret; +} + +#endif diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h new file mode 100644 index 000000000000..b0c1025034b5 --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __IOCTL_HELPER_H__ +#define __IOCTL_HELPER_H__ + +/* Bits pulled from libdrm's include/drm/drm.h */ +#ifndef DRM_CAP_SYNCOBJ_TIMELINE +/* + * Header for the Direct Rendering Manager + * + * Author: Rickard E. (Rik) Faith <faith@valinux.com> + * + * Acknowledgments: + * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg. + */ + +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +struct drm_syncobj_timeline_wait { + __u64 handles; + /* wait on specific timeline point for every handles*/ + __u64 points; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + + +#define DRM_SYNCOBJ_QUERY_FLAGS_LAST_SUBMITTED (1 << 0) +struct drm_syncobj_timeline_array { + __u64 handles; + __u64 points; + __u32 count_handles; + __u32 flags; +}; + +#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) +#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) +#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) + +int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, + uint64_t *points, uint32_t handle_count); +int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, + unsigned int num_handles, + int64_t timeout_nsec, unsigned int flags, + uint32_t *first_signaled); +#endif +#endif /*__IOCTL_HELPER_H__*/ + diff --git a/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c b/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c new file mode 100644 index 000000000000..21474b0d3b9e --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This test exercises basic syncobj ioctl interfaces from + * userland via the libdrm helpers. + * + * Copyright (C) 2022, Google LLC. + * + * Currently covers trivial use of: + * DRM_IOCTL_SYNCOBJ_CREATE + * DRM_IOCTL_SYNCOBJ_DESTROY + * DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD + * DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE + * DRM_IOCTL_SYNCOBJ_WAIT + * DRM_IOCTL_SYNCOBJ_RESET + * DRM_IOCTL_SYNCOBJ_SIGNAL + * DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT + * DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL + * + * TODO: Need coverage for the following ioctls: + * DRM_IOCTL_SYNCOBJ_QUERY + * DRM_IOCTL_SYNCOBJ_TRANSFER + * As well as more complicated use of interface (like + * signal/wait with multiple handles, etc), and sync_file + * import/export. + */ +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <pthread.h> +#include <linux/dma-buf.h> +#include <libdrm/drm.h> +#include <xf86drm.h> + +#include "ioctl-helper.h" + + +#define NSEC_PER_SEC 1000000000ULL +static uint64_t get_abs_timeout(uint64_t rel_nsec) +{ + struct timespec ts; + uint64_t ns; + + clock_gettime(CLOCK_MONOTONIC, &ts); + ns = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; + ns += rel_nsec; + return ns; +} + +struct test_arg { + int dev_fd; + uint32_t handle; + int handle_fd; +}; +#define TEST_TIMES 5 + +void *syncobj_signal_reset(void *arg) +{ + struct test_arg *d = (struct test_arg *)arg; + int ret; + int i; + + for (i = 0; i < TEST_TIMES; i++) { + sleep(3); + printf("%s: sending signal!\n", __func__); + ret = drmSyncobjSignal(d->dev_fd, &d->handle, 1); + if (ret) + printf("Signal failed %i\n", ret); + } + return NULL; +} + +static int syncobj_wait_reset(struct test_arg *d) +{ + uint64_t abs_timeout; + int ret; + int i; + + for (i = 0; i < TEST_TIMES; i++) { + abs_timeout = get_abs_timeout(10*NSEC_PER_SEC); + printf("%s calling drmSyncobjWait\n", __func__); + ret = drmSyncobjWait(d->dev_fd, &d->handle, 1, abs_timeout, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, + NULL); + if (ret) { + printf("Error: syncobjwait failed %i\n", ret); + break; + } + printf("%s: drmSyncobjWait returned!\n", __func__); + + ret = drmSyncobjReset(d->dev_fd, &d->handle, 1); + if (ret) { + printf("Error: syncobjreset failed\n"); + break; + } + } + return ret; +} + +void *syncobj_signal_timeline(void *arg) +{ + struct test_arg *d = (struct test_arg *)arg; + uint64_t point = 0; + int ret; + + for (point = 0; point <= (TEST_TIMES-1)*5; point++) { + sleep(1); + printf("%s: sending signal %lld!\n", __func__, point); + ret = drmSyncobjTimelineSignal(d->dev_fd, &d->handle, &point, 1); + if (ret) + printf("Signal failed %i\n", ret); + } + return NULL; +} + + +int syncobj_timeline_wait(struct test_arg *d) +{ + uint64_t abs_timeout; + uint64_t point; + int ret; + int i; + + for (i = 0; i < TEST_TIMES; i++) { + abs_timeout = get_abs_timeout(10*NSEC_PER_SEC); + + point = i * 5; + printf("%s: drmSyncobjTimelineWait waiting on %lld!\n", __func__, point); + ret = drmSyncobjTimelineWait(d->dev_fd, &d->handle, &point, 1, + abs_timeout, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, + NULL); + if (ret) { + printf("Error: syncobjwait failed %i\n", ret); + return ret; + } + printf("%s: drmSyncobjTimelineWait got %lld!\n", __func__, point); + } + return 0; +} + + +static int test_thread_signal_wait(int devfd, void *(*signal_fn)(void *), + int (*wait_fn)(struct test_arg *)) +{ + uint32_t handle; + struct test_arg d; + pthread_t pth; + int ret; + + ret = drmSyncobjCreate(devfd, 0, &handle); + if (ret) { + printf("Error: Couldn't create syncobj\n"); + return ret; + } + + d.dev_fd = devfd; + d.handle = handle; + + pthread_create(&pth, 0, signal_fn, &d); + ret = wait_fn(&d); + pthread_join(pth, NULL); + drmSyncobjDestroy(devfd, handle); + + return ret; +} + +static int test_fork_signal_wait(int devfd, void *(*signal_fn)(void *), + int (*wait_fn)(struct test_arg *)) +{ + uint32_t handle; + struct test_arg p, c; + pid_t id; + int ret; + + ret = drmSyncobjCreate(devfd, 0, &handle); + if (ret) { + printf("Error: Couldn't create syncobj\n"); + return ret; + } + + p.dev_fd = devfd; + p.handle = 0; + p.handle_fd = 0; + c = p; + p.handle = handle; + + ret = drmSyncobjHandleToFD(devfd, handle, &c.handle_fd); + if (ret) { + printf("Error: Couldn't convert handle to fd\n"); + goto out; + } + + id = fork(); + if (id == 0) { + ret = drmSyncobjFDToHandle(c.dev_fd, c.handle_fd, &c.handle); + if (ret) { + printf("Error: Couldn't convert fd to handle\n"); + exit(-1); + } + close(c.handle_fd); + signal_fn((void *)&c); + exit(0); + } else { + ret = wait_fn(&p); + waitpid(id, 0, 0); + } + +out: + if (c.handle_fd) + close(c.handle_fd); + drmSyncobjDestroy(devfd, handle); + + return ret; +} + + +static int test_badparameters(int devfd) +{ + uint32_t handle1, handle2; + int ret, fail = 0; + + /* create bad fd */ + ret = drmSyncobjCreate(-1, 0, &handle1); + if (!ret || errno != EBADF) { + printf("drmSyncobjCreate - bad fd fails! (%i != EBADF)\n", errno); + fail = 1; + } + /* destroy bad fd */ + ret = drmSyncobjDestroy(-1, handle1); + if (!ret || errno != EBADF) { + printf("drmSyncobjDestroy - bad fd fails! (%i != EBADF)\n", errno); + fail = 1; + } + + /* TODO: Bad flags */ + + ret = drmSyncobjCreate(devfd, 0, &handle1); + if (ret) { + printf("drmSyncobjCreate - unexpected failure!\n"); + fail = 1; + } + + /* Destroy zeroed handle */ + handle2 = 0; + ret = drmSyncobjDestroy(devfd, handle2); + if (!ret || errno != EINVAL) { + printf("drmSyncobjDestroy - zero'ed handle! (%i != EINVAL)\n", errno); + fail = 1; + } + /* Destroy invalid handle */ + handle2 = -1; + ret = drmSyncobjDestroy(devfd, handle2); + if (!ret || errno != EINVAL) { + printf("drmSyncobjDestroy - invalid handle! (%i != EINVAL)\n", errno); + fail = 1; + } + + /* invalid timeouts */ + ret = drmSyncobjWait(devfd, &handle1, 1, 1000, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, + NULL); + if (!ret || errno != ETIME) { + printf("drmSyncobjWait - invalid timeout (relative)! (%i != ETIME)\n", errno); + fail = 1; + } + + ret = drmSyncobjWait(devfd, &handle1, 1, -1, + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, + NULL); + if (!ret || errno != ETIME) { + printf("drmSyncobjWait - invalid timeout (-1)! (%i != ETIME)\n", errno); + fail = 1; + } + + ret = drmSyncobjDestroy(devfd, handle1); + if (ret) { + printf("drmSyncobjDestroy - unexpected failure!\n"); + fail = 1; + } + + + return fail; +} + + +#define NAME_LEN 16 +static int check_device(int fd) +{ + drm_version_t version = { 0 }; + char name[NAME_LEN]; + uint32_t handle; + int ret; + + memset(name, 0, NAME_LEN); + version.name_len = NAME_LEN; + version.name = name; + + ret = ioctl(fd, DRM_IOCTL_VERSION, &version); + if (ret) + return -1; + + printf("%s name: %s\n", __func__, name); + + ret = drmSyncobjCreate(fd, 0, &handle); + if (!ret) { + drmSyncobjDestroy(fd, handle); + printf("%s selected: %s\n", __func__, name); + } + return ret; +} + +static int find_device(void) +{ + int i, fd; + const char *drmstr = "/dev/dri/card"; + + fd = -1; + for (i = 0; i < 16; i++) { + char name[80]; + + snprintf(name, 80, "%s%u", drmstr, i); + + fd = open(name, O_RDWR); + if (fd < 0) + continue; + + if (check_device(fd)) { + close(fd); + fd = -1; + continue; + } else { + break; + } + } + return fd; +} + +int main(int argc, char **argv) +{ + int devfd = find_device(); + char *testname; + int ret; + + if (devfd < 0) { + printf("Error: Couldn't find supported drm device\n"); + return devfd; + } + + testname = "Bad parameters test"; + printf("\n%s\n", testname); + printf("===================\n"); + ret = test_badparameters(devfd); + if (ret) + printf("%s: FAILED\n", testname); + else + printf("%s: PASSED\n", testname); + + + testname = "Threaded reset test"; + printf("\n%s\n", testname); + printf("===================\n"); + ret = test_thread_signal_wait(devfd, &syncobj_signal_reset, &syncobj_wait_reset); + if (ret) + printf("%s: FAILED\n", testname); + else + printf("%s: PASSED\n", testname); + + testname = "Threaded timeline test"; + printf("\n%s\n", testname); + printf("===================\n"); + ret = test_thread_signal_wait(devfd, &syncobj_signal_timeline, &syncobj_timeline_wait); + if (ret) + printf("%s: FAILED\n", testname); + else + printf("%s: PASSED\n", testname); + + + testname = "Forked reset test"; + printf("\n%s\n", testname); + printf("===================\n"); + ret = test_fork_signal_wait(devfd, &syncobj_signal_reset, &syncobj_wait_reset); + if (ret) + printf("\n%s: FAILED\n", testname); + else + printf("\n%s: PASSED\n", testname); + + testname = "Forked timeline test"; + printf("\n%s\n", testname); + printf("===================\n"); + ret = test_fork_signal_wait(devfd, &syncobj_signal_timeline, &syncobj_timeline_wait); + if (ret) + printf("\n%s: FAILED\n", testname); + else + printf("\n%s: PASSED\n", testname); + + + close(devfd); + return 0; +}
An initial pass at a drm_syncobj API test. Currently covers trivial use of: DRM_IOCTL_SYNCOBJ_CREATE DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOCTL_SYNCOBJ_WAIT DRM_IOCTL_SYNCOBJ_RESET DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL And demonstrates how the userspace API can be used, along with some fairly simple bad parameter checking. The patch includes a few helpers taken from libdrm, as at least on the VM I was testing with, I didn't have a new enough libdrm to support the *_TIMELINE_* ioctls. Ideally the ioctl-helper bits can be dropped at a later time. Feedback would be appreciated! Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Thomas Zimmermann <tzimmermann@suse.de> Cc: Jason Ekstrand <jason@jlekstrand.net> Cc: Christian König <christian.koenig@amd.com> Cc: Lionel Landwerlin <lionel.g.landwerlin@intel.com> Cc: Chunming Zhou <david1.zhou@amd.com> Cc: David Airlie <airlied@linux.ie> Cc: Daniel Vetter <daniel@ffwll.ch> Cc: Shuah Khan <shuah@kernel.org> Cc: dri-devel@lists.freedesktop.org Signed-off-by: John Stultz <jstultz@google.com> --- .../drivers/gpu/drm_syncobj/Makefile | 11 + .../drivers/gpu/drm_syncobj/ioctl-helper.c | 85 ++++ .../drivers/gpu/drm_syncobj/ioctl-helper.h | 74 ++++ .../drivers/gpu/drm_syncobj/syncobj-test.c | 410 ++++++++++++++++++ 4 files changed, 580 insertions(+) create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/Makefile create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.c create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/ioctl-helper.h create mode 100644 tools/testing/selftests/drivers/gpu/drm_syncobj/syncobj-test.c