diff options
author | Jan200101 <sentrycraft123@gmail.com> | 2024-04-06 17:05:32 +0200 |
---|---|---|
committer | Jan200101 <sentrycraft123@gmail.com> | 2024-04-06 17:05:32 +0200 |
commit | 684f5ef56790771b425c7007c9dfcfbd8ea1a300 (patch) | |
tree | 118298a92be197f4a59d03cf2a5b447cbe9b9354 /SOURCES/0001-ntsync.patch | |
parent | c0c9b770e4f24e17886587762c42194b6e524720 (diff) | |
download | kernel-fsync-684f5ef56790771b425c7007c9dfcfbd8ea1a300.tar.gz kernel-fsync-684f5ef56790771b425c7007c9dfcfbd8ea1a300.zip |
kernel 6.8.2
Diffstat (limited to 'SOURCES/0001-ntsync.patch')
-rw-r--r-- | SOURCES/0001-ntsync.patch | 5600 |
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 |