* [PATCH i-g-t] igt: add timeline test cases v2
@ 2018-12-11 10:37 Chunming Zhou
2018-12-11 11:18 ` ✓ Fi.CI.BAT: success for igt: add timeline test cases (rev4) Patchwork
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Chunming Zhou @ 2018-12-11 10:37 UTC (permalink / raw)
To: Christian.Koenig, dri-devel, amd-gfx, intel-gfx
v2: adapt to new transfer ioctl
Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
---
include/drm-uapi/drm.h | 33 ++
lib/igt_syncobj.c | 206 ++++++++
lib/igt_syncobj.h | 19 +
tests/meson.build | 1 +
tests/syncobj_timeline.c | 1032 ++++++++++++++++++++++++++++++++++++++
5 files changed, 1291 insertions(+)
create mode 100644 tests/syncobj_timeline.c
diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
index 85c685a2..dcd245d9 100644
--- a/include/drm-uapi/drm.h
+++ b/include/drm-uapi/drm.h
@@ -731,6 +731,8 @@ struct drm_syncobj_handle {
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0)
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1)
+/* wait for time point to become available */
+#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2)
struct drm_syncobj_wait {
__u64 handles;
/* absolute timeout */
@@ -741,11 +743,38 @@ struct drm_syncobj_wait {
__u32 pad;
};
+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;
+};
+
struct drm_syncobj_array {
__u64 handles;
__u32 count_handles;
__u32 pad;
};
+struct drm_syncobj_timeline_array {
+ __u64 handles;
+ __u64 points;
+ __u32 count_handles;
+ __u32 pad;
+};
+
+struct drm_syncobj_transfer {
+ __u32 src_handle;
+ __u32 dst_handle;
+ __u64 src_point;
+ __u64 dst_point;
+ __u32 flags;
+ __u32 pad;
+};
/* Query current scanout sequence number */
struct drm_crtc_get_sequence {
@@ -902,6 +931,10 @@ extern "C" {
#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees)
#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease)
+#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)
/**
* Device specific ioctls should only be in their respective headers
diff --git a/lib/igt_syncobj.c b/lib/igt_syncobj.c
index d9114ca8..efa2adc4 100644
--- a/lib/igt_syncobj.c
+++ b/lib/igt_syncobj.c
@@ -286,3 +286,209 @@ syncobj_signal(int fd, uint32_t *handles, uint32_t count)
{
igt_assert_eq(__syncobj_signal(fd, handles, count), 0);
}
+
+static int
+__syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ int err = 0;
+
+ array.handles = to_user_pointer(handles);
+ array.points = to_user_pointer(points);
+ array.count_handles = count;
+ if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array))
+ err = -errno;
+ return err;
+}
+
+/**
+ * syncobj_signal:
+ * @fd: The DRM file descriptor.
+ * @handles: Array of syncobj handles to signal
+ * @points: List of point of handles to signal.
+ * @count: Count of syncobj handles.
+ *
+ * Signal a set of syncobjs.
+ */
+void
+syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
+{
+ igt_assert_eq(__syncobj_timeline_signal(fd, handles, points, count), 0);
+}
+int
+__syncobj_timeline_wait_ioctl(int fd, struct drm_syncobj_timeline_wait *args)
+{
+ int err = 0;
+ if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, args))
+ err = -errno;
+ return err;
+}
+static int
+__syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ struct drm_syncobj_timeline_wait args;
+ int ret;
+
+ args.handles = to_user_pointer(handles);
+ args.points = (uint64_t)to_user_pointer(points);
+ args.timeout_nsec = timeout_nsec;
+ args.count_handles = num_handles;
+ args.flags = flags;
+ args.first_signaled = 0;
+ args.pad = 0;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
+ if (ret < 0)
+ return -errno;
+
+ if (first_signaled)
+ *first_signaled = args.first_signaled;
+
+ return ret;
+}
+int
+syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags)
+{
+ return __syncobj_timeline_wait(fd, handles, points, num_handles,
+ timeout_nsec, flags, NULL);
+}
+
+/**
+ * syncobj_timeline_wait:
+ * @fd: The DRM file descriptor
+ * @handles: List of syncobj handles to wait for.
+ * @points: List of point of handles to wait for.
+ * @num_handles: Count of handles
+ * @timeout_nsec: Absolute wait timeout in nanoseconds.
+ * @flags: Wait ioctl flags.
+ * @first_signaled: Returned handle for first signaled syncobj.
+ *
+ * Waits in the kernel for any/all the requested syncobjs timeline point
+ * using the timeout and flags.
+ * Returns: bool value - false = timedout, true = signaled
+ */
+bool
+syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ int ret;
+
+ ret = __syncobj_timeline_wait(fd, handles, points, num_handles,
+ timeout_nsec, flags, first_signaled);
+ if (ret == -ETIME)
+ return false;
+ igt_assert_eq(ret, 0);
+
+ return true;
+
+}
+
+static int
+__syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t handle_count)
+{
+ struct drm_syncobj_timeline_array args;
+ int ret;
+
+ args.handles = to_user_pointer(handles);
+ args.points = (uint64_t)to_user_pointer(points);
+ args.count_handles = handle_count;
+ args.pad = 0;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+/**
+ * syncobj_timeline_query:
+ * @fd: The DRM file descriptor.
+ * @handles: Array of syncobj handles.
+ * @points: Array of syncobj points queried.
+ * @count: Count of syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t count)
+{
+ igt_assert_eq(__syncobj_timeline_query(fd, handles, points, count), 0);
+}
+
+static int
+__syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle)
+{
+ struct drm_syncobj_transfer args;
+
+ args.src_handle = binary_handle;
+ args.dst_handle = timeline_handle;
+ args.src_point = 0;
+ args.dst_point = point;
+ args.flags = 0;
+ args.pad = 0;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+}
+
+/**
+ * syncobj_binary_to_timeline:
+ * @fd: The DRM file descriptor.
+ * @timeline_handles: Array of timeline syncobj handles.
+ * @point: syncobj point.
+ * @binary_handle: Array of binary syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle)
+{
+ igt_assert_eq(__syncobj_binary_to_timeline(fd, timeline_handle, point,
+ binary_handle), 0);
+}
+
+static int
+__syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags)
+{
+ struct drm_syncobj_transfer args;
+
+ args.dst_handle = binary_handle;
+ args.src_handle = timeline_handle;
+ args.dst_point = 0;
+ args.src_point = point;
+ args.flags = flags;
+ args.pad = 0;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+}
+
+/**
+ * syncobj_binary_to_timeline:
+ * @fd: The DRM file descriptor.
+ * @timeline_handles: Array of timeline syncobj handles.
+ * @point: syncobj point.
+ * @binary_handle: Array of binary syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags)
+{
+ igt_assert_eq(__syncobj_timeline_to_binary(fd, binary_handle,
+ timeline_handle, point,
+ flags), 0);
+}
diff --git a/lib/igt_syncobj.h b/lib/igt_syncobj.h
index 44d1378d..175e5027 100644
--- a/lib/igt_syncobj.h
+++ b/lib/igt_syncobj.h
@@ -65,7 +65,26 @@ int syncobj_wait_err(int fd, uint32_t *handles, uint32_t count,
bool syncobj_wait(int fd, uint32_t *handles, uint32_t count,
uint64_t abs_timeout_nsec, uint32_t flags,
uint32_t *first_signaled);
+int __syncobj_timeline_wait_ioctl(int fd,
+ struct drm_syncobj_timeline_wait *args);
+bool syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled);
+int syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags);
void syncobj_reset(int fd, uint32_t *handles, uint32_t count);
void syncobj_signal(int fd, uint32_t *handles, uint32_t count);
+void syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t count);
+void syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle);
+void syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags);
+void syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points,
+ uint32_t count);
#endif /* IGT_SYNCOBJ_H */
diff --git a/tests/meson.build b/tests/meson.build
index b8a6e61b..bddc223c 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -77,6 +77,7 @@ test_progs = [
'sw_sync',
'syncobj_basic',
'syncobj_wait',
+ 'syncobj_timeline',
'template',
'tools_test',
'v3d_get_bo_offset',
diff --git a/tests/syncobj_timeline.c b/tests/syncobj_timeline.c
new file mode 100644
index 00000000..505fd9ed
--- /dev/null
+++ b/tests/syncobj_timeline.c
@@ -0,0 +1,1032 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+#include "igt.h"
+#include "sw_sync.h"
+#include "igt_syncobj.h"
+#include <unistd.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <signal.h>
+#include "drm.h"
+
+IGT_TEST_DESCRIPTION("Tests for the drm timeline sync object API");
+
+/* One tenth of a second */
+#define SHORT_TIME_NSEC 100000000ull
+
+#define NSECS_PER_SEC 1000000000ull
+
+static uint64_t
+gettime_ns(void)
+{
+ struct timespec current;
+ clock_gettime(CLOCK_MONOTONIC, ¤t);
+ return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec;
+}
+
+static void
+sleep_nsec(uint64_t time_nsec)
+{
+ struct timespec t;
+ t.tv_sec = time_nsec / NSECS_PER_SEC;
+ t.tv_nsec = time_nsec % NSECS_PER_SEC;
+ igt_assert_eq(nanosleep(&t, NULL), 0);
+}
+
+static uint64_t
+short_timeout(void)
+{
+ return gettime_ns() + SHORT_TIME_NSEC;
+}
+
+static int
+syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point)
+{
+ struct drm_syncobj_handle;
+ uint32_t syncobj = syncobj_create(fd, 0);
+ int timeline, fence;
+
+ timeline = sw_sync_timeline_create();
+ fence = sw_sync_timeline_create_fence(timeline, 1);
+ syncobj_import_sync_file(fd, syncobj, fence);
+ syncobj_binary_to_timeline(fd, handle, point, syncobj);
+ close(fence);
+
+ syncobj_destroy(fd, syncobj);
+ return timeline;
+}
+
+static void
+syncobj_trigger(int fd, uint32_t handle, uint64_t point)
+{
+ int timeline = syncobj_attach_sw_sync(fd, handle, point);
+ sw_sync_timeline_inc(timeline, 1);
+ close(timeline);
+}
+
+static timer_t
+set_timer(void (*cb)(union sigval), void *ptr, int i, uint64_t nsec)
+{
+ timer_t timer;
+ struct sigevent sev;
+ struct itimerspec its;
+
+ memset(&sev, 0, sizeof(sev));
+ sev.sigev_notify = SIGEV_THREAD;
+ if (ptr)
+ sev.sigev_value.sival_ptr = ptr;
+ else
+ sev.sigev_value.sival_int = i;
+ sev.sigev_notify_function = cb;
+ igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
+
+ memset(&its, 0, sizeof(its));
+ its.it_value.tv_sec = nsec / NSEC_PER_SEC;
+ its.it_value.tv_nsec = nsec % NSEC_PER_SEC;
+ igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
+
+ return timer;
+}
+
+struct fd_handle_pair {
+ int fd;
+ uint32_t handle;
+ uint64_t point;
+};
+
+static void
+timeline_inc_func(union sigval sigval)
+{
+ sw_sync_timeline_inc(sigval.sival_int, 1);
+}
+
+static void
+syncobj_trigger_free_pair_func(union sigval sigval)
+{
+ struct fd_handle_pair *pair = sigval.sival_ptr;
+ syncobj_trigger(pair->fd, pair->handle, pair->point);
+ free(pair);
+}
+
+static timer_t
+syncobj_trigger_delayed(int fd, uint32_t syncobj, uint64_t point, uint64_t nsec)
+{
+ struct fd_handle_pair *pair = malloc(sizeof(*pair));
+
+ pair->fd = fd;
+ pair->handle = syncobj;
+ pair->point = syncobj;
+
+ return set_timer(syncobj_trigger_free_pair_func, pair, 0, nsec);
+}
+
+static void
+test_wait_bad_flags(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ wait.flags = 0xdeadbeef;
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
+}
+
+static void
+test_wait_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
+}
+
+static void
+test_wait_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t handle = 0;
+
+ wait.count_handles = 1;
+ wait.handles = to_user_pointer(&handle);
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ENOENT);
+}
+
+static void
+test_query_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ int ret;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_query_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_query_one_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t syncobjs[3];
+ uint64_t initial_point = 1;
+ int ret;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = 0;
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
+ syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
+ &initial_point, 1, 0, 0), 0);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
+ &initial_point, 1, 0, 0), 0);
+
+ array.count_handles = 3;
+ array.handles = to_user_pointer(syncobjs);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
+ igt_assert(ret == -1 && errno == ENOENT);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+static void
+test_query_bad_pad(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ array.pad = 0xdeadbeef;
+ array.count_handles = 1;
+ array.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ int ret;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_signal_illegal_point(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 1;
+ uint64_t point = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ args.points = to_user_pointer(&point);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+static void
+test_signal_one_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t syncobjs[3];
+ uint64_t initial_point = 1;
+ int ret;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = 0;
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
+ syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
+ &initial_point, 1, 0, 0), 0);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
+ &initial_point, 1, 0, 0), 0);
+
+ array.count_handles = 3;
+ array.handles = to_user_pointer(syncobjs);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
+ igt_assert(ret == -1 && errno == ENOENT);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+static void
+test_signal_bad_pad(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ array.pad = 0xdeadbeef;
+ array.count_handles = 1;
+ array.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_array(int fd)
+{
+ uint32_t syncobjs[4];
+ uint64_t points[4] = {1, 1, 1, 0};
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+ syncobjs[2] = syncobj_create(fd, 0);
+ syncobjs[3] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, syncobjs, points, 4);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs,
+ points, 3, 0, 0), 0);
+ igt_assert_eq(syncobj_wait_err(fd, &syncobjs[3], 1, 0, 0), 0);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+ syncobj_destroy(fd, syncobjs[2]);
+ syncobj_destroy(fd, syncobjs[3]);
+}
+
+static void
+test_transfer_illegal_handle(int fd)
+{
+ struct drm_syncobj_transfer args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.src_handle = to_user_pointer(&handle);
+ args.dst_handle = to_user_pointer(&handle);
+ args.src_point = 1;
+ args.dst_point = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_transfer_bad_pad(int fd)
+{
+ struct drm_syncobj_transfer arg = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ arg.pad = 0xdeadbeef;
+ arg.src_handle = to_user_pointer(&handle);
+ arg.dst_handle = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &arg);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+#define WAIT_FOR_SUBMIT (1 << 0)
+#define WAIT_ALL (1 << 1)
+#define WAIT_AVAILABLE (1 << 2)
+#define WAIT_UNSUBMITTED (1 << 3)
+#define WAIT_SUBMITTED (1 << 4)
+#define WAIT_SIGNALED (1 << 5)
+#define WAIT_FLAGS_MAX (1 << 6) - 1
+
+static uint32_t
+flags_for_test_flags(uint32_t test_flags)
+{
+ uint32_t flags = 0;
+
+ if (test_flags & WAIT_FOR_SUBMIT)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+
+ if (test_flags & WAIT_AVAILABLE)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
+
+ if (test_flags & WAIT_ALL)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
+
+ return flags;
+}
+
+static void
+test_single_wait(int fd, uint32_t test_flags, int expect)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = flags_for_test_flags(test_flags);
+ uint64_t point = 1;
+ int timeline = -1;
+
+ if (test_flags & (WAIT_SUBMITTED | WAIT_SIGNALED))
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+
+ if (test_flags & WAIT_SIGNALED)
+ sw_sync_timeline_inc(timeline, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ 0, flags), expect);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ short_timeout(), flags), expect);
+
+ if (expect != -ETIME) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ UINT64_MAX, flags), expect);
+ }
+
+ syncobj_destroy(fd, syncobj);
+ if (timeline != -1)
+ close(timeline);
+}
+
+static void
+test_wait_delayed_signal(int fd, uint32_t test_flags)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = flags_for_test_flags(test_flags);
+ uint64_t point = 1;
+ int timeline = -1;
+ timer_t timer;
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ timer = syncobj_trigger_delayed(fd, syncobj, point, SHORT_TIME_NSEC);
+ } else {
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+ timer = set_timer(timeline_inc_func, NULL,
+ timeline, SHORT_TIME_NSEC);
+ }
+
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
+ gettime_ns() + SHORT_TIME_NSEC * 2,
+ flags, NULL));
+
+ timer_delete(timer);
+
+ if (timeline != -1)
+ close(timeline);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_unsignaled(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_reset(fd, &syncobj, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_signaled(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+
+ syncobj_trigger(fd, syncobj, point);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), 0);
+
+ syncobj_reset(fd, &syncobj, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_multiple_signaled(int fd)
+{
+ uint64_t points[3] = {1, 1, 1};
+ uint32_t syncobjs[3];
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ syncobjs[i] = syncobj_create(fd, 0);
+ syncobj_trigger(fd, syncobjs[i], points[i]);
+ }
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, 0), 0);
+
+ syncobj_reset(fd, syncobjs, 3);
+
+ for (i = 0; i < 3; i++) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[i],
+ &points[i], 1,
+ 0, 0), -EINVAL);
+ syncobj_destroy(fd, syncobjs[i]);
+ }
+}
+
+static void
+reset_and_trigger_func(union sigval sigval)
+{
+ struct fd_handle_pair *pair = sigval.sival_ptr;
+ syncobj_reset(pair->fd, &pair->handle, 1);
+ syncobj_trigger(pair->fd, pair->handle, pair->point);
+}
+
+static void
+test_reset_during_wait_for_submit(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+ struct fd_handle_pair pair;
+ uint64_t point = 1;
+ timer_t timer;
+
+ pair.fd = fd;
+ pair.handle = syncobj;
+ timer = set_timer(reset_and_trigger_func, &pair, 0, SHORT_TIME_NSEC);
+
+ /* A reset should be a no-op even if we're in the middle of a wait */
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
+ gettime_ns() + SHORT_TIME_NSEC * 2,
+ flags, NULL));
+
+ timer_delete(timer);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_signal(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+ uint64_t point = 1;
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, flags), -ETIME);
+
+ syncobj_timeline_signal(fd, &syncobj, &point, 1);
+
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, 0, NULL));
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, flags, NULL));
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_multi_wait(int fd, uint32_t test_flags, int expect)
+{
+ uint32_t syncobjs[3];
+ uint32_t tflag, flags;
+ int i, fidx, timeline;
+ uint64_t points[3] = {1, 1, 1};
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ flags = flags_for_test_flags(test_flags);
+ test_flags &= ~(WAIT_ALL | WAIT_FOR_SUBMIT | WAIT_AVAILABLE);
+
+ for (i = 0; i < 3; i++) {
+ fidx = ffs(test_flags) - 1;
+ tflag = (1 << fidx);
+
+ if (test_flags & ~tflag)
+ test_flags &= ~tflag;
+
+ if (tflag & (WAIT_SUBMITTED | WAIT_SIGNALED))
+ timeline = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ if (tflag & WAIT_SIGNALED)
+ sw_sync_timeline_inc(timeline, 1);
+ }
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, flags), expect);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, short_timeout(),
+ flags), expect);
+
+ if (expect != -ETIME) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, UINT64_MAX,
+ flags), expect);
+ }
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+struct wait_thread_data {
+ int fd;
+ struct drm_syncobj_timeline_wait wait;
+};
+
+static void *
+wait_thread_func(void *data)
+{
+ struct wait_thread_data *wait = data;
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(wait->fd, &wait->wait), 0);
+ return NULL;
+}
+
+static void
+test_wait_snapshot(int fd, uint32_t test_flags)
+{
+ struct wait_thread_data wait = { 0 };
+ uint32_t syncobjs[2];
+ uint64_t points[2] = {1, 1};
+ int timelines[3] = { -1, -1, -1 };
+ pthread_t thread;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+
+ if (!(test_flags & WAIT_FOR_SUBMIT)) {
+ timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+ timelines[1] = syncobj_attach_sw_sync(fd, syncobjs[1], points[1]);
+ }
+
+ wait.fd = fd;
+ wait.wait.handles = to_user_pointer(syncobjs);
+ wait.wait.count_handles = 2;
+ wait.wait.points = to_user_pointer(points);
+ wait.wait.timeout_nsec = short_timeout();
+ wait.wait.flags = flags_for_test_flags(test_flags);
+
+ igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Try to fake the kernel out by triggering or partially triggering
+ * the first fence.
+ */
+ if (test_flags & WAIT_ALL) {
+ /* If it's WAIT_ALL, actually trigger it */
+ if (timelines[0] == -1)
+ syncobj_trigger(fd, syncobjs[0], points[0]);
+ else
+ sw_sync_timeline_inc(timelines[0], 1);
+ } else if (test_flags & WAIT_FOR_SUBMIT) {
+ timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+ }
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Then reset it */
+ syncobj_reset(fd, &syncobjs[0], 1);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Then "submit" it in a way that will never trigger. This way, if
+ * the kernel picks up on the new fence (it shouldn't), we'll get a
+ * timeout.
+ */
+ timelines[2] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Now trigger the second fence to complete the wait */
+
+ if (timelines[1] == -1)
+ syncobj_trigger(fd, syncobjs[1], points[1]);
+ else
+ sw_sync_timeline_inc(timelines[1], 1);
+
+ pthread_join(thread, NULL);
+
+ if (!(test_flags & WAIT_ALL))
+ igt_assert_eq(wait.wait.first_signaled, 1);
+
+ close(timelines[0]);
+ close(timelines[1]);
+ close(timelines[2]);
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+}
+
+/* The numbers 0-7, each repeated 5x and shuffled. */
+static const unsigned shuffled_0_7_x4[] = {
+ 2, 0, 6, 1, 1, 4, 5, 2, 0, 7, 1, 7, 6, 3, 4, 5,
+ 0, 2, 7, 3, 5, 4, 0, 6, 7, 3, 2, 5, 6, 1, 4, 3,
+};
+
+enum syncobj_stage {
+ STAGE_UNSUBMITTED,
+ STAGE_SUBMITTED,
+ STAGE_SIGNALED,
+ STAGE_RESET,
+ STAGE_RESUBMITTED,
+};
+
+static void
+test_wait_complex(int fd, uint32_t test_flags)
+{
+ struct wait_thread_data wait = { 0 };
+ uint32_t syncobjs[8];
+ uint64_t points[8] = {1, 1, 1, 1, 1, 1, 1, 1};
+ enum syncobj_stage stage[8];
+ int i, j, timelines[8];
+ uint32_t first_signaled = -1, num_signaled = 0;
+ pthread_t thread;
+
+ for (i = 0; i < 8; i++) {
+ stage[i] = STAGE_UNSUBMITTED;
+ syncobjs[i] = syncobj_create(fd, 0);
+ }
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ for (i = 0; i < 8; i++)
+ timelines[i] = -1;
+ } else {
+ for (i = 0; i < 8; i++)
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ }
+
+ wait.fd = fd;
+ wait.wait.handles = to_user_pointer(syncobjs);
+ wait.wait.count_handles = 2;
+ wait.wait.points = to_user_pointer(points);
+ wait.wait.timeout_nsec = gettime_ns() + NSECS_PER_SEC;
+ wait.wait.flags = flags_for_test_flags(test_flags);
+
+ igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
+
+ sleep_nsec(NSECS_PER_SEC / 50);
+
+ num_signaled = 0;
+ for (j = 0; j < ARRAY_SIZE(shuffled_0_7_x4); j++) {
+ i = shuffled_0_7_x4[j];
+ igt_assert_lt(i, ARRAY_SIZE(syncobjs));
+
+ switch (stage[i]++) {
+ case STAGE_UNSUBMITTED:
+ /* We need to submit attach a fence */
+ if (!(test_flags & WAIT_FOR_SUBMIT)) {
+ /* We had to attach one up-front */
+ igt_assert_neq(timelines[i], -1);
+ break;
+ }
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ break;
+
+ case STAGE_SUBMITTED:
+ /* We have a fence, trigger it */
+ igt_assert_neq(timelines[i], -1);
+ sw_sync_timeline_inc(timelines[i], 1);
+ close(timelines[i]);
+ timelines[i] = -1;
+ if (num_signaled == 0)
+ first_signaled = i;
+ num_signaled++;
+ break;
+
+ case STAGE_SIGNALED:
+ /* We're already signaled, reset */
+ syncobj_reset(fd, &syncobjs[i], 1);
+ break;
+
+ case STAGE_RESET:
+ /* We're reset, submit and don't signal */
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ break;
+
+ case STAGE_RESUBMITTED:
+ igt_assert(!"Should not reach this stage");
+ break;
+ }
+
+ if (test_flags & WAIT_ALL) {
+ if (num_signaled == ARRAY_SIZE(syncobjs))
+ break;
+ } else {
+ if (num_signaled > 0)
+ break;
+ }
+
+ sleep_nsec(NSECS_PER_SEC / 100);
+ }
+
+ pthread_join(thread, NULL);
+
+ if (test_flags & WAIT_ALL) {
+ igt_assert_eq(num_signaled, ARRAY_SIZE(syncobjs));
+ } else {
+ igt_assert_eq(num_signaled, 1);
+ igt_assert_eq(wait.wait.first_signaled, first_signaled);
+ }
+
+ for (i = 0; i < 8; i++) {
+ close(timelines[i]);
+ syncobj_destroy(fd, syncobjs[i]);
+ }
+}
+
+static void
+test_wait_interrupted(int fd, uint32_t test_flags)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+ int timeline;
+
+ wait.handles = to_user_pointer(&syncobj);
+ wait.points = to_user_pointer(&point);
+ wait.count_handles = 1;
+ wait.flags = flags_for_test_flags(test_flags);
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ wait.timeout_nsec = short_timeout();
+ igt_while_interruptible(true)
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
+ }
+
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+
+ wait.timeout_nsec = short_timeout();
+ igt_while_interruptible(true)
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
+
+ syncobj_destroy(fd, syncobj);
+ close(timeline);
+}
+
+static bool
+has_syncobj_timeline_wait(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t handle = 0;
+ uint64_t value;
+ int ret;
+
+ if (drmGetCap(fd, DRM_CAP_SYNCOBJ, &value))
+ return false;
+ if (!value)
+ return false;
+
+ /* Try waiting for zero sync objects should fail with EINVAL */
+ wait.count_handles = 1;
+ wait.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &wait);
+ return ret == -1 && errno == ENOENT;
+}
+
+igt_main
+{
+ int fd = -1;
+
+ igt_fixture {
+ fd = drm_open_driver(DRIVER_ANY);
+ igt_require(has_syncobj_timeline_wait(fd));
+ igt_require_sw_sync();
+ }
+
+ igt_subtest("invalid-wait-bad-flags")
+ test_wait_bad_flags(fd);
+
+ igt_subtest("invalid-wait-zero-handles")
+ test_wait_zero_handles(fd);
+
+ igt_subtest("invalid-wait-illegal-handle")
+ test_wait_illegal_handle(fd);
+
+ igt_subtest("invalid-query-zero-handles")
+ test_query_zero_handles(fd);
+
+ igt_subtest("invalid-query-illegal-handle")
+ test_query_illegal_handle(fd);
+
+ igt_subtest("invalid-query-one-illegal-handle")
+ test_query_one_illegal_handle(fd);
+
+ igt_subtest("invalid-query-bad-pad")
+ test_query_bad_pad(fd);
+
+ igt_subtest("invalid-signal-zero-handles")
+ test_signal_zero_handles(fd);
+
+ igt_subtest("invalid-signal-illegal-handle")
+ test_signal_illegal_handle(fd);
+
+ igt_subtest("invalid-signal-illegal-point")
+ test_signal_illegal_point(fd);
+
+ igt_subtest("invalid-signal-one-illegal-handle")
+ test_signal_one_illegal_handle(fd);
+
+ igt_subtest("invalid-signal-bad-pad")
+ test_signal_bad_pad(fd);
+
+ igt_subtest("invalid-signal-array")
+ test_signal_array(fd);
+
+ igt_subtest("invalid-transfer-illegal-handle")
+ test_transfer_illegal_handle(fd);
+
+ igt_subtest("invalid-transfer-bad-pad")
+ test_transfer_bad_pad(fd);
+
+ for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
+ int err;
+
+ /* Only one wait mode for single-wait tests */
+ if (__builtin_popcount(flags & (WAIT_UNSUBMITTED |
+ WAIT_SUBMITTED |
+ WAIT_SIGNALED)) != 1)
+ continue;
+
+ if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT))
+ err = -EINVAL;
+ else if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
+ err = -ETIME;
+ else
+ err = 0;
+
+ igt_subtest_f("%ssingle-wait%s%s%s%s%s%s",
+ err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
+ (flags & WAIT_ALL) ? "-all" : "",
+ (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
+ (flags & WAIT_AVAILABLE) ? "-available" : "",
+ (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
+ (flags & WAIT_SUBMITTED) ? "-submitted" : "",
+ (flags & WAIT_SIGNALED) ? "-signaled" : "")
+ test_single_wait(fd, flags, err);
+ }
+
+ igt_subtest("wait-delayed-signal")
+ test_wait_delayed_signal(fd, 0);
+
+ igt_subtest("wait-for-submit-delayed-submit")
+ test_wait_delayed_signal(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-delayed-signal")
+ test_wait_delayed_signal(fd, WAIT_ALL);
+
+ igt_subtest("wait-all-for-submit-delayed-submit")
+ test_wait_delayed_signal(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("reset-unsignaled")
+ test_reset_unsignaled(fd);
+
+ igt_subtest("reset-signaled")
+ test_reset_signaled(fd);
+
+ igt_subtest("reset-multiple-signaled")
+ test_reset_multiple_signaled(fd);
+
+ igt_subtest("reset-during-wait-for-submit")
+ test_reset_during_wait_for_submit(fd);
+
+ igt_subtest("signal")
+ test_signal(fd);
+
+ for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
+ int err;
+
+ /* At least one wait mode for multi-wait tests */
+ if (!(flags & (WAIT_UNSUBMITTED |
+ WAIT_SUBMITTED |
+ WAIT_SIGNALED)))
+ continue;
+
+ err = 0;
+ if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT)) {
+ err = -EINVAL;
+ } else if (flags & WAIT_ALL) {
+ if (flags & (WAIT_UNSUBMITTED | WAIT_SUBMITTED))
+ err = -ETIME;
+ if (!(flags & WAIT_UNSUBMITTED) && (flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE))
+ err = 0;
+ } else {
+ if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
+ err = -ETIME;
+ }
+
+ igt_subtest_f("%smulti-wait%s%s%s%s%s%s",
+ err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
+ (flags & WAIT_ALL) ? "-all" : "",
+ (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
+ (flags & WAIT_AVAILABLE) ? "-available" : "",
+ (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
+ (flags & WAIT_SUBMITTED) ? "-submitted" : "",
+ (flags & WAIT_SIGNALED) ? "-signaled" : "")
+ test_multi_wait(fd, flags, err);
+ }
+ igt_subtest("wait-any-snapshot")
+ test_wait_snapshot(fd, 0);
+
+ igt_subtest("wait-all-snapshot")
+ test_wait_snapshot(fd, WAIT_ALL);
+
+ igt_subtest("wait-for-submit-snapshot")
+ test_wait_snapshot(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-for-submit-snapshot")
+ test_wait_snapshot(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-any-complex")
+ test_wait_complex(fd, 0);
+
+ igt_subtest("wait-all-complex")
+ test_wait_complex(fd, WAIT_ALL);
+
+ igt_subtest("wait-for-submit-complex")
+ test_wait_complex(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-for-submit-complex")
+ test_wait_complex(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-any-interrupted")
+ test_wait_interrupted(fd, 0);
+
+ igt_subtest("wait-all-interrupted")
+ test_wait_interrupted(fd, WAIT_ALL);
+}
--
2.17.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* ✓ Fi.CI.BAT: success for igt: add timeline test cases (rev4)
2018-12-11 10:37 [PATCH i-g-t] igt: add timeline test cases v2 Chunming Zhou
@ 2018-12-11 11:18 ` Patchwork
2018-12-11 13:05 ` ✓ Fi.CI.IGT: " Patchwork
[not found] ` <20181211103735.26058-1-david1.zhou-5C7GfCeVMHo@public.gmane.org>
2 siblings, 0 replies; 6+ messages in thread
From: Patchwork @ 2018-12-11 11:18 UTC (permalink / raw)
To: Chunming Zhou; +Cc: intel-gfx
== Series Details ==
Series: igt: add timeline test cases (rev4)
URL : https://patchwork.freedesktop.org/series/53743/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_5295 -> IGTPW_2141
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/53743/revisions/4/mbox/
Known issues
------------
Here are the changes found in IGTPW_2141 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
- fi-blb-e6850: PASS -> INCOMPLETE [fdo#107718]
#### Possible fixes ####
* igt@i915_selftest@live_coherency:
- fi-gdg-551: DMESG-FAIL [fdo#107164] -> PASS
* igt@kms_chamelium@hdmi-hpd-fast:
- {fi-kbl-7500u}: FAIL [fdo#108767] -> PASS
* igt@kms_pipe_crc_basic@suspend-read-crc-pipe-a:
- fi-byt-clapper: FAIL [fdo#103191] / [fdo#107362] -> PASS
* igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
- fi-cfl-8109u: INCOMPLETE [fdo#106070] / [fdo#108126] -> PASS
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
[fdo#106070]: https://bugs.freedesktop.org/show_bug.cgi?id=106070
[fdo#107164]: https://bugs.freedesktop.org/show_bug.cgi?id=107164
[fdo#107362]: https://bugs.freedesktop.org/show_bug.cgi?id=107362
[fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
[fdo#108126]: https://bugs.freedesktop.org/show_bug.cgi?id=108126
[fdo#108767]: https://bugs.freedesktop.org/show_bug.cgi?id=108767
Participating hosts (49 -> 44)
------------------------------
Missing (5): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-ctg-p8600 fi-bdw-samus
Build changes
-------------
* IGT: IGT_4745 -> IGTPW_2141
CI_DRM_5295: b94537deaa09d831c7e191da57aed98cc11fd49b @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_2141: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2141/
IGT_4745: 3b52e8a5809a4e860350c59476a456745cd9fee0 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
== Testlist changes ==
+++ 113 lines
--- 0 lines
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2141/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 6+ messages in thread
* ✓ Fi.CI.IGT: success for igt: add timeline test cases (rev4)
2018-12-11 10:37 [PATCH i-g-t] igt: add timeline test cases v2 Chunming Zhou
2018-12-11 11:18 ` ✓ Fi.CI.BAT: success for igt: add timeline test cases (rev4) Patchwork
@ 2018-12-11 13:05 ` Patchwork
[not found] ` <20181211103735.26058-1-david1.zhou-5C7GfCeVMHo@public.gmane.org>
2 siblings, 0 replies; 6+ messages in thread
From: Patchwork @ 2018-12-11 13:05 UTC (permalink / raw)
To: Chunming Zhou; +Cc: intel-gfx
== Series Details ==
Series: igt: add timeline test cases (rev4)
URL : https://patchwork.freedesktop.org/series/53743/
State : success
== Summary ==
CI Bug Log - changes from CI_DRM_5295_full -> IGTPW_2141_full
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://patchwork.freedesktop.org/api/1.0/series/53743/revisions/4/mbox/
Known issues
------------
Here are the changes found in IGTPW_2141_full that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@gem_exec_fence@basic-await-default:
- shard-hsw: PASS -> FAIL [fdo#108888]
* igt@kms_busy@extended-pageflip-modeset-hang-oldfb-render-a:
- shard-apl: PASS -> DMESG-WARN [fdo#107956]
* igt@kms_color@pipe-c-ctm-max:
- shard-apl: PASS -> FAIL [fdo#108147] +1
* igt@kms_cursor_crc@cursor-256x256-dpms:
- shard-kbl: PASS -> FAIL [fdo#103232]
* igt@kms_cursor_crc@cursor-256x85-onscreen:
- shard-apl: PASS -> FAIL [fdo#103232] +5
* igt@kms_cursor_crc@cursor-64x64-sliding:
- shard-glk: PASS -> FAIL [fdo#103232] +4
* igt@kms_cursor_crc@cursor-64x64-suspend:
- shard-apl: PASS -> FAIL [fdo#103191] / [fdo#103232]
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-cpu:
- shard-apl: PASS -> FAIL [fdo#103167] +3
* igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-pwrite:
- shard-glk: PASS -> FAIL [fdo#103167] +5
* igt@kms_frontbuffer_tracking@fbc-2p-rte:
- shard-glk: PASS -> FAIL [fdo#103167] / [fdo#105682]
* igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-mmap-gtt:
- shard-snb: SKIP -> INCOMPLETE [fdo#105411]
* igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
- shard-kbl: PASS -> INCOMPLETE [fdo#103665]
* igt@kms_plane@plane-position-covered-pipe-a-planes:
- shard-kbl: PASS -> FAIL [fdo#103166]
* igt@kms_plane_alpha_blend@pipe-b-alpha-opaque-fb:
- shard-glk: PASS -> FAIL [fdo#108145] +1
* igt@kms_plane_multiple@atomic-pipe-a-tiling-y:
- shard-glk: PASS -> FAIL [fdo#103166] +4
* igt@kms_plane_multiple@atomic-pipe-b-tiling-y:
- shard-apl: PASS -> FAIL [fdo#103166] +2
* {igt@kms_rotation_crc@multiplane-rotation-cropping-top}:
- shard-glk: PASS -> DMESG-FAIL [fdo#105763] / [fdo#106538]
* igt@perf_pmu@rc6-runtime-pm:
- shard-apl: PASS -> FAIL [fdo#105010]
#### Possible fixes ####
* igt@kms_color@pipe-c-degamma:
- shard-apl: FAIL [fdo#104782] -> PASS
* igt@kms_cursor_crc@cursor-256x85-onscreen:
- shard-glk: FAIL [fdo#103232] -> PASS
* igt@kms_cursor_crc@cursor-64x64-sliding:
- shard-apl: DMESG-FAIL [fdo#103232] / [fdo#103558] / [fdo#105602] -> PASS
- shard-kbl: DMESG-FAIL [fdo#103232] / [fdo#103558] / [fdo#105602] -> PASS
* igt@kms_cursor_legacy@basic-flip-before-cursor-legacy:
- shard-apl: DMESG-WARN [fdo#103558] / [fdo#105602] -> PASS +16
* igt@kms_draw_crc@draw-method-rgb565-pwrite-ytiled:
- shard-glk: FAIL [fdo#103184] -> PASS
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-mmap-gtt:
- shard-kbl: FAIL [fdo#103167] -> PASS
* igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-gtt:
- shard-apl: FAIL [fdo#103167] -> PASS +1
* igt@kms_frontbuffer_tracking@fbc-1p-rte:
- shard-apl: FAIL [fdo#103167] / [fdo#105682] -> PASS
* igt@kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-onoff:
- shard-glk: FAIL [fdo#103167] -> PASS +5
* igt@kms_plane@plane-position-covered-pipe-c-planes:
- shard-apl: FAIL [fdo#103166] -> PASS +1
* igt@kms_universal_plane@universal-plane-gen9-features-pipe-a:
- shard-kbl: DMESG-WARN [fdo#103558] / [fdo#105602] -> PASS +17
* igt@kms_universal_plane@universal-plane-pipe-b-functional:
- shard-glk: FAIL [fdo#103166] -> PASS +2
- shard-kbl: FAIL [fdo#103166] -> PASS +1
* igt@perf@blocking:
- shard-hsw: FAIL [fdo#102252] -> PASS
* igt@prime_vgem@fence-wait-blt:
- shard-snb: INCOMPLETE [fdo#105411] -> PASS
{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).
[fdo#102252]: https://bugs.freedesktop.org/show_bug.cgi?id=102252
[fdo#103166]: https://bugs.freedesktop.org/show_bug.cgi?id=103166
[fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
[fdo#103184]: https://bugs.freedesktop.org/show_bug.cgi?id=103184
[fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
[fdo#103232]: https://bugs.freedesktop.org/show_bug.cgi?id=103232
[fdo#103558]: https://bugs.freedesktop.org/show_bug.cgi?id=103558
[fdo#103665]: https://bugs.freedesktop.org/show_bug.cgi?id=103665
[fdo#104782]: https://bugs.freedesktop.org/show_bug.cgi?id=104782
[fdo#105010]: https://bugs.freedesktop.org/show_bug.cgi?id=105010
[fdo#105411]: https://bugs.freedesktop.org/show_bug.cgi?id=105411
[fdo#105602]: https://bugs.freedesktop.org/show_bug.cgi?id=105602
[fdo#105682]: https://bugs.freedesktop.org/show_bug.cgi?id=105682
[fdo#105763]: https://bugs.freedesktop.org/show_bug.cgi?id=105763
[fdo#106538]: https://bugs.freedesktop.org/show_bug.cgi?id=106538
[fdo#107956]: https://bugs.freedesktop.org/show_bug.cgi?id=107956
[fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
[fdo#108147]: https://bugs.freedesktop.org/show_bug.cgi?id=108147
[fdo#108888]: https://bugs.freedesktop.org/show_bug.cgi?id=108888
Participating hosts (7 -> 5)
------------------------------
Missing (2): shard-skl shard-iclb
Build changes
-------------
* IGT: IGT_4745 -> IGTPW_2141
* Piglit: piglit_4509 -> None
CI_DRM_5295: b94537deaa09d831c7e191da57aed98cc11fd49b @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_2141: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2141/
IGT_4745: 3b52e8a5809a4e860350c59476a456745cd9fee0 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2141/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH i-g-t] igt: add timeline test cases v2
2018-12-11 10:37 [PATCH i-g-t] igt: add timeline test cases v2 Chunming Zhou
@ 2018-12-14 19:23 ` Wentland, Harry
2018-12-11 13:05 ` ✓ Fi.CI.IGT: " Patchwork
[not found] ` <20181211103735.26058-1-david1.zhou-5C7GfCeVMHo@public.gmane.org>
2 siblings, 0 replies; 6+ messages in thread
From: Wentland, Harry @ 2018-12-14 19:23 UTC (permalink / raw)
To: Zhou, David(ChunMing),
Koenig, Christian, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
intel-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, IGT development
On 2018-12-11 5:37 a.m., Chunming Zhou wrote:
> v2: adapt to new transfer ioctl
>
> Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
+igt-dev
I think intel-gfx still works for IGT development but most of the IGT work happens on igt-dev@lists.freedesktop.org now.
Harry
> ---
> include/drm-uapi/drm.h | 33 ++
> lib/igt_syncobj.c | 206 ++++++++
> lib/igt_syncobj.h | 19 +
> tests/meson.build | 1 +
> tests/syncobj_timeline.c | 1032 ++++++++++++++++++++++++++++++++++++++
> 5 files changed, 1291 insertions(+)
> create mode 100644 tests/syncobj_timeline.c
>
> diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
> index 85c685a2..dcd245d9 100644
> --- a/include/drm-uapi/drm.h
> +++ b/include/drm-uapi/drm.h
> @@ -731,6 +731,8 @@ struct drm_syncobj_handle {
>
> #define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0)
> #define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1)
> +/* wait for time point to become available */
> +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2)
> struct drm_syncobj_wait {
> __u64 handles;
> /* absolute timeout */
> @@ -741,11 +743,38 @@ struct drm_syncobj_wait {
> __u32 pad;
> };
>
> +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;
> +};
> +
> struct drm_syncobj_array {
> __u64 handles;
> __u32 count_handles;
> __u32 pad;
> };
> +struct drm_syncobj_timeline_array {
> + __u64 handles;
> + __u64 points;
> + __u32 count_handles;
> + __u32 pad;
> +};
> +
> +struct drm_syncobj_transfer {
> + __u32 src_handle;
> + __u32 dst_handle;
> + __u64 src_point;
> + __u64 dst_point;
> + __u32 flags;
> + __u32 pad;
> +};
>
> /* Query current scanout sequence number */
> struct drm_crtc_get_sequence {
> @@ -902,6 +931,10 @@ extern "C" {
> #define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees)
> #define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
> #define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease)
> +#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)
>
> /**
> * Device specific ioctls should only be in their respective headers
> diff --git a/lib/igt_syncobj.c b/lib/igt_syncobj.c
> index d9114ca8..efa2adc4 100644
> --- a/lib/igt_syncobj.c
> +++ b/lib/igt_syncobj.c
> @@ -286,3 +286,209 @@ syncobj_signal(int fd, uint32_t *handles, uint32_t count)
> {
> igt_assert_eq(__syncobj_signal(fd, handles, count), 0);
> }
> +
> +static int
> +__syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + int err = 0;
> +
> + array.handles = to_user_pointer(handles);
> + array.points = to_user_pointer(points);
> + array.count_handles = count;
> + if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array))
> + err = -errno;
> + return err;
> +}
> +
> +/**
> + * syncobj_signal:
> + * @fd: The DRM file descriptor.
> + * @handles: Array of syncobj handles to signal
> + * @points: List of point of handles to signal.
> + * @count: Count of syncobj handles.
> + *
> + * Signal a set of syncobjs.
> + */
> +void
> +syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
> +{
> + igt_assert_eq(__syncobj_timeline_signal(fd, handles, points, count), 0);
> +}
> +int
> +__syncobj_timeline_wait_ioctl(int fd, struct drm_syncobj_timeline_wait *args)
> +{
> + int err = 0;
> + if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, args))
> + err = -errno;
> + return err;
> +}
> +static int
> +__syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled)
> +{
> + struct drm_syncobj_timeline_wait args;
> + int ret;
> +
> + args.handles = to_user_pointer(handles);
> + args.points = (uint64_t)to_user_pointer(points);
> + args.timeout_nsec = timeout_nsec;
> + args.count_handles = num_handles;
> + args.flags = flags;
> + args.first_signaled = 0;
> + args.pad = 0;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
> + if (ret < 0)
> + return -errno;
> +
> + if (first_signaled)
> + *first_signaled = args.first_signaled;
> +
> + return ret;
> +}
> +int
> +syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags)
> +{
> + return __syncobj_timeline_wait(fd, handles, points, num_handles,
> + timeout_nsec, flags, NULL);
> +}
> +
> +/**
> + * syncobj_timeline_wait:
> + * @fd: The DRM file descriptor
> + * @handles: List of syncobj handles to wait for.
> + * @points: List of point of handles to wait for.
> + * @num_handles: Count of handles
> + * @timeout_nsec: Absolute wait timeout in nanoseconds.
> + * @flags: Wait ioctl flags.
> + * @first_signaled: Returned handle for first signaled syncobj.
> + *
> + * Waits in the kernel for any/all the requested syncobjs timeline point
> + * using the timeout and flags.
> + * Returns: bool value - false = timedout, true = signaled
> + */
> +bool
> +syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled)
> +{
> + int ret;
> +
> + ret = __syncobj_timeline_wait(fd, handles, points, num_handles,
> + timeout_nsec, flags, first_signaled);
> + if (ret == -ETIME)
> + return false;
> + igt_assert_eq(ret, 0);
> +
> + return true;
> +
> +}
> +
> +static int
> +__syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t handle_count)
> +{
> + struct drm_syncobj_timeline_array args;
> + int ret;
> +
> + args.handles = to_user_pointer(handles);
> + args.points = (uint64_t)to_user_pointer(points);
> + args.count_handles = handle_count;
> + args.pad = 0;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + if (ret)
> + return ret;
> + return 0;
> +}
> +
> +/**
> + * syncobj_timeline_query:
> + * @fd: The DRM file descriptor.
> + * @handles: Array of syncobj handles.
> + * @points: Array of syncobj points queried.
> + * @count: Count of syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t count)
> +{
> + igt_assert_eq(__syncobj_timeline_query(fd, handles, points, count), 0);
> +}
> +
> +static int
> +__syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle)
> +{
> + struct drm_syncobj_transfer args;
> +
> + args.src_handle = binary_handle;
> + args.dst_handle = timeline_handle;
> + args.src_point = 0;
> + args.dst_point = point;
> + args.flags = 0;
> + args.pad = 0;
> + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> +}
> +
> +/**
> + * syncobj_binary_to_timeline:
> + * @fd: The DRM file descriptor.
> + * @timeline_handles: Array of timeline syncobj handles.
> + * @point: syncobj point.
> + * @binary_handle: Array of binary syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle)
> +{
> + igt_assert_eq(__syncobj_binary_to_timeline(fd, timeline_handle, point,
> + binary_handle), 0);
> +}
> +
> +static int
> +__syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags)
> +{
> + struct drm_syncobj_transfer args;
> +
> + args.dst_handle = binary_handle;
> + args.src_handle = timeline_handle;
> + args.dst_point = 0;
> + args.src_point = point;
> + args.flags = flags;
> + args.pad = 0;
> + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> +}
> +
> +/**
> + * syncobj_binary_to_timeline:
> + * @fd: The DRM file descriptor.
> + * @timeline_handles: Array of timeline syncobj handles.
> + * @point: syncobj point.
> + * @binary_handle: Array of binary syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags)
> +{
> + igt_assert_eq(__syncobj_timeline_to_binary(fd, binary_handle,
> + timeline_handle, point,
> + flags), 0);
> +}
> diff --git a/lib/igt_syncobj.h b/lib/igt_syncobj.h
> index 44d1378d..175e5027 100644
> --- a/lib/igt_syncobj.h
> +++ b/lib/igt_syncobj.h
> @@ -65,7 +65,26 @@ int syncobj_wait_err(int fd, uint32_t *handles, uint32_t count,
> bool syncobj_wait(int fd, uint32_t *handles, uint32_t count,
> uint64_t abs_timeout_nsec, uint32_t flags,
> uint32_t *first_signaled);
> +int __syncobj_timeline_wait_ioctl(int fd,
> + struct drm_syncobj_timeline_wait *args);
> +bool syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled);
> +int syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags);
> void syncobj_reset(int fd, uint32_t *handles, uint32_t count);
> void syncobj_signal(int fd, uint32_t *handles, uint32_t count);
> +void syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t count);
> +void syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle);
> +void syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags);
> +void syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points,
> + uint32_t count);
>
> #endif /* IGT_SYNCOBJ_H */
> diff --git a/tests/meson.build b/tests/meson.build
> index b8a6e61b..bddc223c 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -77,6 +77,7 @@ test_progs = [
> 'sw_sync',
> 'syncobj_basic',
> 'syncobj_wait',
> + 'syncobj_timeline',
> 'template',
> 'tools_test',
> 'v3d_get_bo_offset',
> diff --git a/tests/syncobj_timeline.c b/tests/syncobj_timeline.c
> new file mode 100644
> index 00000000..505fd9ed
> --- /dev/null
> +++ b/tests/syncobj_timeline.c
> @@ -0,0 +1,1032 @@
> +/*
> + * Copyright © 2018 Intel Corporation
> + *
> + * 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
> + * THE AUTHORS OR COPYRIGHT HOLDERS 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.
> + */
> +
> +#include "igt.h"
> +#include "sw_sync.h"
> +#include "igt_syncobj.h"
> +#include <unistd.h>
> +#include <time.h>
> +#include <sys/ioctl.h>
> +#include <pthread.h>
> +#include <signal.h>
> +#include "drm.h"
> +
> +IGT_TEST_DESCRIPTION("Tests for the drm timeline sync object API");
> +
> +/* One tenth of a second */
> +#define SHORT_TIME_NSEC 100000000ull
> +
> +#define NSECS_PER_SEC 1000000000ull
> +
> +static uint64_t
> +gettime_ns(void)
> +{
> + struct timespec current;
> + clock_gettime(CLOCK_MONOTONIC, ¤t);
> + return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec;
> +}
> +
> +static void
> +sleep_nsec(uint64_t time_nsec)
> +{
> + struct timespec t;
> + t.tv_sec = time_nsec / NSECS_PER_SEC;
> + t.tv_nsec = time_nsec % NSECS_PER_SEC;
> + igt_assert_eq(nanosleep(&t, NULL), 0);
> +}
> +
> +static uint64_t
> +short_timeout(void)
> +{
> + return gettime_ns() + SHORT_TIME_NSEC;
> +}
> +
> +static int
> +syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point)
> +{
> + struct drm_syncobj_handle;
> + uint32_t syncobj = syncobj_create(fd, 0);
> + int timeline, fence;
> +
> + timeline = sw_sync_timeline_create();
> + fence = sw_sync_timeline_create_fence(timeline, 1);
> + syncobj_import_sync_file(fd, syncobj, fence);
> + syncobj_binary_to_timeline(fd, handle, point, syncobj);
> + close(fence);
> +
> + syncobj_destroy(fd, syncobj);
> + return timeline;
> +}
> +
> +static void
> +syncobj_trigger(int fd, uint32_t handle, uint64_t point)
> +{
> + int timeline = syncobj_attach_sw_sync(fd, handle, point);
> + sw_sync_timeline_inc(timeline, 1);
> + close(timeline);
> +}
> +
> +static timer_t
> +set_timer(void (*cb)(union sigval), void *ptr, int i, uint64_t nsec)
> +{
> + timer_t timer;
> + struct sigevent sev;
> + struct itimerspec its;
> +
> + memset(&sev, 0, sizeof(sev));
> + sev.sigev_notify = SIGEV_THREAD;
> + if (ptr)
> + sev.sigev_value.sival_ptr = ptr;
> + else
> + sev.sigev_value.sival_int = i;
> + sev.sigev_notify_function = cb;
> + igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
> +
> + memset(&its, 0, sizeof(its));
> + its.it_value.tv_sec = nsec / NSEC_PER_SEC;
> + its.it_value.tv_nsec = nsec % NSEC_PER_SEC;
> + igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
> +
> + return timer;
> +}
> +
> +struct fd_handle_pair {
> + int fd;
> + uint32_t handle;
> + uint64_t point;
> +};
> +
> +static void
> +timeline_inc_func(union sigval sigval)
> +{
> + sw_sync_timeline_inc(sigval.sival_int, 1);
> +}
> +
> +static void
> +syncobj_trigger_free_pair_func(union sigval sigval)
> +{
> + struct fd_handle_pair *pair = sigval.sival_ptr;
> + syncobj_trigger(pair->fd, pair->handle, pair->point);
> + free(pair);
> +}
> +
> +static timer_t
> +syncobj_trigger_delayed(int fd, uint32_t syncobj, uint64_t point, uint64_t nsec)
> +{
> + struct fd_handle_pair *pair = malloc(sizeof(*pair));
> +
> + pair->fd = fd;
> + pair->handle = syncobj;
> + pair->point = syncobj;
> +
> + return set_timer(syncobj_trigger_free_pair_func, pair, 0, nsec);
> +}
> +
> +static void
> +test_wait_bad_flags(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + wait.flags = 0xdeadbeef;
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
> +}
> +
> +static void
> +test_wait_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
> +}
> +
> +static void
> +test_wait_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t handle = 0;
> +
> + wait.count_handles = 1;
> + wait.handles = to_user_pointer(&handle);
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ENOENT);
> +}
> +
> +static void
> +test_query_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + int ret;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_query_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_query_one_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t syncobjs[3];
> + uint64_t initial_point = 1;
> + int ret;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = 0;
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
> + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
> + &initial_point, 1, 0, 0), 0);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
> + &initial_point, 1, 0, 0), 0);
> +
> + array.count_handles = 3;
> + array.handles = to_user_pointer(syncobjs);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
> + igt_assert(ret == -1 && errno == ENOENT);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +static void
> +test_query_bad_pad(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + array.pad = 0xdeadbeef;
> + array.count_handles = 1;
> + array.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + int ret;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_signal_illegal_point(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 1;
> + uint64_t point = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + args.points = to_user_pointer(&point);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +static void
> +test_signal_one_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t syncobjs[3];
> + uint64_t initial_point = 1;
> + int ret;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = 0;
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
> + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
> + &initial_point, 1, 0, 0), 0);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
> + &initial_point, 1, 0, 0), 0);
> +
> + array.count_handles = 3;
> + array.handles = to_user_pointer(syncobjs);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
> + igt_assert(ret == -1 && errno == ENOENT);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +static void
> +test_signal_bad_pad(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + array.pad = 0xdeadbeef;
> + array.count_handles = 1;
> + array.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_array(int fd)
> +{
> + uint32_t syncobjs[4];
> + uint64_t points[4] = {1, 1, 1, 0};
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> + syncobjs[2] = syncobj_create(fd, 0);
> + syncobjs[3] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, syncobjs, points, 4);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs,
> + points, 3, 0, 0), 0);
> + igt_assert_eq(syncobj_wait_err(fd, &syncobjs[3], 1, 0, 0), 0);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> + syncobj_destroy(fd, syncobjs[2]);
> + syncobj_destroy(fd, syncobjs[3]);
> +}
> +
> +static void
> +test_transfer_illegal_handle(int fd)
> +{
> + struct drm_syncobj_transfer args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.src_handle = to_user_pointer(&handle);
> + args.dst_handle = to_user_pointer(&handle);
> + args.src_point = 1;
> + args.dst_point = 0;
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_transfer_bad_pad(int fd)
> +{
> + struct drm_syncobj_transfer arg = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + arg.pad = 0xdeadbeef;
> + arg.src_handle = to_user_pointer(&handle);
> + arg.dst_handle = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &arg);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +#define WAIT_FOR_SUBMIT (1 << 0)
> +#define WAIT_ALL (1 << 1)
> +#define WAIT_AVAILABLE (1 << 2)
> +#define WAIT_UNSUBMITTED (1 << 3)
> +#define WAIT_SUBMITTED (1 << 4)
> +#define WAIT_SIGNALED (1 << 5)
> +#define WAIT_FLAGS_MAX (1 << 6) - 1
> +
> +static uint32_t
> +flags_for_test_flags(uint32_t test_flags)
> +{
> + uint32_t flags = 0;
> +
> + if (test_flags & WAIT_FOR_SUBMIT)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> +
> + if (test_flags & WAIT_AVAILABLE)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
> +
> + if (test_flags & WAIT_ALL)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
> +
> + return flags;
> +}
> +
> +static void
> +test_single_wait(int fd, uint32_t test_flags, int expect)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = flags_for_test_flags(test_flags);
> + uint64_t point = 1;
> + int timeline = -1;
> +
> + if (test_flags & (WAIT_SUBMITTED | WAIT_SIGNALED))
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> +
> + if (test_flags & WAIT_SIGNALED)
> + sw_sync_timeline_inc(timeline, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + 0, flags), expect);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + short_timeout(), flags), expect);
> +
> + if (expect != -ETIME) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + UINT64_MAX, flags), expect);
> + }
> +
> + syncobj_destroy(fd, syncobj);
> + if (timeline != -1)
> + close(timeline);
> +}
> +
> +static void
> +test_wait_delayed_signal(int fd, uint32_t test_flags)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = flags_for_test_flags(test_flags);
> + uint64_t point = 1;
> + int timeline = -1;
> + timer_t timer;
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + timer = syncobj_trigger_delayed(fd, syncobj, point, SHORT_TIME_NSEC);
> + } else {
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> + timer = set_timer(timeline_inc_func, NULL,
> + timeline, SHORT_TIME_NSEC);
> + }
> +
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
> + gettime_ns() + SHORT_TIME_NSEC * 2,
> + flags, NULL));
> +
> + timer_delete(timer);
> +
> + if (timeline != -1)
> + close(timeline);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_unsignaled(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_reset(fd, &syncobj, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_signaled(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> +
> + syncobj_trigger(fd, syncobj, point);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), 0);
> +
> + syncobj_reset(fd, &syncobj, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_multiple_signaled(int fd)
> +{
> + uint64_t points[3] = {1, 1, 1};
> + uint32_t syncobjs[3];
> + int i;
> +
> + for (i = 0; i < 3; i++) {
> + syncobjs[i] = syncobj_create(fd, 0);
> + syncobj_trigger(fd, syncobjs[i], points[i]);
> + }
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, 0), 0);
> +
> + syncobj_reset(fd, syncobjs, 3);
> +
> + for (i = 0; i < 3; i++) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[i],
> + &points[i], 1,
> + 0, 0), -EINVAL);
> + syncobj_destroy(fd, syncobjs[i]);
> + }
> +}
> +
> +static void
> +reset_and_trigger_func(union sigval sigval)
> +{
> + struct fd_handle_pair *pair = sigval.sival_ptr;
> + syncobj_reset(pair->fd, &pair->handle, 1);
> + syncobj_trigger(pair->fd, pair->handle, pair->point);
> +}
> +
> +static void
> +test_reset_during_wait_for_submit(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> + struct fd_handle_pair pair;
> + uint64_t point = 1;
> + timer_t timer;
> +
> + pair.fd = fd;
> + pair.handle = syncobj;
> + timer = set_timer(reset_and_trigger_func, &pair, 0, SHORT_TIME_NSEC);
> +
> + /* A reset should be a no-op even if we're in the middle of a wait */
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
> + gettime_ns() + SHORT_TIME_NSEC * 2,
> + flags, NULL));
> +
> + timer_delete(timer);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_signal(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> + uint64_t point = 1;
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, flags), -ETIME);
> +
> + syncobj_timeline_signal(fd, &syncobj, &point, 1);
> +
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, 0, NULL));
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, flags, NULL));
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_multi_wait(int fd, uint32_t test_flags, int expect)
> +{
> + uint32_t syncobjs[3];
> + uint32_t tflag, flags;
> + int i, fidx, timeline;
> + uint64_t points[3] = {1, 1, 1};
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + flags = flags_for_test_flags(test_flags);
> + test_flags &= ~(WAIT_ALL | WAIT_FOR_SUBMIT | WAIT_AVAILABLE);
> +
> + for (i = 0; i < 3; i++) {
> + fidx = ffs(test_flags) - 1;
> + tflag = (1 << fidx);
> +
> + if (test_flags & ~tflag)
> + test_flags &= ~tflag;
> +
> + if (tflag & (WAIT_SUBMITTED | WAIT_SIGNALED))
> + timeline = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + if (tflag & WAIT_SIGNALED)
> + sw_sync_timeline_inc(timeline, 1);
> + }
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, flags), expect);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, short_timeout(),
> + flags), expect);
> +
> + if (expect != -ETIME) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, UINT64_MAX,
> + flags), expect);
> + }
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +struct wait_thread_data {
> + int fd;
> + struct drm_syncobj_timeline_wait wait;
> +};
> +
> +static void *
> +wait_thread_func(void *data)
> +{
> + struct wait_thread_data *wait = data;
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(wait->fd, &wait->wait), 0);
> + return NULL;
> +}
> +
> +static void
> +test_wait_snapshot(int fd, uint32_t test_flags)
> +{
> + struct wait_thread_data wait = { 0 };
> + uint32_t syncobjs[2];
> + uint64_t points[2] = {1, 1};
> + int timelines[3] = { -1, -1, -1 };
> + pthread_t thread;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> +
> + if (!(test_flags & WAIT_FOR_SUBMIT)) {
> + timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> + timelines[1] = syncobj_attach_sw_sync(fd, syncobjs[1], points[1]);
> + }
> +
> + wait.fd = fd;
> + wait.wait.handles = to_user_pointer(syncobjs);
> + wait.wait.count_handles = 2;
> + wait.wait.points = to_user_pointer(points);
> + wait.wait.timeout_nsec = short_timeout();
> + wait.wait.flags = flags_for_test_flags(test_flags);
> +
> + igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Try to fake the kernel out by triggering or partially triggering
> + * the first fence.
> + */
> + if (test_flags & WAIT_ALL) {
> + /* If it's WAIT_ALL, actually trigger it */
> + if (timelines[0] == -1)
> + syncobj_trigger(fd, syncobjs[0], points[0]);
> + else
> + sw_sync_timeline_inc(timelines[0], 1);
> + } else if (test_flags & WAIT_FOR_SUBMIT) {
> + timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> + }
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Then reset it */
> + syncobj_reset(fd, &syncobjs[0], 1);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Then "submit" it in a way that will never trigger. This way, if
> + * the kernel picks up on the new fence (it shouldn't), we'll get a
> + * timeout.
> + */
> + timelines[2] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Now trigger the second fence to complete the wait */
> +
> + if (timelines[1] == -1)
> + syncobj_trigger(fd, syncobjs[1], points[1]);
> + else
> + sw_sync_timeline_inc(timelines[1], 1);
> +
> + pthread_join(thread, NULL);
> +
> + if (!(test_flags & WAIT_ALL))
> + igt_assert_eq(wait.wait.first_signaled, 1);
> +
> + close(timelines[0]);
> + close(timelines[1]);
> + close(timelines[2]);
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> +}
> +
> +/* The numbers 0-7, each repeated 5x and shuffled. */
> +static const unsigned shuffled_0_7_x4[] = {
> + 2, 0, 6, 1, 1, 4, 5, 2, 0, 7, 1, 7, 6, 3, 4, 5,
> + 0, 2, 7, 3, 5, 4, 0, 6, 7, 3, 2, 5, 6, 1, 4, 3,
> +};
> +
> +enum syncobj_stage {
> + STAGE_UNSUBMITTED,
> + STAGE_SUBMITTED,
> + STAGE_SIGNALED,
> + STAGE_RESET,
> + STAGE_RESUBMITTED,
> +};
> +
> +static void
> +test_wait_complex(int fd, uint32_t test_flags)
> +{
> + struct wait_thread_data wait = { 0 };
> + uint32_t syncobjs[8];
> + uint64_t points[8] = {1, 1, 1, 1, 1, 1, 1, 1};
> + enum syncobj_stage stage[8];
> + int i, j, timelines[8];
> + uint32_t first_signaled = -1, num_signaled = 0;
> + pthread_t thread;
> +
> + for (i = 0; i < 8; i++) {
> + stage[i] = STAGE_UNSUBMITTED;
> + syncobjs[i] = syncobj_create(fd, 0);
> + }
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + for (i = 0; i < 8; i++)
> + timelines[i] = -1;
> + } else {
> + for (i = 0; i < 8; i++)
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + }
> +
> + wait.fd = fd;
> + wait.wait.handles = to_user_pointer(syncobjs);
> + wait.wait.count_handles = 2;
> + wait.wait.points = to_user_pointer(points);
> + wait.wait.timeout_nsec = gettime_ns() + NSECS_PER_SEC;
> + wait.wait.flags = flags_for_test_flags(test_flags);
> +
> + igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
> +
> + sleep_nsec(NSECS_PER_SEC / 50);
> +
> + num_signaled = 0;
> + for (j = 0; j < ARRAY_SIZE(shuffled_0_7_x4); j++) {
> + i = shuffled_0_7_x4[j];
> + igt_assert_lt(i, ARRAY_SIZE(syncobjs));
> +
> + switch (stage[i]++) {
> + case STAGE_UNSUBMITTED:
> + /* We need to submit attach a fence */
> + if (!(test_flags & WAIT_FOR_SUBMIT)) {
> + /* We had to attach one up-front */
> + igt_assert_neq(timelines[i], -1);
> + break;
> + }
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + break;
> +
> + case STAGE_SUBMITTED:
> + /* We have a fence, trigger it */
> + igt_assert_neq(timelines[i], -1);
> + sw_sync_timeline_inc(timelines[i], 1);
> + close(timelines[i]);
> + timelines[i] = -1;
> + if (num_signaled == 0)
> + first_signaled = i;
> + num_signaled++;
> + break;
> +
> + case STAGE_SIGNALED:
> + /* We're already signaled, reset */
> + syncobj_reset(fd, &syncobjs[i], 1);
> + break;
> +
> + case STAGE_RESET:
> + /* We're reset, submit and don't signal */
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + break;
> +
> + case STAGE_RESUBMITTED:
> + igt_assert(!"Should not reach this stage");
> + break;
> + }
> +
> + if (test_flags & WAIT_ALL) {
> + if (num_signaled == ARRAY_SIZE(syncobjs))
> + break;
> + } else {
> + if (num_signaled > 0)
> + break;
> + }
> +
> + sleep_nsec(NSECS_PER_SEC / 100);
> + }
> +
> + pthread_join(thread, NULL);
> +
> + if (test_flags & WAIT_ALL) {
> + igt_assert_eq(num_signaled, ARRAY_SIZE(syncobjs));
> + } else {
> + igt_assert_eq(num_signaled, 1);
> + igt_assert_eq(wait.wait.first_signaled, first_signaled);
> + }
> +
> + for (i = 0; i < 8; i++) {
> + close(timelines[i]);
> + syncobj_destroy(fd, syncobjs[i]);
> + }
> +}
> +
> +static void
> +test_wait_interrupted(int fd, uint32_t test_flags)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> + int timeline;
> +
> + wait.handles = to_user_pointer(&syncobj);
> + wait.points = to_user_pointer(&point);
> + wait.count_handles = 1;
> + wait.flags = flags_for_test_flags(test_flags);
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + wait.timeout_nsec = short_timeout();
> + igt_while_interruptible(true)
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
> + }
> +
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> +
> + wait.timeout_nsec = short_timeout();
> + igt_while_interruptible(true)
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
> +
> + syncobj_destroy(fd, syncobj);
> + close(timeline);
> +}
> +
> +static bool
> +has_syncobj_timeline_wait(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t handle = 0;
> + uint64_t value;
> + int ret;
> +
> + if (drmGetCap(fd, DRM_CAP_SYNCOBJ, &value))
> + return false;
> + if (!value)
> + return false;
> +
> + /* Try waiting for zero sync objects should fail with EINVAL */
> + wait.count_handles = 1;
> + wait.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &wait);
> + return ret == -1 && errno == ENOENT;
> +}
> +
> +igt_main
> +{
> + int fd = -1;
> +
> + igt_fixture {
> + fd = drm_open_driver(DRIVER_ANY);
> + igt_require(has_syncobj_timeline_wait(fd));
> + igt_require_sw_sync();
> + }
> +
> + igt_subtest("invalid-wait-bad-flags")
> + test_wait_bad_flags(fd);
> +
> + igt_subtest("invalid-wait-zero-handles")
> + test_wait_zero_handles(fd);
> +
> + igt_subtest("invalid-wait-illegal-handle")
> + test_wait_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-zero-handles")
> + test_query_zero_handles(fd);
> +
> + igt_subtest("invalid-query-illegal-handle")
> + test_query_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-one-illegal-handle")
> + test_query_one_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-bad-pad")
> + test_query_bad_pad(fd);
> +
> + igt_subtest("invalid-signal-zero-handles")
> + test_signal_zero_handles(fd);
> +
> + igt_subtest("invalid-signal-illegal-handle")
> + test_signal_illegal_handle(fd);
> +
> + igt_subtest("invalid-signal-illegal-point")
> + test_signal_illegal_point(fd);
> +
> + igt_subtest("invalid-signal-one-illegal-handle")
> + test_signal_one_illegal_handle(fd);
> +
> + igt_subtest("invalid-signal-bad-pad")
> + test_signal_bad_pad(fd);
> +
> + igt_subtest("invalid-signal-array")
> + test_signal_array(fd);
> +
> + igt_subtest("invalid-transfer-illegal-handle")
> + test_transfer_illegal_handle(fd);
> +
> + igt_subtest("invalid-transfer-bad-pad")
> + test_transfer_bad_pad(fd);
> +
> + for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
> + int err;
> +
> + /* Only one wait mode for single-wait tests */
> + if (__builtin_popcount(flags & (WAIT_UNSUBMITTED |
> + WAIT_SUBMITTED |
> + WAIT_SIGNALED)) != 1)
> + continue;
> +
> + if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT))
> + err = -EINVAL;
> + else if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
> + err = -ETIME;
> + else
> + err = 0;
> +
> + igt_subtest_f("%ssingle-wait%s%s%s%s%s%s",
> + err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
> + (flags & WAIT_ALL) ? "-all" : "",
> + (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
> + (flags & WAIT_AVAILABLE) ? "-available" : "",
> + (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
> + (flags & WAIT_SUBMITTED) ? "-submitted" : "",
> + (flags & WAIT_SIGNALED) ? "-signaled" : "")
> + test_single_wait(fd, flags, err);
> + }
> +
> + igt_subtest("wait-delayed-signal")
> + test_wait_delayed_signal(fd, 0);
> +
> + igt_subtest("wait-for-submit-delayed-submit")
> + test_wait_delayed_signal(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-delayed-signal")
> + test_wait_delayed_signal(fd, WAIT_ALL);
> +
> + igt_subtest("wait-all-for-submit-delayed-submit")
> + test_wait_delayed_signal(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("reset-unsignaled")
> + test_reset_unsignaled(fd);
> +
> + igt_subtest("reset-signaled")
> + test_reset_signaled(fd);
> +
> + igt_subtest("reset-multiple-signaled")
> + test_reset_multiple_signaled(fd);
> +
> + igt_subtest("reset-during-wait-for-submit")
> + test_reset_during_wait_for_submit(fd);
> +
> + igt_subtest("signal")
> + test_signal(fd);
> +
> + for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
> + int err;
> +
> + /* At least one wait mode for multi-wait tests */
> + if (!(flags & (WAIT_UNSUBMITTED |
> + WAIT_SUBMITTED |
> + WAIT_SIGNALED)))
> + continue;
> +
> + err = 0;
> + if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT)) {
> + err = -EINVAL;
> + } else if (flags & WAIT_ALL) {
> + if (flags & (WAIT_UNSUBMITTED | WAIT_SUBMITTED))
> + err = -ETIME;
> + if (!(flags & WAIT_UNSUBMITTED) && (flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE))
> + err = 0;
> + } else {
> + if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
> + err = -ETIME;
> + }
> +
> + igt_subtest_f("%smulti-wait%s%s%s%s%s%s",
> + err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
> + (flags & WAIT_ALL) ? "-all" : "",
> + (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
> + (flags & WAIT_AVAILABLE) ? "-available" : "",
> + (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
> + (flags & WAIT_SUBMITTED) ? "-submitted" : "",
> + (flags & WAIT_SIGNALED) ? "-signaled" : "")
> + test_multi_wait(fd, flags, err);
> + }
> + igt_subtest("wait-any-snapshot")
> + test_wait_snapshot(fd, 0);
> +
> + igt_subtest("wait-all-snapshot")
> + test_wait_snapshot(fd, WAIT_ALL);
> +
> + igt_subtest("wait-for-submit-snapshot")
> + test_wait_snapshot(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-for-submit-snapshot")
> + test_wait_snapshot(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-any-complex")
> + test_wait_complex(fd, 0);
> +
> + igt_subtest("wait-all-complex")
> + test_wait_complex(fd, WAIT_ALL);
> +
> + igt_subtest("wait-for-submit-complex")
> + test_wait_complex(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-for-submit-complex")
> + test_wait_complex(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-any-interrupted")
> + test_wait_interrupted(fd, 0);
> +
> + igt_subtest("wait-all-interrupted")
> + test_wait_interrupted(fd, WAIT_ALL);
> +}
>
_______________________________________________
amd-gfx mailing list
amd-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/amd-gfx
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [igt-dev] [PATCH i-g-t] igt: add timeline test cases v2
@ 2018-12-14 19:23 ` Wentland, Harry
0 siblings, 0 replies; 6+ messages in thread
From: Wentland, Harry @ 2018-12-14 19:23 UTC (permalink / raw)
To: Zhou, David(ChunMing),
Koenig, Christian, dri-devel, amd-gfx, intel-gfx,
IGT development
On 2018-12-11 5:37 a.m., Chunming Zhou wrote:
> v2: adapt to new transfer ioctl
>
> Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
+igt-dev
I think intel-gfx still works for IGT development but most of the IGT work happens on igt-dev@lists.freedesktop.org now.
Harry
> ---
> include/drm-uapi/drm.h | 33 ++
> lib/igt_syncobj.c | 206 ++++++++
> lib/igt_syncobj.h | 19 +
> tests/meson.build | 1 +
> tests/syncobj_timeline.c | 1032 ++++++++++++++++++++++++++++++++++++++
> 5 files changed, 1291 insertions(+)
> create mode 100644 tests/syncobj_timeline.c
>
> diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
> index 85c685a2..dcd245d9 100644
> --- a/include/drm-uapi/drm.h
> +++ b/include/drm-uapi/drm.h
> @@ -731,6 +731,8 @@ struct drm_syncobj_handle {
>
> #define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0)
> #define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1)
> +/* wait for time point to become available */
> +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2)
> struct drm_syncobj_wait {
> __u64 handles;
> /* absolute timeout */
> @@ -741,11 +743,38 @@ struct drm_syncobj_wait {
> __u32 pad;
> };
>
> +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;
> +};
> +
> struct drm_syncobj_array {
> __u64 handles;
> __u32 count_handles;
> __u32 pad;
> };
> +struct drm_syncobj_timeline_array {
> + __u64 handles;
> + __u64 points;
> + __u32 count_handles;
> + __u32 pad;
> +};
> +
> +struct drm_syncobj_transfer {
> + __u32 src_handle;
> + __u32 dst_handle;
> + __u64 src_point;
> + __u64 dst_point;
> + __u32 flags;
> + __u32 pad;
> +};
>
> /* Query current scanout sequence number */
> struct drm_crtc_get_sequence {
> @@ -902,6 +931,10 @@ extern "C" {
> #define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees)
> #define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
> #define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease)
> +#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)
>
> /**
> * Device specific ioctls should only be in their respective headers
> diff --git a/lib/igt_syncobj.c b/lib/igt_syncobj.c
> index d9114ca8..efa2adc4 100644
> --- a/lib/igt_syncobj.c
> +++ b/lib/igt_syncobj.c
> @@ -286,3 +286,209 @@ syncobj_signal(int fd, uint32_t *handles, uint32_t count)
> {
> igt_assert_eq(__syncobj_signal(fd, handles, count), 0);
> }
> +
> +static int
> +__syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + int err = 0;
> +
> + array.handles = to_user_pointer(handles);
> + array.points = to_user_pointer(points);
> + array.count_handles = count;
> + if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array))
> + err = -errno;
> + return err;
> +}
> +
> +/**
> + * syncobj_signal:
> + * @fd: The DRM file descriptor.
> + * @handles: Array of syncobj handles to signal
> + * @points: List of point of handles to signal.
> + * @count: Count of syncobj handles.
> + *
> + * Signal a set of syncobjs.
> + */
> +void
> +syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
> +{
> + igt_assert_eq(__syncobj_timeline_signal(fd, handles, points, count), 0);
> +}
> +int
> +__syncobj_timeline_wait_ioctl(int fd, struct drm_syncobj_timeline_wait *args)
> +{
> + int err = 0;
> + if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, args))
> + err = -errno;
> + return err;
> +}
> +static int
> +__syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled)
> +{
> + struct drm_syncobj_timeline_wait args;
> + int ret;
> +
> + args.handles = to_user_pointer(handles);
> + args.points = (uint64_t)to_user_pointer(points);
> + args.timeout_nsec = timeout_nsec;
> + args.count_handles = num_handles;
> + args.flags = flags;
> + args.first_signaled = 0;
> + args.pad = 0;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
> + if (ret < 0)
> + return -errno;
> +
> + if (first_signaled)
> + *first_signaled = args.first_signaled;
> +
> + return ret;
> +}
> +int
> +syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags)
> +{
> + return __syncobj_timeline_wait(fd, handles, points, num_handles,
> + timeout_nsec, flags, NULL);
> +}
> +
> +/**
> + * syncobj_timeline_wait:
> + * @fd: The DRM file descriptor
> + * @handles: List of syncobj handles to wait for.
> + * @points: List of point of handles to wait for.
> + * @num_handles: Count of handles
> + * @timeout_nsec: Absolute wait timeout in nanoseconds.
> + * @flags: Wait ioctl flags.
> + * @first_signaled: Returned handle for first signaled syncobj.
> + *
> + * Waits in the kernel for any/all the requested syncobjs timeline point
> + * using the timeout and flags.
> + * Returns: bool value - false = timedout, true = signaled
> + */
> +bool
> +syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled)
> +{
> + int ret;
> +
> + ret = __syncobj_timeline_wait(fd, handles, points, num_handles,
> + timeout_nsec, flags, first_signaled);
> + if (ret == -ETIME)
> + return false;
> + igt_assert_eq(ret, 0);
> +
> + return true;
> +
> +}
> +
> +static int
> +__syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t handle_count)
> +{
> + struct drm_syncobj_timeline_array args;
> + int ret;
> +
> + args.handles = to_user_pointer(handles);
> + args.points = (uint64_t)to_user_pointer(points);
> + args.count_handles = handle_count;
> + args.pad = 0;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + if (ret)
> + return ret;
> + return 0;
> +}
> +
> +/**
> + * syncobj_timeline_query:
> + * @fd: The DRM file descriptor.
> + * @handles: Array of syncobj handles.
> + * @points: Array of syncobj points queried.
> + * @count: Count of syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t count)
> +{
> + igt_assert_eq(__syncobj_timeline_query(fd, handles, points, count), 0);
> +}
> +
> +static int
> +__syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle)
> +{
> + struct drm_syncobj_transfer args;
> +
> + args.src_handle = binary_handle;
> + args.dst_handle = timeline_handle;
> + args.src_point = 0;
> + args.dst_point = point;
> + args.flags = 0;
> + args.pad = 0;
> + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> +}
> +
> +/**
> + * syncobj_binary_to_timeline:
> + * @fd: The DRM file descriptor.
> + * @timeline_handles: Array of timeline syncobj handles.
> + * @point: syncobj point.
> + * @binary_handle: Array of binary syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle)
> +{
> + igt_assert_eq(__syncobj_binary_to_timeline(fd, timeline_handle, point,
> + binary_handle), 0);
> +}
> +
> +static int
> +__syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags)
> +{
> + struct drm_syncobj_transfer args;
> +
> + args.dst_handle = binary_handle;
> + args.src_handle = timeline_handle;
> + args.dst_point = 0;
> + args.src_point = point;
> + args.flags = flags;
> + args.pad = 0;
> + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> +}
> +
> +/**
> + * syncobj_binary_to_timeline:
> + * @fd: The DRM file descriptor.
> + * @timeline_handles: Array of timeline syncobj handles.
> + * @point: syncobj point.
> + * @binary_handle: Array of binary syncobj handles.
> + *
> + * query a set of syncobjs.
> + */
> +void
> +syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags)
> +{
> + igt_assert_eq(__syncobj_timeline_to_binary(fd, binary_handle,
> + timeline_handle, point,
> + flags), 0);
> +}
> diff --git a/lib/igt_syncobj.h b/lib/igt_syncobj.h
> index 44d1378d..175e5027 100644
> --- a/lib/igt_syncobj.h
> +++ b/lib/igt_syncobj.h
> @@ -65,7 +65,26 @@ int syncobj_wait_err(int fd, uint32_t *handles, uint32_t count,
> bool syncobj_wait(int fd, uint32_t *handles, uint32_t count,
> uint64_t abs_timeout_nsec, uint32_t flags,
> uint32_t *first_signaled);
> +int __syncobj_timeline_wait_ioctl(int fd,
> + struct drm_syncobj_timeline_wait *args);
> +bool syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags,
> + uint32_t *first_signaled);
> +int syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
> + unsigned num_handles,
> + int64_t timeout_nsec, unsigned flags);
> void syncobj_reset(int fd, uint32_t *handles, uint32_t count);
> void syncobj_signal(int fd, uint32_t *handles, uint32_t count);
> +void syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
> + uint32_t count);
> +void syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
> + uint64_t point, uint32_t binary_handle);
> +void syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
> + uint32_t timeline_handle,
> + uint64_t point,
> + uint32_t flags);
> +void syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points,
> + uint32_t count);
>
> #endif /* IGT_SYNCOBJ_H */
> diff --git a/tests/meson.build b/tests/meson.build
> index b8a6e61b..bddc223c 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -77,6 +77,7 @@ test_progs = [
> 'sw_sync',
> 'syncobj_basic',
> 'syncobj_wait',
> + 'syncobj_timeline',
> 'template',
> 'tools_test',
> 'v3d_get_bo_offset',
> diff --git a/tests/syncobj_timeline.c b/tests/syncobj_timeline.c
> new file mode 100644
> index 00000000..505fd9ed
> --- /dev/null
> +++ b/tests/syncobj_timeline.c
> @@ -0,0 +1,1032 @@
> +/*
> + * Copyright © 2018 Intel Corporation
> + *
> + * 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
> + * THE AUTHORS OR COPYRIGHT HOLDERS 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.
> + */
> +
> +#include "igt.h"
> +#include "sw_sync.h"
> +#include "igt_syncobj.h"
> +#include <unistd.h>
> +#include <time.h>
> +#include <sys/ioctl.h>
> +#include <pthread.h>
> +#include <signal.h>
> +#include "drm.h"
> +
> +IGT_TEST_DESCRIPTION("Tests for the drm timeline sync object API");
> +
> +/* One tenth of a second */
> +#define SHORT_TIME_NSEC 100000000ull
> +
> +#define NSECS_PER_SEC 1000000000ull
> +
> +static uint64_t
> +gettime_ns(void)
> +{
> + struct timespec current;
> + clock_gettime(CLOCK_MONOTONIC, ¤t);
> + return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec;
> +}
> +
> +static void
> +sleep_nsec(uint64_t time_nsec)
> +{
> + struct timespec t;
> + t.tv_sec = time_nsec / NSECS_PER_SEC;
> + t.tv_nsec = time_nsec % NSECS_PER_SEC;
> + igt_assert_eq(nanosleep(&t, NULL), 0);
> +}
> +
> +static uint64_t
> +short_timeout(void)
> +{
> + return gettime_ns() + SHORT_TIME_NSEC;
> +}
> +
> +static int
> +syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point)
> +{
> + struct drm_syncobj_handle;
> + uint32_t syncobj = syncobj_create(fd, 0);
> + int timeline, fence;
> +
> + timeline = sw_sync_timeline_create();
> + fence = sw_sync_timeline_create_fence(timeline, 1);
> + syncobj_import_sync_file(fd, syncobj, fence);
> + syncobj_binary_to_timeline(fd, handle, point, syncobj);
> + close(fence);
> +
> + syncobj_destroy(fd, syncobj);
> + return timeline;
> +}
> +
> +static void
> +syncobj_trigger(int fd, uint32_t handle, uint64_t point)
> +{
> + int timeline = syncobj_attach_sw_sync(fd, handle, point);
> + sw_sync_timeline_inc(timeline, 1);
> + close(timeline);
> +}
> +
> +static timer_t
> +set_timer(void (*cb)(union sigval), void *ptr, int i, uint64_t nsec)
> +{
> + timer_t timer;
> + struct sigevent sev;
> + struct itimerspec its;
> +
> + memset(&sev, 0, sizeof(sev));
> + sev.sigev_notify = SIGEV_THREAD;
> + if (ptr)
> + sev.sigev_value.sival_ptr = ptr;
> + else
> + sev.sigev_value.sival_int = i;
> + sev.sigev_notify_function = cb;
> + igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
> +
> + memset(&its, 0, sizeof(its));
> + its.it_value.tv_sec = nsec / NSEC_PER_SEC;
> + its.it_value.tv_nsec = nsec % NSEC_PER_SEC;
> + igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
> +
> + return timer;
> +}
> +
> +struct fd_handle_pair {
> + int fd;
> + uint32_t handle;
> + uint64_t point;
> +};
> +
> +static void
> +timeline_inc_func(union sigval sigval)
> +{
> + sw_sync_timeline_inc(sigval.sival_int, 1);
> +}
> +
> +static void
> +syncobj_trigger_free_pair_func(union sigval sigval)
> +{
> + struct fd_handle_pair *pair = sigval.sival_ptr;
> + syncobj_trigger(pair->fd, pair->handle, pair->point);
> + free(pair);
> +}
> +
> +static timer_t
> +syncobj_trigger_delayed(int fd, uint32_t syncobj, uint64_t point, uint64_t nsec)
> +{
> + struct fd_handle_pair *pair = malloc(sizeof(*pair));
> +
> + pair->fd = fd;
> + pair->handle = syncobj;
> + pair->point = syncobj;
> +
> + return set_timer(syncobj_trigger_free_pair_func, pair, 0, nsec);
> +}
> +
> +static void
> +test_wait_bad_flags(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + wait.flags = 0xdeadbeef;
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
> +}
> +
> +static void
> +test_wait_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
> +}
> +
> +static void
> +test_wait_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t handle = 0;
> +
> + wait.count_handles = 1;
> + wait.handles = to_user_pointer(&handle);
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ENOENT);
> +}
> +
> +static void
> +test_query_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + int ret;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_query_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_query_one_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t syncobjs[3];
> + uint64_t initial_point = 1;
> + int ret;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = 0;
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
> + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
> + &initial_point, 1, 0, 0), 0);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
> + &initial_point, 1, 0, 0), 0);
> +
> + array.count_handles = 3;
> + array.handles = to_user_pointer(syncobjs);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
> + igt_assert(ret == -1 && errno == ENOENT);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +static void
> +test_query_bad_pad(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + array.pad = 0xdeadbeef;
> + array.count_handles = 1;
> + array.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_zero_handles(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + int ret;
> +
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_signal_illegal_point(int fd)
> +{
> + struct drm_syncobj_timeline_array args = { 0 };
> + uint32_t handle = 1;
> + uint64_t point = 0;
> + int ret;
> +
> + args.count_handles = 1;
> + args.handles = to_user_pointer(&handle);
> + args.points = to_user_pointer(&point);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +static void
> +test_signal_one_illegal_handle(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t syncobjs[3];
> + uint64_t initial_point = 1;
> + int ret;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = 0;
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
> + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
> + &initial_point, 1, 0, 0), 0);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
> + &initial_point, 1, 0, 0), 0);
> +
> + array.count_handles = 3;
> + array.handles = to_user_pointer(syncobjs);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
> + igt_assert(ret == -1 && errno == ENOENT);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +static void
> +test_signal_bad_pad(int fd)
> +{
> + struct drm_syncobj_timeline_array array = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + array.pad = 0xdeadbeef;
> + array.count_handles = 1;
> + array.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +static void
> +test_signal_array(int fd)
> +{
> + uint32_t syncobjs[4];
> + uint64_t points[4] = {1, 1, 1, 0};
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> + syncobjs[2] = syncobj_create(fd, 0);
> + syncobjs[3] = syncobj_create(fd, 0);
> +
> + syncobj_timeline_signal(fd, syncobjs, points, 4);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs,
> + points, 3, 0, 0), 0);
> + igt_assert_eq(syncobj_wait_err(fd, &syncobjs[3], 1, 0, 0), 0);
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> + syncobj_destroy(fd, syncobjs[2]);
> + syncobj_destroy(fd, syncobjs[3]);
> +}
> +
> +static void
> +test_transfer_illegal_handle(int fd)
> +{
> + struct drm_syncobj_transfer args = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + args.src_handle = to_user_pointer(&handle);
> + args.dst_handle = to_user_pointer(&handle);
> + args.src_point = 1;
> + args.dst_point = 0;
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
> + igt_assert(ret == -1 && errno == ENOENT);
> +}
> +
> +static void
> +test_transfer_bad_pad(int fd)
> +{
> + struct drm_syncobj_transfer arg = { 0 };
> + uint32_t handle = 0;
> + int ret;
> +
> + arg.pad = 0xdeadbeef;
> + arg.src_handle = to_user_pointer(&handle);
> + arg.dst_handle = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &arg);
> + igt_assert(ret == -1 && errno == EINVAL);
> +}
> +
> +#define WAIT_FOR_SUBMIT (1 << 0)
> +#define WAIT_ALL (1 << 1)
> +#define WAIT_AVAILABLE (1 << 2)
> +#define WAIT_UNSUBMITTED (1 << 3)
> +#define WAIT_SUBMITTED (1 << 4)
> +#define WAIT_SIGNALED (1 << 5)
> +#define WAIT_FLAGS_MAX (1 << 6) - 1
> +
> +static uint32_t
> +flags_for_test_flags(uint32_t test_flags)
> +{
> + uint32_t flags = 0;
> +
> + if (test_flags & WAIT_FOR_SUBMIT)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> +
> + if (test_flags & WAIT_AVAILABLE)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
> +
> + if (test_flags & WAIT_ALL)
> + flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
> +
> + return flags;
> +}
> +
> +static void
> +test_single_wait(int fd, uint32_t test_flags, int expect)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = flags_for_test_flags(test_flags);
> + uint64_t point = 1;
> + int timeline = -1;
> +
> + if (test_flags & (WAIT_SUBMITTED | WAIT_SIGNALED))
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> +
> + if (test_flags & WAIT_SIGNALED)
> + sw_sync_timeline_inc(timeline, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + 0, flags), expect);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + short_timeout(), flags), expect);
> +
> + if (expect != -ETIME) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
> + UINT64_MAX, flags), expect);
> + }
> +
> + syncobj_destroy(fd, syncobj);
> + if (timeline != -1)
> + close(timeline);
> +}
> +
> +static void
> +test_wait_delayed_signal(int fd, uint32_t test_flags)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = flags_for_test_flags(test_flags);
> + uint64_t point = 1;
> + int timeline = -1;
> + timer_t timer;
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + timer = syncobj_trigger_delayed(fd, syncobj, point, SHORT_TIME_NSEC);
> + } else {
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> + timer = set_timer(timeline_inc_func, NULL,
> + timeline, SHORT_TIME_NSEC);
> + }
> +
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
> + gettime_ns() + SHORT_TIME_NSEC * 2,
> + flags, NULL));
> +
> + timer_delete(timer);
> +
> + if (timeline != -1)
> + close(timeline);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_unsignaled(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_reset(fd, &syncobj, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_signaled(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> +
> + syncobj_trigger(fd, syncobj, point);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), 0);
> +
> + syncobj_reset(fd, &syncobj, 1);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_reset_multiple_signaled(int fd)
> +{
> + uint64_t points[3] = {1, 1, 1};
> + uint32_t syncobjs[3];
> + int i;
> +
> + for (i = 0; i < 3; i++) {
> + syncobjs[i] = syncobj_create(fd, 0);
> + syncobj_trigger(fd, syncobjs[i], points[i]);
> + }
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, 0), 0);
> +
> + syncobj_reset(fd, syncobjs, 3);
> +
> + for (i = 0; i < 3; i++) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[i],
> + &points[i], 1,
> + 0, 0), -EINVAL);
> + syncobj_destroy(fd, syncobjs[i]);
> + }
> +}
> +
> +static void
> +reset_and_trigger_func(union sigval sigval)
> +{
> + struct fd_handle_pair *pair = sigval.sival_ptr;
> + syncobj_reset(pair->fd, &pair->handle, 1);
> + syncobj_trigger(pair->fd, pair->handle, pair->point);
> +}
> +
> +static void
> +test_reset_during_wait_for_submit(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> + struct fd_handle_pair pair;
> + uint64_t point = 1;
> + timer_t timer;
> +
> + pair.fd = fd;
> + pair.handle = syncobj;
> + timer = set_timer(reset_and_trigger_func, &pair, 0, SHORT_TIME_NSEC);
> +
> + /* A reset should be a no-op even if we're in the middle of a wait */
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
> + gettime_ns() + SHORT_TIME_NSEC * 2,
> + flags, NULL));
> +
> + timer_delete(timer);
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_signal(int fd)
> +{
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
> + uint64_t point = 1;
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, 0), -EINVAL);
> + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
> + 1, 0, flags), -ETIME);
> +
> + syncobj_timeline_signal(fd, &syncobj, &point, 1);
> +
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, 0, NULL));
> + igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, flags, NULL));
> +
> + syncobj_destroy(fd, syncobj);
> +}
> +
> +static void
> +test_multi_wait(int fd, uint32_t test_flags, int expect)
> +{
> + uint32_t syncobjs[3];
> + uint32_t tflag, flags;
> + int i, fidx, timeline;
> + uint64_t points[3] = {1, 1, 1};
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> + syncobjs[2] = syncobj_create(fd, 0);
> +
> + flags = flags_for_test_flags(test_flags);
> + test_flags &= ~(WAIT_ALL | WAIT_FOR_SUBMIT | WAIT_AVAILABLE);
> +
> + for (i = 0; i < 3; i++) {
> + fidx = ffs(test_flags) - 1;
> + tflag = (1 << fidx);
> +
> + if (test_flags & ~tflag)
> + test_flags &= ~tflag;
> +
> + if (tflag & (WAIT_SUBMITTED | WAIT_SIGNALED))
> + timeline = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + if (tflag & WAIT_SIGNALED)
> + sw_sync_timeline_inc(timeline, 1);
> + }
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, flags), expect);
> +
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, short_timeout(),
> + flags), expect);
> +
> + if (expect != -ETIME) {
> + igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, UINT64_MAX,
> + flags), expect);
> + }
> +
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> + syncobj_destroy(fd, syncobjs[2]);
> +}
> +
> +struct wait_thread_data {
> + int fd;
> + struct drm_syncobj_timeline_wait wait;
> +};
> +
> +static void *
> +wait_thread_func(void *data)
> +{
> + struct wait_thread_data *wait = data;
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(wait->fd, &wait->wait), 0);
> + return NULL;
> +}
> +
> +static void
> +test_wait_snapshot(int fd, uint32_t test_flags)
> +{
> + struct wait_thread_data wait = { 0 };
> + uint32_t syncobjs[2];
> + uint64_t points[2] = {1, 1};
> + int timelines[3] = { -1, -1, -1 };
> + pthread_t thread;
> +
> + syncobjs[0] = syncobj_create(fd, 0);
> + syncobjs[1] = syncobj_create(fd, 0);
> +
> + if (!(test_flags & WAIT_FOR_SUBMIT)) {
> + timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> + timelines[1] = syncobj_attach_sw_sync(fd, syncobjs[1], points[1]);
> + }
> +
> + wait.fd = fd;
> + wait.wait.handles = to_user_pointer(syncobjs);
> + wait.wait.count_handles = 2;
> + wait.wait.points = to_user_pointer(points);
> + wait.wait.timeout_nsec = short_timeout();
> + wait.wait.flags = flags_for_test_flags(test_flags);
> +
> + igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Try to fake the kernel out by triggering or partially triggering
> + * the first fence.
> + */
> + if (test_flags & WAIT_ALL) {
> + /* If it's WAIT_ALL, actually trigger it */
> + if (timelines[0] == -1)
> + syncobj_trigger(fd, syncobjs[0], points[0]);
> + else
> + sw_sync_timeline_inc(timelines[0], 1);
> + } else if (test_flags & WAIT_FOR_SUBMIT) {
> + timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> + }
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Then reset it */
> + syncobj_reset(fd, &syncobjs[0], 1);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Then "submit" it in a way that will never trigger. This way, if
> + * the kernel picks up on the new fence (it shouldn't), we'll get a
> + * timeout.
> + */
> + timelines[2] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
> +
> + sleep_nsec(SHORT_TIME_NSEC / 5);
> +
> + /* Now trigger the second fence to complete the wait */
> +
> + if (timelines[1] == -1)
> + syncobj_trigger(fd, syncobjs[1], points[1]);
> + else
> + sw_sync_timeline_inc(timelines[1], 1);
> +
> + pthread_join(thread, NULL);
> +
> + if (!(test_flags & WAIT_ALL))
> + igt_assert_eq(wait.wait.first_signaled, 1);
> +
> + close(timelines[0]);
> + close(timelines[1]);
> + close(timelines[2]);
> + syncobj_destroy(fd, syncobjs[0]);
> + syncobj_destroy(fd, syncobjs[1]);
> +}
> +
> +/* The numbers 0-7, each repeated 5x and shuffled. */
> +static const unsigned shuffled_0_7_x4[] = {
> + 2, 0, 6, 1, 1, 4, 5, 2, 0, 7, 1, 7, 6, 3, 4, 5,
> + 0, 2, 7, 3, 5, 4, 0, 6, 7, 3, 2, 5, 6, 1, 4, 3,
> +};
> +
> +enum syncobj_stage {
> + STAGE_UNSUBMITTED,
> + STAGE_SUBMITTED,
> + STAGE_SIGNALED,
> + STAGE_RESET,
> + STAGE_RESUBMITTED,
> +};
> +
> +static void
> +test_wait_complex(int fd, uint32_t test_flags)
> +{
> + struct wait_thread_data wait = { 0 };
> + uint32_t syncobjs[8];
> + uint64_t points[8] = {1, 1, 1, 1, 1, 1, 1, 1};
> + enum syncobj_stage stage[8];
> + int i, j, timelines[8];
> + uint32_t first_signaled = -1, num_signaled = 0;
> + pthread_t thread;
> +
> + for (i = 0; i < 8; i++) {
> + stage[i] = STAGE_UNSUBMITTED;
> + syncobjs[i] = syncobj_create(fd, 0);
> + }
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + for (i = 0; i < 8; i++)
> + timelines[i] = -1;
> + } else {
> + for (i = 0; i < 8; i++)
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + }
> +
> + wait.fd = fd;
> + wait.wait.handles = to_user_pointer(syncobjs);
> + wait.wait.count_handles = 2;
> + wait.wait.points = to_user_pointer(points);
> + wait.wait.timeout_nsec = gettime_ns() + NSECS_PER_SEC;
> + wait.wait.flags = flags_for_test_flags(test_flags);
> +
> + igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
> +
> + sleep_nsec(NSECS_PER_SEC / 50);
> +
> + num_signaled = 0;
> + for (j = 0; j < ARRAY_SIZE(shuffled_0_7_x4); j++) {
> + i = shuffled_0_7_x4[j];
> + igt_assert_lt(i, ARRAY_SIZE(syncobjs));
> +
> + switch (stage[i]++) {
> + case STAGE_UNSUBMITTED:
> + /* We need to submit attach a fence */
> + if (!(test_flags & WAIT_FOR_SUBMIT)) {
> + /* We had to attach one up-front */
> + igt_assert_neq(timelines[i], -1);
> + break;
> + }
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + break;
> +
> + case STAGE_SUBMITTED:
> + /* We have a fence, trigger it */
> + igt_assert_neq(timelines[i], -1);
> + sw_sync_timeline_inc(timelines[i], 1);
> + close(timelines[i]);
> + timelines[i] = -1;
> + if (num_signaled == 0)
> + first_signaled = i;
> + num_signaled++;
> + break;
> +
> + case STAGE_SIGNALED:
> + /* We're already signaled, reset */
> + syncobj_reset(fd, &syncobjs[i], 1);
> + break;
> +
> + case STAGE_RESET:
> + /* We're reset, submit and don't signal */
> + timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
> + points[i]);
> + break;
> +
> + case STAGE_RESUBMITTED:
> + igt_assert(!"Should not reach this stage");
> + break;
> + }
> +
> + if (test_flags & WAIT_ALL) {
> + if (num_signaled == ARRAY_SIZE(syncobjs))
> + break;
> + } else {
> + if (num_signaled > 0)
> + break;
> + }
> +
> + sleep_nsec(NSECS_PER_SEC / 100);
> + }
> +
> + pthread_join(thread, NULL);
> +
> + if (test_flags & WAIT_ALL) {
> + igt_assert_eq(num_signaled, ARRAY_SIZE(syncobjs));
> + } else {
> + igt_assert_eq(num_signaled, 1);
> + igt_assert_eq(wait.wait.first_signaled, first_signaled);
> + }
> +
> + for (i = 0; i < 8; i++) {
> + close(timelines[i]);
> + syncobj_destroy(fd, syncobjs[i]);
> + }
> +}
> +
> +static void
> +test_wait_interrupted(int fd, uint32_t test_flags)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t syncobj = syncobj_create(fd, 0);
> + uint64_t point = 1;
> + int timeline;
> +
> + wait.handles = to_user_pointer(&syncobj);
> + wait.points = to_user_pointer(&point);
> + wait.count_handles = 1;
> + wait.flags = flags_for_test_flags(test_flags);
> +
> + if (test_flags & WAIT_FOR_SUBMIT) {
> + wait.timeout_nsec = short_timeout();
> + igt_while_interruptible(true)
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
> + }
> +
> + timeline = syncobj_attach_sw_sync(fd, syncobj, point);
> +
> + wait.timeout_nsec = short_timeout();
> + igt_while_interruptible(true)
> + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
> +
> + syncobj_destroy(fd, syncobj);
> + close(timeline);
> +}
> +
> +static bool
> +has_syncobj_timeline_wait(int fd)
> +{
> + struct drm_syncobj_timeline_wait wait = { 0 };
> + uint32_t handle = 0;
> + uint64_t value;
> + int ret;
> +
> + if (drmGetCap(fd, DRM_CAP_SYNCOBJ, &value))
> + return false;
> + if (!value)
> + return false;
> +
> + /* Try waiting for zero sync objects should fail with EINVAL */
> + wait.count_handles = 1;
> + wait.handles = to_user_pointer(&handle);
> + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &wait);
> + return ret == -1 && errno == ENOENT;
> +}
> +
> +igt_main
> +{
> + int fd = -1;
> +
> + igt_fixture {
> + fd = drm_open_driver(DRIVER_ANY);
> + igt_require(has_syncobj_timeline_wait(fd));
> + igt_require_sw_sync();
> + }
> +
> + igt_subtest("invalid-wait-bad-flags")
> + test_wait_bad_flags(fd);
> +
> + igt_subtest("invalid-wait-zero-handles")
> + test_wait_zero_handles(fd);
> +
> + igt_subtest("invalid-wait-illegal-handle")
> + test_wait_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-zero-handles")
> + test_query_zero_handles(fd);
> +
> + igt_subtest("invalid-query-illegal-handle")
> + test_query_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-one-illegal-handle")
> + test_query_one_illegal_handle(fd);
> +
> + igt_subtest("invalid-query-bad-pad")
> + test_query_bad_pad(fd);
> +
> + igt_subtest("invalid-signal-zero-handles")
> + test_signal_zero_handles(fd);
> +
> + igt_subtest("invalid-signal-illegal-handle")
> + test_signal_illegal_handle(fd);
> +
> + igt_subtest("invalid-signal-illegal-point")
> + test_signal_illegal_point(fd);
> +
> + igt_subtest("invalid-signal-one-illegal-handle")
> + test_signal_one_illegal_handle(fd);
> +
> + igt_subtest("invalid-signal-bad-pad")
> + test_signal_bad_pad(fd);
> +
> + igt_subtest("invalid-signal-array")
> + test_signal_array(fd);
> +
> + igt_subtest("invalid-transfer-illegal-handle")
> + test_transfer_illegal_handle(fd);
> +
> + igt_subtest("invalid-transfer-bad-pad")
> + test_transfer_bad_pad(fd);
> +
> + for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
> + int err;
> +
> + /* Only one wait mode for single-wait tests */
> + if (__builtin_popcount(flags & (WAIT_UNSUBMITTED |
> + WAIT_SUBMITTED |
> + WAIT_SIGNALED)) != 1)
> + continue;
> +
> + if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT))
> + err = -EINVAL;
> + else if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
> + err = -ETIME;
> + else
> + err = 0;
> +
> + igt_subtest_f("%ssingle-wait%s%s%s%s%s%s",
> + err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
> + (flags & WAIT_ALL) ? "-all" : "",
> + (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
> + (flags & WAIT_AVAILABLE) ? "-available" : "",
> + (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
> + (flags & WAIT_SUBMITTED) ? "-submitted" : "",
> + (flags & WAIT_SIGNALED) ? "-signaled" : "")
> + test_single_wait(fd, flags, err);
> + }
> +
> + igt_subtest("wait-delayed-signal")
> + test_wait_delayed_signal(fd, 0);
> +
> + igt_subtest("wait-for-submit-delayed-submit")
> + test_wait_delayed_signal(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-delayed-signal")
> + test_wait_delayed_signal(fd, WAIT_ALL);
> +
> + igt_subtest("wait-all-for-submit-delayed-submit")
> + test_wait_delayed_signal(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("reset-unsignaled")
> + test_reset_unsignaled(fd);
> +
> + igt_subtest("reset-signaled")
> + test_reset_signaled(fd);
> +
> + igt_subtest("reset-multiple-signaled")
> + test_reset_multiple_signaled(fd);
> +
> + igt_subtest("reset-during-wait-for-submit")
> + test_reset_during_wait_for_submit(fd);
> +
> + igt_subtest("signal")
> + test_signal(fd);
> +
> + for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
> + int err;
> +
> + /* At least one wait mode for multi-wait tests */
> + if (!(flags & (WAIT_UNSUBMITTED |
> + WAIT_SUBMITTED |
> + WAIT_SIGNALED)))
> + continue;
> +
> + err = 0;
> + if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT)) {
> + err = -EINVAL;
> + } else if (flags & WAIT_ALL) {
> + if (flags & (WAIT_UNSUBMITTED | WAIT_SUBMITTED))
> + err = -ETIME;
> + if (!(flags & WAIT_UNSUBMITTED) && (flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE))
> + err = 0;
> + } else {
> + if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
> + err = -ETIME;
> + }
> +
> + igt_subtest_f("%smulti-wait%s%s%s%s%s%s",
> + err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
> + (flags & WAIT_ALL) ? "-all" : "",
> + (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
> + (flags & WAIT_AVAILABLE) ? "-available" : "",
> + (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
> + (flags & WAIT_SUBMITTED) ? "-submitted" : "",
> + (flags & WAIT_SIGNALED) ? "-signaled" : "")
> + test_multi_wait(fd, flags, err);
> + }
> + igt_subtest("wait-any-snapshot")
> + test_wait_snapshot(fd, 0);
> +
> + igt_subtest("wait-all-snapshot")
> + test_wait_snapshot(fd, WAIT_ALL);
> +
> + igt_subtest("wait-for-submit-snapshot")
> + test_wait_snapshot(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-for-submit-snapshot")
> + test_wait_snapshot(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-any-complex")
> + test_wait_complex(fd, 0);
> +
> + igt_subtest("wait-all-complex")
> + test_wait_complex(fd, WAIT_ALL);
> +
> + igt_subtest("wait-for-submit-complex")
> + test_wait_complex(fd, WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-all-for-submit-complex")
> + test_wait_complex(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
> +
> + igt_subtest("wait-any-interrupted")
> + test_wait_interrupted(fd, 0);
> +
> + igt_subtest("wait-all-interrupted")
> + test_wait_interrupted(fd, WAIT_ALL);
> +}
>
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH i-g-t] igt: add timeline test cases v2
@ 2018-12-07 15:57 Chunming Zhou
0 siblings, 0 replies; 6+ messages in thread
From: Chunming Zhou @ 2018-12-07 15:57 UTC (permalink / raw)
To: Christian.Koenig, dri-devel, amd-gfx, intel-gfx; +Cc: Chunming Zhou
v2: adapt to new transfer ioctl
Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
---
include/drm-uapi/drm.h | 33 ++
lib/igt_syncobj.c | 206 ++++++++
lib/igt_syncobj.h | 19 +
tests/meson.build | 1 +
tests/syncobj_timeline.c | 1051 ++++++++++++++++++++++++++++++++++++++
5 files changed, 1310 insertions(+)
create mode 100644 tests/syncobj_timeline.c
diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h
index 85c685a2..dcd245d9 100644
--- a/include/drm-uapi/drm.h
+++ b/include/drm-uapi/drm.h
@@ -731,6 +731,8 @@ struct drm_syncobj_handle {
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0)
#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1)
+/* wait for time point to become available */
+#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2)
struct drm_syncobj_wait {
__u64 handles;
/* absolute timeout */
@@ -741,11 +743,38 @@ struct drm_syncobj_wait {
__u32 pad;
};
+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;
+};
+
struct drm_syncobj_array {
__u64 handles;
__u32 count_handles;
__u32 pad;
};
+struct drm_syncobj_timeline_array {
+ __u64 handles;
+ __u64 points;
+ __u32 count_handles;
+ __u32 pad;
+};
+
+struct drm_syncobj_transfer {
+ __u32 src_handle;
+ __u32 dst_handle;
+ __u64 src_point;
+ __u64 dst_point;
+ __u32 flags;
+ __u32 pad;
+};
/* Query current scanout sequence number */
struct drm_crtc_get_sequence {
@@ -902,6 +931,10 @@ extern "C" {
#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees)
#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease)
#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease)
+#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)
/**
* Device specific ioctls should only be in their respective headers
diff --git a/lib/igt_syncobj.c b/lib/igt_syncobj.c
index d9114ca8..efa2adc4 100644
--- a/lib/igt_syncobj.c
+++ b/lib/igt_syncobj.c
@@ -286,3 +286,209 @@ syncobj_signal(int fd, uint32_t *handles, uint32_t count)
{
igt_assert_eq(__syncobj_signal(fd, handles, count), 0);
}
+
+static int
+__syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ int err = 0;
+
+ array.handles = to_user_pointer(handles);
+ array.points = to_user_pointer(points);
+ array.count_handles = count;
+ if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array))
+ err = -errno;
+ return err;
+}
+
+/**
+ * syncobj_signal:
+ * @fd: The DRM file descriptor.
+ * @handles: Array of syncobj handles to signal
+ * @points: List of point of handles to signal.
+ * @count: Count of syncobj handles.
+ *
+ * Signal a set of syncobjs.
+ */
+void
+syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points, uint32_t count)
+{
+ igt_assert_eq(__syncobj_timeline_signal(fd, handles, points, count), 0);
+}
+int
+__syncobj_timeline_wait_ioctl(int fd, struct drm_syncobj_timeline_wait *args)
+{
+ int err = 0;
+ if (drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, args))
+ err = -errno;
+ return err;
+}
+static int
+__syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ struct drm_syncobj_timeline_wait args;
+ int ret;
+
+ args.handles = to_user_pointer(handles);
+ args.points = (uint64_t)to_user_pointer(points);
+ args.timeout_nsec = timeout_nsec;
+ args.count_handles = num_handles;
+ args.flags = flags;
+ args.first_signaled = 0;
+ args.pad = 0;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &args);
+ if (ret < 0)
+ return -errno;
+
+ if (first_signaled)
+ *first_signaled = args.first_signaled;
+
+ return ret;
+}
+int
+syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags)
+{
+ return __syncobj_timeline_wait(fd, handles, points, num_handles,
+ timeout_nsec, flags, NULL);
+}
+
+/**
+ * syncobj_timeline_wait:
+ * @fd: The DRM file descriptor
+ * @handles: List of syncobj handles to wait for.
+ * @points: List of point of handles to wait for.
+ * @num_handles: Count of handles
+ * @timeout_nsec: Absolute wait timeout in nanoseconds.
+ * @flags: Wait ioctl flags.
+ * @first_signaled: Returned handle for first signaled syncobj.
+ *
+ * Waits in the kernel for any/all the requested syncobjs timeline point
+ * using the timeout and flags.
+ * Returns: bool value - false = timedout, true = signaled
+ */
+bool
+syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled)
+{
+ int ret;
+
+ ret = __syncobj_timeline_wait(fd, handles, points, num_handles,
+ timeout_nsec, flags, first_signaled);
+ if (ret == -ETIME)
+ return false;
+ igt_assert_eq(ret, 0);
+
+ return true;
+
+}
+
+static int
+__syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t handle_count)
+{
+ struct drm_syncobj_timeline_array args;
+ int ret;
+
+ args.handles = to_user_pointer(handles);
+ args.points = (uint64_t)to_user_pointer(points);
+ args.count_handles = handle_count;
+ args.pad = 0;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+/**
+ * syncobj_timeline_query:
+ * @fd: The DRM file descriptor.
+ * @handles: Array of syncobj handles.
+ * @points: Array of syncobj points queried.
+ * @count: Count of syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t count)
+{
+ igt_assert_eq(__syncobj_timeline_query(fd, handles, points, count), 0);
+}
+
+static int
+__syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle)
+{
+ struct drm_syncobj_transfer args;
+
+ args.src_handle = binary_handle;
+ args.dst_handle = timeline_handle;
+ args.src_point = 0;
+ args.dst_point = point;
+ args.flags = 0;
+ args.pad = 0;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+}
+
+/**
+ * syncobj_binary_to_timeline:
+ * @fd: The DRM file descriptor.
+ * @timeline_handles: Array of timeline syncobj handles.
+ * @point: syncobj point.
+ * @binary_handle: Array of binary syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle)
+{
+ igt_assert_eq(__syncobj_binary_to_timeline(fd, timeline_handle, point,
+ binary_handle), 0);
+}
+
+static int
+__syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags)
+{
+ struct drm_syncobj_transfer args;
+
+ args.dst_handle = binary_handle;
+ args.src_handle = timeline_handle;
+ args.dst_point = 0;
+ args.src_point = point;
+ args.flags = flags;
+ args.pad = 0;
+ return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+}
+
+/**
+ * syncobj_binary_to_timeline:
+ * @fd: The DRM file descriptor.
+ * @timeline_handles: Array of timeline syncobj handles.
+ * @point: syncobj point.
+ * @binary_handle: Array of binary syncobj handles.
+ *
+ * query a set of syncobjs.
+ */
+void
+syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags)
+{
+ igt_assert_eq(__syncobj_timeline_to_binary(fd, binary_handle,
+ timeline_handle, point,
+ flags), 0);
+}
diff --git a/lib/igt_syncobj.h b/lib/igt_syncobj.h
index 44d1378d..175e5027 100644
--- a/lib/igt_syncobj.h
+++ b/lib/igt_syncobj.h
@@ -65,7 +65,26 @@ int syncobj_wait_err(int fd, uint32_t *handles, uint32_t count,
bool syncobj_wait(int fd, uint32_t *handles, uint32_t count,
uint64_t abs_timeout_nsec, uint32_t flags,
uint32_t *first_signaled);
+int __syncobj_timeline_wait_ioctl(int fd,
+ struct drm_syncobj_timeline_wait *args);
+bool syncobj_timeline_wait(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags,
+ uint32_t *first_signaled);
+int syncobj_timeline_wait_err(int fd, uint32_t *handles, uint64_t *points,
+ unsigned num_handles,
+ int64_t timeout_nsec, unsigned flags);
void syncobj_reset(int fd, uint32_t *handles, uint32_t count);
void syncobj_signal(int fd, uint32_t *handles, uint32_t count);
+void syncobj_timeline_query(int fd, uint32_t *handles, uint64_t **points,
+ uint32_t count);
+void syncobj_binary_to_timeline(int fd, uint32_t timeline_handle,
+ uint64_t point, uint32_t binary_handle);
+void syncobj_timeline_to_binary(int fd, uint32_t binary_handle,
+ uint32_t timeline_handle,
+ uint64_t point,
+ uint32_t flags);
+void syncobj_timeline_signal(int fd, uint32_t *handles, uint64_t *points,
+ uint32_t count);
#endif /* IGT_SYNCOBJ_H */
diff --git a/tests/meson.build b/tests/meson.build
index b8a6e61b..bddc223c 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -77,6 +77,7 @@ test_progs = [
'sw_sync',
'syncobj_basic',
'syncobj_wait',
+ 'syncobj_timeline',
'template',
'tools_test',
'v3d_get_bo_offset',
diff --git a/tests/syncobj_timeline.c b/tests/syncobj_timeline.c
new file mode 100644
index 00000000..39a8c40e
--- /dev/null
+++ b/tests/syncobj_timeline.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * 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
+ * THE AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+#include "igt.h"
+#include "sw_sync.h"
+#include "igt_syncobj.h"
+#include <unistd.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <signal.h>
+#include "drm.h"
+
+IGT_TEST_DESCRIPTION("Tests for the drm timeline sync object API");
+
+/* One tenth of a second */
+#define SHORT_TIME_NSEC 100000000ull
+
+#define NSECS_PER_SEC 1000000000ull
+
+static uint64_t
+gettime_ns(void)
+{
+ struct timespec current;
+ clock_gettime(CLOCK_MONOTONIC, ¤t);
+ return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec;
+}
+
+static void
+sleep_nsec(uint64_t time_nsec)
+{
+ struct timespec t;
+ t.tv_sec = time_nsec / NSECS_PER_SEC;
+ t.tv_nsec = time_nsec % NSECS_PER_SEC;
+ igt_assert_eq(nanosleep(&t, NULL), 0);
+}
+
+static uint64_t
+short_timeout(void)
+{
+ return gettime_ns() + SHORT_TIME_NSEC;
+}
+
+static int
+syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point)
+{
+ struct drm_syncobj_handle;
+ uint32_t syncobj = syncobj_create(fd, 0);
+ int timeline, fence;
+
+ timeline = sw_sync_timeline_create();
+ fence = sw_sync_timeline_create_fence(timeline, 1);
+ syncobj_import_sync_file(fd, syncobj, fence);
+ syncobj_binary_to_timeline(fd, handle, point, syncobj);
+ close(fence);
+
+ syncobj_destroy(fd, syncobj);
+ return timeline;
+}
+
+static void
+syncobj_trigger(int fd, uint32_t handle, uint64_t point)
+{
+ int timeline = syncobj_attach_sw_sync(fd, handle, point);
+ sw_sync_timeline_inc(timeline, 1);
+ close(timeline);
+}
+
+static timer_t
+set_timer(void (*cb)(union sigval), void *ptr, int i, uint64_t nsec)
+{
+ timer_t timer;
+ struct sigevent sev;
+ struct itimerspec its;
+
+ memset(&sev, 0, sizeof(sev));
+ sev.sigev_notify = SIGEV_THREAD;
+ if (ptr)
+ sev.sigev_value.sival_ptr = ptr;
+ else
+ sev.sigev_value.sival_int = i;
+ sev.sigev_notify_function = cb;
+ igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
+
+ memset(&its, 0, sizeof(its));
+ its.it_value.tv_sec = nsec / NSEC_PER_SEC;
+ its.it_value.tv_nsec = nsec % NSEC_PER_SEC;
+ igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
+
+ return timer;
+}
+
+struct fd_handle_pair {
+ int fd;
+ uint32_t handle;
+ uint64_t point;
+};
+
+static void
+timeline_inc_func(union sigval sigval)
+{
+ sw_sync_timeline_inc(sigval.sival_int, 1);
+}
+
+static void
+syncobj_trigger_free_pair_func(union sigval sigval)
+{
+ struct fd_handle_pair *pair = sigval.sival_ptr;
+ syncobj_trigger(pair->fd, pair->handle, pair->point);
+ free(pair);
+}
+
+static timer_t
+syncobj_trigger_delayed(int fd, uint32_t syncobj, uint64_t point, uint64_t nsec)
+{
+ struct fd_handle_pair *pair = malloc(sizeof(*pair));
+
+ pair->fd = fd;
+ pair->handle = syncobj;
+ pair->point = syncobj;
+
+ return set_timer(syncobj_trigger_free_pair_func, pair, 0, nsec);
+}
+
+static void
+test_wait_bad_flags(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ wait.flags = 0xdeadbeef;
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
+}
+
+static void
+test_wait_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL);
+}
+
+static void
+test_wait_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t handle = 0;
+
+ wait.count_handles = 1;
+ wait.handles = to_user_pointer(&handle);
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ENOENT);
+}
+
+static void
+test_query_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ int ret;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_query_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_query_one_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t syncobjs[3];
+ uint64_t initial_point = 1;
+ int ret;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = 0;
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
+ syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
+ &initial_point, 1, 0, 0), 0);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
+ &initial_point, 1, 0, 0), 0);
+
+ array.count_handles = 3;
+ array.handles = to_user_pointer(syncobjs);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
+ igt_assert(ret == -1 && errno == ENOENT);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+static void
+test_query_bad_pad(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ array.pad = 0xdeadbeef;
+ array.count_handles = 1;
+ array.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_zero_handles(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ int ret;
+
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_signal_illegal_point(int fd)
+{
+ struct drm_syncobj_timeline_array args = { 0 };
+ uint32_t handle = 1;
+ uint64_t point = 0;
+ int ret;
+
+ args.count_handles = 1;
+ args.handles = to_user_pointer(&handle);
+ args.points = to_user_pointer(&point);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+static void
+test_signal_one_illegal_handle(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t syncobjs[3];
+ uint64_t initial_point = 1;
+ int ret;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = 0;
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1);
+ syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0],
+ &initial_point, 1, 0, 0), 0);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2],
+ &initial_point, 1, 0, 0), 0);
+
+ array.count_handles = 3;
+ array.handles = to_user_pointer(syncobjs);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
+ igt_assert(ret == -1 && errno == ENOENT);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+static void
+test_signal_bad_pad(int fd)
+{
+ struct drm_syncobj_timeline_array array = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ array.pad = 0xdeadbeef;
+ array.count_handles = 1;
+ array.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_signal_array(int fd)
+{
+ uint32_t syncobjs[4];
+ uint64_t points[4] = {1, 1, 1, 0};
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+ syncobjs[2] = syncobj_create(fd, 0);
+ syncobjs[3] = syncobj_create(fd, 0);
+
+ syncobj_timeline_signal(fd, syncobjs, points, 4);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs,
+ points, 3, 0, 0), 0);
+ igt_assert_eq(syncobj_wait_err(fd, &syncobjs[3], 1, 0, 0), 0);
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+ syncobj_destroy(fd, syncobjs[2]);
+ syncobj_destroy(fd, syncobjs[3]);
+}
+
+static void
+test_transfer_illegal_handle(int fd)
+{
+ struct drm_syncobj_transfer args = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ args.src_handle = to_user_pointer(&handle);
+ args.dst_handle = to_user_pointer(&handle);
+ args.src_point = 1;
+ args.dst_point = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+ igt_assert(ret == -1 && errno == ENOENT);
+}
+
+static void
+test_transfer_illegal_point(int fd)
+{
+ struct drm_syncobj_transfer args = { 0 };
+ uint32_t handle1 = 1;
+ uint32_t handle2 = 2;
+ int ret;
+
+ args.src_handle = to_user_pointer(&handle1);
+ args.dst_handle = to_user_pointer(&handle2);
+ args.src_point = 0;
+ args.dst_point = 0;
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &args);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+static void
+test_transfer_bad_pad(int fd)
+{
+ struct drm_syncobj_transfer arg = { 0 };
+ uint32_t handle = 0;
+ int ret;
+
+ arg.pad = 0xdeadbeef;
+ arg.src_handle = to_user_pointer(&handle);
+ arg.dst_handle = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TRANSFER, &arg);
+ igt_assert(ret == -1 && errno == EINVAL);
+}
+
+#define WAIT_FOR_SUBMIT (1 << 0)
+#define WAIT_ALL (1 << 1)
+#define WAIT_AVAILABLE (1 << 2)
+#define WAIT_UNSUBMITTED (1 << 3)
+#define WAIT_SUBMITTED (1 << 4)
+#define WAIT_SIGNALED (1 << 5)
+#define WAIT_FLAGS_MAX (1 << 6) - 1
+
+static uint32_t
+flags_for_test_flags(uint32_t test_flags)
+{
+ uint32_t flags = 0;
+
+ if (test_flags & WAIT_FOR_SUBMIT)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+
+ if (test_flags & WAIT_AVAILABLE)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE;
+
+ if (test_flags & WAIT_ALL)
+ flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
+
+ return flags;
+}
+
+static void
+test_single_wait(int fd, uint32_t test_flags, int expect)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = flags_for_test_flags(test_flags);
+ uint64_t point = 1;
+ int timeline = -1;
+
+ if (test_flags & (WAIT_SUBMITTED | WAIT_SIGNALED))
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+
+ if (test_flags & WAIT_SIGNALED)
+ sw_sync_timeline_inc(timeline, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ 0, flags), expect);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ short_timeout(), flags), expect);
+
+ if (expect != -ETIME) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point, 1,
+ UINT64_MAX, flags), expect);
+ }
+
+ syncobj_destroy(fd, syncobj);
+ if (timeline != -1)
+ close(timeline);
+}
+
+static void
+test_wait_delayed_signal(int fd, uint32_t test_flags)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = flags_for_test_flags(test_flags);
+ uint64_t point = 1;
+ int timeline = -1;
+ timer_t timer;
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ timer = syncobj_trigger_delayed(fd, syncobj, point, SHORT_TIME_NSEC);
+ } else {
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+ timer = set_timer(timeline_inc_func, NULL,
+ timeline, SHORT_TIME_NSEC);
+ }
+
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
+ gettime_ns() + SHORT_TIME_NSEC * 2,
+ flags, NULL));
+
+ timer_delete(timer);
+
+ if (timeline != -1)
+ close(timeline);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_unsignaled(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_reset(fd, &syncobj, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_signaled(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+
+ syncobj_trigger(fd, syncobj, point);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), 0);
+
+ syncobj_reset(fd, &syncobj, 1);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_reset_multiple_signaled(int fd)
+{
+ uint64_t points[3] = {1, 1, 1};
+ uint32_t syncobjs[3];
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ syncobjs[i] = syncobj_create(fd, 0);
+ syncobj_trigger(fd, syncobjs[i], points[i]);
+ }
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, 0), 0);
+
+ syncobj_reset(fd, syncobjs, 3);
+
+ for (i = 0; i < 3; i++) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[i],
+ &points[i], 1,
+ 0, 0), -EINVAL);
+ syncobj_destroy(fd, syncobjs[i]);
+ }
+}
+
+static void
+reset_and_trigger_func(union sigval sigval)
+{
+ struct fd_handle_pair *pair = sigval.sival_ptr;
+ syncobj_reset(pair->fd, &pair->handle, 1);
+ syncobj_trigger(pair->fd, pair->handle, pair->point);
+}
+
+static void
+test_reset_during_wait_for_submit(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+ struct fd_handle_pair pair;
+ uint64_t point = 1;
+ timer_t timer;
+
+ pair.fd = fd;
+ pair.handle = syncobj;
+ timer = set_timer(reset_and_trigger_func, &pair, 0, SHORT_TIME_NSEC);
+
+ /* A reset should be a no-op even if we're in the middle of a wait */
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1,
+ gettime_ns() + SHORT_TIME_NSEC * 2,
+ flags, NULL));
+
+ timer_delete(timer);
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_signal(int fd)
+{
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint32_t flags = LOCAL_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
+ uint64_t point = 1;
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, 0), -EINVAL);
+ igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobj, &point,
+ 1, 0, flags), -ETIME);
+
+ syncobj_timeline_signal(fd, &syncobj, &point, 1);
+
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, 0, NULL));
+ igt_assert(syncobj_timeline_wait(fd, &syncobj, &point, 1, 0, flags, NULL));
+
+ syncobj_destroy(fd, syncobj);
+}
+
+static void
+test_multi_wait(int fd, uint32_t test_flags, int expect)
+{
+ uint32_t syncobjs[3];
+ uint32_t tflag, flags;
+ int i, fidx, timeline;
+ uint64_t points[3] = {1, 1, 1};
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+ syncobjs[2] = syncobj_create(fd, 0);
+
+ flags = flags_for_test_flags(test_flags);
+ test_flags &= ~(WAIT_ALL | WAIT_FOR_SUBMIT | WAIT_AVAILABLE);
+
+ for (i = 0; i < 3; i++) {
+ fidx = ffs(test_flags) - 1;
+ tflag = (1 << fidx);
+
+ if (test_flags & ~tflag)
+ test_flags &= ~tflag;
+
+ if (tflag & (WAIT_SUBMITTED | WAIT_SIGNALED))
+ timeline = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ if (tflag & WAIT_SIGNALED)
+ sw_sync_timeline_inc(timeline, 1);
+ }
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, 0, flags), expect);
+
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, short_timeout(),
+ flags), expect);
+
+ if (expect != -ETIME) {
+ igt_assert_eq(syncobj_timeline_wait_err(fd, syncobjs, points, 3, UINT64_MAX,
+ flags), expect);
+ }
+
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+ syncobj_destroy(fd, syncobjs[2]);
+}
+
+struct wait_thread_data {
+ int fd;
+ struct drm_syncobj_timeline_wait wait;
+};
+
+static void *
+wait_thread_func(void *data)
+{
+ struct wait_thread_data *wait = data;
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(wait->fd, &wait->wait), 0);
+ return NULL;
+}
+
+static void
+test_wait_snapshot(int fd, uint32_t test_flags)
+{
+ struct wait_thread_data wait = { 0 };
+ uint32_t syncobjs[2];
+ uint64_t points[2] = {1, 1};
+ int timelines[3] = { -1, -1, -1 };
+ pthread_t thread;
+
+ syncobjs[0] = syncobj_create(fd, 0);
+ syncobjs[1] = syncobj_create(fd, 0);
+
+ if (!(test_flags & WAIT_FOR_SUBMIT)) {
+ timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+ timelines[1] = syncobj_attach_sw_sync(fd, syncobjs[1], points[1]);
+ }
+
+ wait.fd = fd;
+ wait.wait.handles = to_user_pointer(syncobjs);
+ wait.wait.count_handles = 2;
+ wait.wait.points = to_user_pointer(points);
+ wait.wait.timeout_nsec = short_timeout();
+ wait.wait.flags = flags_for_test_flags(test_flags);
+
+ igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Try to fake the kernel out by triggering or partially triggering
+ * the first fence.
+ */
+ if (test_flags & WAIT_ALL) {
+ /* If it's WAIT_ALL, actually trigger it */
+ if (timelines[0] == -1)
+ syncobj_trigger(fd, syncobjs[0], points[0]);
+ else
+ sw_sync_timeline_inc(timelines[0], 1);
+ } else if (test_flags & WAIT_FOR_SUBMIT) {
+ timelines[0] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+ }
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Then reset it */
+ syncobj_reset(fd, &syncobjs[0], 1);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Then "submit" it in a way that will never trigger. This way, if
+ * the kernel picks up on the new fence (it shouldn't), we'll get a
+ * timeout.
+ */
+ timelines[2] = syncobj_attach_sw_sync(fd, syncobjs[0], points[0]);
+
+ sleep_nsec(SHORT_TIME_NSEC / 5);
+
+ /* Now trigger the second fence to complete the wait */
+
+ if (timelines[1] == -1)
+ syncobj_trigger(fd, syncobjs[1], points[1]);
+ else
+ sw_sync_timeline_inc(timelines[1], 1);
+
+ pthread_join(thread, NULL);
+
+ if (!(test_flags & WAIT_ALL))
+ igt_assert_eq(wait.wait.first_signaled, 1);
+
+ close(timelines[0]);
+ close(timelines[1]);
+ close(timelines[2]);
+ syncobj_destroy(fd, syncobjs[0]);
+ syncobj_destroy(fd, syncobjs[1]);
+}
+
+/* The numbers 0-7, each repeated 5x and shuffled. */
+static const unsigned shuffled_0_7_x4[] = {
+ 2, 0, 6, 1, 1, 4, 5, 2, 0, 7, 1, 7, 6, 3, 4, 5,
+ 0, 2, 7, 3, 5, 4, 0, 6, 7, 3, 2, 5, 6, 1, 4, 3,
+};
+
+enum syncobj_stage {
+ STAGE_UNSUBMITTED,
+ STAGE_SUBMITTED,
+ STAGE_SIGNALED,
+ STAGE_RESET,
+ STAGE_RESUBMITTED,
+};
+
+static void
+test_wait_complex(int fd, uint32_t test_flags)
+{
+ struct wait_thread_data wait = { 0 };
+ uint32_t syncobjs[8];
+ uint64_t points[8] = {1, 1, 1, 1, 1, 1, 1, 1};
+ enum syncobj_stage stage[8];
+ int i, j, timelines[8];
+ uint32_t first_signaled = -1, num_signaled = 0;
+ pthread_t thread;
+
+ for (i = 0; i < 8; i++) {
+ stage[i] = STAGE_UNSUBMITTED;
+ syncobjs[i] = syncobj_create(fd, 0);
+ }
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ for (i = 0; i < 8; i++)
+ timelines[i] = -1;
+ } else {
+ for (i = 0; i < 8; i++)
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ }
+
+ wait.fd = fd;
+ wait.wait.handles = to_user_pointer(syncobjs);
+ wait.wait.count_handles = 2;
+ wait.wait.points = to_user_pointer(points);
+ wait.wait.timeout_nsec = gettime_ns() + NSECS_PER_SEC;
+ wait.wait.flags = flags_for_test_flags(test_flags);
+
+ igt_assert_eq(pthread_create(&thread, NULL, wait_thread_func, &wait), 0);
+
+ sleep_nsec(NSECS_PER_SEC / 50);
+
+ num_signaled = 0;
+ for (j = 0; j < ARRAY_SIZE(shuffled_0_7_x4); j++) {
+ i = shuffled_0_7_x4[j];
+ igt_assert_lt(i, ARRAY_SIZE(syncobjs));
+
+ switch (stage[i]++) {
+ case STAGE_UNSUBMITTED:
+ /* We need to submit attach a fence */
+ if (!(test_flags & WAIT_FOR_SUBMIT)) {
+ /* We had to attach one up-front */
+ igt_assert_neq(timelines[i], -1);
+ break;
+ }
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ break;
+
+ case STAGE_SUBMITTED:
+ /* We have a fence, trigger it */
+ igt_assert_neq(timelines[i], -1);
+ sw_sync_timeline_inc(timelines[i], 1);
+ close(timelines[i]);
+ timelines[i] = -1;
+ if (num_signaled == 0)
+ first_signaled = i;
+ num_signaled++;
+ break;
+
+ case STAGE_SIGNALED:
+ /* We're already signaled, reset */
+ syncobj_reset(fd, &syncobjs[i], 1);
+ break;
+
+ case STAGE_RESET:
+ /* We're reset, submit and don't signal */
+ timelines[i] = syncobj_attach_sw_sync(fd, syncobjs[i],
+ points[i]);
+ break;
+
+ case STAGE_RESUBMITTED:
+ igt_assert(!"Should not reach this stage");
+ break;
+ }
+
+ if (test_flags & WAIT_ALL) {
+ if (num_signaled == ARRAY_SIZE(syncobjs))
+ break;
+ } else {
+ if (num_signaled > 0)
+ break;
+ }
+
+ sleep_nsec(NSECS_PER_SEC / 100);
+ }
+
+ pthread_join(thread, NULL);
+
+ if (test_flags & WAIT_ALL) {
+ igt_assert_eq(num_signaled, ARRAY_SIZE(syncobjs));
+ } else {
+ igt_assert_eq(num_signaled, 1);
+ igt_assert_eq(wait.wait.first_signaled, first_signaled);
+ }
+
+ for (i = 0; i < 8; i++) {
+ close(timelines[i]);
+ syncobj_destroy(fd, syncobjs[i]);
+ }
+}
+
+static void
+test_wait_interrupted(int fd, uint32_t test_flags)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t syncobj = syncobj_create(fd, 0);
+ uint64_t point = 1;
+ int timeline;
+
+ wait.handles = to_user_pointer(&syncobj);
+ wait.points = to_user_pointer(&point);
+ wait.count_handles = 1;
+ wait.flags = flags_for_test_flags(test_flags);
+
+ if (test_flags & WAIT_FOR_SUBMIT) {
+ wait.timeout_nsec = short_timeout();
+ igt_while_interruptible(true)
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
+ }
+
+ timeline = syncobj_attach_sw_sync(fd, syncobj, point);
+
+ wait.timeout_nsec = short_timeout();
+ igt_while_interruptible(true)
+ igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ETIME);
+
+ syncobj_destroy(fd, syncobj);
+ close(timeline);
+}
+
+static bool
+has_syncobj_timeline_wait(int fd)
+{
+ struct drm_syncobj_timeline_wait wait = { 0 };
+ uint32_t handle = 0;
+ uint64_t value;
+ int ret;
+
+ if (drmGetCap(fd, DRM_CAP_SYNCOBJ, &value))
+ return false;
+ if (!value)
+ return false;
+
+ /* Try waiting for zero sync objects should fail with EINVAL */
+ wait.count_handles = 1;
+ wait.handles = to_user_pointer(&handle);
+ ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, &wait);
+ return ret == -1 && errno == ENOENT;
+}
+
+igt_main
+{
+ int fd = -1;
+
+ igt_fixture {
+ fd = drm_open_driver(DRIVER_ANY);
+ igt_require(has_syncobj_timeline_wait(fd));
+ igt_require_sw_sync();
+ }
+
+ igt_subtest("invalid-wait-bad-flags")
+ test_wait_bad_flags(fd);
+
+ igt_subtest("invalid-wait-zero-handles")
+ test_wait_zero_handles(fd);
+
+ igt_subtest("invalid-wait-illegal-handle")
+ test_wait_illegal_handle(fd);
+
+ igt_subtest("invalid-query-zero-handles")
+ test_query_zero_handles(fd);
+
+ igt_subtest("invalid-query-illegal-handle")
+ test_query_illegal_handle(fd);
+
+ igt_subtest("invalid-query-one-illegal-handle")
+ test_query_one_illegal_handle(fd);
+
+ igt_subtest("invalid-query-bad-pad")
+ test_query_bad_pad(fd);
+
+ igt_subtest("invalid-signal-zero-handles")
+ test_signal_zero_handles(fd);
+
+ igt_subtest("invalid-signal-illegal-handle")
+ test_signal_illegal_handle(fd);
+
+ igt_subtest("invalid-signal-illegal-point")
+ test_signal_illegal_point(fd);
+
+ igt_subtest("invalid-signal-one-illegal-handle")
+ test_signal_one_illegal_handle(fd);
+
+ igt_subtest("invalid-signal-bad-pad")
+ test_signal_bad_pad(fd);
+
+ igt_subtest("invalid-signal-array")
+ test_signal_array(fd);
+
+ igt_subtest("invalid-transfer-illegal-handle")
+ test_transfer_illegal_handle(fd);
+
+ igt_subtest("invalid-transfer-illegal-point")
+ test_transfer_illegal_point(fd);
+
+ igt_subtest("invalid-transfer-bad-pad")
+ test_transfer_bad_pad(fd);
+
+ for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
+ int err;
+
+ /* Only one wait mode for single-wait tests */
+ if (__builtin_popcount(flags & (WAIT_UNSUBMITTED |
+ WAIT_SUBMITTED |
+ WAIT_SIGNALED)) != 1)
+ continue;
+
+ if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT))
+ err = -EINVAL;
+ else if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
+ err = -ETIME;
+ else
+ err = 0;
+
+ igt_subtest_f("%ssingle-wait%s%s%s%s%s%s",
+ err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
+ (flags & WAIT_ALL) ? "-all" : "",
+ (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
+ (flags & WAIT_AVAILABLE) ? "-available" : "",
+ (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
+ (flags & WAIT_SUBMITTED) ? "-submitted" : "",
+ (flags & WAIT_SIGNALED) ? "-signaled" : "")
+ test_single_wait(fd, flags, err);
+ }
+
+ igt_subtest("wait-delayed-signal")
+ test_wait_delayed_signal(fd, 0);
+
+ igt_subtest("wait-for-submit-delayed-submit")
+ test_wait_delayed_signal(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-delayed-signal")
+ test_wait_delayed_signal(fd, WAIT_ALL);
+
+ igt_subtest("wait-all-for-submit-delayed-submit")
+ test_wait_delayed_signal(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("reset-unsignaled")
+ test_reset_unsignaled(fd);
+
+ igt_subtest("reset-signaled")
+ test_reset_signaled(fd);
+
+ igt_subtest("reset-multiple-signaled")
+ test_reset_multiple_signaled(fd);
+
+ igt_subtest("reset-during-wait-for-submit")
+ test_reset_during_wait_for_submit(fd);
+
+ igt_subtest("signal")
+ test_signal(fd);
+
+ for (unsigned flags = 0; flags < WAIT_FLAGS_MAX; flags++) {
+ int err;
+
+ /* At least one wait mode for multi-wait tests */
+ if (!(flags & (WAIT_UNSUBMITTED |
+ WAIT_SUBMITTED |
+ WAIT_SIGNALED)))
+ continue;
+
+ err = 0;
+ if ((flags & WAIT_UNSUBMITTED) && !(flags & WAIT_FOR_SUBMIT)) {
+ err = -EINVAL;
+ } else if (flags & WAIT_ALL) {
+ if (flags & (WAIT_UNSUBMITTED | WAIT_SUBMITTED))
+ err = -ETIME;
+ if (!(flags & WAIT_UNSUBMITTED) && (flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE))
+ err = 0;
+ } else {
+ if (!(flags & WAIT_SIGNALED) && !((flags & WAIT_SUBMITTED) && (flags & WAIT_AVAILABLE)))
+ err = -ETIME;
+ }
+
+ igt_subtest_f("%smulti-wait%s%s%s%s%s%s",
+ err == -EINVAL ? "invalid-" : err == -ETIME ? "etime-" : "",
+ (flags & WAIT_ALL) ? "-all" : "",
+ (flags & WAIT_FOR_SUBMIT) ? "-for-submit" : "",
+ (flags & WAIT_AVAILABLE) ? "-available" : "",
+ (flags & WAIT_UNSUBMITTED) ? "-unsubmitted" : "",
+ (flags & WAIT_SUBMITTED) ? "-submitted" : "",
+ (flags & WAIT_SIGNALED) ? "-signaled" : "")
+ test_multi_wait(fd, flags, err);
+ }
+ igt_subtest("wait-any-snapshot")
+ test_wait_snapshot(fd, 0);
+
+ igt_subtest("wait-all-snapshot")
+ test_wait_snapshot(fd, WAIT_ALL);
+
+ igt_subtest("wait-for-submit-snapshot")
+ test_wait_snapshot(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-for-submit-snapshot")
+ test_wait_snapshot(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-any-complex")
+ test_wait_complex(fd, 0);
+
+ igt_subtest("wait-all-complex")
+ test_wait_complex(fd, WAIT_ALL);
+
+ igt_subtest("wait-for-submit-complex")
+ test_wait_complex(fd, WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-all-for-submit-complex")
+ test_wait_complex(fd, WAIT_ALL | WAIT_FOR_SUBMIT);
+
+ igt_subtest("wait-any-interrupted")
+ test_wait_interrupted(fd, 0);
+
+ igt_subtest("wait-all-interrupted")
+ test_wait_interrupted(fd, WAIT_ALL);
+}
--
2.17.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2018-12-14 19:23 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-11 10:37 [PATCH i-g-t] igt: add timeline test cases v2 Chunming Zhou
2018-12-11 11:18 ` ✓ Fi.CI.BAT: success for igt: add timeline test cases (rev4) Patchwork
2018-12-11 13:05 ` ✓ Fi.CI.IGT: " Patchwork
[not found] ` <20181211103735.26058-1-david1.zhou-5C7GfCeVMHo@public.gmane.org>
2018-12-14 19:23 ` [PATCH i-g-t] igt: add timeline test cases v2 Wentland, Harry
2018-12-14 19:23 ` [igt-dev] " Wentland, Harry
-- strict thread matches above, loose matches on Subject: below --
2018-12-07 15:57 Chunming Zhou
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.