From patchwork Fri Dec 7 10:05:57 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chunming Zhou X-Patchwork-Id: 10717827 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0E27813BF for ; Fri, 7 Dec 2018 10:06:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EE54A2E5BC for ; Fri, 7 Dec 2018 10:06:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E17EB2E607; Fri, 7 Dec 2018 10:06:12 +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=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED 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 E02E32E5F1 for ; Fri, 7 Dec 2018 10:06:10 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 9CB656E706; Fri, 7 Dec 2018 10:06:08 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from NAM02-CY1-obe.outbound.protection.outlook.com (mail-cys01nam02on0612.outbound.protection.outlook.com [IPv6:2a01:111:f400:fe45::612]) by gabe.freedesktop.org (Postfix) with ESMTPS id 177416E706; Fri, 7 Dec 2018 10:06:07 +0000 (UTC) Received: from DM3PR12CA0138.namprd12.prod.outlook.com (2603:10b6:0:51::34) by DM6PR12MB2635.namprd12.prod.outlook.com (2603:10b6:5:4a::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1382.22; Fri, 7 Dec 2018 10:06:04 +0000 Received: from CO1NAM03FT058.eop-NAM03.prod.protection.outlook.com (2a01:111:f400:7e48::206) by DM3PR12CA0138.outlook.office365.com (2603:10b6:0:51::34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.1404.18 via Frontend Transport; Fri, 7 Dec 2018 10:06:04 +0000 Received-SPF: None (protection.outlook.com: amd.com does not designate permitted sender hosts) Received: from SATLEXCHOV01.amd.com (165.204.84.17) by CO1NAM03FT058.mail.protection.outlook.com (10.152.81.107) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384) id 15.20.1404.17 via Frontend Transport; Fri, 7 Dec 2018 10:06:03 +0000 Received: from zhoucm1.amd.com (10.34.1.3) by SATLEXCHOV01.amd.com (10.181.40.71) with Microsoft SMTP Server id 14.3.389.1; Fri, 7 Dec 2018 04:06:00 -0600 From: Chunming Zhou To: , , , Date: Fri, 7 Dec 2018 18:05:57 +0800 Message-ID: <20181207100557.2536-1-david1.zhou@amd.com> X-Mailer: git-send-email 2.17.1 MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:165.204.84.17; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(39860400002)(396003)(376002)(136003)(346002)(2980300002)(428003)(189003)(199004)(2616005)(47776003)(50466002)(26005)(14444005)(476003)(5024004)(5820100001)(53936002)(186003)(77096007)(53946003)(126002)(104016004)(81166006)(81156014)(72206003)(8676002)(36756003)(478600001)(97736004)(4744004)(2870700001)(68736007)(486006)(106466001)(1076002)(50226002)(356004)(6666004)(8936002)(105586002)(305945005)(575784001)(23676004)(86362001)(2201001)(110136005)(336012)(53416004)(4326008)(450100002)(316002)(7696005)(2906002)(5660300001)(426003)(579004); DIR:OUT; SFP:1101; SCL:1; SRVR:DM6PR12MB2635; H:SATLEXCHOV01.amd.com; FPR:; SPF:None; LANG:en; PTR:InfoDomainNonexistent; MX:1; A:1; X-Microsoft-Exchange-Diagnostics: 1; CO1NAM03FT058; 1:7h0c0epBOvs26rZtlT2vrRExaYs9qAUnCxeU51c/t89rasDp1sZ1bNDOQMv4zNY08bSFDbiD7TQceIic009c5EhcwBp+BWFbhAHaRHTSHI4alxSHOxUse+A/s1NhWxG9 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 19524d4a-fb3a-4060-2ee0-08d65c2b98c4 X-Microsoft-Antispam: BCL:0; PCL:0; RULEID:(2390098)(7020095)(4652040)(8989299)(5600074)(711020)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(2017052603328)(7153060); SRVR:DM6PR12MB2635; X-Microsoft-Exchange-Diagnostics: 1; DM6PR12MB2635; 3:13CRx25uyZwBHqTLssuNhHbjlMxnrS2j6MI4JIWMuaVj/mY7dMOfNczfD1+CqokdteY0TQiU16YIaeS6yeEFZaoalhdzxdPyZo1VjwOkbi3XbzW0rvwXTst6YBFh83E3XvMswW3tSHCw3NRHAKN7PY2uCdciakwew3/t05gsJsEKbD4I9HASooWBUmBGMICkCb20DSyfnk9QtGNv/ZPvNWVp8T6HwxWubmz2AOWS2yZpDOt1HROUCJjwIl/EkZtQWjlcM2V9wFEtm0ixXAtTWbcUFslEMdMq23huC+/rh/HXt/nOfYrPagmVtBibcsROOruTq31gTy0kbdflyKakjuJPixT8QUs4x1NYWy2uoaI=; 25:msLbJkE+9tJMm71hCh8FElk0qZShIIs87jWgePN3kFBREf098WBoTNXW7py9Dd9oiLSrGmgDtCgMYn+xlMn6/qkvulzkj1PjJW+35bSlDNA3m9cj1l3l/BgVbP7k0O9/VZdiQmrOqf0DRFIIEYVBSb7qOOjYmm2KRvaIQRd79HG7o9YZNB0K6x81BEIcAiu3oOnSTtVzeyP8Bd7KwGLbwlb/5Clg/Ni05vOCLtmixDYW7UfmQCc4Oxj/vlQQ8rOTlgtfn2mL9rz+kJCrGSI2IKpFbWcEfr6wqjshTHOcDtH2N/6ln1GGm1QAtHOPfjF8k4UFNPVyvKjT9xPLPLjAcg== X-MS-TrafficTypeDiagnostic: DM6PR12MB2635: X-Microsoft-Exchange-Diagnostics: 1; DM6PR12MB2635; 31:MVbY8i23xfmxZ0GADhLkmtoTfEUHWq4i8UYozYKzJYwQv+87z5rXaq98uJKHdoAGODfVUgF3pxMGXMU90bQRm9o7PC2Ub7blUUcn7A+0yNAP5zYV7SWLwUy6Kpz6N+KLgCEwIvKX/CSHx1PCd8dZPSuXjNgwIeQlyHVJv20Qc9Ot3j9hYqLzusUyD//qu0Nz3Sxh5kv+6r9MI4oh/L2TmjtHybUZyQAj1DRX3w22MTE=; 20:aeNnkP9CtyXJSPtPyW2HzpUEoElTndOFeX+kXLD+IJ3+wmcx/qv9JG9Va3epIkMQQAr9dvUo8T/moqCIPmhglyGRZUIats65b8kHYMG6p7yw6Dozjepi2FffOPEGoJhsvdLFJprKlRunjGSxdaOSuoNcoloDW/II/QZzEq3LZZnLswBkp+vuL60RFUi/jT4A12DWT92P0VI+hmf7hfIXGmeXzKHweG6FQtxcFgw6hCzUordLfIMVXCNSrcEhvlIF4Dd1HxfcQqos3cw4sPEzH3wRjjZRKCrZcFvj/MXBtvM4nb3l4xGQVjpaN/IP1lPTxWxqeH6/O6TU/WFhnlFWENQiNL1w9JEMfj5qmmibHzQubO+RB3uDhSwKkMvTf1enr1sKvwoaUd2U9KOaAtLE9u8k7Z4XPcvYMWg2+4dD1LxcC+ExkqSccFAeTlxdBzvvc4VG8qDBQb0ZPEEHVA42gan9YgYzRepUFmwy7EHWcdyiIUQjx2xfWIqoXOPa323L X-Microsoft-Antispam-PRVS: X-MS-Exchange-SenderADCheck: 1 X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(8211001083)(6040522)(2401047)(5005006)(8121501046)(10201501046)(3002001)(93006095)(93003095)(3231455)(999002)(944501520)(52105112)(6055026)(148016)(149066)(150057)(6041310)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123562045)(20161123560045)(20161123564045)(201708071742011)(7699051)(76991095); SRVR:DM6PR12MB2635; BCL:0; PCL:0; RULEID:; SRVR:DM6PR12MB2635; X-Microsoft-Exchange-Diagnostics: 1; DM6PR12MB2635; 4:PcSP04m4H2g4eMfpTS+EJaH7EGwsnC4UwzAKel2xrKoC9SowU5HrbvegEb5fM3UFP8jk+8iSs1R0TJtc6Hv59ECCVYwsucbHhvFjaFbNGQqczjToZCUIfFxGUGsVLGuzfXxfE5BB9Y+w5Tb3hlZqRsq5CnAfY5kqTNYu4Mk6qEQRvPY+Bx6cTCudwknkLRM00hzRjDCIJBqY88aiyi0qV/EZXH51D8NddlkUNvwzlffFchf/0N6BtVqwpfNHYDJFI3+jZ+GZ2uIl043lRSreFg== X-Forefront-PRVS: 0879599414 X-Microsoft-Exchange-Diagnostics: =?utf-8?q?1=3BDM6PR12MB2635=3B23=3AzKFvIhJ?= =?utf-8?q?TfZDTN2x5wFiw8cLMg/imKhW/nAr32Um/HhNrYS1Q3v944C9K93gfCnK8KvrSABGH?= =?utf-8?q?EZ3ecB+2jajICqN0auonTd/nGhrSffWfbS6kfCHgyrdKPYRGNeJVGoDE+Ew2ly6Zv?= =?utf-8?q?4tE00VqUTh8WU7tEKc0c1XrtPINZ1bp59LWF8YII1di+gL4r1LffzLksx+Cc6o4aE?= =?utf-8?q?hWrzWiTDoXn9uaVP6hV7MD2YXJP1+IVxoe3INHoBdB0mVYv7tYlWOUe+55BuMRuNU?= =?utf-8?q?B4Xv3QAlF66PFYW+UsA6vZFep/kWVGrfWgqWiZP5gd6CX3WMmhiXYw/FMCUroYGBL?= =?utf-8?q?CWvd+gLyyqdPtj3OyWwhXOwHbRf5DLJPcXv7DrLMoybI7Rj6PriINeFAHFUK6Skl3?= =?utf-8?q?Js1zk/XxTv47s0HhfDpl0rp7UX98gfog1/vTFDQ11PArYtGeec/ntCMOOLMmUmiu4?= =?utf-8?q?nEuK9Z5IRf2F3HLxYwPBSELSSrRKzBjY8Ix8EhDwp1Txi/cSt1v2r1p/IIYkaCHml?= =?utf-8?q?7wjO3BloKCnX2IDV00SB8WlX77FwViVuBid5F5RrY1ULvC1TqF3xStPJ0X9eUdkLa?= =?utf-8?q?lV5xenyLj5meBmOehyggYRre4NvkymVXqMCSRV/XnMQPJ3fOh5NgerD4aGqCSX83p?= =?utf-8?q?ErvoUUm+QJgLhmpEDG5GDK7KH5K9yX8UUqvNxEqFLUPQXoVhaM1JDzEw1GoXi3W9k?= =?utf-8?q?Jxs9pDAkIrIjI3389UQh6/c5JZLl9+uuVKgcMA2IMk4tNWgPk4PREU3Vj5uH02Et9?= =?utf-8?q?I+25JeGuewq4apZTRbZn+HHS+ZkxYQ/bBZUW0S7gj91NjtoW+MQxKJslmeL4jE3vs?= =?utf-8?q?VicwXnGP/zSgNzZu2IydVFc1BfN7em3lQ8wZ91ZWfwEcHREv4gc9FYiak1UOzjwBi?= =?utf-8?q?IAdGQsNCRVMcq5folHnJHNhM3cmxlTMZiklj9z6pSM7ylFoJLJnP8ghecIYO9y1Af?= =?utf-8?q?jiViYM0bCsEapzBzHAidYNihmodXuoUti9IoCHFMdyU+qsXNUDt21pSVHrHobfJf5?= =?utf-8?q?KrsDlDFOOFB2OkxzCtLe35ECdXVb5VE8mn1NW8blR1F0fDzcHEmrBPUC2RAOphUVL?= =?utf-8?q?yjWUiQtW1nQZJwZr0tkt06hL/hti8rcCqKwMN6u1vzAYlzi8LlQWLQORaaRcr7dD7?= =?utf-8?q?pXJ+5wjIxD94V6sCyrUQD9PzbMFddNAQIEjbG++eMkPbfrO/ZKyvuEKcmSOoozbZS?= =?utf-8?q?9o6dv13/kENBhJlBwoB/VSqqdPcf1G2EOSBa8H?= X-Microsoft-Antispam-Message-Info: rvcsmVaJmkPT2ss2Kfe0fOFDNbAaGBolBq05OpklWjb9ydL1eSkI4xIgYhPvHnw5pgxjbbcfTQ7zqwW1Og8+Q+azOinrVbwb0CubGthVu4VH3VUx5oaHhJTGO2vFi3YrFYUDER457z105fkCCh2UPbbW29TXrkkWNrfUNMiQUxPe0VesFS8Ao/eHkrRXzNDBN3+CPbcqUaypF6Ngd08ZXqhcuTI4etOrTjxWkKl4fwwydeZGyKRB2MevyvV8FIAOBMoHBruRWkhmyXLDOCTXDVzuXU5PMG+jQte7h20z4HAx6riDgBF0SPXyPN47WfrjPA+1909F0Gsq0lMgpWSBeMype3GJ4FznrbqKcQ3tdPA= X-Microsoft-Exchange-Diagnostics: 1; DM6PR12MB2635; 6:OB/uoJjTJtAkrbJMsrQ/l+L0iBqFqdaeRAZ9LZ30t2xaGmhbJMbdr5v+srVj3+Vwc7kjs9W/pesdquYvgwze9aoKpaLzML/qZSncfDEZe8rLvOZJsD9c4xK8N7/JjOBg6voCeJvoKDzW7qQwhkj0w7ClhG+6Jzf4J2QpHL1cHUqipxhRNiMiN3x46Oru4D8DOuvpMoWyy2lnX4oRyvPLtet8ZP9qj9dmXgfLgi8CGSJ1JedQ0o/Uuv7/QRfzTpY9kFOujwrOa2Jw3dQbZpQnmzvTwR4n71IY5u+8Vg6ct558I86Qnri8wwEYtXk6QPiLK8Ji6KHQAy3IwaXLvg2iE9cAbGoXbHd5X7RmKhV7awXxIM+RG9r4uKQnBEBLdpMzYOuFNiQ66wPCUXS4iOCm6DV4khz+Pl9hMcqKFhn+xZBaokU7Rer0BAO7Mm5hE0dBOj4Awb1xISuDgfAY1B6rOQ==; 5:vAxomZ5KOljc0aKRPa3BU8pyNnKFJ6w+BchgZhBZslhv+UDiqEqkYR/M4FQ11YyND30JFniQd3NONaHXoVdgqL7kugIOBNNK0oJJ0pvmXoh7CCjfOvk60wfCsAacZP2yZXSPZnq4zrCOmxa18skcum0WGgmUjMnTAME4fyf0p4Q=; 7:d36P2KMiHJpEvvuXVQ8K5z3ENE5HwyHYqd0d62hIivecytdsrNzL6ZeRlPnB4oVDSfJnyfOXRZbuuucVwVF1aIL3r/FOZxMEQTaVSJIsAqbaMNLJ3dNdSLNXGCZ2AEsIX0oTipSmVjTEKIV0Vcpi+w== SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; DM6PR12MB2635; 20:AAYS+pSGCnia6M1fFClxSIRkpW6Tdv7QL5UzobYV8+LSXYTpT8Yn++2rG97o9We4Fxtu3h8MCk9rhlVbAP4jOkcDF8A8Ht+BCUfBRM9nD1CbswFoZuPZSg8pBOuSeWXY6MbU5vbmU4/T0iEkYJTDhRZGfrQN98iWATA37mHYR+VYRNOS0Cf9PIn2xVFNT7j0lGTKGAZvt3FuMTEiwY+sSdt8ptWzGL3jJ2XXsKzWED02pBDo1tyvjk7xpN4NVqoc X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 07 Dec 2018 10:06:03.2878 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 19524d4a-fb3a-4060-2ee0-08d65c2b98c4 X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=3dd8961f-e488-4e60-8e11-a82d994e183d; Ip=[165.204.84.17]; Helo=[SATLEXCHOV01.amd.com] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM6PR12MB2635 Subject: [Intel-gfx] [PATCH i-g-t] igt: add timeline test cases X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Chunming Zhou Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Chunming Zhou --- include/drm-uapi/drm.h | 33 ++ lib/igt_syncobj.c | 204 +++++++ lib/igt_syncobj.h | 19 + tests/meson.build | 1 + tests/syncobj_timeline.c | 1100 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1357 insertions(+) create mode 100644 tests/syncobj_timeline.c diff --git a/include/drm-uapi/drm.h b/include/drm-uapi/drm.h index f0bd91de..0f5df0b6 100644 --- a/include/drm-uapi/drm.h +++ b/include/drm-uapi/drm.h @@ -715,6 +715,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 */ @@ -725,11 +727,37 @@ 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 binary_handle; + __u32 timeline_handle; + __u64 point; + __u32 flags; + __u32 pad; +}; /* Query current scanout sequence number */ struct drm_crtc_get_sequence { @@ -886,6 +914,11 @@ 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_BINARY_TO_TIMELINE DRM_IOWR(0xCC, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY DRM_IOWR(0xCD, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCE, 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..a3b6a90b 100644 --- a/lib/igt_syncobj.c +++ b/lib/igt_syncobj.c @@ -286,3 +286,207 @@ 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.binary_handle = binary_handle; + args.timeline_handle = timeline_handle; + args.point = point; + args.flags = 0; + args.pad = 0; + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_BINARY_TO_TIMELINE, &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.binary_handle = binary_handle; + args.timeline_handle = timeline_handle; + args.point = point; + args.flags = flags; + args.pad = 0; + return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY, &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 3020f798..8b5db541 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -83,6 +83,7 @@ test_progs = [ 'sw_sync', 'syncobj_basic', 'syncobj_wait', + 'syncobj_timeline', 'template', 'tools_test', 'vc4_create_bo', diff --git a/tests/syncobj_timeline.c b/tests/syncobj_timeline.c new file mode 100644 index 00000000..aaf58a19 --- /dev/null +++ b/tests/syncobj_timeline.c @@ -0,0 +1,1100 @@ +/* + * 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 +#include +#include +#include +#include +#include "drm.h" + +IGT_TEST_DESCRIPTION("Tests for the drm timeline sync object API"); + +/* One tenth of a second */ +#define SHORT_TIME_NSEC 100000000ull + +#define NSECS_PER_SEC 1000000000ull + +static uint64_t +gettime_ns(void) +{ + struct timespec current; + clock_gettime(CLOCK_MONOTONIC, ¤t); + return (uint64_t)current.tv_sec * NSECS_PER_SEC + current.tv_nsec; +} + +static void +sleep_nsec(uint64_t time_nsec) +{ + struct timespec t; + t.tv_sec = time_nsec / NSECS_PER_SEC; + t.tv_nsec = time_nsec % NSECS_PER_SEC; + igt_assert_eq(nanosleep(&t, NULL), 0); +} + +static uint64_t +short_timeout(void) +{ + return gettime_ns() + SHORT_TIME_NSEC; +} + +static int +syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point) +{ + struct drm_syncobj_handle; + uint32_t syncobj = syncobj_create(fd, 0); + int timeline, fence; + + timeline = sw_sync_timeline_create(); + fence = sw_sync_timeline_create_fence(timeline, 1); + syncobj_import_sync_file(fd, syncobj, fence); + syncobj_binary_to_timeline(fd, handle, point, syncobj); + close(fence); + + syncobj_destroy(fd, syncobj); + return timeline; +} + +static void +syncobj_trigger(int fd, uint32_t handle, uint64_t point) +{ + int timeline = syncobj_attach_sw_sync(fd, handle, point); + sw_sync_timeline_inc(timeline, 1); + close(timeline); +} + +static timer_t +set_timer(void (*cb)(union sigval), void *ptr, int i, uint64_t nsec) +{ + timer_t timer; + struct sigevent sev; + struct itimerspec its; + + memset(&sev, 0, sizeof(sev)); + sev.sigev_notify = SIGEV_THREAD; + if (ptr) + sev.sigev_value.sival_ptr = ptr; + else + sev.sigev_value.sival_int = i; + sev.sigev_notify_function = cb; + igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0); + + memset(&its, 0, sizeof(its)); + its.it_value.tv_sec = nsec / NSEC_PER_SEC; + its.it_value.tv_nsec = nsec % NSEC_PER_SEC; + igt_assert(timer_settime(timer, 0, &its, NULL) == 0); + + return timer; +} + +struct fd_handle_pair { + int fd; + uint32_t handle; + uint64_t point; +}; + +static void +timeline_inc_func(union sigval sigval) +{ + sw_sync_timeline_inc(sigval.sival_int, 1); +} + +static void +syncobj_trigger_free_pair_func(union sigval sigval) +{ + struct fd_handle_pair *pair = sigval.sival_ptr; + syncobj_trigger(pair->fd, pair->handle, pair->point); + free(pair); +} + +static timer_t +syncobj_trigger_delayed(int fd, uint32_t syncobj, uint64_t point, uint64_t nsec) +{ + struct fd_handle_pair *pair = malloc(sizeof(*pair)); + + pair->fd = fd; + pair->handle = syncobj; + pair->point = syncobj; + + return set_timer(syncobj_trigger_free_pair_func, pair, 0, nsec); +} + +static void +test_wait_bad_flags(int fd) +{ + struct drm_syncobj_timeline_wait wait = { 0 }; + wait.flags = 0xdeadbeef; + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL); +} + +static void +test_wait_zero_handles(int fd) +{ + struct drm_syncobj_timeline_wait wait = { 0 }; + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -EINVAL); +} + +static void +test_wait_illegal_handle(int fd) +{ + struct drm_syncobj_timeline_wait wait = { 0 }; + uint32_t handle = 0; + + wait.count_handles = 1; + wait.handles = to_user_pointer(&handle); + igt_assert_eq(__syncobj_timeline_wait_ioctl(fd, &wait), -ENOENT); +} + +static void +test_query_zero_handles(int fd) +{ + struct drm_syncobj_timeline_array args = { 0 }; + int ret; + + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_query_illegal_handle(int fd) +{ + struct drm_syncobj_timeline_array args = { 0 }; + uint32_t handle = 0; + int ret; + + args.count_handles = 1; + args.handles = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_query_one_illegal_handle(int fd) +{ + struct drm_syncobj_timeline_array array = { 0 }; + uint32_t syncobjs[3]; + uint64_t initial_point = 1; + int ret; + + syncobjs[0] = syncobj_create(fd, 0); + syncobjs[1] = 0; + syncobjs[2] = syncobj_create(fd, 0); + + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1); + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1); + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0], + &initial_point, 1, 0, 0), 0); + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2], + &initial_point, 1, 0, 0), 0); + + array.count_handles = 3; + array.handles = to_user_pointer(syncobjs); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array); + igt_assert(ret == -1 && errno == ENOENT); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[2]); +} + +static void +test_query_bad_pad(int fd) +{ + struct drm_syncobj_timeline_array array = { 0 }; + uint32_t handle = 0; + int ret; + + array.pad = 0xdeadbeef; + array.count_handles = 1; + array.handles = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_QUERY, &array); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_signal_zero_handles(int fd) +{ + struct drm_syncobj_timeline_array args = { 0 }; + int ret; + + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_signal_illegal_handle(int fd) +{ + struct drm_syncobj_timeline_array args = { 0 }; + uint32_t handle = 0; + int ret; + + args.count_handles = 1; + args.handles = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_signal_illegal_point(int fd) +{ + struct drm_syncobj_timeline_array args = { 0 }; + uint32_t handle = 1; + uint64_t point = 0; + int ret; + + args.count_handles = 1; + args.handles = to_user_pointer(&handle); + args.points = to_user_pointer(&point); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &args); + igt_assert(ret == -1 && errno == ENOENT); +} +static void +test_signal_one_illegal_handle(int fd) +{ + struct drm_syncobj_timeline_array array = { 0 }; + uint32_t syncobjs[3]; + uint64_t initial_point = 1; + int ret; + + syncobjs[0] = syncobj_create(fd, 0); + syncobjs[1] = 0; + syncobjs[2] = syncobj_create(fd, 0); + + syncobj_timeline_signal(fd, &syncobjs[0], &initial_point, 1); + syncobj_timeline_signal(fd, &syncobjs[2], &initial_point, 1); + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[0], + &initial_point, 1, 0, 0), 0); + igt_assert_eq(syncobj_timeline_wait_err(fd, &syncobjs[2], + &initial_point, 1, 0, 0), 0); + + array.count_handles = 3; + array.handles = to_user_pointer(syncobjs); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array); + igt_assert(ret == -1 && errno == ENOENT); + + syncobj_destroy(fd, syncobjs[0]); + syncobj_destroy(fd, syncobjs[2]); +} + +static void +test_signal_bad_pad(int fd) +{ + struct drm_syncobj_timeline_array array = { 0 }; + uint32_t handle = 0; + int ret; + + array.pad = 0xdeadbeef; + array.count_handles = 1; + array.handles = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &array); + igt_assert(ret == -1 && errno == EINVAL); +} + +static void +test_binary_to_timeline_illegal_binary_handle(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle = 0; + int ret; + + args.binary_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_BINARY_TO_TIMELINE, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_binary_to_timeline_illegal_timeline_handle(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle = 0; + int ret; + + args.timeline_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_BINARY_TO_TIMELINE, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_binary_to_timeline_illegal_point(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle1 = 1; + uint32_t handle2 = 2; + int ret; + + args.binary_handle = to_user_pointer(&handle1); + args.timeline_handle = to_user_pointer(&handle2); + args.point = 0; + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_BINARY_TO_TIMELINE, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_binary_to_timeline_bad_pad(int fd) +{ + struct drm_syncobj_transfer arg = { 0 }; + uint32_t handle = 0; + int ret; + + arg.pad = 0xdeadbeef; + arg.binary_handle = to_user_pointer(&handle); + arg.timeline_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_BINARY_TO_TIMELINE, &arg); + igt_assert(ret == -1 && errno == EINVAL); +} +static void +test_timeline_to_binary_illegal_binary_handle(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle = 0; + int ret; + + args.binary_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_timeline_to_binary_illegal_timeline_handle(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle = 0; + int ret; + + args.timeline_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_timeline_to_binary_illegal_point(int fd) +{ + struct drm_syncobj_transfer args = { 0 }; + uint32_t handle1 = 1; + uint32_t handle2 = 2; + int ret; + + args.binary_handle = to_user_pointer(&handle1); + args.timeline_handle = to_user_pointer(&handle2); + args.point = 0; + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY, &args); + igt_assert(ret == -1 && errno == ENOENT); +} + +static void +test_timeline_to_binary_bad_pad(int fd) +{ + struct drm_syncobj_transfer arg = { 0 }; + uint32_t handle = 0; + int ret; + + arg.pad = 0xdeadbeef; + arg.binary_handle = to_user_pointer(&handle); + arg.timeline_handle = to_user_pointer(&handle); + ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_TIMELINE_TO_BINARY, &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-binary_to_timeline-illegal-binary-handle") + test_binary_to_timeline_illegal_binary_handle(fd); + + igt_subtest("invalid-binary_to_timeline-illegal-timeline-handle") + test_binary_to_timeline_illegal_timeline_handle(fd); + + igt_subtest("invalid-binary_to_timeline-illegal-point") + test_binary_to_timeline_illegal_point(fd); + + igt_subtest("invalid-bianry_to_timeline-bad-pad") + test_binary_to_timeline_bad_pad(fd); + + igt_subtest("invalid-timeline_to_binary-illegal-binary-handle") + test_timeline_to_binary_illegal_binary_handle(fd); + + igt_subtest("invalid-timeline_to_binary-illegal-timeline-handle") + test_timeline_to_binary_illegal_timeline_handle(fd); + + igt_subtest("invalid-timeline_to_binary-illegal-point") + test_timeline_to_binary_illegal_point(fd); + + igt_subtest("invalid-timeline_to_binary-bad-pad") + test_timeline_to_binary_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); +}