<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Wed, Aug 9, 2017 at 10:28 AM, Chris Wilson <span dir="ltr"><<a href="mailto:chris@chris-wilson.co.uk" target="_blank">chris@chris-wilson.co.uk</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Quoting Jason Ekstrand (2017-08-09 18:04:42)<br>
<span class="">> This adds both trivial error-checking tests as well as more complex<br>
> tests which actually test whether or not waits do what they're supposed<br>
> to do. They only currently work on i915 but it should be simple to hook<br>
> them up for other drivers by simply implementing the little function<br>
> pointer hook provided at the top for triggering a syncobj.<br>
<br>
</span>Note that this requires a libdrm version more recent than is requested.<br>
<div><div class="h5"><br>
> ---<br>
> tests/Makefile.sources | 1 +<br>
> tests/syncobj_wait.c | 624 ++++++++++++++++++++++++++++++<wbr>+++++++++++++++++++<br>
> 2 files changed, 625 insertions(+)<br>
> create mode 100644 tests/syncobj_wait.c<br>
><br>
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources<br>
> index bb013c7..430b637 100644<br>
> --- a/tests/Makefile.sources<br>
> +++ b/tests/Makefile.sources<br>
> @@ -230,6 +230,7 @@ TESTS_progs = \<br>
> prime_vgem \<br>
> sw_sync \<br>
> syncobj_basic \<br>
> + syncobj_wait \<br>
> template \<br>
> tools_test \<br>
> vgem_basic \<br>
> diff --git a/tests/syncobj_wait.c b/tests/syncobj_wait.c<br>
> new file mode 100644<br>
> index 0000000..6689d34<br>
> --- /dev/null<br>
> +++ b/tests/syncobj_wait.c<br>
> @@ -0,0 +1,624 @@<br>
> +/*<br>
> + * Copyright © 2017 Intel Corporation<br>
> + *<br>
> + * Permission is hereby granted, free of charge, to any person obtaining a<br>
> + * copy of this software and associated documentation files (the "Software"),<br>
> + * to deal in the Software without restriction, including without limitation<br>
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
> + * and/or sell copies of the Software, and to permit persons to whom the<br>
> + * Software is furnished to do so, subject to the following conditions:<br>
> + *<br>
> + * The above copyright notice and this permission notice (including the next<br>
> + * paragraph) shall be included in all copies or substantial portions of the<br>
> + * Software.<br>
> + *<br>
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL<br>
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
> + * IN THE SOFTWARE.<br>
> + */<br>
> +<br>
> +#include "igt.h"<br>
> +#include <unistd.h><br>
> +#include <time.h><br>
> +#include <sys/ioctl.h><br>
> +#include "drm.h"<br>
> +<br>
> +IGT_TEST_DESCRIPTION("Tests for the drm sync object wait API");<br>
> +<br>
> +/* One one tenth of a second */<br>
> +#define SHORT_TIME_NSEC 100000000ull<br>
> +<br>
> +/** A per-platform function which triggers a set of sync objects<br>
> + *<br>
> + * If wait is set, the function should wait for the work to complete so<br>
> + * that an immediate call to SYNCOBJ_WAIT will return success. If wait is<br>
> + * not set, then the function should try to submit enough work that an<br>
> + * immediate call to SYNCOBJ_WAIT with a timeout of 0 will time out.<br>
> + */<br>
> +void (*trigger_syncobj)(int fd, uint32_t *syncobjs, int count, bool wait);<br>
> +<br>
> +#define NSECS_PER_SEC 1000000000ull<br>
> +<br>
> +static uint64_t<br>
> +gettime_ns(void)<br>
> +{<br>
> + struct timespec current;<br>
> + clock_gettime(CLOCK_MONOTONIC, ¤t);<br>
> + return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec;<br>
> +}<br>
> +<br>
> +static uint64_t<br>
> +short_timeout(void)<br>
> +{<br>
> + return gettime_ns() + SHORT_TIME_NSEC;<br>
> +}<br>
> +<br>
> +static uint32_t<br>
> +syncobj_create(int fd)<br>
> +{<br>
> + struct drm_syncobj_create create = { 0 };<br>
> + int ret;<br>
> +<br>
> + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &create);<br>
> + igt_assert(ret == 0);<br>
> + igt_assert(create.handle > 0);<br>
> +<br>
> + return create.handle;<br>
> +}<br>
> +<br>
> +static void<br>
> +syncobj_destroy(int fd, uint32_t handle)<br>
> +{<br>
> + struct drm_syncobj_destroy destroy = { 0 };<br>
> + int ret;<br>
> +<br>
> + destroy.handle = handle;<br>
> + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy);<br>
> + igt_assert(ret == 0);<br>
> +}<br>
> +<br>
> +struct delayed_trigger {<br>
> + int fd;<br>
> + uint32_t *syncobjs;<br>
> + int count;<br>
> + uint64_t nsec;<br>
> +};<br>
> +<br>
> +static void *<br>
> +trigger_syncobj_delayed_func(<wbr>void *data)<br>
> +{<br>
> + struct delayed_trigger *trigger = data;<br>
> + struct timespec time;<br>
> +<br>
> + time.tv_sec = trigger->nsec / NSECS_PER_SEC;<br>
> + time.tv_nsec = trigger->nsec % NSECS_PER_SEC;<br>
> +<br>
> + nanosleep(&time, NULL);<br>
> + trigger_syncobj(trigger->fd, trigger->syncobjs, trigger->count, true);<br>
> + free(data);<br>
> +<br>
> + return NULL;<br>
> +}<br>
> +<br>
> +static pthread_t<br>
> +trigger_syncobj_delayed(int fd, uint32_t *syncobjs, int count, uint64_t nsec)<br>
> +{<br>
> + struct delayed_trigger *trigger;<br>
> + pthread_t thread;<br>
> + int ret;<br>
> +<br>
> + trigger = malloc(sizeof(*trigger));<br>
> + trigger->fd = fd;<br>
> + trigger->syncobjs = syncobjs;<br>
> + trigger->count = count;<br>
> + trigger->nsec = nsec;<br>
> +<br>
> + ret = pthread_create(&thread, NULL,<br>
> + trigger_syncobj_delayed_func, trigger);<br>
<br>
</div></div>This is just a timer:<br>
<br>
timer_t timer;<br>
struct sigevent sev;<br>
struct itimerspec its;<br>
<br>
memset(&sev, 0, sizeof(sev));<br>
sev.sigev_notify = SIGEV_THREAD;<br>
sev.sigev_value.sival_ptr = tigger;<br>
sev.sigev_notify_function = trigger_syncobj_delayed_func;<br>
igt_assert(timer_create(CLOCK_<wbr>MONOTONIC, &sev, &timer) == 0);<br>
<br>
memset(&its, 0, sizeof(its));<br>
its.it_value.tv_sec = nsec / NSEC_PER_SEC;<br>
its.it_value.tv_nsec = nsec % NSEC_PER_SEC;<br>
igt_assert(timer_settime(<wbr>timer, 0, &its, NULL) == 0);<span class=""><br></span></blockquote><div><br></div><div>How do I do the equivalent of pthread_join() on said timer?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> + igt_assert(ret == 0);<br>
> +<br>
> + return thread;<br>
> +}<br>
<br>
</span>[snip]<br>
<br>
Key tests missing here are signal (SIGINT) handling, especially how the<br>
timeout parameters is handled on repeats, see igt_interruptible(), though<br>
you must use igt_ioctl(). Polling multiple handles is not<br>
checked, especially combinations of signaled/unsignaled syncojbs.<br>
<span class=""><br>
> +<br>
> +/******** i915 specific bits ******/<br>
> +struct {<br>
> + uint32_t batch;<br>
> +} i915;<br>
> +<br>
> +#define I915_BATCH_COUNT 128<br>
<br>
</span>Is overrunning the ring/guc-wq and blocking a concern?<br>
<br>
Do 128 provide any more significance than 2, one for wait, one for signal?<br>
Do 2 provide any more than 1?<br></blockquote><div><br></div><div>1 appears to work reliably. I just wanted to make sure. I'll drop it to one. It should only produce warnings at worst.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Does this provide any more value than the coverage (of execbuf +<br>
wait/signaling) in gem_exec_fence?<span class=""><br></span></blockquote><div><br></div><div>They're different wait mechanisms...<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> +static void<br>
> +i915_trigger_syncobj(int fd, uint32_t *syncobjs, int count, bool wait)<br>
> +{<br>
> + struct drm_i915_gem_exec_object2 exec_obj = { 0 };<br>
> + struct drm_i915_gem_exec_fence *fence_array;<br>
> + struct drm_i915_gem_execbuffer2 execbuf = { 0 };<br>
> + struct drm_i915_gem_wait gem_wait;<br>
> + int i, ret;<br>
> +<br>
> + fence_array = calloc(count, sizeof(*fence_array));<br>
> + for (i = 0; i < count; i++) {<br>
> + fence_array[i].handle = syncobjs[i];<br>
> + fence_array[i].flags = I915_EXEC_FENCE_SIGNAL;<br>
> + }<br>
> +<br>
> + exec_obj.handle = i915.batch;<br>
<br>
</span>Is there any advantage in keeping the handle around in a global?<span class=""><br></span></blockquote><div><br></div><div>Less work at trigger_syncobj time but that's all. I can create and destroy it here if you'd rather.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> +<br>
> + execbuf.buffers_ptr = to_user_pointer(&exec_obj);<br>
> + execbuf.buffer_count = 1;<br>
> + execbuf.batch_start_offset = 0;<br>
> + execbuf.batch_len = 8;<br>
> + execbuf.flags = I915_EXEC_RENDER | I915_EXEC_FENCE_ARRAY;<br>
> +<br>
> + for (i = 0; i < I915_BATCH_COUNT; i++) {<br>
> + if (i == I915_BATCH_COUNT - 1) {<br>
> + execbuf.cliprects_ptr = to_user_pointer(fence_array);<br>
> + execbuf.num_cliprects = count;<br>
> + }<br>
> + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_<wbr>EXECBUFFER2, &execbuf);<br>
<br>
</span>gem_execbuf<span class=""><br></span></blockquote><div><br>?<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> + igt_assert(ret == 0);<br>
> + }<br>
> +<br>
> + free(fence_array);<br>
> +<br>
> + if (wait) {<br>
<br>
</span>This is just gem_sync() (actually just gem_wait, but you since you just<br>
want a sync, you can use gem_sync)<span class=""><br></span></blockquote><div><br></div><div>What's the difference between wait and sync?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">
> + gem_wait.bo_handle = i915.batch;<br>
> + gem_wait.flags = 0;<br>
> + gem_wait.timeout_ns = INT64_MAX;<br>
> + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait);<br>
> + igt_assert(ret == 0);<br>
> + }<br>
> +}<br>
> +<br>
> +static void<br>
> +i915_init(int fd)<br>
> +{<br>
> + uint32_t batch_data[2] = {0, MI_BATCH_BUFFER_END};<br>
> +<br>
> + i915.batch = gem_create(fd, 4096);<br>
> + gem_write(fd, i915.batch, 0, batch_data, sizeof(batch_data));<br>
> + trigger_syncobj = i915_trigger_syncobj;<br>
> +}<br>
> +/******** end of i915 bits ******/<br>
<br>
</span>If this used either vgem or sw_sync, you wouldn't need i915 specific details.<br><div><div class="h5"></div></div></blockquote><div><br></div><div>Those paths might be worth implementing as well but I wanted to make sure it worked for syncobjs triggered by the actual driver. See also our discussion about default waits. Also, that requires adding syncobj support to vgem. Maybe a worth endeavor but I don't really see the point atm.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
> +<br>
> +static bool<br>
> +has_syncobj_wait(int fd)<br>
> +{<br>
> + struct drm_syncobj_wait wait = { 0 };<br>
> + uint64_t value;<br>
> + int ret;<br>
> +<br>
> + if (drmGetCap(fd, DRM_CAP_SYNCOBJ, &value))<br>
> + return false;<br>
> + if (!value)<br>
> + return false;<br>
> +<br>
> + /* Try waiting for zero sync objects should fail with EINVAL */<br>
> + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait);<br>
> + return ret == -1 && errno == EINVAL;<br>
> +}<br>
> +<br>
> +igt_main<br>
> +{<br>
> + int fd;<br>
> +<br>
> + igt_fixture {<br>
> + fd = drm_open_driver_render(DRIVER_<wbr>INTEL);<br>
> + igt_require_gem(fd);<br>
> + igt_require(has_syncobj_wait(<wbr>fd));<br>
> +<br>
> + if (is_i915_device(fd))<br>
> + i915_init(fd);<br>
<br>
</div></div>You asked for i915-only anyway, see DRIVER_INTEL. You meant DRIVER_ANY,<br>
but maybe just using DRIVER_VGEM and avoiding driver details would be<br>
better.<span class="HOEnZb"><font color="#888888"><br>
</font></span></blockquote></div><br></div><div class="gmail_extra">I was hoping that Dave would come along and add amdgpu bits and then we would need it. But maybe the extra check here is overkill.<br></div></div>