From patchwork Wed Aug 9 17:25:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jason Ekstrand X-Patchwork-Id: 9891395 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E59A5601EB for ; Wed, 9 Aug 2017 17:25:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CB2DF288EF for ; Wed, 9 Aug 2017 17:25:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BF71928A94; Wed, 9 Aug 2017 17:25:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BA3B2288EF for ; Wed, 9 Aug 2017 17:25:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 171CD6E380; Wed, 9 Aug 2017 17:25:15 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mail-pg0-x244.google.com (mail-pg0-x244.google.com [IPv6:2607:f8b0:400e:c05::244]) by gabe.freedesktop.org (Postfix) with ESMTPS id 422886E380 for ; Wed, 9 Aug 2017 17:25:13 +0000 (UTC) Received: by mail-pg0-x244.google.com with SMTP id 83so6374029pgb.4 for ; Wed, 09 Aug 2017 10:25:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jlekstrand-net.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=U052BW6XSdkgf/5XSJNIiGlxe46Dn15Ut6G764PiA/A=; b=Ab6xNn+O6GkvzPfWo0CG5MTtCerSIYOg13wLNFnl12m4johP8pTnK+qstFZUww+rFR v0WCUjV1EgNQevyXE9PrnlhqZDzBj0URlcCB3TS5ELmbkj0Uomj6VPT3DpF+g2bFS8YW NH9pB/LVfsIcJtvPb13kqtj3md36gFocsQPmbt9Tnvu5KXPPWGmr4T5wiSLiK6+EqIgt aOIz7iVXztqlwQ0lhyZPG+P5tWo9ChdGqHHc1IV81RS5xfqRUiAMA1MJsrdgnHkMAp97 sT8ujv5+Wu5fq7QH9O7RfKxIHX8C7YVyTRyXhPbSuStT+YbfGYNutCJFBu8TYNnKvux8 ZfsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=U052BW6XSdkgf/5XSJNIiGlxe46Dn15Ut6G764PiA/A=; b=RNNF6aXfKdWWEiX4IraF3UahS4WVzijmy7KODwG8mS/B5+TEqChtw7b6xsTdXh55Gl GENggM11zyyOC6efqqTTUlSr3C7eY92wFR5J5K0TI9waDnOcC7Nl3l9j/e3NGn+RaeAo DBR3/PNJYnaFcLoGFptpASht3SVlxqbCyuac1I7fBm3I5C5CdWxb2E9Radfr5CehGdvc M4sl4IUoQqmlZP0chppiBIhFoOqwJhhIfgNqQdD26n+S3ufyjL+OIv/K41ZUlmWDplJg Fa/RG2RCNEsM5tisBDbWmpYNLfM1NSRslwNtaeyNuHWTUBzm7RSolp8LUGPVMUIV5ojG k8yA== X-Gm-Message-State: AHYfb5gLq7zwpLAIxV/UGSy3n76VNcD+gPsxT/vZJiOsPl7N1S26BItg Bd5msINkQ8JsqPQ8b2955A== X-Received: by 10.98.79.136 with SMTP id f8mr7898149pfj.24.1502299512141; Wed, 09 Aug 2017 10:25:12 -0700 (PDT) Received: from omlet.jlekstrand.net (static-50-43-41-117.bvtn.or.frontiernet.net. [50.43.41.117]) by smtp.gmail.com with ESMTPSA id g79sm7881922pfd.163.2017.08.09.10.25.10 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 09 Aug 2017 10:25:10 -0700 (PDT) From: Jason Ekstrand X-Google-Original-From: Jason Ekstrand To: intel-gfx@lists.freedesktop.org Date: Wed, 9 Aug 2017 10:25:08 -0700 Message-Id: <1502299508-24111-1-git-send-email-jason.ekstrand@intel.com> X-Mailer: git-send-email 2.5.0.400.gff86faf In-Reply-To: <1502298282-12425-1-git-send-email-jason.ekstrand@intel.com> References: <1502298282-12425-1-git-send-email-jason.ekstrand@intel.com> MIME-Version: 1.0 Cc: Jason Ekstrand Subject: [Intel-gfx] [PATCH i-g-t] syncobj: Add some wait and reset tests (v2) X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP This adds both trivial error-checking tests as well as more complex tests which actually test whether or not waits do what they're supposed to do. They only currently work on i915 but it should be simple to hook them up for other drivers by simply implementing the little function pointer hook provided at the top for triggering a syncobj. v2: - Actually add the reset tests. Signed-off-by: Jason Ekstrand --- tests/Makefile.sources | 1 + tests/syncobj_wait.c | 714 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 715 insertions(+) create mode 100644 tests/syncobj_wait.c diff --git a/tests/Makefile.sources b/tests/Makefile.sources index bb013c7..430b637 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -230,6 +230,7 @@ TESTS_progs = \ prime_vgem \ sw_sync \ syncobj_basic \ + syncobj_wait \ template \ tools_test \ vgem_basic \ diff --git a/tests/syncobj_wait.c b/tests/syncobj_wait.c new file mode 100644 index 0000000..2e39e6d --- /dev/null +++ b/tests/syncobj_wait.c @@ -0,0 +1,714 @@ +/* + * Copyright © 2017 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 +#include +#include +#include "drm.h" + +IGT_TEST_DESCRIPTION("Tests for the drm sync object wait API"); + +/* One one tenth of a second */ +#define SHORT_TIME_NSEC 100000000ull + +/** A per-platform function which triggers a set of sync objects + * + * If wait is set, the function should wait for the work to complete so + * that an immediate call to SYNCOBJ_WAIT will return success. If wait is + * not set, then the function should try to submit enough work that an + * immediate call to SYNCOBJ_WAIT with a timeout of 0 will time out. + */ +void (*trigger_syncobj)(int fd, uint32_t *syncobjs, int count, bool wait); + +#define NSECS_PER_SEC 1000000000ull + +static uint64_t +gettime_ns(void) +{ + struct timespec current; + clock_gettime(CLOCK_MONOTONIC, ¤t); + return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec; +} + +static uint64_t +short_timeout(void) +{ + return gettime_ns() + SHORT_TIME_NSEC; +} + +static uint32_t +syncobj_create(int fd) +{ + struct drm_syncobj_create create = { 0 }; + int ret; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &create); + igt_assert(ret == 0); + igt_assert(create.handle > 0); + + return create.handle; +} + +static void +syncobj_destroy(int fd, uint32_t handle) +{ + struct drm_syncobj_destroy destroy = { 0 }; + int ret; + + destroy.handle = handle; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &destroy); + igt_assert(ret == 0); +} + +struct delayed_trigger { + int fd; + uint32_t *syncobjs; + int count; + uint64_t nsec; +}; + +static void * +trigger_syncobj_delayed_func(void *data) +{ + struct delayed_trigger *trigger = data; + struct timespec time; + + time.tv_sec = trigger->nsec / NSECS_PER_SEC; + time.tv_nsec = trigger->nsec % NSECS_PER_SEC; + + nanosleep(&time, NULL); + trigger_syncobj(trigger->fd, trigger->syncobjs, trigger->count, true); + free(data); + + return NULL; +} + +static pthread_t +trigger_syncobj_delayed(int fd, uint32_t *syncobjs, int count, uint64_t nsec) +{ + struct delayed_trigger *trigger; + pthread_t thread; + int ret; + + trigger = malloc(sizeof(*trigger)); + trigger->fd = fd; + trigger->syncobjs = syncobjs; + trigger->count = count; + trigger->nsec = nsec; + + ret = pthread_create(&thread, NULL, + trigger_syncobj_delayed_func, trigger); + igt_assert(ret == 0); + + return thread; +} + +static void +test_wait_bad_flags(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.flags = 0xdeadbeef; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_wait_zero_handles(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + int ret; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_wait_illegal_handle(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t handle = 2; + int ret; + + wait.count_handles = 1; + wait.handles = (unsigned long)(void *)&handle; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_reset_bad_flags(int fd) +{ + struct drm_syncobj_reset reset = { 0 }; + int ret; + + reset.flags = 0xdeadbeef; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &reset); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_reset_illegal_handle(int fd) +{ + struct drm_syncobj_reset reset = { 0 }; + int ret; + + reset.handle = 2; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &reset); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_wait_unsignaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.timeout_nsec = short_timeout(); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == EINVAL); + + syncobj_destroy(fd, syncobj); +} + +static void +test_wait_for_submit_unsignaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + wait.timeout_nsec = short_timeout(); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + syncobj_destroy(fd, syncobj); +} + +static void +test_wait_signaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + + trigger_syncobj(fd, &syncobj, 1, false); + + wait.timeout_nsec = 0; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_warn_on(ret != -1 || errno != ETIME); + + wait.timeout_nsec = short_timeout(); + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + syncobj_destroy(fd, syncobj); +} + +static void +test_wait_for_submit_signaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + trigger_syncobj(fd, &syncobj, 1, false); + + wait.timeout_nsec = 0; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_warn_on(ret != -1 || errno != ETIME); + + wait.timeout_nsec = short_timeout(); + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + syncobj_destroy(fd, syncobj); +} + +static void +test_wait_for_submit_delayed_signal(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + trigger_syncobj_delayed(fd, &syncobj, 1, SHORT_TIME_NSEC); + + wait.timeout_nsec = 0; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + wait.timeout_nsec = gettime_ns() + SHORT_TIME_NSEC * 2; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + syncobj_destroy(fd, syncobj); +} + +static void +test_reset_unsignaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_reset reset = { 0 }; + struct drm_syncobj_wait wait = { 0 }; + int ret; + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + wait.timeout_nsec = 0; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + reset.handle = syncobj; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &reset); + igt_assert(ret == 0); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + syncobj_destroy(fd, syncobj); +} + +static void +test_reset_signaled(int fd) +{ + uint32_t syncobj = syncobj_create(fd); + struct drm_syncobj_reset reset = { 0 }; + struct drm_syncobj_wait wait = { 0 }; + int ret; + + trigger_syncobj(fd, &syncobj, 1, true); + + wait.handles = to_user_pointer(&syncobj); + wait.count_handles = 1; + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + wait.timeout_nsec = 0; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + reset.handle = syncobj; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_RESET, &reset); + igt_assert(ret == 0); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + syncobj_destroy(fd, syncobj); +} + +static void +test_wait_all_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 2, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_all_some_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 1, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == EINVAL); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_any_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 2, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_any_some_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 1, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + + /* Even though we're waiting for anything, the kernel should still + * reject the unsignaled syncobj. + */ + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == EINVAL); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_for_submit_all_some_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 1, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL | + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_for_submit_any_some_signaled(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t tmp, syncobjs[2]; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 1, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0 && wait.first_signaled == 0); + + /* Swap and try again */ + tmp = syncobjs[0]; + syncobjs[0] = syncobjs[1]; + syncobjs[1] = tmp; + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0 && wait.first_signaled == 1); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_all_for_submit_some_delayed_signal(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + pthread_t thread; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + trigger_syncobj(fd, syncobjs, 1, true); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL | + DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + thread = trigger_syncobj_delayed(fd, &syncobjs[1], 1, SHORT_TIME_NSEC); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + wait.timeout_nsec = gettime_ns() + 2 * SHORT_TIME_NSEC; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0); + + pthread_join(thread, NULL); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +static void +test_wait_any_for_submit_some_delayed_signal(int fd) +{ + struct drm_syncobj_wait wait = { 0 }; + uint32_t syncobjs[2]; + pthread_t thread; + int ret; + + syncobjs[0] = syncobj_create(fd); + syncobjs[1] = syncobj_create(fd); + + wait.handles = to_user_pointer(&syncobjs); + wait.count_handles = 2; + wait.timeout_nsec = short_timeout(); + wait.flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT; + + thread = trigger_syncobj_delayed(fd, &syncobjs[1], 1, SHORT_TIME_NSEC); + + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == -1 && errno == ETIME); + + wait.timeout_nsec = gettime_ns() + 2 * SHORT_TIME_NSEC; + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + igt_assert(ret == 0 && wait.first_signaled == 1); + + pthread_join(thread, NULL); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[1]); +} + +/******** i915 specific bits ******/ +struct { + uint32_t batch; +} i915; + +#define I915_BATCH_COUNT 128 + +static void +i915_trigger_syncobj(int fd, uint32_t *syncobjs, int count, bool wait) +{ + struct drm_i915_gem_exec_object2 exec_obj = { 0 }; + struct drm_i915_gem_exec_fence *fence_array; + struct drm_i915_gem_execbuffer2 execbuf = { 0 }; + struct drm_i915_gem_wait gem_wait; + int i, ret; + + fence_array = calloc(count, sizeof(*fence_array)); + for (i = 0; i < count; i++) { + fence_array[i].handle = syncobjs[i]; + fence_array[i].flags = I915_EXEC_FENCE_SIGNAL; + } + + exec_obj.handle = i915.batch; + + execbuf.buffers_ptr = to_user_pointer(&exec_obj); + execbuf.buffer_count = 1; + execbuf.batch_start_offset = 0; + execbuf.batch_len = 8; + execbuf.flags = I915_EXEC_RENDER | I915_EXEC_FENCE_ARRAY; + + for (i = 0; i < I915_BATCH_COUNT; i++) { + if (i == I915_BATCH_COUNT - 1) { + execbuf.cliprects_ptr = to_user_pointer(fence_array); + execbuf.num_cliprects = count; + } + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf); + igt_assert(ret == 0); + } + + free(fence_array); + + if (wait) { + gem_wait.bo_handle = i915.batch; + gem_wait.flags = 0; + gem_wait.timeout_ns = INT64_MAX; + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait); + igt_assert(ret == 0); + } +} + +static void +i915_init(int fd) +{ + uint32_t batch_data[2] = {0, MI_BATCH_BUFFER_END}; + + i915.batch = gem_create(fd, 4096); + gem_write(fd, i915.batch, 0, batch_data, sizeof(batch_data)); + trigger_syncobj = i915_trigger_syncobj; +} +/******** end of i915 bits ******/ + +static bool +has_syncobj_wait(int fd) +{ + struct drm_syncobj_wait wait = { 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 */ + ret = ioctl(fd, DRM_IOCTL_SYNCOBJ_WAIT, &wait); + return ret == -1 && errno == EINVAL; +} + +igt_main +{ + int fd; + + igt_fixture { + fd = drm_open_driver_render(DRIVER_INTEL); + igt_require_gem(fd); + igt_require(has_syncobj_wait(fd)); + + if (is_i915_device(fd)) + i915_init(fd); + } + + igt_subtest("wait-bad-flags") + test_wait_bad_flags(fd); + + igt_subtest("wait-zero-handles") + test_wait_zero_handles(fd); + + igt_subtest("wait-illegal-handle") + test_wait_illegal_handle(fd); + + igt_subtest("reset-bad-flags") + test_reset_bad_flags(fd); + + igt_subtest("reset-illegal-handle") + test_reset_illegal_handle(fd); + + igt_subtest("wait-unsignaled") + test_wait_unsignaled(fd); + + igt_subtest("wait-for-submit-unsignaled") + test_wait_for_submit_unsignaled(fd); + + igt_subtest("wait-signaled") + test_wait_signaled(fd); + + igt_subtest("wait-for-submit-signaled") + test_wait_for_submit_signaled(fd); + + igt_subtest("wait-for-submit-delayed-signal") + test_wait_for_submit_delayed_signal(fd); + + igt_subtest("reset-unsignaled") + test_reset_unsignaled(fd); + + igt_subtest("reset-signaled") + test_reset_signaled(fd); + + igt_subtest("wait-all-signaled") + test_wait_all_signaled(fd); + + igt_subtest("wait-all-some-signaled") + test_wait_all_some_signaled(fd); + + igt_subtest("wait-any-signaled") + test_wait_any_signaled(fd); + + igt_subtest("wait-any-some-signaled") + test_wait_any_some_signaled(fd); + + igt_subtest("wait-for-submit-all-some-signaled") + test_wait_for_submit_all_some_signaled(fd); + + igt_subtest("wait-for-submit-any-some-signaled") + test_wait_for_submit_any_some_signaled(fd); + + igt_subtest("wait-all-for-submit-some-delayed-signal") + test_wait_all_for_submit_some_delayed_signal(fd); + + igt_subtest("wait-any-for-submit-some-delayed-signal") + test_wait_any_for_submit_some_delayed_signal(fd); +}