All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Wentland, Harry" <Harry.Wentland-5C7GfCeVMHo@public.gmane.org>
To: "Zhou,
	David(ChunMing)" <David1.Zhou-5C7GfCeVMHo@public.gmane.org>,
	"Koenig,
	Christian" <Christian.Koenig-5C7GfCeVMHo@public.gmane.org>,
	"dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org"
	<dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org>,
	"amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org"
	<amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org>,
	"intel-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org"
	<intel-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org>,
	IGT development
	<igt-dev-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org>
Subject: Re: [PATCH i-g-t] igt: add timeline test cases v2
Date: Fri, 14 Dec 2018 19:23:53 +0000	[thread overview]
Message-ID: <42de691f-3631-49d4-2149-9cee0b338d03@amd.com> (raw)
In-Reply-To: <20181211103735.26058-1-david1.zhou-5C7GfCeVMHo@public.gmane.org>

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, &current);
> +	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

WARNING: multiple messages have this Message-ID (diff)
From: "Wentland, Harry" <Harry.Wentland@amd.com>
To: "Zhou, David(ChunMing)" <David1.Zhou@amd.com>,
	"Koenig, Christian" <Christian.Koenig@amd.com>,
	"dri-devel@lists.freedesktop.org"
	<dri-devel@lists.freedesktop.org>,
	"amd-gfx@lists.freedesktop.org" <amd-gfx@lists.freedesktop.org>,
	"intel-gfx@lists.freedesktop.org"
	<intel-gfx@lists.freedesktop.org>,
	IGT development <igt-dev@lists.freedesktop.org>
Subject: Re: [igt-dev] [PATCH i-g-t] igt: add timeline test cases v2
Date: Fri, 14 Dec 2018 19:23:53 +0000	[thread overview]
Message-ID: <42de691f-3631-49d4-2149-9cee0b338d03@amd.com> (raw)
In-Reply-To: <20181211103735.26058-1-david1.zhou@amd.com>

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, &current);
> +	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

  parent reply	other threads:[~2018-12-14 19:23 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` Wentland, Harry [this message]
2018-12-14 19:23     ` [igt-dev] [PATCH i-g-t] igt: add timeline test cases v2 Wentland, Harry
  -- strict thread matches above, loose matches on Subject: below --
2018-12-07 15:57 Chunming Zhou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=42de691f-3631-49d4-2149-9cee0b338d03@amd.com \
    --to=harry.wentland-5c7gfcevmho@public.gmane.org \
    --cc=Christian.Koenig-5C7GfCeVMHo@public.gmane.org \
    --cc=David1.Zhou-5C7GfCeVMHo@public.gmane.org \
    --cc=amd-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    --cc=dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    --cc=igt-dev-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    --cc=intel-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.