aboutsummaryrefslogtreecommitdiff
path: root/SOURCES/0001-ntsync.patch
diff options
context:
space:
mode:
Diffstat (limited to 'SOURCES/0001-ntsync.patch')
-rw-r--r--SOURCES/0001-ntsync.patch5600
1 files changed, 1896 insertions, 3704 deletions
diff --git a/SOURCES/0001-ntsync.patch b/SOURCES/0001-ntsync.patch
index 34a383a..fa47d21 100644
--- a/SOURCES/0001-ntsync.patch
+++ b/SOURCES/0001-ntsync.patch
@@ -1,883 +1,590 @@
-From 34529eb8302090ff79e04d95a6bb7f5fd48cecb6 Mon Sep 17 00:00:00 2001
+Subject: [PATCH v2 0/31] NT synchronization primitive driver
From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 10:50:45 -0600
-Subject: [PATCH 01/32] ntsync: Introduce the ntsync driver and character
- device.
-
-ntsync uses a misc device as the simplest and least intrusive uAPI interface.
-
-Each file description on the device represents an isolated NT instance, intended
-to correspond to a single NT virtual machine.
----
- drivers/misc/Kconfig | 9 ++++++++
- drivers/misc/Makefile | 1 +
- drivers/misc/ntsync.c | 53 +++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 63 insertions(+)
- create mode 100644 drivers/misc/ntsync.c
-
-diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
-index f37c4b8380ae..622eb26ac040 100644
---- a/drivers/misc/Kconfig
-+++ b/drivers/misc/Kconfig
-@@ -504,6 +504,15 @@ config OPEN_DICE
- measured boot flow. Userspace can use CDIs for remote attestation
- and sealing.
-
-+config NTSYNC
-+ tristate "NT synchronization primitive emulation"
-+ help
-+ This module provides kernel support for emulation of Windows NT
-+ synchronization primitives. It is not a hardware driver.
-+
-+ To compile this driver as a module, choose M here: the
-+ module will be called ntsync.
-+
- If unsure, say N.
-
- config VCPU_STALL_DETECTOR
-diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
-index f2a4d1ff65d4..bd12e9a3b8c5 100644
---- a/drivers/misc/Makefile
-+++ b/drivers/misc/Makefile
-@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
- obj-$(CONFIG_UACCE) += uacce/
- obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
- obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
-+obj-$(CONFIG_NTSYNC) += ntsync.o
- obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
- obj-$(CONFIG_OPEN_DICE) += open-dice.o
- obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-new file mode 100644
-index 000000000000..9424c6210e51
---- /dev/null
-+++ b/drivers/misc/ntsync.c
-@@ -0,0 +1,53 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * ntsync.c - Kernel driver for NT synchronization primitives
-+ *
-+ * Copyright (C) 2021-2022 Elizabeth Figura
-+ */
-+
-+#include <linux/fs.h>
-+#include <linux/miscdevice.h>
-+#include <linux/module.h>
-+
-+#define NTSYNC_NAME "ntsync"
-+
-+static int ntsync_char_open(struct inode *inode, struct file *file)
-+{
-+ return nonseekable_open(inode, file);
-+}
-+
-+static int ntsync_char_release(struct inode *inode, struct file *file)
-+{
-+ return 0;
-+}
-+
-+static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
-+ unsigned long parm)
-+{
-+ switch (cmd) {
-+ default:
-+ return -ENOIOCTLCMD;
-+ }
-+}
-+
-+static const struct file_operations ntsync_fops = {
-+ .owner = THIS_MODULE,
-+ .open = ntsync_char_open,
-+ .release = ntsync_char_release,
-+ .unlocked_ioctl = ntsync_char_ioctl,
-+ .compat_ioctl = ntsync_char_ioctl,
-+ .llseek = no_llseek,
-+};
-+
-+static struct miscdevice ntsync_misc = {
-+ .minor = MISC_DYNAMIC_MINOR,
-+ .name = NTSYNC_NAME,
-+ .fops = &ntsync_fops,
-+};
-+
-+module_misc_device(ntsync_misc);
-+
-+MODULE_AUTHOR("Elizabeth Figura");
-+MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("devname:" NTSYNC_NAME);
---
-2.43.0
-
-From a375bffd8ab8d06b81e4b83c9f5577fdc97246f9 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 10:57:06 -0600
-Subject: [PATCH 02/32] ntsync: Reserve a minor device number and ioctl range.
-
----
- Documentation/admin-guide/devices.txt | 3 ++-
- Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++
- drivers/misc/ntsync.c | 3 ++-
- include/linux/miscdevice.h | 1 +
- 4 files changed, 7 insertions(+), 2 deletions(-)
-
-diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt
-index 839054923530..13841636ce92 100644
---- a/Documentation/admin-guide/devices.txt
-+++ b/Documentation/admin-guide/devices.txt
-@@ -376,8 +376,9 @@
- 240 = /dev/userio Serio driver testing device
- 241 = /dev/vhost-vsock Host kernel driver for virtio vsock
- 242 = /dev/rfkill Turning off radio transmissions (rfkill)
-+ 243 = /dev/ntsync NT synchronization primitive device
+Date: Mon, 19 Feb 2024 16:38:02 -0600
+Message-Id: <20240219223833.95710-1-zfigura@codeweavers.com>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+This patch series introduces a new char misc driver, /dev/ntsync, which is used
+to implement Windows NT synchronization primitives.
+
+ Documentation/userspace-api/index.rst | 1 +
+ Documentation/userspace-api/ioctl/ioctl-number.rst | 2 +
+ Documentation/userspace-api/ntsync.rst | 399 ++++++
+ MAINTAINERS | 9 +
+ drivers/misc/Kconfig | 11 +
+ drivers/misc/Makefile | 1 +
+ drivers/misc/ntsync.c | 1159 ++++++++++++++++
+ include/uapi/linux/ntsync.h | 62 +
+ tools/testing/selftests/Makefile | 1 +
+ tools/testing/selftests/drivers/ntsync/Makefile | 8 +
+ tools/testing/selftests/drivers/ntsync/config | 1 +
+ tools/testing/selftests/drivers/ntsync/ntsync.c | 1407 ++++++++++++++++++++
+ 12 files changed, 3061 insertions(+)
+diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
+index 09f61bd2ac2e..f5a72ed27def 100644
+--- a/Documentation/userspace-api/index.rst
++++ b/Documentation/userspace-api/index.rst
+@@ -34,6 +34,7 @@ place where this information is gathered.
+ tee
+ isapnp
+ dcdbas
++ ntsync
-- 243-254 Reserved for local use
-+ 244-254 Reserved for local use
- 255 Reserved for MISC_DYNAMIC_MINOR
+ .. only:: subproject and html
- 11 char Raw keyboard device (Linux/SPARC only)
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
-index 4ea5b837399a..055482769b35 100644
+index 457e16f06e04..2f5c6994f042 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
-@@ -375,6 +375,8 @@ Code Seq# Include File Comments
- <mailto:thomas@winischhofer.net>
- 0xF6 all LTTng Linux Trace Toolkit Next Generation
- <mailto:mathieu.desnoyers@efficios.com>
-+0xF7 00-1F uapi/linux/ntsync.h NT synchronization primitives
+@@ -173,6 +173,8 @@ Code Seq# Include File Comments
+ 'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
+ 'N' 00-1F drivers/usb/scanner.h
+ 'N' 40-7F drivers/block/nvme.c
++'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
+ <mailto:wine-devel@winehq.org>
- 0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver
- <mailto:nchatrad@amd.com>
- 0xFD all linux/dm-ioctl.h
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 9424c6210e51..84b498e2b2d5 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -40,7 +40,7 @@ static const struct file_operations ntsync_fops = {
- };
-
- static struct miscdevice ntsync_misc = {
-- .minor = MISC_DYNAMIC_MINOR,
-+ .minor = NTSYNC_MINOR,
- .name = NTSYNC_NAME,
- .fops = &ntsync_fops,
- };
-@@ -51,3 +51,4 @@ MODULE_AUTHOR("Elizabeth Figura");
- MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("devname:" NTSYNC_NAME);
-+MODULE_ALIAS_MISCDEV(NTSYNC_MINOR);
-diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
-index c0fea6ca5076..fe5d9366fdf7 100644
---- a/include/linux/miscdevice.h
-+++ b/include/linux/miscdevice.h
-@@ -71,6 +71,7 @@
- #define USERIO_MINOR 240
- #define VHOST_VSOCK_MINOR 241
- #define RFKILL_MINOR 242
-+#define NTSYNC_MINOR 243
- #define MISC_DYNAMIC_MINOR 255
-
- struct device;
---
-2.43.0
-
-From a5f6ffc3056884b48bb9161bc1246d841d9ed961 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:15:39 -0600
-Subject: [PATCH 03/32] ntsync: Introduce NTSYNC_IOC_CREATE_SEM and
- NTSYNC_IOC_DELETE.
-
-These correspond to the NT syscalls NtCreateSemaphore() and NtClose().
-Unlike those functions, however, these ioctls do not handle object names, or
-lookup of existing objects, or handle reference counting, but simply create the
-underlying primitive. The user space emulator is expected to implement those
-functions if they are required.
----
- drivers/misc/ntsync.c | 117 ++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 25 ++++++++
- 2 files changed, 142 insertions(+)
- create mode 100644 include/uapi/linux/ntsync.h
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 84b498e2b2d5..3287b94be351 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -8,23 +8,140 @@
- #include <linux/fs.h>
- #include <linux/miscdevice.h>
- #include <linux/module.h>
-+#include <linux/slab.h>
-+#include <linux/xarray.h>
-+#include <uapi/linux/ntsync.h>
-
- #define NTSYNC_NAME "ntsync"
-
-+enum ntsync_type {
-+ NTSYNC_TYPE_SEM,
-+};
-+
-+struct ntsync_obj {
-+ struct rcu_head rhead;
-+ struct kref refcount;
-+
-+ enum ntsync_type type;
-+
-+ union {
-+ struct {
-+ __u32 count;
-+ __u32 max;
-+ } sem;
-+ } u;
-+};
-+
-+struct ntsync_device {
-+ struct xarray objects;
-+};
-+
-+static void destroy_obj(struct kref *ref)
-+{
-+ struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount);
+ 'O' 00-06 mtd/ubi-user.h UBI
+ 'P' all linux/soundcard.h conflict!
+ 'P' 60-6F sound/sscape_ioctl.h conflict!
+diff --git a/Documentation/userspace-api/ntsync.rst b/Documentation/userspace-api/ntsync.rst
+new file mode 100644
+index 000000000000..202c2350d3af
+--- /dev/null
++++ b/Documentation/userspace-api/ntsync.rst
+@@ -0,0 +1,399 @@
++===================================
++NT synchronization primitive driver
++===================================
+
-+ kfree_rcu(obj, rhead);
-+}
++This page documents the user-space API for the ntsync driver.
+
-+static void put_obj(struct ntsync_obj *obj)
-+{
-+ kref_put(&obj->refcount, destroy_obj);
-+}
++ntsync is a support driver for emulation of NT synchronization
++primitives by user-space NT emulators. It exists because implementation
++in user-space, using existing tools, cannot match Windows performance
++while offering accurate semantics. It is implemented entirely in
++software, and does not drive any hardware device.
+
- static int ntsync_char_open(struct inode *inode, struct file *file)
- {
-+ struct ntsync_device *dev;
++This interface is meant as a compatibility tool only, and should not
++be used for general synchronization. Instead use generic, versatile
++interfaces such as futex(2) and poll(2).
+
-+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-+ if (!dev)
-+ return -ENOMEM;
++Synchronization primitives
++==========================
+
-+ xa_init_flags(&dev->objects, XA_FLAGS_ALLOC);
++The ntsync driver exposes three types of synchronization primitives:
++semaphores, mutexes, and events.
+
-+ file->private_data = dev;
- return nonseekable_open(inode, file);
- }
-
- static int ntsync_char_release(struct inode *inode, struct file *file)
- {
-+ struct ntsync_device *dev = file->private_data;
-+ struct ntsync_obj *obj;
-+ unsigned long id;
++A semaphore holds a single volatile 32-bit counter, and a static 32-bit
++integer denoting the maximum value. It is considered signaled when the
++counter is nonzero. The counter is decremented by one when a wait is
++satisfied. Both the initial and maximum count are established when the
++semaphore is created.
+
-+ xa_for_each(&dev->objects, id, obj)
-+ put_obj(obj);
++A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit
++identifier denoting its owner. A mutex is considered signaled when its
++owner is zero (indicating that it is not owned). The recursion count is
++incremented when a wait is satisfied, and ownership is set to the given
++identifier.
+
-+ xa_destroy(&dev->objects);
++A mutex also holds an internal flag denoting whether its previous owner
++has died; such a mutex is said to be abandoned. Owner death is not
++tracked automatically based on thread death, but rather must be
++communicated using ``NTSYNC_IOC_MUTEX_KILL``. An abandoned mutex is
++inherently considered unowned.
+
-+ kfree(dev);
++Except for the "unowned" semantics of zero, the actual value of the
++owner identifier is not interpreted by the ntsync driver at all. The
++intended use is to store a thread identifier; however, the ntsync
++driver does not actually validate that a calling thread provides
++consistent or unique identifiers.
+
-+ return 0;
-+}
++An event holds a volatile boolean state denoting whether it is signaled
++or not. There are two types of events, auto-reset and manual-reset. An
++auto-reset event is designaled when a wait is satisfied; a manual-reset
++event is not. The event type is specified when the event is created.
+
-+static void init_obj(struct ntsync_obj *obj)
-+{
-+ kref_init(&obj->refcount);
-+}
++Unless specified otherwise, all operations on an object are atomic and
++totally ordered with respect to other operations on the same object.
+
-+static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
-+{
-+ struct ntsync_sem_args __user *user_args = argp;
-+ struct ntsync_sem_args args;
-+ struct ntsync_obj *sem;
-+ __u32 id;
-+ int ret;
++Objects are represented by files. When all file descriptors to an
++object are closed, that object is deleted.
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
++Char device
++===========
+
-+ if (args.count > args.max)
-+ return -EINVAL;
++The ntsync driver creates a single char device /dev/ntsync. Each file
++description opened on the device represents a unique instance intended
++to back an individual NT virtual machine. Objects created by one ntsync
++instance may only be used with other objects created by the same
++instance.
+
-+ sem = kzalloc(sizeof(*sem), GFP_KERNEL);
-+ if (!sem)
-+ return -ENOMEM;
++ioctl reference
++===============
+
-+ init_obj(sem);
-+ sem->type = NTSYNC_TYPE_SEM;
-+ sem->u.sem.count = args.count;
-+ sem->u.sem.max = args.max;
++All operations on the device are done through ioctls. There are four
++structures used in ioctl calls::
+
-+ ret = xa_alloc(&dev->objects, &id, sem, xa_limit_32b, GFP_KERNEL);
-+ if (ret < 0) {
-+ kfree(sem);
-+ return ret;
-+ }
++ struct ntsync_sem_args {
++ __u32 sem;
++ __u32 count;
++ __u32 max;
++ };
+
-+ return put_user(id, &user_args->sem);
-+}
++ struct ntsync_mutex_args {
++ __u32 mutex;
++ __u32 owner;
++ __u32 count;
++ };
+
-+static int ntsync_delete(struct ntsync_device *dev, void __user *argp)
-+{
-+ struct ntsync_obj *obj;
-+ __u32 id;
++ struct ntsync_event_args {
++ __u32 event;
++ __u32 signaled;
++ __u32 manual;
++ };
+
-+ if (get_user(id, (__u32 __user *)argp))
-+ return -EFAULT;
++ struct ntsync_wait_args {
++ __u64 timeout;
++ __u64 objs;
++ __u32 count;
++ __u32 owner;
++ __u32 index;
++ __u32 alert;
++ __u32 flags;
++ __u32 pad;
++ };
+
-+ obj = xa_erase(&dev->objects, id);
-+ if (!obj)
-+ return -EINVAL;
++Depending on the ioctl, members of the structure may be used as input,
++output, or not at all. All ioctls return 0 on success.
+
-+ put_obj(obj);
- return 0;
- }
-
- static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- unsigned long parm)
- {
-+ struct ntsync_device *dev = file->private_data;
-+ void __user *argp = (void __user *)parm;
++The ioctls on the device file are as follows:
+
- switch (cmd) {
-+ case NTSYNC_IOC_CREATE_SEM:
-+ return ntsync_create_sem(dev, argp);
-+ case NTSYNC_IOC_DELETE:
-+ return ntsync_delete(dev, argp);
- default:
- return -ENOIOCTLCMD;
- }
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-new file mode 100644
-index 000000000000..d97afc138dcc
---- /dev/null
-+++ b/include/uapi/linux/ntsync.h
-@@ -0,0 +1,25 @@
-+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-+/*
-+ * Kernel support for NT synchronization primitive emulation
-+ *
-+ * Copyright (C) 2021-2022 Elizabeth Figura
-+ */
++.. c:macro:: NTSYNC_IOC_CREATE_SEM
+
-+#ifndef __LINUX_NTSYNC_H
-+#define __LINUX_NTSYNC_H
++ Create a semaphore object. Takes a pointer to struct
++ :c:type:`ntsync_sem_args`, which is used as follows:
+
-+#include <linux/types.h>
++ .. list-table::
+
-+struct ntsync_sem_args {
-+ __u32 sem;
-+ __u32 count;
-+ __u32 max;
-+};
++ * - ``sem``
++ - On output, contains a file descriptor to the created semaphore.
++ * - ``count``
++ - Initial count of the semaphore.
++ * - ``max``
++ - Maximum count of the semaphore.
+
-+#define NTSYNC_IOC_BASE 0xf7
++ Fails with ``EINVAL`` if ``count`` is greater than ``max``.
+
-+#define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \
-+ struct ntsync_sem_args)
-+#define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32)
++.. c:macro:: NTSYNC_IOC_CREATE_MUTEX
+
-+#endif
---
-2.43.0
-
-From 81b4a45e9f5ab9275e42e0edb6cbf6073c44d38e Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:22:42 -0600
-Subject: [PATCH 04/32] ntsync: Introduce NTSYNC_IOC_PUT_SEM.
-
-This corresponds to the NT syscall NtReleaseSemaphore().
----
- drivers/misc/ntsync.c | 76 +++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 +
- 2 files changed, 78 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 3287b94be351..d1c91c2a4f1a 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -21,9 +21,11 @@ enum ntsync_type {
- struct ntsync_obj {
- struct rcu_head rhead;
- struct kref refcount;
-+ spinlock_t lock;
-
- enum ntsync_type type;
-
-+ /* The following fields are protected by the object lock. */
- union {
- struct {
- __u32 count;
-@@ -36,6 +38,19 @@ struct ntsync_device {
- struct xarray objects;
- };
-
-+static struct ntsync_obj *get_obj(struct ntsync_device *dev, __u32 id)
-+{
-+ struct ntsync_obj *obj;
++ Create a mutex object. Takes a pointer to struct
++ :c:type:`ntsync_mutex_args`, which is used as follows:
+
-+ rcu_read_lock();
-+ obj = xa_load(&dev->objects, id);
-+ if (obj && !kref_get_unless_zero(&obj->refcount))
-+ obj = NULL;
-+ rcu_read_unlock();
++ .. list-table::
+
-+ return obj;
-+}
++ * - ``mutex``
++ - On output, contains a file descriptor to the created mutex.
++ * - ``count``
++ - Initial recursion count of the mutex.
++ * - ``owner``
++ - Initial owner of the mutex.
+
- static void destroy_obj(struct kref *ref)
- {
- struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount);
-@@ -48,6 +63,18 @@ static void put_obj(struct ntsync_obj *obj)
- kref_put(&obj->refcount, destroy_obj);
- }
-
-+static struct ntsync_obj *get_obj_typed(struct ntsync_device *dev, __u32 id,
-+ enum ntsync_type type)
-+{
-+ struct ntsync_obj *obj = get_obj(dev, id);
++ If ``owner`` is nonzero and ``count`` is zero, or if ``owner`` is
++ zero and ``count`` is nonzero, the function fails with ``EINVAL``.
+
-+ if (obj && obj->type != type) {
-+ put_obj(obj);
-+ return NULL;
-+ }
-+ return obj;
-+}
++.. c:macro:: NTSYNC_IOC_CREATE_EVENT
+
- static int ntsync_char_open(struct inode *inode, struct file *file)
- {
- struct ntsync_device *dev;
-@@ -81,6 +108,7 @@ static int ntsync_char_release(struct inode *inode, struct file *file)
- static void init_obj(struct ntsync_obj *obj)
- {
- kref_init(&obj->refcount);
-+ spin_lock_init(&obj->lock);
- }
-
- static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
-@@ -131,6 +159,52 @@ static int ntsync_delete(struct ntsync_device *dev, void __user *argp)
- return 0;
- }
-
-+/*
-+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
-+ * invalid.
-+ */
-+static int put_sem_state(struct ntsync_obj *sem, __u32 count)
-+{
-+ lockdep_assert_held(&sem->lock);
++ Create an event object. Takes a pointer to struct
++ :c:type:`ntsync_event_args`, which is used as follows:
+
-+ if (sem->u.sem.count + count < sem->u.sem.count ||
-+ sem->u.sem.count + count > sem->u.sem.max)
-+ return -EOVERFLOW;
++ .. list-table::
+
-+ sem->u.sem.count += count;
-+ return 0;
-+}
++ * - ``event``
++ - On output, contains a file descriptor to the created event.
++ * - ``signaled``
++ - If nonzero, the event is initially signaled, otherwise
++ nonsignaled.
++ * - ``manual``
++ - If nonzero, the event is a manual-reset event, otherwise
++ auto-reset.
+
-+static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp)
-+{
-+ struct ntsync_sem_args __user *user_args = argp;
-+ struct ntsync_sem_args args;
-+ struct ntsync_obj *sem;
-+ __u32 prev_count;
-+ int ret;
++The ioctls on the individual objects are as follows:
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
++.. c:macro:: NTSYNC_IOC_SEM_POST
+
-+ sem = get_obj_typed(dev, args.sem, NTSYNC_TYPE_SEM);
-+ if (!sem)
-+ return -EINVAL;
++ Post to a semaphore object. Takes a pointer to a 32-bit integer,
++ which on input holds the count to be added to the semaphore, and on
++ output contains its previous count.
+
-+ spin_lock(&sem->lock);
++ If adding to the semaphore's current count would raise the latter
++ past the semaphore's maximum count, the ioctl fails with
++ ``EOVERFLOW`` and the semaphore is not affected. If raising the
++ semaphore's count causes it to become signaled, eligible threads
++ waiting on this semaphore will be woken and the semaphore's count
++ decremented appropriately.
+
-+ prev_count = sem->u.sem.count;
-+ ret = put_sem_state(sem, args.count);
++.. c:macro:: NTSYNC_IOC_MUTEX_UNLOCK
+
-+ spin_unlock(&sem->lock);
++ Release a mutex object. Takes a pointer to struct
++ :c:type:`ntsync_mutex_args`, which is used as follows:
+
-+ put_obj(sem);
++ .. list-table::
+
-+ if (!ret && put_user(prev_count, &user_args->count))
-+ ret = -EFAULT;
++ * - ``mutex``
++ - Ignored.
++ * - ``owner``
++ - Specifies the owner trying to release this mutex.
++ * - ``count``
++ - On output, contains the previous recursion count.
+
-+ return ret;
-+}
++ If ``owner`` is zero, the ioctl fails with ``EINVAL``. If ``owner``
++ is not the current owner of the mutex, the ioctl fails with
++ ``EPERM``.
+
- static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- unsigned long parm)
- {
-@@ -142,6 +216,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_create_sem(dev, argp);
- case NTSYNC_IOC_DELETE:
- return ntsync_delete(dev, argp);
-+ case NTSYNC_IOC_PUT_SEM:
-+ return ntsync_put_sem(dev, argp);
- default:
- return -ENOIOCTLCMD;
- }
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index d97afc138dcc..8c610d65f8ef 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -21,5 +21,7 @@ struct ntsync_sem_args {
- #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \
- struct ntsync_sem_args)
- #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32)
-+#define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \
-+ struct ntsync_sem_args)
-
- #endif
---
-2.43.0
-
-From 68f1adf9a8e440dcb00b6665b8bc0f8aee275857 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:31:44 -0600
-Subject: [PATCH 05/32] ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
-
-This corresponds to part of the functionality of the NT syscall
-NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
-the third argument (wait_any) is TRUE, and it does not handle alertable waits.
-Those features have been split out into separate patches to ease review.
----
- drivers/misc/ntsync.c | 229 ++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 13 ++
- 2 files changed, 242 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index d1c91c2a4f1a..2e8d3c2d51a4 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -23,6 +23,8 @@ struct ntsync_obj {
- struct kref refcount;
- spinlock_t lock;
-
-+ struct list_head any_waiters;
++ The mutex's count will be decremented by one. If decrementing the
++ mutex's count causes it to become zero, the mutex is marked as
++ unowned and signaled, and eligible threads waiting on it will be
++ woken as appropriate.
+
- enum ntsync_type type;
-
- /* The following fields are protected by the object lock. */
-@@ -34,6 +36,28 @@ struct ntsync_obj {
- } u;
- };
-
-+struct ntsync_q_entry {
-+ struct list_head node;
-+ struct ntsync_q *q;
-+ struct ntsync_obj *obj;
-+ __u32 index;
-+};
++.. c:macro:: NTSYNC_IOC_SET_EVENT
+
-+struct ntsync_q {
-+ struct task_struct *task;
-+ __u32 owner;
++ Signal an event object. Takes a pointer to a 32-bit integer, which on
++ output contains the previous state of the event.
+
-+ /*
-+ * Protected via atomic_cmpxchg(). Only the thread that wins the
-+ * compare-and-swap may actually change object states and wake this
-+ * task.
-+ */
-+ atomic_t signaled;
++ Eligible threads will be woken, and auto-reset events will be
++ designaled appropriately.
+
-+ __u32 count;
-+ struct ntsync_q_entry entries[];
-+};
++.. c:macro:: NTSYNC_IOC_RESET_EVENT
+
- struct ntsync_device {
- struct xarray objects;
- };
-@@ -109,6 +133,26 @@ static void init_obj(struct ntsync_obj *obj)
- {
- kref_init(&obj->refcount);
- spin_lock_init(&obj->lock);
-+ INIT_LIST_HEAD(&obj->any_waiters);
-+}
++ Designal an event object. Takes a pointer to a 32-bit integer, which
++ on output contains the previous state of the event.
+
-+static void try_wake_any_sem(struct ntsync_obj *sem)
-+{
-+ struct ntsync_q_entry *entry;
++.. c:macro:: NTSYNC_IOC_PULSE_EVENT
+
-+ lockdep_assert_held(&sem->lock);
++ Wake threads waiting on an event object while leaving it in an
++ unsignaled state. Takes a pointer to a 32-bit integer, which on
++ output contains the previous state of the event.
+
-+ list_for_each_entry(entry, &sem->any_waiters, node) {
-+ struct ntsync_q *q = entry->q;
++ A pulse operation can be thought of as a set followed by a reset,
++ performed as a single atomic operation. If two threads are waiting on
++ an auto-reset event which is pulsed, only one will be woken. If two
++ threads are waiting a manual-reset event which is pulsed, both will
++ be woken. However, in both cases, the event will be unsignaled
++ afterwards, and a simultaneous read operation will always report the
++ event as unsignaled.
+
-+ if (!sem->u.sem.count)
-+ break;
++.. c:macro:: NTSYNC_IOC_READ_SEM
+
-+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
-+ sem->u.sem.count--;
-+ wake_up_process(q->task);
-+ }
-+ }
- }
-
- static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
-@@ -194,6 +238,8 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp)
-
- prev_count = sem->u.sem.count;
- ret = put_sem_state(sem, args.count);
-+ if (!ret)
-+ try_wake_any_sem(sem);
-
- spin_unlock(&sem->lock);
-
-@@ -205,6 +251,187 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
-+static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
-+{
-+ int ret = 0;
++ Read the current state of a semaphore object. Takes a pointer to
++ struct :c:type:`ntsync_sem_args`, which is used as follows:
+
-+ do {
-+ if (signal_pending(current)) {
-+ ret = -ERESTARTSYS;
-+ break;
-+ }
++ .. list-table::
+
-+ set_current_state(TASK_INTERRUPTIBLE);
-+ if (atomic_read(&q->signaled) != -1) {
-+ ret = 0;
-+ break;
-+ }
-+ ret = schedule_hrtimeout(timeout, HRTIMER_MODE_ABS);
-+ } while (ret < 0);
-+ __set_current_state(TASK_RUNNING);
++ * - ``sem``
++ - Ignored.
++ * - ``count``
++ - On output, contains the current count of the semaphore.
++ * - ``max``
++ - On output, contains the maximum count of the semaphore.
+
-+ return ret;
-+}
++.. c:macro:: NTSYNC_IOC_READ_MUTEX
+
-+/*
-+ * Allocate and initialize the ntsync_q structure, but do not queue us yet.
-+ * Also, calculate the relative timeout.
-+ */
-+static int setup_wait(struct ntsync_device *dev,
-+ const struct ntsync_wait_args *args,
-+ ktime_t *ret_timeout, struct ntsync_q **ret_q)
-+{
-+ const __u32 count = args->count;
-+ struct ntsync_q *q;
-+ ktime_t timeout = 0;
-+ __u32 *ids;
-+ __u32 i, j;
++ Read the current state of a mutex object. Takes a pointer to struct
++ :c:type:`ntsync_mutex_args`, which is used as follows:
+
-+ if (!args->owner || args->pad)
-+ return -EINVAL;
++ .. list-table::
+
-+ if (args->count > NTSYNC_MAX_WAIT_COUNT)
-+ return -EINVAL;
++ * - ``mutex``
++ - Ignored.
++ * - ``owner``
++ - On output, contains the current owner of the mutex, or zero
++ if the mutex is not currently owned.
++ * - ``count``
++ - On output, contains the current recursion count of the mutex.
+
-+ if (args->timeout) {
-+ struct timespec64 to;
++ If the mutex is marked as abandoned, the function fails with
++ ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
++ zero.
+
-+ if (get_timespec64(&to, u64_to_user_ptr(args->timeout)))
-+ return -EFAULT;
-+ if (!timespec64_valid(&to))
-+ return -EINVAL;
++.. c:macro:: NTSYNC_IOC_READ_EVENT
+
-+ timeout = timespec64_to_ns(&to);
-+ }
++ Read the current state of an event object. Takes a pointer to struct
++ :c:type:`ntsync_event_args`, which is used as follows:
+
-+ ids = kmalloc_array(count, sizeof(*ids), GFP_KERNEL);
-+ if (!ids)
-+ return -ENOMEM;
-+ if (copy_from_user(ids, u64_to_user_ptr(args->objs),
-+ array_size(count, sizeof(*ids)))) {
-+ kfree(ids);
-+ return -EFAULT;
-+ }
++ .. list-table::
+
-+ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
-+ if (!q) {
-+ kfree(ids);
-+ return -ENOMEM;
-+ }
-+ q->task = current;
-+ q->owner = args->owner;
-+ atomic_set(&q->signaled, -1);
-+ q->count = count;
++ * - ``event``
++ - Ignored.
++ * - ``signaled``
++ - On output, contains the current state of the event.
++ * - ``manual``
++ - On output, contains 1 if the event is a manual-reset event,
++ and 0 otherwise.
+
-+ for (i = 0; i < count; i++) {
-+ struct ntsync_q_entry *entry = &q->entries[i];
-+ struct ntsync_obj *obj = get_obj(dev, ids[i]);
++.. c:macro:: NTSYNC_IOC_KILL_OWNER
+
-+ if (!obj)
-+ goto err;
++ Mark a mutex as unowned and abandoned if it is owned by the given
++ owner. Takes an input-only pointer to a 32-bit integer denoting the
++ owner. If the owner is zero, the ioctl fails with ``EINVAL``. If the
++ owner does not own the mutex, the function fails with ``EPERM``.
+
-+ entry->obj = obj;
-+ entry->q = q;
-+ entry->index = i;
-+ }
++ Eligible threads waiting on the mutex will be woken as appropriate
++ (and such waits will fail with ``EOWNERDEAD``, as described below).
+
-+ kfree(ids);
++.. c:macro:: NTSYNC_IOC_WAIT_ANY
+
-+ *ret_q = q;
-+ *ret_timeout = timeout;
-+ return 0;
++ Poll on any of a list of objects, atomically acquiring at most one.
++ Takes a pointer to struct :c:type:`ntsync_wait_args`, which is
++ used as follows:
+
-+err:
-+ for (j = 0; j < i; j++)
-+ put_obj(q->entries[j].obj);
-+ kfree(ids);
-+ kfree(q);
-+ return -EINVAL;
-+}
++ .. list-table::
+
-+static void try_wake_any_obj(struct ntsync_obj *obj)
-+{
-+ switch (obj->type) {
-+ case NTSYNC_TYPE_SEM:
-+ try_wake_any_sem(obj);
-+ break;
-+ }
-+}
++ * - ``timeout``
++ - Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
++ is set, the timeout is measured against the REALTIME clock;
++ otherwise it is measured against the MONOTONIC clock. If the
++ timeout is equal to or earlier than the current time, the
++ function returns immediately without sleeping. If ``timeout``
++ is U64_MAX, the function will sleep until an object is
++ signaled, and will not fail with ``ETIMEDOUT``.
++ * - ``objs``
++ - Pointer to an array of ``count`` file descriptors
++ (specified as an integer so that the structure has the same
++ size regardless of architecture). If any object is
++ invalid, the function fails with ``EINVAL``.
++ * - ``count``
++ - Number of objects specified in the ``objs`` array.
++ If greater than ``NTSYNC_MAX_WAIT_COUNT``, the function fails
++ with ``EINVAL``.
++ * - ``owner``
++ - Mutex owner identifier. If any object in ``objs`` is a mutex,
++ the ioctl will attempt to acquire that mutex on behalf of
++ ``owner``. If ``owner`` is zero, the ioctl fails with
++ ``EINVAL``.
++ * - ``index``
++ - On success, contains the index (into ``objs``) of the object
++ which was signaled. If ``alert`` was signaled instead,
++ this contains ``count``.
++ * - ``alert``
++ - Optional event object file descriptor. If nonzero, this
++ specifies an "alert" event object which, if signaled, will
++ terminate the wait. If nonzero, the identifier must point to a
++ valid event.
++ * - ``flags``
++ - Zero or more flags. Currently the only flag is
++ ``NTSYNC_WAIT_REALTIME``, which causes the timeout to be
++ measured against the REALTIME clock instead of MONOTONIC.
++ * - ``pad``
++ - Unused, must be set to zero.
++
++ This function attempts to acquire one of the given objects. If unable
++ to do so, it sleeps until an object becomes signaled, subsequently
++ acquiring it, or the timeout expires. In the latter case the ioctl
++ fails with ``ETIMEDOUT``. The function only acquires one object, even
++ if multiple objects are signaled.
++
++ A semaphore is considered to be signaled if its count is nonzero, and
++ is acquired by decrementing its count by one. A mutex is considered
++ to be signaled if it is unowned or if its owner matches the ``owner``
++ argument, and is acquired by incrementing its recursion count by one
++ and setting its owner to the ``owner`` argument. An auto-reset event
++ is acquired by designaling it; a manual-reset event is not affected
++ by acquisition.
+
-+static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
-+{
-+ struct ntsync_wait_args args;
-+ struct ntsync_q *q;
-+ ktime_t timeout;
-+ int signaled;
-+ __u32 i;
-+ int ret;
++ Acquisition is atomic and totally ordered with respect to other
++ operations on the same object. If two wait operations (with different
++ ``owner`` identifiers) are queued on the same mutex, only one is
++ signaled. If two wait operations are queued on the same semaphore,
++ and a value of one is posted to it, only one is signaled. The order
++ in which threads are signaled is not specified.
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
++ If an abandoned mutex is acquired, the ioctl fails with
++ ``EOWNERDEAD``. Although this is a failure return, the function may
++ otherwise be considered successful. The mutex is marked as owned by
++ the given owner (with a recursion count of 1) and as no longer
++ abandoned, and ``index`` is still set to the index of the mutex.
+
-+ ret = setup_wait(dev, &args, &timeout, &q);
-+ if (ret < 0)
-+ return ret;
++ The ``alert`` argument is an "extra" event which can terminate the
++ wait, independently of all other objects. If members of ``objs`` and
++ ``alert`` are both simultaneously signaled, a member of ``objs`` will
++ always be given priority and acquired first.
+
-+ /* queue ourselves */
++ It is valid to pass the same object more than once, including by
++ passing the same event in the ``objs`` array and in ``alert``. If a
++ wakeup occurs due to that object being signaled, ``index`` is set to
++ the lowest index corresponding to that object.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct ntsync_q_entry *entry = &q->entries[i];
-+ struct ntsync_obj *obj = entry->obj;
++ The function may fail with ``EINTR`` if a signal is received.
+
-+ spin_lock(&obj->lock);
-+ list_add_tail(&entry->node, &obj->any_waiters);
-+ spin_unlock(&obj->lock);
-+ }
++.. c:macro:: NTSYNC_IOC_WAIT_ALL
+
-+ /* check if we are already signaled */
++ Poll on a list of objects, atomically acquiring all of them. Takes a
++ pointer to struct :c:type:`ntsync_wait_args`, which is used
++ identically to ``NTSYNC_IOC_WAIT_ANY``, except that ``index`` is
++ always filled with zero on success if not woken via alert.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct ntsync_obj *obj = q->entries[i].obj;
++ This function attempts to simultaneously acquire all of the given
++ objects. If unable to do so, it sleeps until all objects become
++ simultaneously signaled, subsequently acquiring them, or the timeout
++ expires. In the latter case the ioctl fails with ``ETIMEDOUT`` and no
++ objects are modified.
+
-+ if (atomic_read(&q->signaled) != -1)
-+ break;
++ Objects may become signaled and subsequently designaled (through
++ acquisition by other threads) while this thread is sleeping. Only
++ once all objects are simultaneously signaled does the ioctl acquire
++ them and return. The entire acquisition is atomic and totally ordered
++ with respect to other operations on any of the given objects.
++
++ If an abandoned mutex is acquired, the ioctl fails with
++ ``EOWNERDEAD``. Similarly to ``NTSYNC_IOC_WAIT_ANY``, all objects are
++ nevertheless marked as acquired. Note that if multiple mutex objects
++ are specified, there is no way to know which were marked as
++ abandoned.
++
++ As with "any" waits, the ``alert`` argument is an "extra" event which
++ can terminate the wait. Critically, however, an "all" wait will
++ succeed if all members in ``objs`` are signaled, *or* if ``alert`` is
++ signaled. In the latter case ``index`` will be set to ``count``. As
++ with "any" waits, if both conditions are filled, the former takes
++ priority, and objects in ``objs`` will be acquired.
+
-+ spin_lock(&obj->lock);
-+ try_wake_any_obj(obj);
-+ spin_unlock(&obj->lock);
-+ }
++ Unlike ``NTSYNC_IOC_WAIT_ANY``, it is not valid to pass the same
++ object more than once, nor is it valid to pass the same object in
++ ``objs`` and in ``alert``. If this is attempted, the function fails
++ with ``EINVAL``.
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 9ed4d3868539..d83dd35d9f73 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15595,6 +15595,15 @@ T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
+ F: Documentation/filesystems/ntfs3.rst
+ F: fs/ntfs3/
+
++NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
++M: Elizabeth Figura <zfigura@codeweavers.com>
++L: wine-devel@winehq.org
++S: Supported
++F: Documentation/userspace-api/ntsync.rst
++F: drivers/misc/ntsync.c
++F: include/uapi/linux/ntsync.h
++F: tools/testing/selftests/drivers/ntsync/
+
-+ /* sleep */
+ NUBUS SUBSYSTEM
+ M: Finn Thain <fthain@linux-m68k.org>
+ L: linux-m68k@lists.linux-m68k.org
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 4fb291f0bf7c..801ed229ed7d 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -506,6 +506,17 @@ config OPEN_DICE
+
+ If unsure, say N.
+
++config NTSYNC
++ tristate "NT synchronization primitive emulation"
++ help
++ This module provides kernel support for emulation of Windows NT
++ synchronization primitives. It is not a hardware driver.
+
-+ ret = ntsync_schedule(q, args.timeout ? &timeout : NULL);
++ To compile this driver as a module, choose M here: the
++ module will be called ntsync.
+
-+ /* and finally, unqueue */
++ If unsure, say N.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct ntsync_q_entry *entry = &q->entries[i];
-+ struct ntsync_obj *obj = entry->obj;
+ config VCPU_STALL_DETECTOR
+ tristate "Guest vCPU stall detector"
+ depends on OF && HAS_IOMEM
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index ea6ea5bbbc9c..153a3f4837e8 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
+ obj-$(CONFIG_UACCE) += uacce/
+ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
+ obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
++obj-$(CONFIG_NTSYNC) += ntsync.o
+ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
+ obj-$(CONFIG_OPEN_DICE) += open-dice.o
+ obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
+diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
+new file mode 100644
+index 000000000000..f54c81dada3d
+--- /dev/null
++++ b/drivers/misc/ntsync.c
+@@ -0,0 +1,1159 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * ntsync.c - Kernel driver for NT synchronization primitives
++ *
++ * Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
++ */
+
-+ spin_lock(&obj->lock);
-+ list_del(&entry->node);
-+ spin_unlock(&obj->lock);
++#include <linux/anon_inodes.h>
++#include <linux/atomic.h>
++#include <linux/file.h>
++#include <linux/fs.h>
++#include <linux/hrtimer.h>
++#include <linux/ktime.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/overflow.h>
++#include <linux/sched.h>
++#include <linux/sched/signal.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <uapi/linux/ntsync.h>
+
-+ put_obj(obj);
-+ }
++#define NTSYNC_NAME "ntsync"
+
-+ signaled = atomic_read(&q->signaled);
-+ if (signaled != -1) {
-+ struct ntsync_wait_args __user *user_args = argp;
++enum ntsync_type {
++ NTSYNC_TYPE_SEM,
++ NTSYNC_TYPE_MUTEX,
++ NTSYNC_TYPE_EVENT,
++};
+
-+ /* even if we caught a signal, we need to communicate success */
-+ ret = 0;
++/*
++ * Individual synchronization primitives are represented by
++ * struct ntsync_obj, and each primitive is backed by a file.
++ *
++ * The whole namespace is represented by a struct ntsync_device also
++ * backed by a file.
++ *
++ * Both rely on struct file for reference counting. Individual
++ * ntsync_obj objects take a reference to the device when created.
++ * Wait operations take a reference to each object being waited on for
++ * the duration of the wait.
++ */
+
-+ if (put_user(signaled, &user_args->index))
-+ ret = -EFAULT;
-+ } else if (!ret) {
-+ ret = -ETIMEDOUT;
-+ }
++struct ntsync_obj {
++ spinlock_t lock;
+
-+ kfree(q);
-+ return ret;
-+}
++ enum ntsync_type type;
+
- static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- unsigned long parm)
- {
-@@ -218,6 +445,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_delete(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
-+ case NTSYNC_IOC_WAIT_ANY:
-+ return ntsync_wait_any(dev, argp);
- default:
- return -ENOIOCTLCMD;
- }
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 8c610d65f8ef..10f07da7864e 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -16,6 +16,17 @@ struct ntsync_sem_args {
- __u32 max;
- };
-
-+struct ntsync_wait_args {
-+ __u64 timeout;
-+ __u64 objs;
-+ __u32 count;
-+ __u32 owner;
-+ __u32 index;
-+ __u32 pad;
-+};
++ struct file *file;
++ struct ntsync_device *dev;
+
-+#define NTSYNC_MAX_WAIT_COUNT 64
++ /* The following fields are protected by the object lock. */
++ union {
++ struct {
++ __u32 count;
++ __u32 max;
++ } sem;
++ struct {
++ __u32 count;
++ __u32 owner;
++ bool ownerdead;
++ } mutex;
++ struct {
++ bool manual;
++ bool signaled;
++ } event;
++ } u;
+
- #define NTSYNC_IOC_BASE 0xf7
-
- #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \
-@@ -23,5 +34,7 @@ struct ntsync_sem_args {
- #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32)
- #define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \
- struct ntsync_sem_args)
-+#define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \
-+ struct ntsync_wait_args)
-
- #endif
---
-2.43.0
-
-From ec146c6daf3cd94d70135965f3260f2bea90f305 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:36:09 -0600
-Subject: [PATCH 06/32] ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
-
-This corresponds to part of the functionality of the NT syscall
-NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
-the third argument (wait_any) is FALSE, and it does not yet handle alertable
-waits.
----
- drivers/misc/ntsync.c | 241 ++++++++++++++++++++++++++++++++++--
- include/uapi/linux/ntsync.h | 2 +
- 2 files changed, 235 insertions(+), 8 deletions(-)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 2e8d3c2d51a4..2685363fae9e 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -23,7 +23,34 @@ struct ntsync_obj {
- struct kref refcount;
- spinlock_t lock;
-
+ /*
+ * any_waiters is protected by the object lock, but all_waiters is
+ * protected by the device wait_all_lock.
+ */
- struct list_head any_waiters;
++ struct list_head any_waiters;
+ struct list_head all_waiters;
+
+ /*
@@ -889,36 +596,50 @@ index 2e8d3c2d51a4..2685363fae9e 100644
+ * lock all of the objects, and that means grabbing the wait_all_lock
+ * below (and, due to lock ordering rules, before locking this object).
+ * However, wait-all is a rare operation, and grabbing the wait-all
-+ * lock for every wake would create unnecessary contention. Therefore we
-+ * first check whether all_hint is zero, and, if it is, we skip trying
-+ * to wake "all" waiters.
++ * lock for every wake would create unnecessary contention.
++ * Therefore we first check whether all_hint is zero, and, if it is,
++ * we skip trying to wake "all" waiters.
+ *
+ * This hint isn't protected by any lock. It might change during the
+ * course of a wake, but there's no meaningful race there; it's only a
+ * hint.
+ *
+ * Since wait requests must originate from user-space threads, we're
-+ * limited here by PID_MAX_LIMIT, so there's no risk of saturation.
++ * limited here by PID_MAX_LIMIT, so there's no risk of overflow.
+ */
+ atomic_t all_hint;
-
- enum ntsync_type type;
-
-@@ -54,11 +81,25 @@ struct ntsync_q {
- */
- atomic_t signaled;
-
++};
++
++struct ntsync_q_entry {
++ struct list_head node;
++ struct ntsync_q *q;
++ struct ntsync_obj *obj;
++ __u32 index;
++};
++
++struct ntsync_q {
++ struct task_struct *task;
++ __u32 owner;
++
++ /*
++ * Protected via atomic_cmpxchg(). Only the thread that wins the
++ * compare-and-swap may actually change object states and wake this
++ * task.
++ */
++ atomic_t signaled;
++
+ bool all;
- __u32 count;
- struct ntsync_q_entry entries[];
- };
-
- struct ntsync_device {
++ bool ownerdead;
++ __u32 count;
++ struct ntsync_q_entry entries[];
++};
++
++struct ntsync_device {
+ /*
+ * Wait-all operations must atomically grab all objects, and be totally
-+ * ordered with respect to each other and wait-any operations. If one
-+ * thread is trying to acquire several objects, another thread cannot
-+ * touch the object at the same time.
++ * ordered with respect to each other and wait-any operations.
++ * If one thread is trying to acquire several objects, another thread
++ * cannot touch the object at the same time.
+ *
+ * We achieve this by grabbing multiple object locks at the same time.
+ * However, this creates a lock ordering problem. To solve that problem,
@@ -927,27 +648,8 @@ index 2e8d3c2d51a4..2685363fae9e 100644
+ */
+ spinlock_t wait_all_lock;
+
- struct xarray objects;
- };
-
-@@ -107,6 +148,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file)
- if (!dev)
- return -ENOMEM;
-
-+ spin_lock_init(&dev->wait_all_lock);
-+
- xa_init_flags(&dev->objects, XA_FLAGS_ALLOC);
-
- file->private_data = dev;
-@@ -132,8 +175,81 @@ static int ntsync_char_release(struct inode *inode, struct file *file)
- static void init_obj(struct ntsync_obj *obj)
- {
- kref_init(&obj->refcount);
-+ atomic_set(&obj->all_hint, 0);
- spin_lock_init(&obj->lock);
- INIT_LIST_HEAD(&obj->any_waiters);
-+ INIT_LIST_HEAD(&obj->all_waiters);
-+}
++ struct file *file;
++};
+
+static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
+{
@@ -956,6 +658,12 @@ index 2e8d3c2d51a4..2685363fae9e 100644
+ switch (obj->type) {
+ case NTSYNC_TYPE_SEM:
+ return !!obj->u.sem.count;
++ case NTSYNC_TYPE_MUTEX:
++ if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
++ return false;
++ return obj->u.mutex.count < UINT_MAX;
++ case NTSYNC_TYPE_EVENT:
++ return obj->u.event.signaled;
+ }
+
+ WARN(1, "bad object type %#x\n", obj->type);
@@ -998,6 +706,17 @@ index 2e8d3c2d51a4..2685363fae9e 100644
+ case NTSYNC_TYPE_SEM:
+ obj->u.sem.count--;
+ break;
++ case NTSYNC_TYPE_MUTEX:
++ if (obj->u.mutex.ownerdead)
++ q->ownerdead = true;
++ obj->u.mutex.ownerdead = false;
++ obj->u.mutex.count++;
++ obj->u.mutex.owner = q->owner;
++ break;
++ case NTSYNC_TYPE_EVENT:
++ if (!obj->u.event.manual)
++ obj->u.event.signaled = false;
++ break;
+ }
+ }
+ wake_up_process(q->task);
@@ -1018,260 +737,27 @@ index 2e8d3c2d51a4..2685363fae9e 100644
+
+ list_for_each_entry(entry, &obj->all_waiters, node)
+ try_wake_all(dev, entry->q, obj);
- }
-
- static void try_wake_any_sem(struct ntsync_obj *sem)
-@@ -234,14 +350,29 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp)
- if (!sem)
- return -EINVAL;
-
-- spin_lock(&sem->lock);
-+ if (atomic_read(&sem->all_hint) > 0) {
-+ spin_lock(&dev->wait_all_lock);
-+ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock);
-+
-+ prev_count = sem->u.sem.count;
-+ ret = put_sem_state(sem, args.count);
-+ if (!ret) {
-+ try_wake_all_obj(dev, sem);
-+ try_wake_any_sem(sem);
-+ }
-
-- prev_count = sem->u.sem.count;
-- ret = put_sem_state(sem, args.count);
-- if (!ret)
-- try_wake_any_sem(sem);
-+ spin_unlock(&sem->lock);
-+ spin_unlock(&dev->wait_all_lock);
-+ } else {
-+ spin_lock(&sem->lock);
-
-- spin_unlock(&sem->lock);
-+ prev_count = sem->u.sem.count;
-+ ret = put_sem_state(sem, args.count);
-+ if (!ret)
-+ try_wake_any_sem(sem);
-+
-+ spin_unlock(&sem->lock);
-+ }
-
- put_obj(sem);
-
-@@ -278,7 +409,7 @@ static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
- * Also, calculate the relative timeout.
- */
- static int setup_wait(struct ntsync_device *dev,
-- const struct ntsync_wait_args *args,
-+ const struct ntsync_wait_args *args, bool all,
- ktime_t *ret_timeout, struct ntsync_q **ret_q)
- {
- const __u32 count = args->count;
-@@ -321,6 +452,7 @@ static int setup_wait(struct ntsync_device *dev,
- q->task = current;
- q->owner = args->owner;
- atomic_set(&q->signaled, -1);
-+ q->all = all;
- q->count = count;
-
- for (i = 0; i < count; i++) {
-@@ -330,6 +462,16 @@ static int setup_wait(struct ntsync_device *dev,
- if (!obj)
- goto err;
-
-+ if (all) {
-+ /* Check that the objects are all distinct. */
-+ for (j = 0; j < i; j++) {
-+ if (obj == q->entries[j].obj) {
-+ put_obj(obj);
-+ goto err;
-+ }
-+ }
-+ }
++}
+
- entry->obj = obj;
- entry->q = q;
- entry->index = i;
-@@ -370,7 +512,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- if (copy_from_user(&args, argp, sizeof(args)))
- return -EFAULT;
-
-- ret = setup_wait(dev, &args, &timeout, &q);
-+ ret = setup_wait(dev, &args, false, &timeout, &q);
- if (ret < 0)
- return ret;
-
-@@ -432,6 +574,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
-+static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
++static void try_wake_any_sem(struct ntsync_obj *sem)
+{
-+ struct ntsync_wait_args args;
-+ struct ntsync_q *q;
-+ ktime_t timeout;
-+ int signaled;
-+ __u32 i;
-+ int ret;
-+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
-+
-+ ret = setup_wait(dev, &args, true, &timeout, &q);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* queue ourselves */
-+
-+ spin_lock(&dev->wait_all_lock);
-+
-+ for (i = 0; i < args.count; i++) {
-+ struct ntsync_q_entry *entry = &q->entries[i];
-+ struct ntsync_obj *obj = entry->obj;
-+
-+ atomic_inc(&obj->all_hint);
-+
-+ /*
-+ * obj->all_waiters is protected by dev->wait_all_lock rather
-+ * than obj->lock, so there is no need to acquire it here.
-+ */
-+ list_add_tail(&entry->node, &obj->all_waiters);
-+ }
-+
-+ /* check if we are already signaled */
-+
-+ try_wake_all(dev, q, NULL);
-+
-+ spin_unlock(&dev->wait_all_lock);
-+
-+ /* sleep */
-+
-+ ret = ntsync_schedule(q, args.timeout ? &timeout : NULL);
-+
-+ /* and finally, unqueue */
-+
-+ spin_lock(&dev->wait_all_lock);
-+
-+ for (i = 0; i < args.count; i++) {
-+ struct ntsync_q_entry *entry = &q->entries[i];
-+ struct ntsync_obj *obj = entry->obj;
-+
-+ /*
-+ * obj->all_waiters is protected by dev->wait_all_lock rather
-+ * than obj->lock, so there is no need to acquire it here.
-+ */
-+ list_del(&entry->node);
-+
-+ atomic_dec(&obj->all_hint);
-+
-+ put_obj(obj);
-+ }
++ struct ntsync_q_entry *entry;
+
-+ spin_unlock(&dev->wait_all_lock);
++ lockdep_assert_held(&sem->lock);
+
-+ signaled = atomic_read(&q->signaled);
-+ if (signaled != -1) {
-+ struct ntsync_wait_args __user *user_args = argp;
++ list_for_each_entry(entry, &sem->any_waiters, node) {
++ struct ntsync_q *q = entry->q;
+
-+ /* even if we caught a signal, we need to communicate success */
-+ ret = 0;
++ if (!sem->u.sem.count)
++ break;
+
-+ if (put_user(signaled, &user_args->index))
-+ ret = -EFAULT;
-+ } else if (!ret) {
-+ ret = -ETIMEDOUT;
++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
++ sem->u.sem.count--;
++ wake_up_process(q->task);
++ }
+ }
-+
-+ kfree(q);
-+ return ret;
+}
+
- static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- unsigned long parm)
- {
-@@ -445,6 +668,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_delete(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
-+ case NTSYNC_IOC_WAIT_ALL:
-+ return ntsync_wait_all(dev, argp);
- case NTSYNC_IOC_WAIT_ANY:
- return ntsync_wait_any(dev, argp);
- default:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 10f07da7864e..a5bed5a39b21 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -36,5 +36,7 @@ struct ntsync_wait_args {
- struct ntsync_sem_args)
- #define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \
- struct ntsync_wait_args)
-+#define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \
-+ struct ntsync_wait_args)
-
- #endif
---
-2.43.0
-
-From 965d8711ba4fbb0867c8baef95129f0ff62d9419 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:41:10 -0600
-Subject: [PATCH 07/32] ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
-
-This corresponds to the NT syscall NtCreateMutant().
----
- drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 8 +++++
- 2 files changed, 80 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 2685363fae9e..d48f2ef41341 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -16,6 +16,7 @@
-
- enum ntsync_type {
- NTSYNC_TYPE_SEM,
-+ NTSYNC_TYPE_MUTEX,
- };
-
- struct ntsync_obj {
-@@ -60,6 +61,10 @@ struct ntsync_obj {
- __u32 count;
- __u32 max;
- } sem;
-+ struct {
-+ __u32 count;
-+ __u32 owner;
-+ } mutex;
- } u;
- };
-
-@@ -188,6 +193,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
- switch (obj->type) {
- case NTSYNC_TYPE_SEM:
- return !!obj->u.sem.count;
-+ case NTSYNC_TYPE_MUTEX:
-+ if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
-+ return false;
-+ return obj->u.mutex.count < UINT_MAX;
- }
-
- WARN(1, "bad object type %#x\n", obj->type);
-@@ -230,6 +239,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
- case NTSYNC_TYPE_SEM:
- obj->u.sem.count--;
- break;
-+ case NTSYNC_TYPE_MUTEX:
-+ obj->u.mutex.count++;
-+ obj->u.mutex.owner = q->owner;
-+ break;
- }
- }
- wake_up_process(q->task);
-@@ -271,6 +284,28 @@ static void try_wake_any_sem(struct ntsync_obj *sem)
- }
- }
-
+static void try_wake_any_mutex(struct ntsync_obj *mutex)
+{
+ struct ntsync_q_entry *entry;
@@ -1287,6 +773,9 @@ index 2685363fae9e..d48f2ef41341 100644
+ continue;
+
+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
++ if (mutex->u.mutex.ownerdead)
++ q->ownerdead = true;
++ mutex->u.mutex.ownerdead = false;
+ mutex->u.mutex.count++;
+ mutex->u.mutex.owner = q->owner;
+ wake_up_process(q->task);
@@ -1294,119 +783,93 @@ index 2685363fae9e..d48f2ef41341 100644
+ }
+}
+
- static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
- {
- struct ntsync_sem_args __user *user_args = argp;
-@@ -303,6 +338,38 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
- return put_user(id, &user_args->sem);
- }
-
-+static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
++static void try_wake_any_event(struct ntsync_obj *event)
+{
-+ struct ntsync_mutex_args __user *user_args = argp;
-+ struct ntsync_mutex_args args;
-+ struct ntsync_obj *mutex;
-+ __u32 id;
++ struct ntsync_q_entry *entry;
++
++ lockdep_assert_held(&event->lock);
++
++ list_for_each_entry(entry, &event->any_waiters, node) {
++ struct ntsync_q *q = entry->q;
++
++ if (!event->u.event.signaled)
++ break;
++
++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
++ if (!event->u.event.manual)
++ event->u.event.signaled = false;
++ wake_up_process(q->task);
++ }
++ }
++}
++
++/*
++ * Actually change the semaphore state, returning -EOVERFLOW if it is made
++ * invalid.
++ */
++static int post_sem_state(struct ntsync_obj *sem, __u32 count)
++{
++ __u32 sum;
++
++ lockdep_assert_held(&sem->lock);
++
++ if (check_add_overflow(sem->u.sem.count, count, &sum) ||
++ sum > sem->u.sem.max)
++ return -EOVERFLOW;
++
++ sem->u.sem.count = sum;
++ return 0;
++}
++
++static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
++{
++ struct ntsync_device *dev = sem->dev;
++ __u32 __user *user_args = argp;
++ __u32 prev_count;
++ __u32 args;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
-+ if (!args.owner != !args.count)
++ if (sem->type != NTSYNC_TYPE_SEM)
+ return -EINVAL;
+
-+ mutex = kzalloc(sizeof(*mutex), GFP_KERNEL);
-+ if (!mutex)
-+ return -ENOMEM;
++ if (atomic_read(&sem->all_hint) > 0) {
++ spin_lock(&dev->wait_all_lock);
++ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock);
+
-+ init_obj(mutex);
-+ mutex->type = NTSYNC_TYPE_MUTEX;
-+ mutex->u.mutex.count = args.count;
-+ mutex->u.mutex.owner = args.owner;
++ prev_count = sem->u.sem.count;
++ ret = post_sem_state(sem, args);
++ if (!ret) {
++ try_wake_all_obj(dev, sem);
++ try_wake_any_sem(sem);
++ }
+
-+ ret = xa_alloc(&dev->objects, &id, mutex, xa_limit_32b, GFP_KERNEL);
-+ if (ret < 0) {
-+ kfree(mutex);
-+ return ret;
++ spin_unlock(&sem->lock);
++ spin_unlock(&dev->wait_all_lock);
++ } else {
++ spin_lock(&sem->lock);
++
++ prev_count = sem->u.sem.count;
++ ret = post_sem_state(sem, args);
++ if (!ret)
++ try_wake_any_sem(sem);
++
++ spin_unlock(&sem->lock);
+ }
+
-+ return put_user(id, &user_args->mutex);
-+}
++ if (!ret && put_user(prev_count, user_args))
++ ret = -EFAULT;
+
- static int ntsync_delete(struct ntsync_device *dev, void __user *argp)
- {
- struct ntsync_obj *obj;
-@@ -497,6 +564,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
- case NTSYNC_TYPE_SEM:
- try_wake_any_sem(obj);
- break;
-+ case NTSYNC_TYPE_MUTEX:
-+ try_wake_any_mutex(obj);
-+ break;
- }
- }
-
-@@ -662,6 +732,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- void __user *argp = (void __user *)parm;
-
- switch (cmd) {
-+ case NTSYNC_IOC_CREATE_MUTEX:
-+ return ntsync_create_mutex(dev, argp);
- case NTSYNC_IOC_CREATE_SEM:
- return ntsync_create_sem(dev, argp);
- case NTSYNC_IOC_DELETE:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index a5bed5a39b21..26d1b3d4847f 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -16,6 +16,12 @@ struct ntsync_sem_args {
- __u32 max;
- };
-
-+struct ntsync_mutex_args {
-+ __u32 mutex;
-+ __u32 owner;
-+ __u32 count;
-+};
++ return ret;
++}
+
- struct ntsync_wait_args {
- __u64 timeout;
- __u64 objs;
-@@ -38,5 +44,7 @@ struct ntsync_wait_args {
- struct ntsync_wait_args)
- #define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \
- struct ntsync_wait_args)
-+#define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \
-+ struct ntsync_mutex_args)
-
- #endif
---
-2.43.0
-
-From b348f2e4e4054c20f0f1852ffaa90095e1f32415 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:44:41 -0600
-Subject: [PATCH 08/32] ntsync: Introduce NTSYNC_IOC_PUT_MUTEX.
-
-This corresponds to the NT syscall NtReleaseMutant().
----
- drivers/misc/ntsync.c | 67 +++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 69 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index d48f2ef41341..28f43768d1c3 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -449,6 +449,71 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
+/*
+ * Actually change the mutex state, returning -EPERM if not the owner.
+ */
-+static int put_mutex_state(struct ntsync_obj *mutex,
-+ const struct ntsync_mutex_args *args)
++static int unlock_mutex_state(struct ntsync_obj *mutex,
++ const struct ntsync_mutex_args *args)
+{
+ lockdep_assert_held(&mutex->lock);
+
@@ -1418,11 +881,11 @@ index d48f2ef41341..28f43768d1c3 100644
+ return 0;
+}
+
-+static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp)
++static int ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
+{
+ struct ntsync_mutex_args __user *user_args = argp;
++ struct ntsync_device *dev = mutex->dev;
+ struct ntsync_mutex_args args;
-+ struct ntsync_obj *mutex;
+ __u32 prev_count;
+ int ret;
+
@@ -1431,8 +894,7 @@ index d48f2ef41341..28f43768d1c3 100644
+ if (!args.owner)
+ return -EINVAL;
+
-+ mutex = get_obj_typed(dev, args.mutex, NTSYNC_TYPE_MUTEX);
-+ if (!mutex)
++ if (mutex->type != NTSYNC_TYPE_MUTEX)
+ return -EINVAL;
+
+ if (atomic_read(&mutex->all_hint) > 0) {
@@ -1440,7 +902,7 @@ index d48f2ef41341..28f43768d1c3 100644
+ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
+
+ prev_count = mutex->u.mutex.count;
-+ ret = put_mutex_state(mutex, &args);
++ ret = unlock_mutex_state(mutex, &args);
+ if (!ret) {
+ try_wake_all_obj(dev, mutex);
+ try_wake_any_mutex(mutex);
@@ -1452,943 +914,530 @@ index d48f2ef41341..28f43768d1c3 100644
+ spin_lock(&mutex->lock);
+
+ prev_count = mutex->u.mutex.count;
-+ ret = put_mutex_state(mutex, &args);
++ ret = unlock_mutex_state(mutex, &args);
+ if (!ret)
+ try_wake_any_mutex(mutex);
+
+ spin_unlock(&mutex->lock);
+ }
+
-+ put_obj(mutex);
-+
+ if (!ret && put_user(prev_count, &user_args->count))
+ ret = -EFAULT;
+
+ return ret;
+}
+
- static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
- {
- int ret = 0;
-@@ -738,6 +803,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_create_sem(dev, argp);
- case NTSYNC_IOC_DELETE:
- return ntsync_delete(dev, argp);
-+ case NTSYNC_IOC_PUT_MUTEX:
-+ return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
- case NTSYNC_IOC_WAIT_ALL:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 26d1b3d4847f..2e44e7e77776 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -46,5 +46,7 @@ struct ntsync_wait_args {
- struct ntsync_wait_args)
- #define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \
- struct ntsync_mutex_args)
-+#define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \
-+ struct ntsync_mutex_args)
-
- #endif
---
-2.43.0
-
-From f042ca07e1744d2bb04942163e4e65fc6e624f41 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:46:46 -0600
-Subject: [PATCH 09/32] ntsync: Introduce NTSYNC_IOC_KILL_OWNER.
-
-This does not correspond to any NT syscall, but rather should be called by the
-user-space NT emulator when a thread dies. It is responsible for marking any
-mutexes owned by that thread as abandoned.
----
- drivers/misc/ntsync.c | 80 ++++++++++++++++++++++++++++++++++++-
- include/uapi/linux/ntsync.h | 1 +
- 2 files changed, 79 insertions(+), 2 deletions(-)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 28f43768d1c3..1173c750c106 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -64,6 +64,7 @@ struct ntsync_obj {
- struct {
- __u32 count;
- __u32 owner;
-+ bool ownerdead;
- } mutex;
- } u;
- };
-@@ -87,6 +88,7 @@ struct ntsync_q {
- atomic_t signaled;
-
- bool all;
-+ bool ownerdead;
- __u32 count;
- struct ntsync_q_entry entries[];
- };
-@@ -240,6 +242,9 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
- obj->u.sem.count--;
- break;
- case NTSYNC_TYPE_MUTEX:
-+ if (obj->u.mutex.ownerdead)
-+ q->ownerdead = true;
-+ obj->u.mutex.ownerdead = false;
- obj->u.mutex.count++;
- obj->u.mutex.owner = q->owner;
- break;
-@@ -299,6 +304,9 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
- continue;
-
- if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
-+ if (mutex->u.mutex.ownerdead)
-+ q->ownerdead = true;
-+ mutex->u.mutex.ownerdead = false;
- mutex->u.mutex.count++;
- mutex->u.mutex.owner = q->owner;
- wake_up_process(q->task);
-@@ -514,6 +522,71 @@ static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
+/*
-+ * Actually change the mutex state to mark its owner as dead.
++ * Actually change the mutex state to mark its owner as dead,
++ * returning -EPERM if not the owner.
+ */
-+static void put_mutex_ownerdead_state(struct ntsync_obj *mutex)
++static int kill_mutex_state(struct ntsync_obj *mutex, __u32 owner)
+{
+ lockdep_assert_held(&mutex->lock);
+
++ if (mutex->u.mutex.owner != owner)
++ return -EPERM;
++
+ mutex->u.mutex.ownerdead = true;
+ mutex->u.mutex.owner = 0;
+ mutex->u.mutex.count = 0;
++ return 0;
+}
+
-+static int ntsync_kill_owner(struct ntsync_device *dev, void __user *argp)
++static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
+{
-+ struct ntsync_obj *obj;
-+ unsigned long id;
++ struct ntsync_device *dev = mutex->dev;
+ __u32 owner;
++ int ret;
+
+ if (get_user(owner, (__u32 __user *)argp))
+ return -EFAULT;
+ if (!owner)
+ return -EINVAL;
+
-+ rcu_read_lock();
++ if (mutex->type != NTSYNC_TYPE_MUTEX)
++ return -EINVAL;
+
-+ xa_for_each(&dev->objects, id, obj) {
-+ if (!kref_get_unless_zero(&obj->refcount))
-+ continue;
++ if (atomic_read(&mutex->all_hint) > 0) {
++ spin_lock(&dev->wait_all_lock);
++ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
+
-+ if (obj->type != NTSYNC_TYPE_MUTEX) {
-+ put_obj(obj);
-+ continue;
++ ret = kill_mutex_state(mutex, owner);
++ if (!ret) {
++ try_wake_all_obj(dev, mutex);
++ try_wake_any_mutex(mutex);
+ }
+
-+ if (atomic_read(&obj->all_hint) > 0) {
-+ spin_lock(&dev->wait_all_lock);
-+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
++ spin_unlock(&mutex->lock);
++ spin_unlock(&dev->wait_all_lock);
++ } else {
++ spin_lock(&mutex->lock);
+
-+ if (obj->u.mutex.owner == owner) {
-+ put_mutex_ownerdead_state(obj);
-+ try_wake_all_obj(dev, obj);
-+ try_wake_any_mutex(obj);
-+ }
++ ret = kill_mutex_state(mutex, owner);
++ if (!ret)
++ try_wake_any_mutex(mutex);
+
-+ spin_unlock(&obj->lock);
-+ spin_unlock(&dev->wait_all_lock);
-+ } else {
-+ spin_lock(&obj->lock);
++ spin_unlock(&mutex->lock);
++ }
+
-+ if (obj->u.mutex.owner == owner) {
-+ put_mutex_ownerdead_state(obj);
-+ try_wake_any_mutex(obj);
-+ }
++ return ret;
++}
+
-+ spin_unlock(&obj->lock);
-+ }
++static int ntsync_event_set(struct ntsync_obj *event, void __user *argp, bool pulse)
++{
++ struct ntsync_device *dev = event->dev;
++ __u32 prev_state;
+
-+ put_obj(obj);
++ if (event->type != NTSYNC_TYPE_EVENT)
++ return -EINVAL;
++
++ if (atomic_read(&event->all_hint) > 0) {
++ spin_lock(&dev->wait_all_lock);
++ spin_lock_nest_lock(&event->lock, &dev->wait_all_lock);
++
++ prev_state = event->u.event.signaled;
++ event->u.event.signaled = true;
++ try_wake_all_obj(dev, event);
++ try_wake_any_event(event);
++ if (pulse)
++ event->u.event.signaled = false;
++
++ spin_unlock(&event->lock);
++ spin_unlock(&dev->wait_all_lock);
++ } else {
++ spin_lock(&event->lock);
++
++ prev_state = event->u.event.signaled;
++ event->u.event.signaled = true;
++ try_wake_any_event(event);
++ if (pulse)
++ event->u.event.signaled = false;
++
++ spin_unlock(&event->lock);
+ }
+
-+ rcu_read_unlock();
++ if (put_user(prev_state, (__u32 __user *)argp))
++ return -EFAULT;
+
+ return 0;
+}
+
- static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
- {
- int ret = 0;
-@@ -585,6 +658,7 @@ static int setup_wait(struct ntsync_device *dev,
- q->owner = args->owner;
- atomic_set(&q->signaled, -1);
- q->all = all;
-+ q->ownerdead = false;
- q->count = count;
-
- for (i = 0; i < count; i++) {
-@@ -697,7 +771,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- struct ntsync_wait_args __user *user_args = argp;
-
- /* even if we caught a signal, we need to communicate success */
-- ret = 0;
-+ ret = q->ownerdead ? -EOWNERDEAD : 0;
-
- if (put_user(signaled, &user_args->index))
- ret = -EFAULT;
-@@ -778,7 +852,7 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
- struct ntsync_wait_args __user *user_args = argp;
-
- /* even if we caught a signal, we need to communicate success */
-- ret = 0;
-+ ret = q->ownerdead ? -EOWNERDEAD : 0;
-
- if (put_user(signaled, &user_args->index))
- ret = -EFAULT;
-@@ -803,6 +877,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_create_sem(dev, argp);
- case NTSYNC_IOC_DELETE:
- return ntsync_delete(dev, argp);
-+ case NTSYNC_IOC_KILL_OWNER:
-+ return ntsync_kill_owner(dev, argp);
- case NTSYNC_IOC_PUT_MUTEX:
- return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 2e44e7e77776..fec9a3993322 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -48,5 +48,6 @@ struct ntsync_wait_args {
- struct ntsync_mutex_args)
- #define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \
- struct ntsync_mutex_args)
-+#define NTSYNC_IOC_KILL_OWNER _IOW (NTSYNC_IOC_BASE, 7, __u32)
-
- #endif
---
-2.43.0
-
-From fc85bff90ef0348584fca122f96aa3ec1a5fe604 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:47:55 -0600
-Subject: [PATCH 10/32] ntsync: Introduce NTSYNC_IOC_READ_SEM.
-
-This corresponds to the NT syscall NtQuerySemaphore().
----
- drivers/misc/ntsync.c | 29 +++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 31 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 1173c750c106..70c60f294bb2 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -522,6 +522,33 @@ static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
-+static int ntsync_read_sem(struct ntsync_device *dev, void __user *argp)
++static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
+{
-+ struct ntsync_sem_args __user *user_args = argp;
-+ struct ntsync_sem_args args;
-+ struct ntsync_obj *sem;
-+ __u32 id;
++ __u32 prev_state;
++
++ if (event->type != NTSYNC_TYPE_EVENT)
++ return -EINVAL;
++
++ spin_lock(&event->lock);
++
++ prev_state = event->u.event.signaled;
++ event->u.event.signaled = false;
++
++ spin_unlock(&event->lock);
+
-+ if (get_user(id, &user_args->sem))
++ if (put_user(prev_state, (__u32 __user *)argp))
+ return -EFAULT;
+
-+ sem = get_obj_typed(dev, id, NTSYNC_TYPE_SEM);
-+ if (!sem)
++ return 0;
++}
++
++static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
++{
++ struct ntsync_sem_args __user *user_args = argp;
++ struct ntsync_sem_args args;
++
++ if (sem->type != NTSYNC_TYPE_SEM)
+ return -EINVAL;
+
-+ args.sem = id;
++ args.sem = 0;
+ spin_lock(&sem->lock);
+ args.count = sem->u.sem.count;
+ args.max = sem->u.sem.max;
+ spin_unlock(&sem->lock);
+
-+ put_obj(sem);
-+
+ if (copy_to_user(user_args, &args, sizeof(args)))
+ return -EFAULT;
+ return 0;
+}
+
- /*
- * Actually change the mutex state to mark its owner as dead.
- */
-@@ -883,6 +910,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
-+ case NTSYNC_IOC_READ_SEM:
-+ return ntsync_read_sem(dev, argp);
- case NTSYNC_IOC_WAIT_ALL:
- return ntsync_wait_all(dev, argp);
- case NTSYNC_IOC_WAIT_ANY:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index fec9a3993322..b84a57c13d80 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -49,5 +49,7 @@ struct ntsync_wait_args {
- #define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \
- struct ntsync_mutex_args)
- #define NTSYNC_IOC_KILL_OWNER _IOW (NTSYNC_IOC_BASE, 7, __u32)
-+#define NTSYNC_IOC_READ_SEM _IOWR(NTSYNC_IOC_BASE, 8, \
-+ struct ntsync_sem_args)
-
- #endif
---
-2.43.0
-
-From 4acc2acbbcacf80485e7d443c0a2327d50879c64 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 11:48:10 -0600
-Subject: [PATCH 11/32] ntsync: Introduce NTSYNC_IOC_READ_MUTEX.
-
-This corresponds to the NT syscall NtQueryMutant().
----
- drivers/misc/ntsync.c | 31 +++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 33 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 70c60f294bb2..88adb52bad93 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -549,6 +549,35 @@ static int ntsync_read_sem(struct ntsync_device *dev, void __user *argp)
- return 0;
- }
-
-+static int ntsync_read_mutex(struct ntsync_device *dev, void __user *argp)
++static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
+{
+ struct ntsync_mutex_args __user *user_args = argp;
+ struct ntsync_mutex_args args;
-+ struct ntsync_obj *mutex;
-+ __u32 id;
+ int ret;
+
-+ if (get_user(id, &user_args->mutex))
-+ return -EFAULT;
-+
-+ mutex = get_obj_typed(dev, id, NTSYNC_TYPE_MUTEX);
-+ if (!mutex)
++ if (mutex->type != NTSYNC_TYPE_MUTEX)
+ return -EINVAL;
+
-+ args.mutex = id;
++ args.mutex = 0;
+ spin_lock(&mutex->lock);
+ args.count = mutex->u.mutex.count;
+ args.owner = mutex->u.mutex.owner;
+ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
+ spin_unlock(&mutex->lock);
+
-+ put_obj(mutex);
-+
+ if (copy_to_user(user_args, &args, sizeof(args)))
+ return -EFAULT;
+ return ret;
+}
+
- /*
- * Actually change the mutex state to mark its owner as dead.
- */
-@@ -910,6 +939,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
-+ case NTSYNC_IOC_READ_MUTEX:
-+ return ntsync_read_mutex(dev, argp);
- case NTSYNC_IOC_READ_SEM:
- return ntsync_read_sem(dev, argp);
- case NTSYNC_IOC_WAIT_ALL:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index b84a57c13d80..fa57f190c80e 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -51,5 +51,7 @@ struct ntsync_wait_args {
- #define NTSYNC_IOC_KILL_OWNER _IOW (NTSYNC_IOC_BASE, 7, __u32)
- #define NTSYNC_IOC_READ_SEM _IOWR(NTSYNC_IOC_BASE, 8, \
- struct ntsync_sem_args)
-+#define NTSYNC_IOC_READ_MUTEX _IOWR(NTSYNC_IOC_BASE, 9, \
-+ struct ntsync_mutex_args)
-
- #endif
---
-2.43.0
-
-From 1bc3fa335b3c30797586eebbf533135ccddcb934 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 18:21:03 -0600
-Subject: [PATCH 12/32] ntsync: Introduce NTSYNC_IOC_CREATE_EVENT.
-
-This corresponds to the NT syscall NtCreateEvent().
----
- drivers/misc/ntsync.c | 65 +++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 8 +++++
- 2 files changed, 73 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 88adb52bad93..3778dbe617e9 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -17,6 +17,7 @@
- enum ntsync_type {
- NTSYNC_TYPE_SEM,
- NTSYNC_TYPE_MUTEX,
-+ NTSYNC_TYPE_EVENT,
- };
-
- struct ntsync_obj {
-@@ -66,6 +67,10 @@ struct ntsync_obj {
- __u32 owner;
- bool ownerdead;
- } mutex;
-+ struct {
-+ bool manual;
-+ bool signaled;
-+ } event;
- } u;
- };
-
-@@ -199,6 +204,8 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
- if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
- return false;
- return obj->u.mutex.count < UINT_MAX;
-+ case NTSYNC_TYPE_EVENT:
-+ return obj->u.event.signaled;
- }
-
- WARN(1, "bad object type %#x\n", obj->type);
-@@ -248,6 +255,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
- obj->u.mutex.count++;
- obj->u.mutex.owner = q->owner;
- break;
-+ case NTSYNC_TYPE_EVENT:
-+ if (!obj->u.event.manual)
-+ obj->u.event.signaled = false;
-+ break;
- }
- }
- wake_up_process(q->task);
-@@ -314,6 +325,26 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
- }
- }
-
-+static void try_wake_any_event(struct ntsync_obj *event)
++static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
+{
-+ struct ntsync_q_entry *entry;
++ struct ntsync_event_args __user *user_args = argp;
++ struct ntsync_event_args args;
+
-+ lockdep_assert_held(&event->lock);
++ if (event->type != NTSYNC_TYPE_EVENT)
++ return -EINVAL;
+
-+ list_for_each_entry(entry, &event->any_waiters, node) {
-+ struct ntsync_q *q = entry->q;
++ args.event = 0;
++ spin_lock(&event->lock);
++ args.manual = event->u.event.manual;
++ args.signaled = event->u.event.signaled;
++ spin_unlock(&event->lock);
+
-+ if (!event->u.event.signaled)
-+ break;
++ if (copy_to_user(user_args, &args, sizeof(args)))
++ return -EFAULT;
++ return 0;
++}
+
-+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
-+ if (!event->u.event.manual)
-+ event->u.event.signaled = false;
-+ wake_up_process(q->task);
-+ }
++static int ntsync_obj_release(struct inode *inode, struct file *file)
++{
++ struct ntsync_obj *obj = file->private_data;
++
++ fput(obj->dev->file);
++ kfree(obj);
++
++ return 0;
++}
++
++static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
++ unsigned long parm)
++{
++ struct ntsync_obj *obj = file->private_data;
++ void __user *argp = (void __user *)parm;
++
++ switch (cmd) {
++ case NTSYNC_IOC_SEM_POST:
++ return ntsync_sem_post(obj, argp);
++ case NTSYNC_IOC_SEM_READ:
++ return ntsync_sem_read(obj, argp);
++ case NTSYNC_IOC_MUTEX_UNLOCK:
++ return ntsync_mutex_unlock(obj, argp);
++ case NTSYNC_IOC_MUTEX_KILL:
++ return ntsync_mutex_kill(obj, argp);
++ case NTSYNC_IOC_MUTEX_READ:
++ return ntsync_mutex_read(obj, argp);
++ case NTSYNC_IOC_EVENT_SET:
++ return ntsync_event_set(obj, argp, false);
++ case NTSYNC_IOC_EVENT_RESET:
++ return ntsync_event_reset(obj, argp);
++ case NTSYNC_IOC_EVENT_PULSE:
++ return ntsync_event_set(obj, argp, true);
++ case NTSYNC_IOC_EVENT_READ:
++ return ntsync_event_read(obj, argp);
++ default:
++ return -ENOIOCTLCMD;
+ }
+}
+
- static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
- {
- struct ntsync_sem_args __user *user_args = argp;
-@@ -378,6 +409,35 @@ static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
- return put_user(id, &user_args->mutex);
- }
-
-+static int ntsync_create_event(struct ntsync_device *dev, void __user *argp)
++static const struct file_operations ntsync_obj_fops = {
++ .owner = THIS_MODULE,
++ .release = ntsync_obj_release,
++ .unlocked_ioctl = ntsync_obj_ioctl,
++ .compat_ioctl = compat_ptr_ioctl,
++ .llseek = no_llseek,
++};
++
++static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
++ enum ntsync_type type)
+{
-+ struct ntsync_event_args __user *user_args = argp;
-+ struct ntsync_event_args args;
-+ struct ntsync_obj *event;
-+ __u32 id;
-+ int ret;
++ struct ntsync_obj *obj;
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
++ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
++ if (!obj)
++ return NULL;
++ obj->type = type;
++ obj->dev = dev;
++ get_file(dev->file);
++ spin_lock_init(&obj->lock);
++ INIT_LIST_HEAD(&obj->any_waiters);
++ INIT_LIST_HEAD(&obj->all_waiters);
++ atomic_set(&obj->all_hint, 0);
+
-+ event = kzalloc(sizeof(*event), GFP_KERNEL);
-+ if (!event)
-+ return -ENOMEM;
++ return obj;
++}
+
-+ init_obj(event);
-+ event->type = NTSYNC_TYPE_EVENT;
-+ event->u.event.manual = args.manual;
-+ event->u.event.signaled = args.signaled;
++static int ntsync_obj_get_fd(struct ntsync_obj *obj)
++{
++ struct file *file;
++ int fd;
+
-+ ret = xa_alloc(&dev->objects, &id, event, xa_limit_32b, GFP_KERNEL);
-+ if (ret < 0) {
-+ kfree(event);
-+ return ret;
++ fd = get_unused_fd_flags(O_CLOEXEC);
++ if (fd < 0)
++ return fd;
++ file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
++ if (IS_ERR(file)) {
++ put_unused_fd(fd);
++ return PTR_ERR(file);
+ }
++ obj->file = file;
++ fd_install(fd, file);
+
-+ return put_user(id, &user_args->event);
++ return fd;
+}
+
- static int ntsync_delete(struct ntsync_device *dev, void __user *argp)
- {
- struct ntsync_obj *obj;
-@@ -762,6 +822,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
- case NTSYNC_TYPE_MUTEX:
- try_wake_any_mutex(obj);
- break;
-+ case NTSYNC_TYPE_EVENT:
-+ try_wake_any_event(obj);
-+ break;
- }
- }
-
-@@ -927,6 +990,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- void __user *argp = (void __user *)parm;
-
- switch (cmd) {
-+ case NTSYNC_IOC_CREATE_EVENT:
-+ return ntsync_create_event(dev, argp);
- case NTSYNC_IOC_CREATE_MUTEX:
- return ntsync_create_mutex(dev, argp);
- case NTSYNC_IOC_CREATE_SEM:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index fa57f190c80e..c7cbdeff32c9 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -22,6 +22,12 @@ struct ntsync_mutex_args {
- __u32 count;
- };
-
-+struct ntsync_event_args {
-+ __u32 event;
-+ __u32 manual;
-+ __u32 signaled;
-+};
-+
- struct ntsync_wait_args {
- __u64 timeout;
- __u64 objs;
-@@ -53,5 +59,7 @@ struct ntsync_wait_args {
- struct ntsync_sem_args)
- #define NTSYNC_IOC_READ_MUTEX _IOWR(NTSYNC_IOC_BASE, 9, \
- struct ntsync_mutex_args)
-+#define NTSYNC_IOC_CREATE_EVENT _IOWR(NTSYNC_IOC_BASE, 10, \
-+ struct ntsync_event_args)
-
- #endif
---
-2.43.0
-
-From d4d9e2196dc13cf89b8181eb4eee86d4a26ef026 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 18:43:30 -0600
-Subject: [PATCH 13/32] ntsync: Introduce NTSYNC_IOC_SET_EVENT.
-
-This corresponds to the NT syscall NtSetEvent().
----
- drivers/misc/ntsync.c | 45 +++++++++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 47 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 3778dbe617e9..cd6478bd93bd 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -703,6 +703,49 @@ static int ntsync_kill_owner(struct ntsync_device *dev, void __user *argp)
- return 0;
- }
-
-+static int ntsync_set_event(struct ntsync_device *dev, void __user *argp)
++static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
+{
-+ struct ntsync_event_args __user *user_args = argp;
-+ struct ntsync_event_args args;
-+ struct ntsync_obj *event;
-+ bool prev_state;
++ struct ntsync_sem_args __user *user_args = argp;
++ struct ntsync_sem_args args;
++ struct ntsync_obj *sem;
++ int fd;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
-+ event = get_obj_typed(dev, args.event, NTSYNC_TYPE_EVENT);
-+ if (!event)
++ if (args.count > args.max)
+ return -EINVAL;
+
-+ if (atomic_read(&event->all_hint) > 0) {
-+ spin_lock(&dev->wait_all_lock);
-+ spin_lock_nest_lock(&event->lock, &dev->wait_all_lock);
-+
-+ prev_state = event->u.event.signaled;
-+ event->u.event.signaled = true;
-+ try_wake_all_obj(dev, event);
-+ try_wake_any_event(event);
++ sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
++ if (!sem)
++ return -ENOMEM;
++ sem->u.sem.count = args.count;
++ sem->u.sem.max = args.max;
++ fd = ntsync_obj_get_fd(sem);
++ if (fd < 0) {
++ kfree(sem);
++ return fd;
++ }
+
-+ spin_unlock(&event->lock);
-+ spin_unlock(&dev->wait_all_lock);
-+ } else {
-+ spin_lock(&event->lock);
++ return put_user(fd, &user_args->sem);
++}
+
-+ prev_state = event->u.event.signaled;
-+ event->u.event.signaled = true;
-+ try_wake_any_event(event);
++static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
++{
++ struct ntsync_mutex_args __user *user_args = argp;
++ struct ntsync_mutex_args args;
++ struct ntsync_obj *mutex;
++ int fd;
+
-+ spin_unlock(&event->lock);
-+ }
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
+
-+ put_obj(event);
++ if (!args.owner != !args.count)
++ return -EINVAL;
+
-+ if (put_user(prev_state, &user_args->signaled))
-+ return -EFAULT;
++ mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX);
++ if (!mutex)
++ return -ENOMEM;
++ mutex->u.mutex.count = args.count;
++ mutex->u.mutex.owner = args.owner;
++ fd = ntsync_obj_get_fd(mutex);
++ if (fd < 0) {
++ kfree(mutex);
++ return fd;
++ }
+
-+ return 0;
++ return put_user(fd, &user_args->mutex);
+}
+
- static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
- {
- int ret = 0;
-@@ -1008,6 +1051,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_read_mutex(dev, argp);
- case NTSYNC_IOC_READ_SEM:
- return ntsync_read_sem(dev, argp);
-+ case NTSYNC_IOC_SET_EVENT:
-+ return ntsync_set_event(dev, argp);
- case NTSYNC_IOC_WAIT_ALL:
- return ntsync_wait_all(dev, argp);
- case NTSYNC_IOC_WAIT_ANY:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index c7cbdeff32c9..8a230f5d4b3f 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -61,5 +61,7 @@ struct ntsync_wait_args {
- struct ntsync_mutex_args)
- #define NTSYNC_IOC_CREATE_EVENT _IOWR(NTSYNC_IOC_BASE, 10, \
- struct ntsync_event_args)
-+#define NTSYNC_IOC_SET_EVENT _IOWR(NTSYNC_IOC_BASE, 11, \
-+ struct ntsync_event_args)
-
- #endif
---
-2.43.0
-
-From 03224658123725d6b8a27b55cd103ad782ebad7b Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 19:00:25 -0600
-Subject: [PATCH 14/32] ntsync: Introduce NTSYNC_IOC_RESET_EVENT.
-
-This corresponds to the NT syscall NtResetEvent().
----
- drivers/misc/ntsync.c | 31 +++++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 33 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index cd6478bd93bd..151714dee0ff 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -746,6 +746,35 @@ static int ntsync_set_event(struct ntsync_device *dev, void __user *argp)
- return 0;
- }
-
-+static int ntsync_reset_event(struct ntsync_device *dev, void __user *argp)
++static int ntsync_create_event(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_event_args __user *user_args = argp;
+ struct ntsync_event_args args;
+ struct ntsync_obj *event;
-+ bool prev_state;
++ int fd;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
-+ event = get_obj_typed(dev, args.event, NTSYNC_TYPE_EVENT);
++ event = ntsync_alloc_obj(dev, NTSYNC_TYPE_EVENT);
+ if (!event)
-+ return -EINVAL;
++ return -ENOMEM;
++ event->u.event.manual = args.manual;
++ event->u.event.signaled = args.signaled;
++ fd = ntsync_obj_get_fd(event);
++ if (fd < 0) {
++ kfree(event);
++ return fd;
++ }
+
-+ spin_lock(&event->lock);
++ return put_user(fd, &user_args->event);
++}
+
-+ prev_state = event->u.event.signaled;
-+ event->u.event.signaled = false;
++static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
++{
++ struct file *file = fget(fd);
++ struct ntsync_obj *obj;
+
-+ spin_unlock(&event->lock);
++ if (file->f_op != &ntsync_obj_fops) {
++ fput(file);
++ return NULL;
++ }
+
-+ put_obj(event);
++ obj = file->private_data;
++ if (obj->dev != dev) {
++ fput(file);
++ return NULL;
++ }
+
-+ if (put_user(prev_state, &user_args->signaled))
-+ return -EFAULT;
++ return obj;
++}
+
-+ return 0;
++static void put_obj(struct ntsync_obj *obj)
++{
++ fput(obj->file);
+}
+
- static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout)
- {
- int ret = 0;
-@@ -1051,6 +1080,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_read_mutex(dev, argp);
- case NTSYNC_IOC_READ_SEM:
- return ntsync_read_sem(dev, argp);
-+ case NTSYNC_IOC_RESET_EVENT:
-+ return ntsync_reset_event(dev, argp);
- case NTSYNC_IOC_SET_EVENT:
- return ntsync_set_event(dev, argp);
- case NTSYNC_IOC_WAIT_ALL:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 8a230f5d4b3f..806125856117 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -63,5 +63,7 @@ struct ntsync_wait_args {
- struct ntsync_event_args)
- #define NTSYNC_IOC_SET_EVENT _IOWR(NTSYNC_IOC_BASE, 11, \
- struct ntsync_event_args)
-+#define NTSYNC_IOC_RESET_EVENT _IOWR(NTSYNC_IOC_BASE, 12, \
-+ struct ntsync_event_args)
-
- #endif
---
-2.43.0
-
-From 16daeb0c9b8d95ab27c0c807c49ddebb5dba52c0 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 19:10:12 -0600
-Subject: [PATCH 15/32] ntsync: Introduce NTSYNC_IOC_PULSE_EVENT.
-
-This corresponds to the NT syscall NtPulseEvent().
----
- drivers/misc/ntsync.c | 11 +++++++++--
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 11 insertions(+), 2 deletions(-)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 151714dee0ff..45405d01e11d 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -703,7 +703,8 @@ static int ntsync_kill_owner(struct ntsync_device *dev, void __user *argp)
- return 0;
- }
-
--static int ntsync_set_event(struct ntsync_device *dev, void __user *argp)
-+static int ntsync_set_event(struct ntsync_device *dev, void __user *argp,
-+ bool pulse)
- {
- struct ntsync_event_args __user *user_args = argp;
- struct ntsync_event_args args;
-@@ -725,6 +726,8 @@ static int ntsync_set_event(struct ntsync_device *dev, void __user *argp)
- event->u.event.signaled = true;
- try_wake_all_obj(dev, event);
- try_wake_any_event(event);
-+ if (pulse)
-+ event->u.event.signaled = false;
-
- spin_unlock(&event->lock);
- spin_unlock(&dev->wait_all_lock);
-@@ -734,6 +737,8 @@ static int ntsync_set_event(struct ntsync_device *dev, void __user *argp)
- prev_state = event->u.event.signaled;
- event->u.event.signaled = true;
- try_wake_any_event(event);
-+ if (pulse)
-+ event->u.event.signaled = false;
-
- spin_unlock(&event->lock);
- }
-@@ -1072,6 +1077,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_delete(dev, argp);
- case NTSYNC_IOC_KILL_OWNER:
- return ntsync_kill_owner(dev, argp);
-+ case NTSYNC_IOC_PULSE_EVENT:
-+ return ntsync_set_event(dev, argp, true);
- case NTSYNC_IOC_PUT_MUTEX:
- return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
-@@ -1083,7 +1090,7 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- case NTSYNC_IOC_RESET_EVENT:
- return ntsync_reset_event(dev, argp);
- case NTSYNC_IOC_SET_EVENT:
-- return ntsync_set_event(dev, argp);
-+ return ntsync_set_event(dev, argp, false);
- case NTSYNC_IOC_WAIT_ALL:
- return ntsync_wait_all(dev, argp);
- case NTSYNC_IOC_WAIT_ANY:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 806125856117..30f56b4388c8 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -65,5 +65,7 @@ struct ntsync_wait_args {
- struct ntsync_event_args)
- #define NTSYNC_IOC_RESET_EVENT _IOWR(NTSYNC_IOC_BASE, 12, \
- struct ntsync_event_args)
-+#define NTSYNC_IOC_PULSE_EVENT _IOWR(NTSYNC_IOC_BASE, 13, \
-+ struct ntsync_event_args)
-
- #endif
---
-2.43.0
-
-From c264fe3627e7032d88cd6d56f4ce6a2c2fbc30af Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 19:14:00 -0600
-Subject: [PATCH 16/32] ntsync: Introduce NTSYNC_IOC_READ_EVENT.
-
-This corresponds to the NT syscall NtQueryEvent().
----
- drivers/misc/ntsync.c | 29 +++++++++++++++++++++++++++++
- include/uapi/linux/ntsync.h | 2 ++
- 2 files changed, 31 insertions(+)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 45405d01e11d..6821d9aa967d 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -638,6 +638,33 @@ static int ntsync_read_mutex(struct ntsync_device *dev, void __user *argp)
- return ret;
- }
-
-+static int ntsync_read_event(struct ntsync_device *dev, void __user *argp)
++static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
+{
-+ struct ntsync_event_args __user *user_args = argp;
-+ struct ntsync_event_args args;
-+ struct ntsync_obj *event;
-+ __u32 id;
++ ktime_t timeout = ns_to_ktime(args->timeout);
++ clockid_t clock = CLOCK_MONOTONIC;
++ ktime_t *timeout_ptr;
++ int ret = 0;
+
-+ if (get_user(id, &user_args->event))
-+ return -EFAULT;
++ timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
+
-+ event = get_obj_typed(dev, id, NTSYNC_TYPE_EVENT);
-+ if (!event)
-+ return -EINVAL;
++ if (args->flags & NTSYNC_WAIT_REALTIME)
++ clock = CLOCK_REALTIME;
+
-+ args.event = id;
-+ spin_lock(&event->lock);
-+ args.manual = event->u.event.manual;
-+ args.signaled = event->u.event.signaled;
-+ spin_unlock(&event->lock);
++ do {
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
+
-+ put_obj(event);
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (atomic_read(&q->signaled) != -1) {
++ ret = 0;
++ break;
++ }
++ ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
++ } while (ret < 0);
++ __set_current_state(TASK_RUNNING);
+
-+ if (copy_to_user(user_args, &args, sizeof(args)))
-+ return -EFAULT;
-+ return 0;
++ return ret;
+}
+
- /*
- * Actually change the mutex state to mark its owner as dead.
- */
-@@ -1083,6 +1110,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
- return ntsync_put_mutex(dev, argp);
- case NTSYNC_IOC_PUT_SEM:
- return ntsync_put_sem(dev, argp);
-+ case NTSYNC_IOC_READ_EVENT:
-+ return ntsync_read_event(dev, argp);
- case NTSYNC_IOC_READ_MUTEX:
- return ntsync_read_mutex(dev, argp);
- case NTSYNC_IOC_READ_SEM:
-diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 30f56b4388c8..86c28b909777 100644
---- a/include/uapi/linux/ntsync.h
-+++ b/include/uapi/linux/ntsync.h
-@@ -67,5 +67,7 @@ struct ntsync_wait_args {
- struct ntsync_event_args)
- #define NTSYNC_IOC_PULSE_EVENT _IOWR(NTSYNC_IOC_BASE, 13, \
- struct ntsync_event_args)
-+#define NTSYNC_IOC_READ_EVENT _IOWR(NTSYNC_IOC_BASE, 14, \
-+ struct ntsync_event_args)
-
- #endif
---
-2.43.0
-
-From 198ddbebdbe3d062505bde44619da050eead04e8 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 13 Apr 2022 20:02:39 -0500
-Subject: [PATCH 17/32] ntsync: Introduce alertable waits.
-
-This implements the "alertable" feature of NtWaitForMultipleObjects().
-Alerts are implemented using events; the user-space NT emulator is expected to
-create an internal ntsync event for each thread and pass that event to wait
-functions.
----
- drivers/misc/ntsync.c | 68 ++++++++++++++++++++++++++++++++-----
- include/uapi/linux/ntsync.h | 2 +-
- 2 files changed, 60 insertions(+), 10 deletions(-)
-
-diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
-index 6821d9aa967d..6f4cb9a3ed5a 100644
---- a/drivers/misc/ntsync.c
-+++ b/drivers/misc/ntsync.c
-@@ -840,10 +840,11 @@ static int setup_wait(struct ntsync_device *dev,
- const __u32 count = args->count;
- struct ntsync_q *q;
- ktime_t timeout = 0;
++/*
++ * Allocate and initialize the ntsync_q structure, but do not queue us yet.
++ */
++static int setup_wait(struct ntsync_device *dev,
++ const struct ntsync_wait_args *args, bool all,
++ struct ntsync_q **ret_q)
++{
++ int fds[NTSYNC_MAX_WAIT_COUNT + 1];
++ const __u32 count = args->count;
++ struct ntsync_q *q;
+ __u32 total_count;
- __u32 *ids;
- __u32 i, j;
-
-- if (!args->owner || args->pad)
++ __u32 i, j;
++
+ if (!args->owner)
- return -EINVAL;
-
- if (args->count > NTSYNC_MAX_WAIT_COUNT)
-@@ -860,7 +861,11 @@ static int setup_wait(struct ntsync_device *dev,
- timeout = timespec64_to_ns(&to);
- }
-
-- ids = kmalloc_array(count, sizeof(*ids), GFP_KERNEL);
++ return -EINVAL;
++
++ if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
++ return -EINVAL;
++
++ if (args->count > NTSYNC_MAX_WAIT_COUNT)
++ return -EINVAL;
++
+ total_count = count;
+ if (args->alert)
+ total_count++;
+
-+ ids = kmalloc_array(total_count, sizeof(*ids), GFP_KERNEL);
- if (!ids)
- return -ENOMEM;
- if (copy_from_user(ids, u64_to_user_ptr(args->objs),
-@@ -868,8 +873,10 @@ static int setup_wait(struct ntsync_device *dev,
- kfree(ids);
- return -EFAULT;
- }
++ if (copy_from_user(fds, u64_to_user_ptr(args->objs),
++ array_size(count, sizeof(*fds))))
++ return -EFAULT;
+ if (args->alert)
-+ ids[count] = args->alert;
-
-- q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
++ fds[count] = args->alert;
++
+ q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL);
- if (!q) {
- kfree(ids);
- return -ENOMEM;
-@@ -881,7 +888,7 @@ static int setup_wait(struct ntsync_device *dev,
- q->ownerdead = false;
- q->count = count;
-
-- for (i = 0; i < count; i++) {
++ if (!q)
++ return -ENOMEM;
++ q->task = current;
++ q->owner = args->owner;
++ atomic_set(&q->signaled, -1);
++ q->all = all;
++ q->ownerdead = false;
++ q->count = count;
++
+ for (i = 0; i < total_count; i++) {
- struct ntsync_q_entry *entry = &q->entries[i];
- struct ntsync_obj *obj = get_obj(dev, ids[i]);
-
-@@ -936,9 +943,9 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- {
- struct ntsync_wait_args args;
- struct ntsync_q *q;
++ struct ntsync_q_entry *entry = &q->entries[i];
++ struct ntsync_obj *obj = get_obj(dev, fds[i]);
++
++ if (!obj)
++ goto err;
++
++ if (all) {
++ /* Check that the objects are all distinct. */
++ for (j = 0; j < i; j++) {
++ if (obj == q->entries[j].obj) {
++ put_obj(obj);
++ goto err;
++ }
++ }
++ }
++
++ entry->obj = obj;
++ entry->q = q;
++ entry->index = i;
++ }
++
++ *ret_q = q;
++ return 0;
++
++err:
++ for (j = 0; j < i; j++)
++ put_obj(q->entries[j].obj);
++ kfree(q);
++ return -EINVAL;
++}
++
++static void try_wake_any_obj(struct ntsync_obj *obj)
++{
++ switch (obj->type) {
++ case NTSYNC_TYPE_SEM:
++ try_wake_any_sem(obj);
++ break;
++ case NTSYNC_TYPE_MUTEX:
++ try_wake_any_mutex(obj);
++ break;
++ case NTSYNC_TYPE_EVENT:
++ try_wake_any_event(obj);
++ break;
++ }
++}
++
++static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
++{
++ struct ntsync_wait_args args;
+ __u32 i, total_count;
- ktime_t timeout;
- int signaled;
-- __u32 i;
- int ret;
-
- if (copy_from_user(&args, argp, sizeof(args)))
-@@ -948,9 +955,13 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- if (ret < 0)
- return ret;
-
++ struct ntsync_q *q;
++ int signaled;
++ int ret;
++
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
++
++ ret = setup_wait(dev, &args, false, &q);
++ if (ret < 0)
++ return ret;
++
+ total_count = args.count;
+ if (args.alert)
+ total_count++;
+
- /* queue ourselves */
-
-- for (i = 0; i < args.count; i++) {
++ /* queue ourselves */
++
+ for (i = 0; i < total_count; i++) {
- struct ntsync_q_entry *entry = &q->entries[i];
- struct ntsync_obj *obj = entry->obj;
-
-@@ -959,9 +970,15 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
- spin_unlock(&obj->lock);
- }
-
-- /* check if we are already signaled */
++ struct ntsync_q_entry *entry = &q->entries[i];
++ struct ntsync_obj *obj = entry->obj;
++
++ spin_lock(&obj->lock);
++ list_add_tail(&entry->node, &obj->any_waiters);
++ spin_unlock(&obj->lock);
++ }
++
+ /*
+ * Check if we are already signaled.
+ *
@@ -2396,25 +1445,84 @@ index 6821d9aa967d..6f4cb9a3ed5a 100644
+ * the alert event. Hence we queue the alert event last, and check
+ * objects in order.
+ */
-
-- for (i = 0; i < args.count; i++) {
++
+ for (i = 0; i < total_count; i++) {
- struct ntsync_obj *obj = q->entries[i].obj;
-
- if (atomic_read(&q->signaled) != -1)
-@@ -978,7 +995,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
-
- /* and finally, unqueue */
-
-- for (i = 0; i < args.count; i++) {
++ struct ntsync_obj *obj = q->entries[i].obj;
++
++ if (atomic_read(&q->signaled) != -1)
++ break;
++
++ spin_lock(&obj->lock);
++ try_wake_any_obj(obj);
++ spin_unlock(&obj->lock);
++ }
++
++ /* sleep */
++
++ ret = ntsync_schedule(q, &args);
++
++ /* and finally, unqueue */
++
+ for (i = 0; i < total_count; i++) {
- struct ntsync_q_entry *entry = &q->entries[i];
- struct ntsync_obj *obj = entry->obj;
-
-@@ -1038,6 +1055,14 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
- */
- list_add_tail(&entry->node, &obj->all_waiters);
- }
++ struct ntsync_q_entry *entry = &q->entries[i];
++ struct ntsync_obj *obj = entry->obj;
++
++ spin_lock(&obj->lock);
++ list_del(&entry->node);
++ spin_unlock(&obj->lock);
++
++ put_obj(obj);
++ }
++
++ signaled = atomic_read(&q->signaled);
++ if (signaled != -1) {
++ struct ntsync_wait_args __user *user_args = argp;
++
++ /* even if we caught a signal, we need to communicate success */
++ ret = q->ownerdead ? -EOWNERDEAD : 0;
++
++ if (put_user(signaled, &user_args->index))
++ ret = -EFAULT;
++ } else if (!ret) {
++ ret = -ETIMEDOUT;
++ }
++
++ kfree(q);
++ return ret;
++}
++
++static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
++{
++ struct ntsync_wait_args args;
++ struct ntsync_q *q;
++ int signaled;
++ __u32 i;
++ int ret;
++
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
++
++ ret = setup_wait(dev, &args, true, &q);
++ if (ret < 0)
++ return ret;
++
++ /* queue ourselves */
++
++ spin_lock(&dev->wait_all_lock);
++
++ for (i = 0; i < args.count; i++) {
++ struct ntsync_q_entry *entry = &q->entries[i];
++ struct ntsync_obj *obj = entry->obj;
++
++ atomic_inc(&obj->all_hint);
++
++ /*
++ * obj->all_waiters is protected by dev->wait_all_lock rather
++ * than obj->lock, so there is no need to acquire obj->lock
++ * here.
++ */
++ list_add_tail(&entry->node, &obj->all_waiters);
++ }
+ if (args.alert) {
+ struct ntsync_q_entry *entry = &q->entries[args.count];
+ struct ntsync_obj *obj = entry->obj;
@@ -2423,13 +1531,13 @@ index 6821d9aa967d..6f4cb9a3ed5a 100644
+ list_add_tail(&entry->node, &obj->any_waiters);
+ spin_unlock(&obj->lock);
+ }
-
- /* check if we are already signaled */
-
-@@ -1045,6 +1070,21 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
-
- spin_unlock(&dev->wait_all_lock);
-
++
++ /* check if we are already signaled */
++
++ try_wake_all(dev, q, NULL);
++
++ spin_unlock(&dev->wait_all_lock);
++
+ /*
+ * Check if the alert event is signaled, making sure to do so only
+ * after checking if the other objects are signaled.
@@ -2445,13 +1553,28 @@ index 6821d9aa967d..6f4cb9a3ed5a 100644
+ }
+ }
+
- /* sleep */
-
- ret = ntsync_schedule(q, args.timeout ? &timeout : NULL);
-@@ -1067,6 +1107,16 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
-
- put_obj(obj);
- }
++ /* sleep */
++
++ ret = ntsync_schedule(q, &args);
++
++ /* and finally, unqueue */
++
++ spin_lock(&dev->wait_all_lock);
++
++ for (i = 0; i < args.count; i++) {
++ struct ntsync_q_entry *entry = &q->entries[i];
++ struct ntsync_obj *obj = entry->obj;
++
++ /*
++ * obj->all_waiters is protected by dev->wait_all_lock rather
++ * than obj->lock, so there is no need to acquire it here.
++ */
++ list_del(&entry->node);
++
++ atomic_dec(&obj->all_hint);
++
++ put_obj(obj);
++ }
+ if (args.alert) {
+ struct ntsync_q_entry *entry = &q->entries[args.count];
+ struct ntsync_obj *obj = entry->obj;
@@ -2462,42 +1585,162 @@ index 6821d9aa967d..6f4cb9a3ed5a 100644
+
+ put_obj(obj);
+ }
-
- spin_unlock(&dev->wait_all_lock);
-
++
++ spin_unlock(&dev->wait_all_lock);
++
++ signaled = atomic_read(&q->signaled);
++ if (signaled != -1) {
++ struct ntsync_wait_args __user *user_args = argp;
++
++ /* even if we caught a signal, we need to communicate success */
++ ret = q->ownerdead ? -EOWNERDEAD : 0;
++
++ if (put_user(signaled, &user_args->index))
++ ret = -EFAULT;
++ } else if (!ret) {
++ ret = -ETIMEDOUT;
++ }
++
++ kfree(q);
++ return ret;
++}
++
++static int ntsync_char_open(struct inode *inode, struct file *file)
++{
++ struct ntsync_device *dev;
++
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++
++ spin_lock_init(&dev->wait_all_lock);
++
++ file->private_data = dev;
++ dev->file = file;
++ return nonseekable_open(inode, file);
++}
++
++static int ntsync_char_release(struct inode *inode, struct file *file)
++{
++ struct ntsync_device *dev = file->private_data;
++
++ kfree(dev);
++
++ return 0;
++}
++
++static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
++ unsigned long parm)
++{
++ struct ntsync_device *dev = file->private_data;
++ void __user *argp = (void __user *)parm;
++
++ switch (cmd) {
++ case NTSYNC_IOC_CREATE_EVENT:
++ return ntsync_create_event(dev, argp);
++ case NTSYNC_IOC_CREATE_MUTEX:
++ return ntsync_create_mutex(dev, argp);
++ case NTSYNC_IOC_CREATE_SEM:
++ return ntsync_create_sem(dev, argp);
++ case NTSYNC_IOC_WAIT_ALL:
++ return ntsync_wait_all(dev, argp);
++ case NTSYNC_IOC_WAIT_ANY:
++ return ntsync_wait_any(dev, argp);
++ default:
++ return -ENOIOCTLCMD;
++ }
++}
++
++static const struct file_operations ntsync_fops = {
++ .owner = THIS_MODULE,
++ .open = ntsync_char_open,
++ .release = ntsync_char_release,
++ .unlocked_ioctl = ntsync_char_ioctl,
++ .compat_ioctl = compat_ptr_ioctl,
++ .llseek = no_llseek,
++};
++
++static struct miscdevice ntsync_misc = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = NTSYNC_NAME,
++ .fops = &ntsync_fops,
++};
++
++module_misc_device(ntsync_misc);
++
++MODULE_AUTHOR("Elizabeth Figura <zfigura@codeweavers.com>");
++MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
++MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
-index 86c28b909777..e145b36a15ea 100644
---- a/include/uapi/linux/ntsync.h
+new file mode 100644
+index 000000000000..b5e835d8dba8
+--- /dev/null
+++ b/include/uapi/linux/ntsync.h
-@@ -34,7 +34,7 @@ struct ntsync_wait_args {
- __u32 count;
- __u32 owner;
- __u32 index;
-- __u32 pad;
+@@ -0,0 +1,62 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++/*
++ * Kernel support for NT synchronization primitive emulation
++ *
++ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
++ */
++
++#ifndef __LINUX_NTSYNC_H
++#define __LINUX_NTSYNC_H
++
++#include <linux/types.h>
++
++struct ntsync_sem_args {
++ __u32 sem;
++ __u32 count;
++ __u32 max;
++};
++
++struct ntsync_mutex_args {
++ __u32 mutex;
++ __u32 owner;
++ __u32 count;
++};
++
++struct ntsync_event_args {
++ __u32 event;
++ __u32 manual;
++ __u32 signaled;
++};
++
++#define NTSYNC_WAIT_REALTIME 0x1
++
++struct ntsync_wait_args {
++ __u64 timeout;
++ __u64 objs;
++ __u32 count;
++ __u32 owner;
++ __u32 index;
+ __u32 alert;
- };
-
- #define NTSYNC_MAX_WAIT_COUNT 64
---
-2.43.0
-
-From 9208836580ed5e5aadfdfaf59dc8352ff6c4a07c Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:06:23 -0600
-Subject: [PATCH 18/32] selftests: ntsync: Add some tests for semaphore state.
-
----
- tools/testing/selftests/Makefile | 1 +
- .../testing/selftests/drivers/ntsync/Makefile | 8 +
- tools/testing/selftests/drivers/ntsync/config | 1 +
- .../testing/selftests/drivers/ntsync/ntsync.c | 153 ++++++++++++++++++
- 4 files changed, 163 insertions(+)
- create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
- create mode 100644 tools/testing/selftests/drivers/ntsync/config
- create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c
-
++ __u32 flags;
++ __u32 pad;
++};
++
++#define NTSYNC_MAX_WAIT_COUNT 64
++
++#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
++#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
++#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
++#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
++#define NTSYNC_IOC_CREATE_EVENT _IOWR('N', 0x87, struct ntsync_event_args)
++
++#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
++#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
++#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
++#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
++#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
++#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
++#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
++#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
++#define NTSYNC_IOC_EVENT_READ _IOR ('N', 0x8d, struct ntsync_event_args)
++
++#endif
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
-index 8247a7c69c36..40b7318a8e22 100644
+index 15b6a111c3be..6c714a4e6478 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -15,6 +15,7 @@ TARGETS += cpu-hotplug
@@ -2531,15 +1774,15 @@ index 000000000000..60539c826d06
+CONFIG_WINESYNC=y
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
new file mode 100644
-index 000000000000..777c408e8f9e
+index 000000000000..5fa2c9a0768c
--- /dev/null
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -0,0 +1,153 @@
+@@ -0,0 +1,1407 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Various unit tests for the "ntsync" synchronization primitive driver.
+ *
-+ * Copyright (C) 2021-2022 Elizabeth Figura
++ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#define _GNU_SOURCE
@@ -2551,43 +1794,88 @@ index 000000000000..777c408e8f9e
+#include <linux/ntsync.h>
+#include "../../kselftest_harness.h"
+
-+static int read_sem_state(int fd, __u32 sem, __u32 *count, __u32 *max)
++static int read_sem_state(int sem, __u32 *count, __u32 *max)
+{
+ struct ntsync_sem_args args;
+ int ret;
+
-+ args.sem = sem;
-+ args.count = 0xdeadbeef;
-+ args.max = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_SEM, &args);
++ memset(&args, 0xcc, sizeof(args));
++ ret = ioctl(sem, NTSYNC_IOC_SEM_READ, &args);
+ *count = args.count;
+ *max = args.max;
+ return ret;
+}
+
-+#define check_sem_state(fd, sem, count, max) \
++#define check_sem_state(sem, count, max) \
+ ({ \
+ __u32 __count, __max; \
-+ int ret = read_sem_state((fd), (sem), &__count, &__max); \
++ int ret = read_sem_state((sem), &__count, &__max); \
+ EXPECT_EQ(0, ret); \
+ EXPECT_EQ((count), __count); \
+ EXPECT_EQ((max), __max); \
+ })
+
-+static int put_sem(int fd, __u32 sem, __u32 *count)
++static int post_sem(int sem, __u32 *count)
+{
-+ struct ntsync_sem_args args;
++ return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
++}
++
++static int read_mutex_state(int mutex, __u32 *count, __u32 *owner)
++{
++ struct ntsync_mutex_args args;
++ int ret;
++
++ memset(&args, 0xcc, sizeof(args));
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &args);
++ *count = args.count;
++ *owner = args.owner;
++ return ret;
++}
++
++#define check_mutex_state(mutex, count, owner) \
++ ({ \
++ __u32 __count, __owner; \
++ int ret = read_mutex_state((mutex), &__count, &__owner); \
++ EXPECT_EQ(0, ret); \
++ EXPECT_EQ((count), __count); \
++ EXPECT_EQ((owner), __owner); \
++ })
++
++static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
++{
++ struct ntsync_mutex_args args;
+ int ret;
+
-+ args.sem = sem;
-+ args.count = *count;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &args);
++ args.owner = owner;
++ args.count = 0xdeadbeef;
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_UNLOCK, &args);
+ *count = args.count;
+ return ret;
+}
+
-+static int wait_any(int fd, __u32 count, const __u32 *objs, __u32 owner,
-+ __u32 *index)
++static int read_event_state(int event, __u32 *signaled, __u32 *manual)
++{
++ struct ntsync_event_args args;
++ int ret;
++
++ memset(&args, 0xcc, sizeof(args));
++ ret = ioctl(event, NTSYNC_IOC_EVENT_READ, &args);
++ *signaled = args.signaled;
++ *manual = args.manual;
++ return ret;
++}
++
++#define check_event_state(event, signaled, manual) \
++ ({ \
++ __u32 __signaled, __manual; \
++ int ret = read_event_state((event), &__signaled, &__manual); \
++ EXPECT_EQ(0, ret); \
++ EXPECT_EQ((signaled), __signaled); \
++ EXPECT_EQ((manual), __manual); \
++ })
++
++static int wait_objs(int fd, unsigned long request, __u32 count,
++ const int *objs, __u32 owner, int alert, __u32 *index)
+{
+ struct ntsync_wait_args args = {0};
+ struct timespec timeout;
@@ -2595,22 +1883,47 @@ index 000000000000..777c408e8f9e
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+
-+ args.timeout = (uintptr_t)&timeout;
++ args.timeout = timeout.tv_sec * 1000000000 + timeout.tv_nsec;
+ args.count = count;
+ args.objs = (uintptr_t)objs;
+ args.owner = owner;
+ args.index = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
++ args.alert = alert;
++ ret = ioctl(fd, request, &args);
+ *index = args.index;
+ return ret;
+}
+
++static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
++{
++ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
++}
++
++static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
++{
++ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
++}
++
++static int wait_any_alert(int fd, __u32 count, const int *objs,
++ __u32 owner, int alert, __u32 *index)
++{
++ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
++ count, objs, owner, alert, index);
++}
++
++static int wait_all_alert(int fd, __u32 count, const int *objs,
++ __u32 owner, int alert, __u32 *index)
++{
++ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
++ count, objs, owner, alert, index);
++}
++
+TEST(semaphore_state)
+{
+ struct ntsync_sem_args sem_args;
+ struct timespec timeout;
+ __u32 count, index;
-+ int fd, ret;
++ int fd, ret, sem;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+
@@ -2630,133 +1943,75 @@ index 000000000000..777c408e8f9e
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
-+ check_sem_state(fd, sem_args.sem, 2, 2);
++ sem = sem_args.sem;
++ check_sem_state(sem, 2, 2);
+
+ count = 0;
-+ ret = put_sem(fd, sem_args.sem, &count);
++ ret = post_sem(sem, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, count);
-+ check_sem_state(fd, sem_args.sem, 2, 2);
++ check_sem_state(sem, 2, 2);
+
+ count = 1;
-+ ret = put_sem(fd, sem_args.sem, &count);
++ ret = post_sem(sem, &count);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOVERFLOW, errno);
-+ check_sem_state(fd, sem_args.sem, 2, 2);
++ check_sem_state(sem, 2, 2);
+
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
++ ret = wait_any(fd, 1, &sem, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 1, 2);
++ check_sem_state(sem, 1, 2);
+
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
++ ret = wait_any(fd, 1, &sem, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 0, 2);
++ check_sem_state(sem, 0, 2);
+
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
++ ret = wait_any(fd, 1, &sem, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ count = 3;
-+ ret = put_sem(fd, sem_args.sem, &count);
++ ret = post_sem(sem, &count);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOVERFLOW, errno);
-+ check_sem_state(fd, sem_args.sem, 0, 2);
++ check_sem_state(sem, 0, 2);
+
+ count = 2;
-+ ret = put_sem(fd, sem_args.sem, &count);
++ ret = post_sem(sem, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, count);
-+ check_sem_state(fd, sem_args.sem, 2, 2);
++ check_sem_state(sem, 2, 2);
+
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
++ ret = wait_any(fd, 1, &sem, 123, &index);
+ EXPECT_EQ(0, ret);
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
++ ret = wait_any(fd, 1, &sem, 123, &index);
+ EXPECT_EQ(0, ret);
+
+ count = 1;
-+ ret = put_sem(fd, sem_args.sem, &count);
++ ret = post_sem(sem, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, count);
-+ check_sem_state(fd, sem_args.sem, 1, 2);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ close(fd);
-+}
-+
-+TEST_HARNESS_MAIN
---
-2.43.0
-
-From 7f8e2b5dedf3fe0192bcf7924d651d33df8898a9 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:07:04 -0600
-Subject: [PATCH 19/32] selftests: ntsync: Add some tests for mutex state.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 188 ++++++++++++++++++
- 1 file changed, 188 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 777c408e8f9e..8390dbbb1dd5 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -49,6 +49,42 @@ static int put_sem(int fd, __u32 sem, __u32 *count)
- return ret;
- }
-
-+static int read_mutex_state(int fd, __u32 mutex, __u32 *count, __u32 *owner)
-+{
-+ struct ntsync_mutex_args args;
-+ int ret;
-+
-+ args.mutex = mutex;
-+ args.count = 0xdeadbeef;
-+ args.owner = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &args);
-+ *count = args.count;
-+ *owner = args.owner;
-+ return ret;
-+}
++ check_sem_state(sem, 1, 2);
+
-+#define check_mutex_state(fd, mutex, count, owner) \
-+ ({ \
-+ __u32 __count, __owner; \
-+ int ret = read_mutex_state((fd), (mutex), &__count, &__owner); \
-+ EXPECT_EQ(0, ret); \
-+ EXPECT_EQ((count), __count); \
-+ EXPECT_EQ((owner), __owner); \
-+ })
++ count = ~0u;
++ ret = post_sem(sem, &count);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(EOVERFLOW, errno);
++ check_sem_state(sem, 1, 2);
+
-+static int put_mutex(int fd, __u32 mutex, __u32 owner, __u32 *count)
-+{
-+ struct ntsync_mutex_args args;
-+ int ret;
++ close(sem);
+
-+ args.mutex = mutex;
-+ args.owner = owner;
-+ args.count = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_MUTEX, &args);
-+ *count = args.count;
-+ return ret;
++ close(fd);
+}
+
- static int wait_any(int fd, __u32 count, const __u32 *objs, __u32 owner,
- __u32 *index)
- {
-@@ -150,4 +186,156 @@ TEST(semaphore_state)
- close(fd);
- }
-
+TEST(mutex_state)
+{
+ struct ntsync_mutex_args mutex_args;
-+ __u32 mutex, owner, count, index;
++ __u32 owner, count, index;
+ struct timespec timeout;
-+ int fd, ret;
++ int fd, ret, mutex;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+
@@ -2782,75 +2037,74 @@ index 777c408e8f9e..8390dbbb1dd5 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+ mutex = mutex_args.mutex;
-+ check_mutex_state(fd, mutex, 2, 123);
++ check_mutex_state(mutex, 2, 123);
+
-+ ret = put_mutex(fd, mutex, 0, &count);
++ ret = unlock_mutex(mutex, 0, &count);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
-+ ret = put_mutex(fd, mutex, 456, &count);
++ ret = unlock_mutex(mutex, 456, &count);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EPERM, errno);
-+ check_mutex_state(fd, mutex, 2, 123);
++ check_mutex_state(mutex, 2, 123);
+
-+ ret = put_mutex(fd, mutex, 123, &count);
++ ret = unlock_mutex(mutex, 123, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, count);
-+ check_mutex_state(fd, mutex, 1, 123);
++ check_mutex_state(mutex, 1, 123);
+
-+ ret = put_mutex(fd, mutex, 123, &count);
++ ret = unlock_mutex(mutex, 123, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, count);
-+ check_mutex_state(fd, mutex, 0, 0);
++ check_mutex_state(mutex, 0, 0);
+
-+ ret = put_mutex(fd, mutex, 123, &count);
++ ret = unlock_mutex(mutex, 123, &count);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EPERM, errno);
+
+ ret = wait_any(fd, 1, &mutex, 456, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_mutex_state(fd, mutex, 1, 456);
++ check_mutex_state(mutex, 1, 456);
+
+ ret = wait_any(fd, 1, &mutex, 456, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_mutex_state(fd, mutex, 2, 456);
++ check_mutex_state(mutex, 2, 456);
+
-+ ret = put_mutex(fd, mutex, 456, &count);
++ ret = unlock_mutex(mutex, 456, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, count);
-+ check_mutex_state(fd, mutex, 1, 456);
++ check_mutex_state(mutex, 1, 456);
+
+ ret = wait_any(fd, 1, &mutex, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ owner = 0;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+ owner = 123;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
-+ EXPECT_EQ(0, ret);
-+ check_mutex_state(fd, mutex, 1, 456);
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(EPERM, errno);
++ check_mutex_state(mutex, 1, 456);
+
+ owner = 456;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
+ EXPECT_EQ(0, ret);
+
-+ mutex_args.count = 0xdeadbeef;
-+ mutex_args.owner = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &mutex_args);
++ memset(&mutex_args, 0xcc, sizeof(mutex_args));
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
+ EXPECT_EQ(0, mutex_args.count);
+ EXPECT_EQ(0, mutex_args.owner);
+
-+ mutex_args.count = 0xdeadbeef;
-+ mutex_args.owner = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &mutex_args);
++ memset(&mutex_args, 0xcc, sizeof(mutex_args));
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
+ EXPECT_EQ(0, mutex_args.count);
@@ -2860,15 +2114,14 @@ index 777c408e8f9e..8390dbbb1dd5 100644
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
+ EXPECT_EQ(0, index);
-+ check_mutex_state(fd, mutex, 1, 123);
++ check_mutex_state(mutex, 1, 123);
+
+ owner = 123;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
+ EXPECT_EQ(0, ret);
+
-+ mutex_args.count = 0xdeadbeef;
-+ mutex_args.owner = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &mutex_args);
++ memset(&mutex_args, 0xcc, sizeof(mutex_args));
++ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
+ EXPECT_EQ(0, mutex_args.count);
@@ -2878,10 +2131,9 @@ index 777c408e8f9e..8390dbbb1dd5 100644
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
+ EXPECT_EQ(0, index);
-+ check_mutex_state(fd, mutex, 1, 123);
++ check_mutex_state(mutex, 1, 123);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex);
-+ EXPECT_EQ(0, ret);
++ close(mutex);
+
+ mutex_args.owner = 0;
+ mutex_args.count = 0;
@@ -2890,49 +2142,167 @@ index 777c408e8f9e..8390dbbb1dd5 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+ mutex = mutex_args.mutex;
-+ check_mutex_state(fd, mutex, 0, 0);
++ check_mutex_state(mutex, 0, 0);
+
+ ret = wait_any(fd, 1, &mutex, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_mutex_state(fd, mutex, 1, 123);
++ check_mutex_state(mutex, 1, 123);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
++ close(mutex);
++
++ mutex_args.owner = 123;
++ mutex_args.count = ~0u;
++ mutex_args.mutex = 0xdeadbeef;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
+ EXPECT_EQ(0, ret);
++ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
++ mutex = mutex_args.mutex;
++ check_mutex_state(mutex, ~0u, 123);
++
++ ret = wait_any(fd, 1, &mutex, 123, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(ETIMEDOUT, errno);
++
++ close(mutex);
++
++ close(fd);
++}
++
++TEST(manual_event_state)
++{
++ struct ntsync_event_args event_args;
++ __u32 index, signaled;
++ int fd, event, ret;
++
++ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
++ ASSERT_LE(0, fd);
++
++ event_args.manual = 1;
++ event_args.signaled = 0;
++ event_args.event = 0xdeadbeef;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
++ EXPECT_EQ(0, ret);
++ EXPECT_NE(0xdeadbeef, event_args.event);
++ event = event_args.event;
++ check_event_state(event, 0, 1);
++
++ signaled = 0xdeadbeef;
++ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event, 1, 1);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++ check_event_state(event, 1, 1);
++
++ ret = wait_any(fd, 1, &event, 123, &index);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, index);
++ check_event_state(event, 1, 1);
++
++ signaled = 0xdeadbeef;
++ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++ check_event_state(event, 0, 1);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event, 0, 1);
++
++ ret = wait_any(fd, 1, &event, 123, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(ETIMEDOUT, errno);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++ check_event_state(event, 0, 1);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event, 0, 1);
++
++ close(event);
++
++ close(fd);
++}
++
++TEST(auto_event_state)
++{
++ struct ntsync_event_args event_args;
++ __u32 index, signaled;
++ int fd, event, ret;
++
++ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
++ ASSERT_LE(0, fd);
++
++ event_args.manual = 0;
++ event_args.signaled = 1;
++ event_args.event = 0xdeadbeef;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
++ EXPECT_EQ(0, ret);
++ EXPECT_NE(0xdeadbeef, event_args.event);
++ event = event_args.event;
++
++ check_event_state(event, 1, 0);
++
++ signaled = 0xdeadbeef;
++ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++ check_event_state(event, 1, 0);
++
++ ret = wait_any(fd, 1, &event, 123, &index);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, index);
++ check_event_state(event, 0, 0);
++
++ signaled = 0xdeadbeef;
++ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event, 0, 0);
++
++ ret = wait_any(fd, 1, &event, 123, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(ETIMEDOUT, errno);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++ check_event_state(event, 0, 0);
++
++ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event, 0, 0);
++
++ close(event);
+
+ close(fd);
+}
+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From 596798d186edb9ca00396222e9dc0c43fcb40548 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:07:45 -0600
-Subject: [PATCH 20/32] selftests: ntsync: Add some tests for
- WINESYNC_IOC_WAIT_ANY.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 107 ++++++++++++++++++
- 1 file changed, 107 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 8390dbbb1dd5..8ae930e7a601 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -338,4 +338,111 @@ TEST(mutex_state)
- close(fd);
- }
-
+TEST(test_wait_any)
+{
++ int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
+ struct ntsync_mutex_args mutex_args = {0};
-+ struct ntsync_wait_args wait_args = {0};
+ struct ntsync_sem_args sem_args = {0};
-+ __u32 objs[2], owner, index;
++ __u32 owner, index, count, i;
+ struct timespec timeout;
-+ int fd, ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+
@@ -2959,44 +2329,44 @@ index 8390dbbb1dd5..8ae930e7a601 100644
+ ret = wait_any(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 0, 0);
++ check_sem_state(sem_args.sem, 1, 3);
++ check_mutex_state(mutex_args.mutex, 0, 0);
+
+ ret = wait_any(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 0, 0);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 0, 0);
+
+ ret = wait_any(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 123);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 1, 123);
+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
++ count = 1;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
++ EXPECT_EQ(0, count);
+
+ ret = wait_any(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 123);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 1, 123);
+
+ ret = wait_any(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 2, 123);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 2, 123);
+
+ ret = wait_any(fd, 2, objs, 456, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ owner = 123;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
++ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any(fd, 2, objs, 456, &index);
@@ -3009,93 +2379,49 @@ index 8390dbbb1dd5..8ae930e7a601 100644
+ EXPECT_EQ(1, index);
+
+ /* test waiting on the same object twice */
-+ sem_args.count = 2;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
++ count = 2;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
++ EXPECT_EQ(0, count);
+
+ objs[0] = objs[1] = sem_args.sem;
+ ret = wait_any(fd, 2, objs, 456, &index);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, wait_args.index);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
++ EXPECT_EQ(0, index);
++ check_sem_state(sem_args.sem, 1, 3);
+
+ ret = wait_any(fd, 0, NULL, 456, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
++ for (i = 0; i < NTSYNC_MAX_WAIT_COUNT + 1; ++i)
++ objs[i] = sem_args.sem;
++
++ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT, objs, 123, &index);
+ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, index);
+
-+ close(fd);
-+}
++ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT + 1, objs, 123, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(EINVAL, errno);
+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From 4610258d640a5bd75b7482b92cb74c4ffba58b84 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:08:25 -0600
-Subject: [PATCH 21/32] selftests: ntsync: Add some tests for
- WINESYNC_IOC_WAIT_ALL.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 104 +++++++++++++++++-
- 1 file changed, 101 insertions(+), 3 deletions(-)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 8ae930e7a601..586e798d3228 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -85,8 +85,8 @@ static int put_mutex(int fd, __u32 mutex, __u32 owner, __u32 *count)
- return ret;
- }
-
--static int wait_any(int fd, __u32 count, const __u32 *objs, __u32 owner,
-- __u32 *index)
-+static int wait_objs(int fd, unsigned long request, __u32 count,
-+ const __u32 *objs, __u32 owner, __u32 *index)
- {
- struct ntsync_wait_args args = {0};
- struct timespec timeout;
-@@ -99,11 +99,23 @@ static int wait_any(int fd, __u32 count, const __u32 *objs, __u32 owner,
- args.objs = (uintptr_t)objs;
- args.owner = owner;
- args.index = 0xdeadbeef;
-- ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
-+ ret = ioctl(fd, request, &args);
- *index = args.index;
- return ret;
- }
-
-+static int wait_any(int fd, __u32 count, const __u32 *objs,
-+ __u32 owner, __u32 *index)
-+{
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
-+}
++ ret = wait_any(fd, -1, objs, 123, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(EINVAL, errno);
+
-+static int wait_all(int fd, __u32 count, const __u32 *objs,
-+ __u32 owner, __u32 *index)
-+{
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
++ close(sem_args.sem);
++ close(mutex_args.mutex);
++
++ close(fd);
+}
+
- TEST(semaphore_state)
- {
- struct ntsync_sem_args sem_args;
-@@ -445,4 +457,90 @@ TEST(test_wait_any)
- close(fd);
- }
-
+TEST(test_wait_all)
+{
++ struct ntsync_event_args event_args = {0};
+ struct ntsync_mutex_args mutex_args = {0};
+ struct ntsync_sem_args sem_args = {0};
-+ __u32 objs[2], owner, index;
-+ int fd, ret;
++ __u32 owner, index, count;
++ int objs[2], fd, ret;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
@@ -3114,213 +2440,86 @@ index 8ae930e7a601..586e798d3228 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
++ event_args.manual = true;
++ event_args.signaled = true;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
++ EXPECT_EQ(0, ret);
++
+ objs[0] = sem_args.sem;
+ objs[1] = mutex_args.mutex;
+
+ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 123);
++ check_sem_state(sem_args.sem, 1, 3);
++ check_mutex_state(mutex_args.mutex, 1, 123);
+
+ ret = wait_all(fd, 2, objs, 456, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 123);
++ check_sem_state(sem_args.sem, 1, 3);
++ check_mutex_state(mutex_args.mutex, 1, 123);
+
+ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 2, 123);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 2, 123);
+
+ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 2, 123);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_mutex_state(mutex_args.mutex, 2, 123);
+
-+ sem_args.count = 3;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
++ count = 3;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
++ EXPECT_EQ(0, count);
+
+ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 2, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 3, 123);
++ check_sem_state(sem_args.sem, 2, 3);
++ check_mutex_state(mutex_args.mutex, 3, 123);
+
+ owner = 123;
-+ ret = ioctl(fd, NTSYNC_IOC_KILL_OWNER, &owner);
++ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EOWNERDEAD, errno);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 123);
-+
-+ /* test waiting on the same object twice */
-+ objs[0] = objs[1] = sem_args.sem;
-+ ret = wait_all(fd, 2, objs, 123, &index);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
-+ EXPECT_EQ(0, ret);
-+
-+ close(fd);
-+}
-+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From a18ca868f284f8eec745aec2346b2ff138370473 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:08:54 -0600
-Subject: [PATCH 22/32] selftests: ntsync: Add some tests for invalid object
- handling.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 93 +++++++++++++++++++
- 1 file changed, 93 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 586e798d3228..ac24a94f2518 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -543,4 +543,97 @@ TEST(test_wait_all)
- close(fd);
- }
-
-+TEST(invalid_objects)
-+{
-+ struct ntsync_mutex_args mutex_args = {0};
-+ struct ntsync_wait_args wait_args = {0};
-+ struct ntsync_sem_args sem_args = {0};
-+ __u32 objs[2] = {0};
-+ int fd, ret;
-+
-+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-+ ASSERT_LE(0, fd);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_MUTEX, &mutex_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &mutex_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ wait_args.objs = (uintptr_t)objs;
-+ wait_args.count = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ALL, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &objs[0]);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ sem_args.max = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+
-+ mutex_args.mutex = sem_args.sem;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_MUTEX, &mutex_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_MUTEX, &mutex_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ check_sem_state(sem_args.sem, 1, 3);
++ check_mutex_state(mutex_args.mutex, 1, 123);
+
+ objs[0] = sem_args.sem;
-+ objs[1] = sem_args.sem + 1;
-+ wait_args.count = 2;
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ALL, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ objs[0] = sem_args.sem + 1;
-+ objs[1] = sem_args.sem;
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ALL, &wait_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
++ objs[1] = event_args.event;
++ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, index);
++ check_sem_state(sem_args.sem, 0, 3);
++ check_event_state(event_args.event, 1, 1);
+
-+ sem_args.sem = mutex_args.mutex;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_SEM, &sem_args);
++ /* test waiting on the same object twice */
++ objs[0] = objs[1] = sem_args.sem;
++ ret = wait_all(fd, 2, objs, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
-+ EXPECT_EQ(0, ret);
++ close(sem_args.sem);
++ close(mutex_args.mutex);
++ close(event_args.event);
+
+ close(fd);
+}
+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From e49d10455ebe2a5d056523a225b8b179b1da932a Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:09:32 -0600
-Subject: [PATCH 23/32] selftests: ntsync: Add some tests for wakeup signaling
- with WINESYNC_IOC_WAIT_ANY.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 154 ++++++++++++++++++
- 1 file changed, 154 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index ac24a94f2518..919cabc3ba5e 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -636,4 +636,158 @@ TEST(invalid_objects)
- close(fd);
- }
-
-+struct wake_args
-+{
++struct wake_args {
+ int fd;
-+ __u32 obj;
++ int obj;
+};
+
-+struct wait_args
-+{
++struct wait_args {
+ int fd;
+ unsigned long request;
+ struct ntsync_wait_args *args;
@@ -3337,32 +2536,34 @@ index ac24a94f2518..919cabc3ba5e 100644
+ return NULL;
+}
+
-+static void get_abs_timeout(struct timespec *timeout, clockid_t clock,
-+ unsigned int ms)
++static __u64 get_abs_timeout(unsigned int ms)
+{
-+ clock_gettime(clock, timeout);
-+ timeout->tv_nsec += ms * 1000000;
-+ timeout->tv_sec += (timeout->tv_nsec / 1000000000);
-+ timeout->tv_nsec %= 1000000000;
++ struct timespec timeout;
++ clock_gettime(CLOCK_MONOTONIC, &timeout);
++ return (timeout.tv_sec * 1000000000) + timeout.tv_nsec + (ms * 1000000);
+}
+
+static int wait_for_thread(pthread_t thread, unsigned int ms)
+{
+ struct timespec timeout;
-+ get_abs_timeout(&timeout, CLOCK_REALTIME, ms);
++
++ clock_gettime(CLOCK_REALTIME, &timeout);
++ timeout.tv_nsec += ms * 1000000;
++ timeout.tv_sec += (timeout.tv_nsec / 1000000000);
++ timeout.tv_nsec %= 1000000000;
+ return pthread_timedjoin_np(thread, NULL, &timeout);
+}
+
+TEST(wake_any)
+{
++ struct ntsync_event_args event_args = {0};
+ struct ntsync_mutex_args mutex_args = {0};
+ struct ntsync_wait_args wait_args = {0};
+ struct ntsync_sem_args sem_args = {0};
+ struct wait_args thread_args;
-+ __u32 objs[2], count, index;
-+ struct timespec timeout;
++ __u32 count, index, signaled;
++ int objs[2], fd, ret;
+ pthread_t thread;
-+ int fd, ret;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
@@ -3386,8 +2587,7 @@ index ac24a94f2518..919cabc3ba5e 100644
+
+ /* test waking the semaphore */
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.timeout = (uintptr_t)&timeout;
++ wait_args.timeout = get_abs_timeout(1000);
+ wait_args.objs = (uintptr_t)objs;
+ wait_args.count = 2;
+ wait_args.owner = 456;
@@ -3401,11 +2601,11 @@ index ac24a94f2518..919cabc3ba5e 100644
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
++ count = 1;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
++ EXPECT_EQ(0, count);
++ check_sem_state(sem_args.sem, 0, 3);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
@@ -3419,7 +2619,7 @@ index ac24a94f2518..919cabc3ba5e 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
++ wait_args.timeout = get_abs_timeout(1000);
+ wait_args.owner = 456;
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
@@ -3427,569 +2627,164 @@ index ac24a94f2518..919cabc3ba5e 100644
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = put_mutex(fd, mutex_args.mutex, 123, &count);
++ ret = unlock_mutex(mutex_args.mutex, 123, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, count);
+
+ ret = pthread_tryjoin_np(thread, NULL);
+ EXPECT_EQ(EBUSY, ret);
+
-+ ret = put_mutex(fd, mutex_args.mutex, 123, &count);
++ ret = unlock_mutex(mutex_args.mutex, 123, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, mutex_args.count);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 456);
++ check_mutex_state(mutex_args.mutex, 1, 456);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
+ EXPECT_EQ(1, wait_args.index);
+
-+ /* delete an object while it's being waited on */
-+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 200);
-+ wait_args.owner = 123;
-+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(ETIMEDOUT, ret);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = wait_for_thread(thread, 200);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(-1, thread_args.ret);
-+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
-+
-+ close(fd);
-+}
-+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From a9f683caedb447a30b28a3ebb3d8fc597fe26d73 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:09:36 -0600
-Subject: [PATCH 24/32] selftests: ntsync: Add some tests for wakeup signaling
- with WINESYNC_IOC_WAIT_ALL.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 102 ++++++++++++++++++
- 1 file changed, 102 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 919cabc3ba5e..3dbae7c8ac1f 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -790,4 +790,106 @@ TEST(wake_any)
- close(fd);
- }
-
-+TEST(wake_all)
-+{
-+ struct ntsync_mutex_args mutex_args = {0};
-+ struct ntsync_wait_args wait_args = {0};
-+ struct ntsync_sem_args sem_args = {0};
-+ struct wait_args thread_args;
-+ __u32 objs[2], count, index;
-+ struct timespec timeout;
-+ pthread_t thread;
-+ int fd, ret;
-+
-+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-+ ASSERT_LE(0, fd);
-+
-+ sem_args.count = 0;
-+ sem_args.max = 3;
-+ sem_args.sem = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_NE(0xdeadbeef, sem_args.sem);
++ /* test waking events */
+
-+ mutex_args.owner = 123;
-+ mutex_args.count = 1;
-+ mutex_args.mutex = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
++ event_args.manual = false;
++ event_args.signaled = false;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
-+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
-+
-+ objs[0] = sem_args.sem;
-+ objs[1] = mutex_args.mutex;
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.timeout = (uintptr_t)&timeout;
-+ wait_args.objs = (uintptr_t)objs;
-+ wait_args.count = 2;
-+ wait_args.owner = 456;
-+ thread_args.fd = fd;
-+ thread_args.args = &wait_args;
-+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
++ objs[1] = event_args.event;
++ wait_args.timeout = get_abs_timeout(1000);
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ ret = pthread_tryjoin_np(thread, NULL);
-+ EXPECT_EQ(EBUSY, ret);
-+
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+
-+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, index);
-+
-+ ret = put_mutex(fd, mutex_args.mutex, 123, &count);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, count);
-+
-+ ret = pthread_tryjoin_np(thread, NULL);
-+ EXPECT_EQ(EBUSY, ret);
-+
-+ check_mutex_state(fd, mutex_args.mutex, 0, 0);
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ check_sem_state(fd, sem_args.sem, 1, 3);
-+ check_mutex_state(fd, mutex_args.mutex, 1, 456);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event_args.event, 0, 0);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
++ EXPECT_EQ(1, wait_args.index);
+
-+ /* delete an object while it's being waited on */
-+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 200);
-+ wait_args.owner = 123;
++ wait_args.timeout = get_abs_timeout(1000);
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &sem_args.sem);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = wait_for_thread(thread, 200);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(-1, thread_args.ret);
-+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
-+
-+ close(fd);
-+}
++ EXPECT_EQ(0, signaled);
++ check_event_state(event_args.event, 0, 0);
+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From f56885c1a183947d3b1f5e11a80ce23c7f150e33 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 19:34:47 -0600
-Subject: [PATCH 25/32] selftests: ntsync: Add some tests for manual-reset
- event state.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 92 +++++++++++++++++++
- 1 file changed, 92 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 3dbae7c8ac1f..cbe4ed73ffb9 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -85,6 +85,30 @@ static int put_mutex(int fd, __u32 mutex, __u32 owner, __u32 *count)
- return ret;
- }
-
-+static int read_event_state(int fd, __u32 event, __u32 *signaled, __u32 *manual)
-+{
-+ struct ntsync_event_args args;
-+ int ret;
-+
-+ args.event = event;
-+ args.signaled = 0xdeadbeef;
-+ args.manual = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_READ_EVENT, &args);
-+ *signaled = args.signaled;
-+ *manual = args.manual;
-+ return ret;
-+}
-+
-+#define check_event_state(fd, event, signaled, manual) \
-+ ({ \
-+ __u32 __signaled, __manual; \
-+ int ret = read_event_state((fd), (event), \
-+ &__signaled, &__manual); \
-+ EXPECT_EQ(0, ret); \
-+ EXPECT_EQ((signaled), __signaled); \
-+ EXPECT_EQ((manual), __manual); \
-+ })
-+
- static int wait_objs(int fd, unsigned long request, __u32 count,
- const __u32 *objs, __u32 owner, __u32 *index)
- {
-@@ -350,6 +374,74 @@ TEST(mutex_state)
- close(fd);
- }
-
-+TEST(manual_event_state)
-+{
-+ struct ntsync_event_args event_args;
-+ __u32 index;
-+ int fd, ret;
-+
-+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-+ ASSERT_LE(0, fd);
-+
-+ event_args.manual = 1;
-+ event_args.signaled = 0;
-+ event_args.event = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_NE(0xdeadbeef, event_args.event);
-+ check_event_state(fd, event_args.event, 0, 1);
-+
-+ event_args.signaled = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 1, 1);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
-+ check_event_state(fd, event_args.event, 1, 1);
-+
-+ ret = wait_any(fd, 1, &event_args.event, 123, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, index);
-+ check_event_state(fd, event_args.event, 1, 1);
-+
-+ event_args.signaled = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 1);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 1);
-+
-+ ret = wait_any(fd, 1, &event_args.event, 123, &index);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(ETIMEDOUT, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 1);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 1);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
-+
-+ close(fd);
-+}
-+
- TEST(test_wait_any)
- {
- struct ntsync_mutex_args mutex_args = {0};
---
-2.43.0
-
-From d1e2db12a317942c72505085459dad6d64c8926a Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 19:45:39 -0600
-Subject: [PATCH 26/32] selftests: ntsync: Add some tests for auto-reset event
- state.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 59 +++++++++++++++++++
- 1 file changed, 59 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index cbe4ed73ffb9..c158c15fc03e 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -442,6 +442,65 @@ TEST(manual_event_state)
- close(fd);
- }
-
-+TEST(auto_event_state)
-+{
-+ struct ntsync_event_args event_args;
-+ __u32 index;
-+ int fd, ret;
-+
-+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-+ ASSERT_LE(0, fd);
-+
-+ event_args.manual = 0;
-+ event_args.signaled = 1;
-+ event_args.event = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_NE(0xdeadbeef, event_args.event);
-+
-+ check_event_state(fd, event_args.event, 1, 0);
-+
-+ event_args.signaled = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
-+ check_event_state(fd, event_args.event, 1, 0);
-+
-+ ret = wait_any(fd, 1, &event_args.event, 123, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, index);
-+ check_event_state(fd, event_args.event, 0, 0);
-+
-+ event_args.signaled = 0xdeadbeef;
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 0);
-+
-+ ret = wait_any(fd, 1, &event_args.event, 123, &index);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(ETIMEDOUT, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 0);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
++ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 0);
++ EXPECT_EQ(0, thread_args.ret);
++ EXPECT_EQ(1, wait_args.index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
++ close(event_args.event);
+
-+ close(fd);
-+}
-+
- TEST(test_wait_any)
- {
- struct ntsync_mutex_args mutex_args = {0};
---
-2.43.0
-
-From 011b5115949be3b87e3009ea5e4ede6708cf3ece Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 21:00:50 -0600
-Subject: [PATCH 27/32] selftests: ntsync: Add some tests for wakeup signaling
- with events.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 152 +++++++++++++++++-
- 1 file changed, 150 insertions(+), 2 deletions(-)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index c158c15fc03e..8f07577cf156 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -610,6 +610,7 @@ TEST(test_wait_any)
-
- TEST(test_wait_all)
- {
-+ struct ntsync_event_args event_args = {0};
- struct ntsync_mutex_args mutex_args = {0};
- struct ntsync_sem_args sem_args = {0};
- __u32 objs[2], owner, index;
-@@ -632,6 +633,11 @@ TEST(test_wait_all)
- EXPECT_EQ(0, ret);
- EXPECT_NE(0xdeadbeef, mutex_args.mutex);
-
+ event_args.manual = true;
-+ event_args.signaled = true;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+
- objs[0] = sem_args.sem;
- objs[1] = mutex_args.mutex;
-
-@@ -680,6 +686,14 @@ TEST(test_wait_all)
- check_sem_state(fd, sem_args.sem, 1, 3);
- check_mutex_state(fd, mutex_args.mutex, 1, 123);
-
-+ objs[0] = sem_args.sem;
-+ objs[1] = event_args.event;
-+ ret = wait_all(fd, 2, objs, 123, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, index);
-+ check_sem_state(fd, sem_args.sem, 0, 3);
-+ check_event_state(fd, event_args.event, 1, 1);
-+
- /* test waiting on the same object twice */
- objs[0] = objs[1] = sem_args.sem;
- ret = wait_all(fd, 2, objs, 123, &index);
-@@ -690,6 +704,8 @@ TEST(test_wait_all)
- EXPECT_EQ(0, ret);
- ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
- EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
-
- close(fd);
- }
-@@ -829,6 +845,7 @@ static int wait_for_thread(pthread_t thread, unsigned int ms)
-
- TEST(wake_any)
- {
-+ struct ntsync_event_args event_args = {0};
- struct ntsync_mutex_args mutex_args = {0};
- struct ntsync_wait_args wait_args = {0};
- struct ntsync_sem_args sem_args = {0};
-@@ -918,10 +935,103 @@ TEST(wake_any)
- EXPECT_EQ(0, thread_args.ret);
- EXPECT_EQ(1, wait_args.index);
-
-+ /* test waking events */
-+
-+ event_args.manual = false;
+ event_args.signaled = false;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
+ objs[1] = event_args.event;
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
++ wait_args.timeout = get_abs_timeout(1000);
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 0);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event_args.event, 1, 1);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
+ EXPECT_EQ(1, wait_args.index);
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
++
++ wait_args.timeout = get_abs_timeout(1000);
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 0);
++ EXPECT_EQ(0, signaled);
++ check_event_state(event_args.event, 0, 1);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
+ EXPECT_EQ(1, wait_args.index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
++ close(event_args.event);
+
-+ event_args.manual = true;
-+ event_args.signaled = false;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
++ /* delete an object while it's being waited on */
+
-+ objs[1] = event_args.event;
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
++ wait_args.timeout = get_abs_timeout(200);
++ wait_args.owner = 123;
++ objs[1] = mutex_args.mutex;
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 1, 1);
-+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, thread_args.ret);
-+ EXPECT_EQ(1, wait_args.index);
++ close(sem_args.sem);
++ close(mutex_args.mutex);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
++ ret = wait_for_thread(thread, 200);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, event_args.signaled);
++ EXPECT_EQ(-1, thread_args.ret);
++ EXPECT_EQ(ETIMEDOUT, thread_args.err);
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
-+ EXPECT_EQ(0, ret);
++ close(fd);
++}
+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(ETIMEDOUT, ret);
++TEST(wake_all)
++{
++ struct ntsync_event_args manual_event_args = {0};
++ struct ntsync_event_args auto_event_args = {0};
++ struct ntsync_mutex_args mutex_args = {0};
++ struct ntsync_wait_args wait_args = {0};
++ struct ntsync_sem_args sem_args = {0};
++ struct wait_args thread_args;
++ __u32 count, index, signaled;
++ int objs[4], fd, ret;
++ pthread_t thread;
+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, event_args.signaled);
-+ check_event_state(fd, event_args.event, 0, 1);
++ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
++ ASSERT_LE(0, fd);
+
-+ ret = wait_for_thread(thread, 100);
++ sem_args.count = 0;
++ sem_args.max = 3;
++ sem_args.sem = 0xdeadbeef;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, thread_args.ret);
-+ EXPECT_EQ(1, wait_args.index);
++ EXPECT_NE(0xdeadbeef, sem_args.sem);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
++ mutex_args.owner = 123;
++ mutex_args.count = 1;
++ mutex_args.mutex = 0xdeadbeef;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
+ EXPECT_EQ(0, ret);
++ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
- /* delete an object while it's being waited on */
-
- get_abs_timeout(&timeout, CLOCK_MONOTONIC, 200);
- wait_args.owner = 123;
-+ objs[1] = mutex_args.mutex;
- ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
- EXPECT_EQ(0, ret);
-
-@@ -943,11 +1053,13 @@ TEST(wake_any)
-
- TEST(wake_all)
- {
-+ struct ntsync_event_args manual_event_args = {0};
-+ struct ntsync_event_args auto_event_args = {0};
- struct ntsync_mutex_args mutex_args = {0};
- struct ntsync_wait_args wait_args = {0};
- struct ntsync_sem_args sem_args = {0};
- struct wait_args thread_args;
-- __u32 objs[2], count, index;
-+ __u32 objs[4], count, index;
- struct timespec timeout;
- pthread_t thread;
- int fd, ret;
-@@ -969,13 +1081,25 @@ TEST(wake_all)
- EXPECT_EQ(0, ret);
- EXPECT_NE(0xdeadbeef, mutex_args.mutex);
-
+ manual_event_args.manual = true;
+ manual_event_args.signaled = true;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &manual_event_args);
@@ -4000,208 +2795,110 @@ index c158c15fc03e..8f07577cf156 100644
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args);
+ EXPECT_EQ(0, ret);
+
- objs[0] = sem_args.sem;
- objs[1] = mutex_args.mutex;
++ objs[0] = sem_args.sem;
++ objs[1] = mutex_args.mutex;
+ objs[2] = manual_event_args.event;
+ objs[3] = auto_event_args.event;
-
- get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
- wait_args.timeout = (uintptr_t)&timeout;
- wait_args.objs = (uintptr_t)objs;
-- wait_args.count = 2;
++
++ wait_args.timeout = get_abs_timeout(1000);
++ wait_args.objs = (uintptr_t)objs;
+ wait_args.count = 4;
- wait_args.owner = 456;
- thread_args.fd = fd;
- thread_args.args = &wait_args;
-@@ -1009,12 +1133,32 @@ TEST(wake_all)
-
- check_mutex_state(fd, mutex_args.mutex, 0, 0);
-
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &manual_event_args);
++ wait_args.owner = 456;
++ thread_args.fd = fd;
++ thread_args.args = &wait_args;
++ thread_args.request = NTSYNC_IOC_WAIT_ALL;
++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, manual_event_args.signaled);
+
- sem_args.count = 2;
- ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
- EXPECT_EQ(0, ret);
- EXPECT_EQ(0, sem_args.count);
-+ check_sem_state(fd, sem_args.sem, 2, 3);
++ ret = wait_for_thread(thread, 100);
++ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &auto_event_args);
++ count = 1;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, auto_event_args.signaled);
++ EXPECT_EQ(0, count);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &manual_event_args);
++ ret = pthread_tryjoin_np(thread, NULL);
++ EXPECT_EQ(EBUSY, ret);
++
++ check_sem_state(sem_args.sem, 1, 3);
++
++ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, manual_event_args.signaled);
++ EXPECT_EQ(0, index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &auto_event_args);
++ ret = unlock_mutex(mutex_args.mutex, 123, &count);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, auto_event_args.signaled);
++ EXPECT_EQ(1, count);
+
- check_sem_state(fd, sem_args.sem, 1, 3);
- check_mutex_state(fd, mutex_args.mutex, 1, 456);
-+ check_event_state(fd, manual_event_args.event, 1, 1);
-+ check_event_state(fd, auto_event_args.event, 0, 0);
-
- ret = wait_for_thread(thread, 100);
- EXPECT_EQ(0, ret);
-@@ -1034,6 +1178,10 @@ TEST(wake_all)
- EXPECT_EQ(0, ret);
- ret = ioctl(fd, NTSYNC_IOC_DELETE, &mutex_args.mutex);
- EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &manual_event_args.event);
++ ret = pthread_tryjoin_np(thread, NULL);
++ EXPECT_EQ(EBUSY, ret);
++
++ check_mutex_state(mutex_args.mutex, 0, 0);
++
++ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &auto_event_args.event);
++ EXPECT_EQ(1, signaled);
++
++ count = 2;
++ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
-
- ret = wait_for_thread(thread, 200);
- EXPECT_EQ(0, ret);
---
-2.43.0
-
-From 74c862089a6975afeb0a11bfb6e1c632ae231419 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 19 Jan 2022 21:06:22 -0600
-Subject: [PATCH 28/32] selftests: ntsync: Add some tests for invalid object
- handling with events.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 34 +++++++++++++++++++
- 1 file changed, 34 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 8f07577cf156..01bd034d4467 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -712,6 +712,7 @@ TEST(test_wait_all)
-
- TEST(invalid_objects)
- {
-+ struct ntsync_event_args event_args = {0};
- struct ntsync_mutex_args mutex_args = {0};
- struct ntsync_wait_args wait_args = {0};
- struct ntsync_sem_args sem_args = {0};
-@@ -737,6 +738,22 @@ TEST(invalid_objects)
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EINVAL, errno);
-
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ EXPECT_EQ(0, count);
++ check_sem_state(sem_args.sem, 2, 3);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(1, signaled);
+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, signaled);
+
- wait_args.objs = (uintptr_t)objs;
- wait_args.count = 1;
- ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args);
-@@ -763,6 +780,23 @@ TEST(invalid_objects)
- EXPECT_EQ(-1, ret);
- EXPECT_EQ(EINVAL, errno);
-
-+ event_args.event = sem_args.sem;
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ check_sem_state(sem_args.sem, 1, 3);
++ check_mutex_state(mutex_args.mutex, 1, 456);
++ check_event_state(manual_event_args.event, 1, 1);
++ check_event_state(auto_event_args.event, 0, 0);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ ret = wait_for_thread(thread, 100);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, thread_args.ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_PULSE_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ /* delete an object while it's being waited on */
+
-+ ret = ioctl(fd, NTSYNC_IOC_READ_EVENT, &event_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
++ wait_args.timeout = get_abs_timeout(200);
++ wait_args.owner = 123;
++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
++ EXPECT_EQ(0, ret);
+
- objs[0] = sem_args.sem;
- objs[1] = sem_args.sem + 1;
- wait_args.count = 2;
---
-2.43.0
-
-From 8550d7aec916d737b2e81f5916c25da69d0d3de0 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 20 Apr 2022 18:08:37 -0500
-Subject: [PATCH 29/32] selftests: ntsync: Add tests for alertable waits.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 189 +++++++++++++++++-
- 1 file changed, 186 insertions(+), 3 deletions(-)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index 01bd034d4467..a58387d6744b 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -110,7 +110,7 @@ static int read_event_state(int fd, __u32 event, __u32 *signaled, __u32 *manual)
- })
-
- static int wait_objs(int fd, unsigned long request, __u32 count,
-- const __u32 *objs, __u32 owner, __u32 *index)
-+ const __u32 *objs, __u32 owner, __u32 alert, __u32 *index)
- {
- struct ntsync_wait_args args = {0};
- struct timespec timeout;
-@@ -123,6 +123,7 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
- args.objs = (uintptr_t)objs;
- args.owner = owner;
- args.index = 0xdeadbeef;
-+ args.alert = alert;
- ret = ioctl(fd, request, &args);
- *index = args.index;
- return ret;
-@@ -131,13 +132,27 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
- static int wait_any(int fd, __u32 count, const __u32 *objs,
- __u32 owner, __u32 *index)
- {
-- return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
- }
-
- static int wait_all(int fd, __u32 count, const __u32 *objs,
- __u32 owner, __u32 *index)
- {
-- return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
-+}
++ ret = wait_for_thread(thread, 100);
++ EXPECT_EQ(ETIMEDOUT, ret);
+
-+static int wait_any_alert(int fd, __u32 count, const __u32 *objs,
-+ __u32 owner, __u32 alert, __u32 *index)
-+{
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
-+ count, objs, owner, alert, index);
++ close(sem_args.sem);
++ close(mutex_args.mutex);
++ close(manual_event_args.event);
++ close(auto_event_args.event);
++
++ ret = wait_for_thread(thread, 200);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(-1, thread_args.ret);
++ EXPECT_EQ(ETIMEDOUT, thread_args.err);
++
++ close(fd);
+}
+
-+static int wait_all_alert(int fd, __u32 count, const __u32 *objs,
-+ __u32 owner, __u32 alert, __u32 *index)
-+{
-+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
-+ count, objs, owner, alert, index);
- }
-
- TEST(semaphore_state)
-@@ -1225,4 +1240,172 @@ TEST(wake_all)
- close(fd);
- }
-
+TEST(alert_any)
+{
+ struct ntsync_event_args event_args = {0};
++ struct ntsync_wait_args wait_args = {0};
+ struct ntsync_sem_args sem_args = {0};
-+ __u32 objs[2], index;
-+ int fd, ret;
++ __u32 index, count, signaled;
++ struct wait_args thread_args;
++ int objs[2], fd, ret;
++ pthread_t thread;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
@@ -4231,14 +2928,14 @@ index 01bd034d4467..a58387d6744b 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
@@ -4249,9 +2946,36 @@ index 01bd034d4467..a58387d6744b 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
++ /* test wakeup via alert */
++
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+
++ wait_args.timeout = get_abs_timeout(1000);
++ wait_args.objs = (uintptr_t)objs;
++ wait_args.count = 2;
++ wait_args.owner = 123;
++ wait_args.index = 0xdeadbeef;
++ wait_args.alert = event_args.event;
++ thread_args.fd = fd;
++ thread_args.args = &wait_args;
++ thread_args.request = NTSYNC_IOC_WAIT_ANY;
++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
++ EXPECT_EQ(0, ret);
++
++ ret = wait_for_thread(thread, 100);
++ EXPECT_EQ(ETIMEDOUT, ret);
++
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
++
++ ret = wait_for_thread(thread, 100);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(0, thread_args.ret);
++ EXPECT_EQ(2, wait_args.index);
++
++ close(event_args.event);
++
+ /* test with an auto-reset event */
+
+ event_args.manual = false;
@@ -4259,9 +2983,8 @@ index 01bd034d4467..a58387d6744b 100644
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
-+ sem_args.sem = objs[0];
-+ sem_args.count = 1;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
++ count = 1;
++ ret = post_sem(objs[0], &count);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
@@ -4276,13 +2999,10 @@ index 01bd034d4467..a58387d6744b 100644
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
++ close(event_args.event);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &objs[0]);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &objs[1]);
-+ EXPECT_EQ(0, ret);
++ close(objs[0]);
++ close(objs[1]);
+
+ close(fd);
+}
@@ -4290,9 +3010,12 @@ index 01bd034d4467..a58387d6744b 100644
+TEST(alert_all)
+{
+ struct ntsync_event_args event_args = {0};
++ struct ntsync_wait_args wait_args = {0};
+ struct ntsync_sem_args sem_args = {0};
-+ __u32 objs[2], index;
-+ int fd, ret;
++ struct wait_args thread_args;
++ __u32 index, count, signaled;
++ int objs[2], fd, ret;
++ pthread_t thread;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
@@ -4326,86 +3049,12 @@ index 01bd034d4467..a58387d6744b 100644
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
-+
-+ /* test with an auto-reset event */
-+
-+ event_args.manual = false;
-+ event_args.signaled = true;
-+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.sem = objs[1];
-+ sem_args.count = 2;
-+ ret = ioctl(fd, NTSYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, index);
-+
-+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, index);
-+
-+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(ETIMEDOUT, errno);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
-+ EXPECT_EQ(0, ret);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &objs[0]);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, NTSYNC_IOC_DELETE, &objs[1]);
-+ EXPECT_EQ(0, ret);
-+
-+ close(fd);
-+}
-+
- TEST_HARNESS_MAIN
---
-2.43.0
-
-From 96d776b0b301b8a9f0b2fc27e62ff8b26e407072 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Wed, 20 Apr 2022 18:24:43 -0500
-Subject: [PATCH 30/32] selftests: ntsync: Add some tests for wakeup signaling
- via alerts.
-
----
- .../testing/selftests/drivers/ntsync/ntsync.c | 66 +++++++++++++++++++
- 1 file changed, 66 insertions(+)
-
-diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
-index a58387d6744b..fda9f1c5e810 100644
---- a/tools/testing/selftests/drivers/ntsync/ntsync.c
-+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
-@@ -1243,8 +1243,12 @@ TEST(wake_all)
- TEST(alert_any)
- {
- struct ntsync_event_args event_args = {0};
-+ struct ntsync_wait_args wait_args = {0};
- struct ntsync_sem_args sem_args = {0};
-+ struct wait_args thread_args;
-+ struct timespec timeout;
- __u32 objs[2], index;
-+ pthread_t thread;
- int fd, ret;
-
- fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-@@ -1293,6 +1297,35 @@ TEST(alert_any)
- EXPECT_EQ(0, ret);
- EXPECT_EQ(2, index);
-
+ /* test wakeup via alert */
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.timeout = (uintptr_t)&timeout;
++ wait_args.timeout = get_abs_timeout(1000);
+ wait_args.objs = (uintptr_t)objs;
+ wait_args.count = 2;
+ wait_args.owner = 123;
@@ -4413,14 +3062,14 @@ index a58387d6744b..fda9f1c5e810 100644
+ wait_args.alert = event_args.event;
+ thread_args.fd = fd;
+ thread_args.args = &wait_args;
-+ thread_args.request = NTSYNC_IOC_WAIT_ANY;
++ thread_args.request = NTSYNC_IOC_WAIT_ALL;
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
++ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
@@ -4428,568 +3077,111 @@ index a58387d6744b..fda9f1c5e810 100644
+ EXPECT_EQ(0, thread_args.ret);
+ EXPECT_EQ(2, wait_args.index);
+
- ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
- EXPECT_EQ(0, ret);
-
-@@ -1334,8 +1367,12 @@ TEST(alert_any)
- TEST(alert_all)
- {
- struct ntsync_event_args event_args = {0};
-+ struct ntsync_wait_args wait_args = {0};
- struct ntsync_sem_args sem_args = {0};
-+ struct wait_args thread_args;
-+ struct timespec timeout;
- __u32 objs[2], index;
-+ pthread_t thread;
- int fd, ret;
-
- fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
-@@ -1370,6 +1407,35 @@ TEST(alert_all)
- EXPECT_EQ(0, ret);
- EXPECT_EQ(2, index);
-
-+ /* test wakeup via alert */
++ close(event_args.event);
+
-+ ret = ioctl(fd, NTSYNC_IOC_RESET_EVENT, &event_args);
-+ EXPECT_EQ(0, ret);
++ /* test with an auto-reset event */
+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.timeout = (uintptr_t)&timeout;
-+ wait_args.objs = (uintptr_t)objs;
-+ wait_args.count = 2;
-+ wait_args.owner = 123;
-+ wait_args.index = 0xdeadbeef;
-+ wait_args.alert = event_args.event;
-+ thread_args.fd = fd;
-+ thread_args.args = &wait_args;
-+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
-+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
++ event_args.manual = false;
++ event_args.signaled = true;
++ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(ETIMEDOUT, ret);
-+
-+ ret = ioctl(fd, NTSYNC_IOC_SET_EVENT, &event_args);
++ count = 2;
++ ret = post_sem(objs[1], &count);
+ EXPECT_EQ(0, ret);
+
-+ ret = wait_for_thread(thread, 100);
++ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, thread_args.ret);
-+ EXPECT_EQ(2, wait_args.index);
-+
- ret = ioctl(fd, NTSYNC_IOC_DELETE, &event_args.event);
- EXPECT_EQ(0, ret);
-
---
-2.43.0
-
-From 5ecf8efc0d4f019edcf4334b54314226bc30bd62 Mon Sep 17 00:00:00 2001
-From: Elizabeth Figura <zfigura@codeweavers.com>
-Date: Fri, 5 Mar 2021 12:22:55 -0600
-Subject: [PATCH 31/32] maintainers: Add an entry for ntsync.
-
----
- MAINTAINERS | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/MAINTAINERS b/MAINTAINERS
-index a7c4cf8201e0..90e84b768c42 100644
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -15341,6 +15341,15 @@ T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
- F: Documentation/filesystems/ntfs3.rst
- F: fs/ntfs3/
-
-+NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
-+M: Elizabeth Figura <zfigura@codeweavers.com>
-+L: wine-devel@winehq.org
-+S: Supported
-+F: Documentation/userspace-api/ntsync.rst
-+F: drivers/misc/ntsync.c
-+F: include/uapi/linux/ntsync.h
-+F: tools/testing/selftests/drivers/ntsync/
-+
- NUBUS SUBSYSTEM
- M: Finn Thain <fthain@linux-m68k.org>
- L: linux-m68k@lists.linux-m68k.org
---
-2.43.0
-
-From e4db1ebaa7b9b150bcf54a4d57699e746b4d5b75 Mon Sep 17 00:00:00 2001
-From: Peter Jung <admin@ptr1337.dev>
-Date: Wed, 24 Jan 2024 17:37:06 +0100
-Subject: [PATCH 32/32] docs: ntsync: Add documentation for the ntsync uAPI.
-
-Signed-off-by: Peter Jung <admin@ptr1337.dev>
----
- Documentation/userspace-api/index.rst | 1 +
- Documentation/userspace-api/ntsync.rst | 445 +++++++++++++++++++++++++
- 2 files changed, 446 insertions(+)
- create mode 100644 Documentation/userspace-api/ntsync.rst
-
-diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
-index 031df47a7c19..188bf15c4ecf 100644
---- a/Documentation/userspace-api/index.rst
-+++ b/Documentation/userspace-api/index.rst
-@@ -33,6 +33,7 @@ place where this information is gathered.
- sysfs-platform_profile
- vduse
- futex2
-+ ntsync
-
- .. only:: subproject and html
-
-diff --git a/Documentation/userspace-api/ntsync.rst b/Documentation/userspace-api/ntsync.rst
-new file mode 100644
-index 000000000000..45d945b3e246
---- /dev/null
-+++ b/Documentation/userspace-api/ntsync.rst
-@@ -0,0 +1,445 @@
-+===================================
-+NT synchronization primitive driver
-+===================================
-+
-+This page documents the user-space API for the ntsync driver.
-+
-+ntsync is a support driver for emulation of NT synchronization
-+primitives by user-space NT emulators. It exists because implementation
-+in user-space, using existing tools, cannot match Windows performance
-+while offering accurate semantics. It is implemented entirely in
-+software, and does not drive any hardware device.
-+
-+This interface is meant as a compatibility tool only, and should not
-+be used for general synchronization. Instead use generic, versatile
-+interfaces such as futex(2) and poll(2).
-+
-+Synchronization primitives
-+==========================
-+
-+The ntsync driver exposes three types of synchronization primitives:
-+semaphores, mutexes, and events.
-+
-+A semaphore holds a single volatile 32-bit counter, and a static
-+32-bit integer denoting the maximum value. It is considered signaled
-+when the counter is nonzero. The counter is decremented by one when a
-+wait is satisfied. Both the initial and maximum count are established
-+when the semaphore is created.
-+
-+A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit
-+identifier denoting its owner. A mutex is considered signaled when its
-+owner is zero (indicating that it is not owned). The recursion count
-+is incremented when a wait is satisfied, and ownership is set to the
-+given identifier.
-+
-+A mutex also holds an internal flag denoting whether its previous
-+owner has died; such a mutex is said to be inconsistent. Owner death
-+is not tracked automatically based on thread death, but rather must be
-+communicated using ``NTSYNC_IOC_KILL_OWNER``. An inconsistent mutex
-+is inherently considered unowned.
-+
-+Except for the "unowned" semantics of zero, the actual value of the
-+owner identifier is not interpreted by the ntsync driver at all. The
-+intended use is to store a thread identifier; however, the ntsync
-+driver does not actually validate that a calling thread provides
-+consistent or unique identifiers.
-+
-+An event holds a volatile boolean state denoting whether it is
-+signaled or not. There are two types of events, auto-reset and
-+manual-reset. An auto-reset event is designaled when a wait is
-+satisfied; a manual-reset event is not. The event type is specified
-+when the event is created.
-+
-+Unless specified otherwise, all operations on an object are atomic and
-+totally ordered with respect to other operations on the same object.
-+
-+Objects are represented by unsigned 32-bit integers.
-+
-+Char device
-+===========
-+
-+The ntsync driver creates a single char device /dev/ntsync. Each
-+file description opened on the device represents a unique namespace.
-+That is, objects created on one open file description are shared
-+across all its individual descriptors, but are not shared with other
-+open() calls on the same device. The same file description may be
-+shared across multiple processes.
-+
-+ioctl reference
-+===============
-+
-+All operations on the device are done through ioctls. There are four
-+structures used in ioctl calls::
-+
-+ struct ntsync_sem_args {
-+ __u32 sem;
-+ __u32 count;
-+ __u32 max;
-+ };
-+
-+ struct ntsync_mutex_args {
-+ __u32 mutex;
-+ __u32 owner;
-+ __u32 count;
-+ };
-+
-+ struct ntsync_event_args {
-+ __u32 event;
-+ __u32 signaled;
-+ __u32 manual;
-+ };
-+
-+ struct ntsync_wait_args {
-+ __u64 timeout;
-+ __u64 objs;
-+ __u32 count;
-+ __u32 owner;
-+ __u32 index;
-+ __u32 alert;
-+ };
-+
-+Depending on the ioctl, members of the structure may be used as input,
-+output, or not at all. All ioctls return 0 on success.
-+
-+The ioctls are as follows:
-+
-+.. c:macro:: NTSYNC_IOC_CREATE_SEM
-+
-+ Create a semaphore object. Takes a pointer to struct
-+ :c:type:`ntsync_sem_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``sem``
-+ - On output, contains the identifier of the created semaphore.
-+ * - ``count``
-+ - Initial count of the semaphore.
-+ * - ``max``
-+ - Maximum count of the semaphore.
-+
-+ Fails with ``EINVAL`` if ``count`` is greater than ``max``.
-+
-+.. c:macro:: NTSYNC_IOC_CREATE_MUTEX
-+
-+ Create a mutex object. Takes a pointer to struct
-+ :c:type:`ntsync_mutex_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``mutex``
-+ - On output, contains the identifier of the created mutex.
-+ * - ``count``
-+ - Initial recursion count of the mutex.
-+ * - ``owner``
-+ - Initial owner of the mutex.
-+
-+ If ``owner`` is nonzero and ``count`` is zero, or if ``owner`` is
-+ zero and ``count`` is nonzero, the function fails with ``EINVAL``.
-+
-+.. c:macro:: NTSYNC_IOC_CREATE_EVENT
-+
-+ Create an event object. Takes a pointer to struct
-+ :c:type:`ntsync_event_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``event``
-+ - On output, contains the identifier of the created event.
-+ * - ``signaled``
-+ - If nonzero, the event is initially signaled, otherwise
-+ nonsignaled.
-+ * - ``manual``
-+ - If nonzero, the event is a manual-reset event, otherwise
-+ auto-reset.
-+
-+.. c:macro:: NTSYNC_IOC_DELETE
-+
-+ Delete an object of any type. Takes an input-only pointer to a
-+ 32-bit integer denoting the object to delete.
-+
-+ Wait ioctls currently in progress are not interrupted, and behave as
-+ if the object remains valid.
-+
-+.. c:macro:: NTSYNC_IOC_PUT_SEM
-+
-+ Post to a semaphore object. Takes a pointer to struct
-+ :c:type:`ntsync_sem_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``sem``
-+ - Semaphore object to post to.
-+ * - ``count``
-+ - Count to add to the semaphore. On output, contains the
-+ previous count of the semaphore.
-+ * - ``max``
-+ - Not used.
-+
-+ If adding ``count`` to the semaphore's current count would raise the
-+ latter past the semaphore's maximum count, the ioctl fails with
-+ ``EOVERFLOW`` and the semaphore is not affected. If raising the
-+ semaphore's count causes it to become signaled, eligible threads
-+ waiting on this semaphore will be woken and the semaphore's count
-+ decremented appropriately.
-+
-+.. c:macro:: NTSYNC_IOC_PUT_MUTEX
-+
-+ Release a mutex object. Takes a pointer to struct
-+ :c:type:`ntsync_mutex_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``mutex``
-+ - Mutex object to release.
-+ * - ``owner``
-+ - Mutex owner identifier.
-+ * - ``count``
-+ - On output, contains the previous recursion count.
-+
-+ If ``owner`` is zero, the ioctl fails with ``EINVAL``. If ``owner``
-+ is not the current owner of the mutex, the ioctl fails with
-+ ``EPERM``.
-+
-+ The mutex's count will be decremented by one. If decrementing the
-+ mutex's count causes it to become zero, the mutex is marked as
-+ unowned and signaled, and eligible threads waiting on it will be
-+ woken as appropriate.
-+
-+.. c:macro:: NTSYNC_IOC_SET_EVENT
-+
-+ Signal an event object. Takes a pointer to struct
-+ :c:type:`ntsync_event_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``event``
-+ - Event object to set.
-+ * - ``signaled``
-+ - On output, contains the previous state of the event.
-+ * - ``manual``
-+ - Unused.
-+
-+ Eligible threads will be woken, and auto-reset events will be
-+ designaled appropriately.
-+
-+.. c:macro:: NTSYNC_IOC_RESET_EVENT
-+
-+ Designal an event object. Takes a pointer to struct
-+ :c:type:`ntsync_event_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``event``
-+ - Event object to reset.
-+ * - ``signaled``
-+ - On output, contains the previous state of the event.
-+ * - ``manual``
-+ - Unused.
-+
-+.. c:macro:: NTSYNC_IOC_PULSE_EVENT
-+
-+ Wake threads waiting on an event object without leaving it in a
-+ signaled state. Takes a pointer to struct
-+ :c:type:`ntsync_event_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``event``
-+ - Event object to pulse.
-+ * - ``signaled``
-+ - On output, contains the previous state of the event.
-+ * - ``manual``
-+ - Unused.
-+
-+ A pulse operation can be thought of as a set followed by a reset,
-+ performed as a single atomic operation. If two threads are waiting
-+ on an auto-reset event which is pulsed, only one will be woken. If
-+ two threads are waiting a manual-reset event which is pulsed, both
-+ will be woken. However, in both cases, the event will be unsignaled
-+ afterwards, and a simultaneous read operation will always report the
-+ event as unsignaled.
-+
-+.. c:macro:: NTSYNC_IOC_READ_SEM
-+
-+ Read the current state of a semaphore object. Takes a pointer to
-+ struct :c:type:`ntsync_sem_args`, which is used as follows:
-+
-+ .. list-table::
-+
-+ * - ``sem``
-+ - Semaphore object to read.
-+ * - ``count``
-+ - On output, contains the current count of the semaphore.
-+ * - ``max``
-+ - On output, contains the maximum count of the semaphore.
-+
-+.. c:macro:: NTSYNC_IOC_READ_MUTEX
-+
-+ Read the current state of a mutex object. Takes a pointer to struct
-+ :c:type:`ntsync_mutex_args`, which is used as follows:
++ EXPECT_EQ(0, index);
+
-+ .. list-table::
++ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
++ EXPECT_EQ(0, ret);
++ EXPECT_EQ(2, index);
+
-+ * - ``mutex``
-+ - Mutex object to read.
-+ * - ``owner``
-+ - On output, contains the current owner of the mutex, or zero
-+ if the mutex is not currently owned.
-+ * - ``count``
-+ - On output, contains the current recursion count of the mutex.
++ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
++ EXPECT_EQ(-1, ret);
++ EXPECT_EQ(ETIMEDOUT, errno);
+
-+ If the mutex is marked as inconsistent, the function fails with
-+ ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
-+ zero.
++ close(event_args.event);
+
-+.. c:macro:: NTSYNC_IOC_READ_EVENT
++ close(objs[0]);
++ close(objs[1]);
+
-+ Read the current state of an event object. Takes a pointer to struct
-+ :c:type:`ntsync_event_args`, which is used as follows:
++ close(fd);
++}
+
-+ .. list-table::
++#define STRESS_LOOPS 10000
++#define STRESS_THREADS 4
+
-+ * - ``event``
-+ - Event object.
-+ * - ``signaled``
-+ - On output, contains the current state of the event.
-+ * - ``manual``
-+ - On output, contains 1 if the event is a manual-reset event,
-+ and 0 otherwise.
++static unsigned int stress_counter;
++static int stress_device, stress_start_event, stress_mutex;
+
-+.. c:macro:: NTSYNC_IOC_KILL_OWNER
++static void *stress_thread(void *arg)
++{
++ struct ntsync_wait_args wait_args = {0};
++ __u32 index, count, i;
++ int ret;
+
-+ Mark any mutexes owned by the given owner as unowned and
-+ inconsistent. Takes an input-only pointer to a 32-bit integer
-+ denoting the owner. If the owner is zero, the ioctl fails with
-+ ``EINVAL``.
++ wait_args.timeout = UINT64_MAX;
++ wait_args.count = 1;
++ wait_args.objs = (uintptr_t)&stress_start_event;
++ wait_args.owner = gettid();
++ wait_args.index = 0xdeadbeef;
+
-+ For each mutex currently owned by the given owner, eligible threads
-+ waiting on said mutex will be woken as appropriate (and such waits
-+ will fail with ``EOWNERDEAD``, as described below).
++ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
+
-+ The operation as a whole is not atomic; however, the modification of
-+ each mutex is atomic and totally ordered with respect to other
-+ operations on the same mutex.
++ wait_args.objs = (uintptr_t)&stress_mutex;
+
-+.. c:macro:: NTSYNC_IOC_WAIT_ANY
++ for (i = 0; i < STRESS_LOOPS; ++i) {
++ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
+
-+ Poll on any of a list of objects, atomically acquiring at most one.
-+ Takes a pointer to struct :c:type:`ntsync_wait_args`, which is
-+ used as follows:
++ ++stress_counter;
+
-+ .. list-table::
++ unlock_mutex(stress_mutex, wait_args.owner, &count);
++ }
+
-+ * - ``timeout``
-+ - Optional pointer to a 64-bit struct :c:type:`timespec`
-+ (specified as an integer so that the structure has the same
-+ size regardless of architecture). The timeout is specified in
-+ absolute format, as measured against the MONOTONIC clock. If
-+ the timeout is equal to or earlier than the current time, the
-+ function returns immediately without sleeping. If ``timeout``
-+ is zero, i.e. NULL, the function will sleep until an object
-+ is signaled, and will not fail with ``ETIMEDOUT``.
-+ * - ``objs``
-+ - Pointer to an array of ``count`` 32-bit object identifiers
-+ (specified as an integer so that the structure has the same
-+ size regardless of architecture). If any identifier is
-+ invalid, the function fails with ``EINVAL``.
-+ * - ``count``
-+ - Number of object identifiers specified in the ``objs`` array.
-+ If greater than ``NTSYNC_MAX_WAIT_COUNT``, the function fails
-+ with ``EINVAL``.
-+ * - ``owner``
-+ - Mutex owner identifier. If any object in ``objs`` is a mutex,
-+ the ioctl will attempt to acquire that mutex on behalf of
-+ ``owner``. If ``owner`` is zero, the ioctl fails with
-+ ``EINVAL``.
-+ * - ``index``
-+ - On success, contains the index (into ``objs``) of the object
-+ which was signaled. If ``alert`` was signaled instead,
-+ this contains ``count``.
-+ * - ``alert``
-+ - Optional event object identifier. If nonzero, this specifies
-+ an "alert" event object which, if signaled, will terminate
-+ the wait. If nonzero, the identifier must point to a valid
-+ event.
-+
-+ This function attempts to acquire one of the given objects. If
-+ unable to do so, it sleeps until an object becomes signaled,
-+ subsequently acquiring it, or the timeout expires. In the latter
-+ case the ioctl fails with ``ETIMEDOUT``. The function only acquires
-+ one object, even if multiple objects are signaled.
-+
-+ A semaphore is considered to be signaled if its count is nonzero,
-+ and is acquired by decrementing its count by one. A mutex is
-+ considered to be signaled if it is unowned or if its owner matches
-+ the ``owner`` argument, and is acquired by incrementing its
-+ recursion count by one and setting its owner to the ``owner``
-+ argument. An auto-reset event is acquired by designaling it; a
-+ manual-reset event is not affected by acquisition.
++ return NULL;
++}
+
-+ Acquisition is atomic and totally ordered with respect to other
-+ operations on the same object. If two wait operations (with
-+ different ``owner`` identifiers) are queued on the same mutex, only
-+ one is signaled. If two wait operations are queued on the same
-+ semaphore, and a value of one is posted to it, only one is signaled.
-+ The order in which threads are signaled is not specified.
++TEST(stress_wait)
++{
++ struct ntsync_event_args event_args;
++ struct ntsync_mutex_args mutex_args;
++ pthread_t threads[STRESS_THREADS];
++ __u32 signaled, i;
++ int ret;
+
-+ If an inconsistent mutex is acquired, the ioctl fails with
-+ ``EOWNERDEAD``. Although this is a failure return, the function may
-+ otherwise be considered successful. The mutex is marked as owned by
-+ the given owner (with a recursion count of 1) and as no longer
-+ inconsistent, and ``index`` is still set to the index of the mutex.
++ stress_device = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
++ ASSERT_LE(0, stress_device);
+
-+ The ``alert`` argument is an "extra" event which can terminate the
-+ wait, independently of all other objects. If members of ``objs`` and
-+ ``alert`` are both simultaneously signaled, a member of ``objs``
-+ will always be given priority and acquired first. Aside from this,
-+ for "any" waits, there is no difference between passing an event as
-+ this parameter, and passing it as an additional object at the end of
-+ the ``objs`` array. For "all" waits, there is an additional
-+ difference, as described below.
++ mutex_args.owner = 0;
++ mutex_args.count = 0;
++ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
++ EXPECT_EQ(0, ret);
++ stress_mutex = mutex_args.mutex;
+
-+ It is valid to pass the same object more than once, including by
-+ passing the same event in the ``objs`` array and in ``alert``. If a
-+ wakeup occurs due to that object being signaled, ``index`` is set to
-+ the lowest index corresponding to that object.
++ event_args.manual = 1;
++ event_args.signaled = 0;
++ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_EVENT, &event_args);
++ EXPECT_EQ(0, ret);
++ stress_start_event = event_args.event;
+
-+ The function may fail with ``EINTR`` if a signal is received.
++ for (i = 0; i < STRESS_THREADS; ++i)
++ pthread_create(&threads[i], NULL, stress_thread, NULL);
+
-+.. c:macro:: NTSYNC_IOC_WAIT_ALL
++ ret = ioctl(stress_start_event, NTSYNC_IOC_EVENT_SET, &signaled);
++ EXPECT_EQ(0, ret);
+
-+ Poll on a list of objects, atomically acquiring all of them. Takes a
-+ pointer to struct :c:type:`ntsync_wait_args`, which is used
-+ identically to ``NTSYNC_IOC_WAIT_ANY``, except that ``index`` is
-+ always filled with zero on success if not woken via alert.
++ for (i = 0; i < STRESS_THREADS; ++i) {
++ ret = pthread_join(threads[i], NULL);
++ EXPECT_EQ(0, ret);
++ }
+
-+ This function attempts to simultaneously acquire all of the given
-+ objects. If unable to do so, it sleeps until all objects become
-+ simultaneously signaled, subsequently acquiring them, or the timeout
-+ expires. In the latter case the ioctl fails with ``ETIMEDOUT`` and
-+ no objects are modified.
++ EXPECT_EQ(STRESS_LOOPS * STRESS_THREADS, stress_counter);
+
-+ Objects may become signaled and subsequently designaled (through
-+ acquisition by other threads) while this thread is sleeping. Only
-+ once all objects are simultaneously signaled does the ioctl acquire
-+ them and return. The entire acquisition is atomic and totally
-+ ordered with respect to other operations on any of the given
-+ objects.
-+
-+ If an inconsistent mutex is acquired, the ioctl fails with
-+ ``EOWNERDEAD``. Similarly to ``NTSYNC_IOC_WAIT_ANY``, all objects
-+ are nevertheless marked as acquired. Note that if multiple mutex
-+ objects are specified, there is no way to know which were marked as
-+ inconsistent.
-+
-+ As with "any" waits, the ``alert`` argument is an "extra" event
-+ which can terminate the wait. Critically, however, an "all" wait
-+ will succeed if all members in ``objs`` are signaled, *or* if
-+ ``alert`` is signaled. In the latter case ``index`` will be set to
-+ ``count``. As with "any" waits, if both conditions are filled, the
-+ former takes priority, and objects in ``objs`` will be acquired.
++ close(stress_start_event);
++ close(stress_mutex);
++ close(stress_device);
++}
+
-+ Unlike ``NTSYNC_IOC_WAIT_ANY``, it is not valid to pass the same
-+ object more than once, nor is it valid to pass the same object in
-+ ``objs`` and in ``alert``. If this is attempted, the function fails
-+ with ``EINVAL``.
---
-2.43.0
-
++TEST_HARNESS_MAIN