summaryrefslogtreecommitdiff
path: root/SOURCES/winesync.patch
diff options
context:
space:
mode:
Diffstat (limited to 'SOURCES/winesync.patch')
-rw-r--r--SOURCES/winesync.patch4176
1 files changed, 2712 insertions, 1464 deletions
diff --git a/SOURCES/winesync.patch b/SOURCES/winesync.patch
index e9ad829..f62b102 100644
--- a/SOURCES/winesync.patch
+++ b/SOURCES/winesync.patch
@@ -1,3 +1,134 @@
+From b99219c187fa5933d0507b1ce67d33cf1e42be6a Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 10:50:45 -0600
+Subject: [PATCH 01/25] winesync: Introduce the winesync driver and character
+ device.
+
+---
+ drivers/misc/Kconfig | 11 +++++++
+ drivers/misc/Makefile | 1 +
+ drivers/misc/winesync.c | 64 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 76 insertions(+)
+ create mode 100644 drivers/misc/winesync.c
+
+diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
+index 0f5a49fc7c9e..e21e4424d6a2 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -470,6 +470,17 @@ config HISI_HIKEY_USB
+ switching between the dual-role USB-C port and the USB-A host ports
+ using only one USB controller.
+
++config WINESYNC
++ tristate "Synchronization primitives for Wine"
++ help
++ This module provides kernel support for synchronization primitives
++ used by Wine. It is not a hardware driver.
++
++ To compile this driver as a module, choose M here: the
++ module will be called winesync.
++
++ If unsure, say N.
++
+ source "drivers/misc/c2port/Kconfig"
+ source "drivers/misc/eeprom/Kconfig"
+ source "drivers/misc/cb710/Kconfig"
+diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
+index a086197af544..1fb39bc4637b 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -58,4 +58,5 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/
+ obj-$(CONFIG_UACCE) += uacce/
+ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
+ obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
++obj-$(CONFIG_WINESYNC) += winesync.o
+ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+new file mode 100644
+index 000000000000..111f33c5676e
+--- /dev/null
++++ b/drivers/misc/winesync.c
+@@ -0,0 +1,64 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * winesync.c - Kernel driver for Wine synchronization primitives
++ *
++ * Copyright (C) 2021 Zebediah Figura
++ */
++
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++
++#define WINESYNC_NAME "winesync"
++
++static int winesync_char_open(struct inode *inode, struct file *file)
++{
++ return nonseekable_open(inode, file);
++}
++
++static int winesync_char_release(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++static long winesync_char_ioctl(struct file *file, unsigned int cmd,
++ unsigned long parm)
++{
++ switch (cmd) {
++ default:
++ return -ENOSYS;
++ }
++}
++
++static const struct file_operations winesync_fops = {
++ .owner = THIS_MODULE,
++ .open = winesync_char_open,
++ .release = winesync_char_release,
++ .unlocked_ioctl = winesync_char_ioctl,
++ .compat_ioctl = winesync_char_ioctl,
++ .llseek = no_llseek,
++};
++
++static struct miscdevice winesync_misc = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = WINESYNC_NAME,
++ .fops = &winesync_fops,
++};
++
++static int __init winesync_init(void)
++{
++ return misc_register(&winesync_misc);
++}
++
++static void __exit winesync_exit(void)
++{
++ misc_deregister(&winesync_misc);
++}
++
++module_init(winesync_init);
++module_exit(winesync_exit);
++
++MODULE_AUTHOR("Zebediah Figura");
++MODULE_DESCRIPTION("Kernel driver for Wine synchronization primitives");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("devname:" WINESYNC_NAME);
+--
+2.34.1
+
+From 0580c3831216d8795661f7863e57555096d0ab67 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 10:57:06 -0600
+Subject: [PATCH 02/25] winesync: Reserve a minor device number and ioctl
+ range.
+
+---
+ Documentation/admin-guide/devices.txt | 3 ++-
+ Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++
+ drivers/misc/winesync.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 922c23bb4372..ae39732318a7 100644
--- a/Documentation/admin-guide/devices.txt
@@ -13,18 +144,6 @@ index 922c23bb4372..ae39732318a7 100644
255 Reserved for MISC_DYNAMIC_MINOR
11 char Raw keyboard device (Linux/SPARC only)
-diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
-index c432be070f67..fde565a8005c 100644
---- a/Documentation/userspace-api/index.rst
-+++ b/Documentation/userspace-api/index.rst
-@@ -28,6 +28,7 @@ place where this information is gathered.
- media/index
- sysfs-platform_profile
- vduse
-+ winesync
-
- .. only:: subproject and html
-
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 6655d929a351..9d5f1f87c2ee 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -38,473 +157,700 @@ index 6655d929a351..9d5f1f87c2ee 100644
0xFD all linux/dm-ioctl.h
0xFE all linux/isst_if.h
==== ===== ======================================================= ================================================================
-diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst
-new file mode 100644
-index 000000000000..751c70f1ffce
---- /dev/null
-+++ b/Documentation/userspace-api/winesync.rst
-@@ -0,0 +1,373 @@
-+=====================================
-+Wine synchronization primitive driver
-+=====================================
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 111f33c5676e..85cb6ccaa077 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -40,7 +40,7 @@ static const struct file_operations winesync_fops = {
+ };
+
+ static struct miscdevice winesync_misc = {
+- .minor = MISC_DYNAMIC_MINOR,
++ .minor = WINESYNC_MINOR,
+ .name = WINESYNC_NAME,
+ .fops = &winesync_fops,
+ };
+@@ -62,3 +62,4 @@ MODULE_AUTHOR("Zebediah Figura");
+ MODULE_DESCRIPTION("Kernel driver for Wine synchronization primitives");
+ MODULE_LICENSE("GPL");
+ MODULE_ALIAS("devname:" WINESYNC_NAME);
++MODULE_ALIAS_MISCDEV(WINESYNC_MINOR);
+diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
+index 0676f18093f9..350aecfcfb29 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 WINESYNC_MINOR 243
+ #define MISC_DYNAMIC_MINOR 255
+
+ struct device;
+--
+2.34.1
+
+From 67252a879ef5e0585d5be13182d31718c59d8947 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:15:39 -0600
+Subject: [PATCH 03/25] winesync: Introduce WINESYNC_IOC_CREATE_SEM and
+ WINESYNC_IOC_DELETE.
+
+---
+ drivers/misc/winesync.c | 117 ++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 25 ++++++++
+ 2 files changed, 142 insertions(+)
+ create mode 100644 include/uapi/linux/winesync.h
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 85cb6ccaa077..36e31bbe0390 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.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/winesync.h>
+
+ #define WINESYNC_NAME "winesync"
+
++enum winesync_type {
++ WINESYNC_TYPE_SEM,
++};
+
-+This page documents the user-space API for the winesync driver.
++struct winesync_obj {
++ struct rcu_head rhead;
++ struct kref refcount;
+
-+winesync is a support driver for emulation of NT synchronization
-+primitives by the Wine project. It exists because implementation in
-+user-space, using existing tools, cannot satisfy performance,
-+correctness, and security constraints. It is implemented entirely in
-+software, and does not drive any hardware device.
++ enum winesync_type type;
+
-+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).
++ union {
++ struct {
++ __u32 count;
++ __u32 max;
++ } sem;
++ } u;
++};
+
-+Synchronization primitives
-+==========================
++struct winesync_device {
++ struct xarray objects;
++};
+
-+The winesync driver exposes two types of synchronization primitives,
-+semaphores and mutexes.
++static void destroy_obj(struct kref *ref)
++{
++ struct winesync_obj *obj = container_of(ref, struct winesync_obj, refcount);
+
-+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.
++ kfree_rcu(obj, rhead);
++}
+
-+A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit
-+identifier denoting its owner. The latter is intended to identify the
-+thread holding the mutex; however, it is not actually validated
-+against earlier calls made by the same thread. 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
-+``WINESYNC_IOC_KILL_OWNER``.
-+
-+Objects are represented by signed 32-bit integers. A valid object
-+identifier will always be nonnegative.
++static void put_obj(struct winesync_obj *obj)
++{
++ kref_put(&obj->refcount, destroy_obj);
++}
+
-+Char device
-+===========
+ static int winesync_char_open(struct inode *inode, struct file *file)
+ {
++ struct winesync_device *dev;
+
-+The winesync driver creates a single char device /dev/winesync. 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.
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
+
-+ioctl reference
-+===============
++ xa_init_flags(&dev->objects, XA_FLAGS_ALLOC);
+
-+All operations on the device are done through ioctls. There are three
-+structures used in ioctl calls::
++ file->private_data = dev;
+ return nonseekable_open(inode, file);
+ }
+
+ static int winesync_char_release(struct inode *inode, struct file *file)
+ {
++ struct winesync_device *dev = file->private_data;
++ struct winesync_obj *obj;
++ unsigned long id;
+
-+ struct winesync_sem_args {
-+ __s32 sem;
-+ __u32 count;
-+ __u32 max;
-+ __u32 flags;
-+ };
++ xa_for_each(&dev->objects, id, obj)
++ put_obj(obj);
+
-+ struct winesync_mutex_args {
-+ __s32 mutex;
-+ __u32 owner;
-+ __u32 count;
-+ };
++ xa_destroy(&dev->objects);
+
-+ struct winesync_wait_args {
-+ __u64 timeout;
-+ __u64 objs;
-+ __u32 count;
-+ __u32 owner;
-+ __u32 index;
-+ __u32 pad;
-+ };
++ kfree(dev);
+
-+Depending on the ioctl, members of the structure may be used as input,
-+output, or not at all.
++ return 0;
++}
+
-+All ioctls return 0 on success, and -1 on error, in which case `errno`
-+will be set to a nonzero error code.
++static void init_obj(struct winesync_obj *obj)
++{
++ kref_init(&obj->refcount);
++}
+
-+The ioctls are as follows:
++static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
++{
++ struct winesync_sem_args __user *user_args = argp;
++ struct winesync_sem_args args;
++ struct winesync_obj *sem;
++ __u32 id;
++ int ret;
+
-+.. c:macro:: WINESYNC_IOC_CREATE_SEM
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
+
-+ Create a semaphore object. Takes a pointer to struct
-+ :c:type:`winesync_sem_args`, which is used as follows:
++ if (args.count > args.max)
++ return -EINVAL;
+
-+ ``count`` and ``max`` are input-only arguments, denoting the
-+ initial and maximum count of the semaphore.
++ sem = kzalloc(sizeof(*sem), GFP_KERNEL);
++ if (!sem)
++ return -ENOMEM;
+
-+ ``flags`` is an input-only argument, which specifies additional
-+ flags modifying the behaviour of the semaphore. There is only one
-+ flag defined, ``WINESYNC_SEM_GETONWAIT``. If present, wait
-+ operations on this semaphore will acquire it, decrementing its
-+ count by one; otherwise, wait operations will not affect the
-+ semaphore's state.
++ init_obj(sem);
++ sem->type = WINESYNC_TYPE_SEM;
++ sem->u.sem.count = args.count;
++ sem->u.sem.max = args.max;
+
-+ ``sem`` is an output-only argument, which will be filled with the
-+ allocated identifier if successful.
++ ret = xa_alloc(&dev->objects, &id, sem, xa_limit_32b, GFP_KERNEL);
++ if (ret < 0) {
++ kfree(sem);
++ return ret;
++ }
+
-+ Fails with ``EINVAL`` if ``count`` is greater than ``max``, or
-+ ``ENOMEM`` if not enough memory is available.
++ return put_user(id, &user_args->sem);
++}
+
-+.. c:macro:: WINESYNC_IOC_CREATE_MUTEX
++static int winesync_delete(struct winesync_device *dev, void __user *argp)
++{
++ struct winesync_obj *obj;
++ __u32 id;
+
-+ Create a mutex object. Takes a pointer to struct
-+ :c:type:`winesync_mutex_args`, which is used as follows:
++ if (get_user(id, (__u32 __user *)argp))
++ return -EFAULT;
+
-+ ``owner`` is an input-only argument denoting the initial owner of
-+ the mutex.
++ obj = xa_erase(&dev->objects, id);
++ if (!obj)
++ return -EINVAL;
+
-+ ``count`` is an input-only argument denoting the initial recursion
-+ count 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``.
++ put_obj(obj);
+ return 0;
+ }
+
+ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+ {
++ struct winesync_device *dev = file->private_data;
++ void __user *argp = (void __user *)parm;
+
-+ ``mutex`` is an output-only argument, which will be filled with
-+ the allocated identifier if successful.
+ switch (cmd) {
++ case WINESYNC_IOC_CREATE_SEM:
++ return winesync_create_sem(dev, argp);
++ case WINESYNC_IOC_DELETE:
++ return winesync_delete(dev, argp);
+ default:
+ return -ENOSYS;
+ }
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+new file mode 100644
+index 000000000000..aabb491f39d2
+--- /dev/null
++++ b/include/uapi/linux/winesync.h
+@@ -0,0 +1,25 @@
++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
++/*
++ * Kernel support for Wine synchronization primitives
++ *
++ * Copyright (C) 2021 Zebediah Figura
++ */
+
-+ Fails with ``ENOMEM`` if not enough memory is available.
++#ifndef __LINUX_WINESYNC_H
++#define __LINUX_WINESYNC_H
+
-+.. c:macro:: WINESYNC_IOC_DELETE
++#include <linux/types.h>
+
-+ Delete an object of any type. Takes an input-only pointer to a
-+ 32-bit integer denoting the object to delete. Fails with ``EINVAL``
-+ if the object is not valid. Further ioctls attempting to use the
-+ object return ``EINVAL``, unless the object identifier is reused.
-+ However, wait ioctls currently in progress are not interrupted, and
-+ behave as if the object remains valid.
++struct winesync_sem_args {
++ __u32 sem;
++ __u32 count;
++ __u32 max;
++};
+
-+.. c:macro:: WINESYNC_IOC_PUT_SEM
++#define WINESYNC_IOC_BASE 0xf7
+
-+ Post to a semaphore object. Takes a pointer to struct
-+ :c:type:`winesync_sem_args`, which is used as follows:
++#define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \
++ struct winesync_sem_args)
++#define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 1, __u32)
+
-+ ``sem`` is an input-only argument denoting the semaphore object.
-+ If ``sem`` is not a valid semaphore object, the ioctl fails with
-+ ``EINVAL``.
++#endif
+--
+2.34.1
+
+From be751be4f73c0b574c50789e0cfc2e9100d0e124 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:22:42 -0600
+Subject: [PATCH 04/25] winesync: Introduce WINESYNC_PUT_SEM.
+
+---
+ drivers/misc/winesync.c | 68 +++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 2 ++
+ 2 files changed, 70 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 36e31bbe0390..2f048a39e4eb 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -21,9 +21,11 @@ enum winesync_type {
+ struct winesync_obj {
+ struct rcu_head rhead;
+ struct kref refcount;
++ spinlock_t lock;
+
+ enum winesync_type type;
+
++ /* The following fields are protected by the object lock. */
+ union {
+ struct {
+ __u32 count;
+@@ -36,6 +38,19 @@ struct winesync_device {
+ struct xarray objects;
+ };
+
++static struct winesync_obj *get_obj(struct winesync_device *dev, __u32 id)
++{
++ struct winesync_obj *obj;
+
-+ ``count`` contains on input the count to add to the semaphore, and
-+ on output is filled with its previous count.
++ rcu_read_lock();
++ obj = xa_load(&dev->objects, id);
++ if (obj && !kref_get_unless_zero(&obj->refcount))
++ obj = NULL;
++ rcu_read_unlock();
+
-+ ``max`` and ``flags`` are not used.
++ return obj;
++}
+
-+ The operation is atomic and totally ordered with respect to other
-+ operations on the same semaphore. 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.
+ static void destroy_obj(struct kref *ref)
+ {
+ struct winesync_obj *obj = container_of(ref, struct winesync_obj, refcount);
+@@ -81,6 +96,7 @@ static int winesync_char_release(struct inode *inode, struct file *file)
+ static void init_obj(struct winesync_obj *obj)
+ {
+ kref_init(&obj->refcount);
++ spin_lock_init(&obj->lock);
+ }
+
+ static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
+@@ -131,6 +147,56 @@ static int winesync_delete(struct winesync_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 winesync_obj *sem, __u32 count)
++{
++ lockdep_assert_held(&sem->lock);
+
-+.. c:macro:: WINESYNC_IOC_PULSE_SEM
++ if (sem->u.sem.count + count < sem->u.sem.count ||
++ sem->u.sem.count + count > sem->u.sem.max)
++ return -EOVERFLOW;
+
-+ This operation is identical to ``WINESYNC_IOC_PUT_SEM``, with one
-+ notable exception: the semaphore is always left in an *unsignaled*
-+ state, regardless of the initial count or the count added by the
-+ ioctl. That is, the count after a pulse operation will always be
-+ zero. The entire operation is atomic.
-+
-+ Hence, if the semaphore was created with the
-+ ``WINESYNC_SEM_GETONWAIT`` flag set, and an unsignaled semaphore is
-+ "pulsed" with a count of 2, at most two eligible threads (i.e.
-+ threads not otherwise constrained due to ``WINESYNC_IOC_WAIT_ALL``)
-+ will be woken up, and any others will remain sleeping. If less than
-+ two eligible threads are waiting on the semaphore, all of them will
-+ be woken up, and the semaphore's count will remain at zero. On the
-+ other hand, if the semaphore was created without the
-+ ``WINESYNC_SEM_GETONWAIT``, all eligible threads will be woken up,
-+ making ``count`` effectively redundant. In either case, a
-+ simultaneous ``WINESYNC_IOC_READ_SEM`` ioctl from another thread
-+ will always report a count of zero.
++ sem->u.sem.count += count;
++ return 0;
++}
+
-+ If adding ``count`` to the semaphore's current count would raise the
-+ latter past the semaphore's maximum count, the ioctl fails with
-+ ``EOVERFLOW``. However, in this case the semaphore's count will
-+ still be reset to zero.
++static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
++{
++ struct winesync_sem_args __user *user_args = argp;
++ struct winesync_sem_args args;
++ struct winesync_obj *sem;
++ __u32 prev_count;
++ int ret;
+
-+.. c:macro:: WINESYNC_IOC_GET_SEM
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
+
-+ Attempt to acquire a semaphore object. Takes an input-only pointer
-+ to a 32-bit integer denoting the semaphore to acquire.
++ sem = get_obj(dev, args.sem);
++ if (!sem)
++ return -EINVAL;
++ if (sem->type != WINESYNC_TYPE_SEM) {
++ put_obj(sem);
++ return -EINVAL;
++ }
+
-+ This operation does not block. If the semaphore's count was zero, it
-+ fails with ``EWOULDBLOCK``. Otherwise, the semaphore's count is
-+ decremented by one. The behaviour of this operation is unaffected by
-+ whether the semaphore was created with the
-+ ``WINESYNC_SEM_GETONWAIT`` flag set.
++ spin_lock(&sem->lock);
+
-+ The operation is atomic and totally ordered with respect to other
-+ operations on the same semaphore.
++ prev_count = sem->u.sem.count;
++ ret = put_sem_state(sem, args.count);
+
-+.. c:macro:: WINESYNC_IOC_PUT_MUTEX
++ spin_unlock(&sem->lock);
+
-+ Release a mutex object. Takes a pointer to struct
-+ :c:type:`winesync_mutex_args`, which is used as follows:
++ put_obj(sem);
+
-+ ``mutex`` is an input-only argument denoting the mutex object. If
-+ ``mutex`` is not a valid mutex object, the ioctl fails with
-+ ``EINVAL``.
++ if (!ret && put_user(prev_count, &user_args->count))
++ ret = -EFAULT;
+
-+ ``owner`` is an input-only argument denoting the mutex owner.
-+ ``owner`` must be nonzero, else the ioctl fails with ``EINVAL``.
-+ If ``owner`` is not the current owner of the mutex, the ioctl
-+ fails with ``EPERM``.
++ return ret;
++}
+
-+ ``count`` is an output-only argument which will be filled on
-+ success with the mutex's previous recursion count.
+ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+ {
+@@ -142,6 +208,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_create_sem(dev, argp);
+ case WINESYNC_IOC_DELETE:
+ return winesync_delete(dev, argp);
++ case WINESYNC_IOC_PUT_SEM:
++ return winesync_put_sem(dev, argp);
+ default:
+ return -ENOSYS;
+ }
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index aabb491f39d2..7681a168eb92 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -21,5 +21,7 @@ struct winesync_sem_args {
+ #define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \
+ struct winesync_sem_args)
+ #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 1, __u32)
++#define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 2, \
++ struct winesync_sem_args)
+
+ #endif
+--
+2.34.1
+
+From c5327f5ecdcb94c6ada71c036a0be5accee390dc Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:31:44 -0600
+Subject: [PATCH 05/25] winesync: Introduce WINESYNC_IOC_WAIT_ANY.
+
+---
+ drivers/misc/winesync.c | 225 ++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 11 ++
+ 2 files changed, 236 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 2f048a39e4eb..e74dba90d525 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -23,6 +23,8 @@ struct winesync_obj {
+ struct kref refcount;
+ spinlock_t lock;
+
++ struct list_head any_waiters;
+
-+ The mutex's count will be decremented by one. The operation is
-+ atomic and totally ordered with respect to other operations on the
-+ same mutex. 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 winesync_type type;
+
+ /* The following fields are protected by the object lock. */
+@@ -34,6 +36,28 @@ struct winesync_obj {
+ } u;
+ };
+
++struct winesync_q_entry {
++ struct list_head node;
++ struct winesync_q *q;
++ struct winesync_obj *obj;
++ __u32 index;
++};
+
-+.. c:macro:: WINESYNC_IOC_READ_SEM
++struct winesync_q {
++ struct task_struct *task;
++ __u32 owner;
+
-+ Read the current state of a semaphore object. Takes a pointer to
-+ struct :c:type:`winesync_sem_args`, which is used as follows:
++ /*
++ * 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;
+
-+ ``sem`` is an input-only argument denoting the semaphore object.
-+ If ``sem`` is not a valid semaphore object, the ioctl fails with
-+ ``EINVAL``.
++ __u32 count;
++ struct winesync_q_entry entries[];
++};
+
-+ ``count`` and ``max`` are output-only arguments, which will be
-+ filled with the current and maximum count of the given semaphore.
+ struct winesync_device {
+ struct xarray objects;
+ };
+@@ -97,6 +121,26 @@ static void init_obj(struct winesync_obj *obj)
+ {
+ kref_init(&obj->refcount);
+ spin_lock_init(&obj->lock);
++ INIT_LIST_HEAD(&obj->any_waiters);
++}
+
-+ ``flags`` is an output-only argument, which will be filled with
-+ the flags used to create the semaphore.
++static void try_wake_any_sem(struct winesync_obj *sem)
++{
++ struct winesync_q_entry *entry;
+
-+ The operation is atomic and totally ordered with respect to other
-+ operations on the same semaphore.
++ lockdep_assert_held(&sem->lock);
+
-+.. c:macro:: WINESYNC_IOC_READ_MUTEX
++ list_for_each_entry(entry, &sem->any_waiters, node) {
++ struct winesync_q *q = entry->q;
+
-+ Read the current state of a mutex object. Takes a pointer to struct
-+ :c:type:`winesync_mutex_args`, which is used as follows:
++ if (!sem->u.sem.count)
++ break;
+
-+ ``mutex`` is an input-only argument denoting the mutex object. If
-+ ``mutex`` is not a valid mutex object, the ioctl fails with
-+ ``EINVAL``.
++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
++ sem->u.sem.count--;
++ wake_up_process(q->task);
++ }
++ }
+ }
+
+ static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
+@@ -186,6 +230,8 @@ static int winesync_put_sem(struct winesync_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);
+
+@@ -197,6 +243,183 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
+ return ret;
+ }
+
++static int winesync_schedule(const struct winesync_q *q, ktime_t *timeout)
++{
++ int ret = 0;
+
-+ ``count`` and ``owner`` are output-only arguments, which will be
-+ filled with the current recursion count and owner of the given
-+ mutex. If the mutex is not owned, both ``count`` and ``owner`` are
-+ set to zero.
++ do {
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
+
-+ If the mutex is marked as inconsistent, the function fails with
-+ ``EOWNERDEAD``.
++ 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);
+
-+ The operation is atomic and totally ordered with respect to other
-+ operations on the same mutex.
++ return ret;
++}
+
-+.. c:macro:: WINESYNC_IOC_KILL_OWNER
++/*
++ * Allocate and initialize the winesync_q structure, but do not queue us yet.
++ * Also, calculate the relative timeout.
++ */
++static int setup_wait(struct winesync_device *dev,
++ const struct winesync_wait_args *args,
++ ktime_t *ret_timeout, struct winesync_q **ret_q)
++{
++ const __u32 count = args->count;
++ struct winesync_q *q;
++ ktime_t timeout = 0;
++ __u32 *ids;
++ __u32 i, j;
+
-+ Mark any mutexes owned by the given identifier 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``.
++ if (!args->owner)
++ return -EINVAL;
+
-+.. c:macro:: WINESYNC_IOC_WAIT_ANY
++ if (args->timeout) {
++ struct timespec64 to;
+
-+ Poll on any of a list of objects, atomically acquiring (at most)
-+ one. Takes a pointer to struct :c:type:`winesync_wait_args`, which
-+ is used as follows:
++ if (get_timespec64(&to, u64_to_user_ptr(args->timeout)))
++ return -EFAULT;
++ if (!timespec64_valid(&to))
++ return -EINVAL;
+
-+ ``timeout`` is an optional input-only 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``.
++ timeout = timespec64_to_ns(&to);
++ }
+
-+ ``objs`` is a input-only 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``.
++ ids = kmalloc_array(args->count, sizeof(*ids), GFP_KERNEL);
++ if (!ids)
++ return -ENOMEM;
++ if (copy_from_user(ids, u64_to_user_ptr(args->objs),
++ array_size(args->count, sizeof(*ids)))) {
++ kfree(ids);
++ return -EFAULT;
++ }
+
-+ ``count`` is an input-only argument denoting the number of
-+ elements in ``objs``.
++ 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;
+
-+ ``owner`` is an input-only argument denoting the 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``.
++ for (i = 0; i < count; i++) {
++ struct winesync_q_entry *entry = &q->entries[i];
++ struct winesync_obj *obj = get_obj(dev, ids[i]);
+
-+ ``index`` is an output-only argument which, if the ioctl is
-+ successful, is filled with the index of the object actually
-+ signaled.
++ if (!obj)
++ goto err;
+
-+ ``pad`` is unused, and exists to keep a consistent structure size.
++ entry->obj = obj;
++ entry->q = q;
++ entry->index = i;
++ }
+
-+ 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.
++ kfree(ids);
+
-+ A semaphore is considered to be signaled if its count is nonzero. It
-+ is acquired by decrementing its count by one if the
-+ ``WINESYNC_SEM_GETONWAIT`` flag was used to create it; otherwise no
-+ operation is done to acquire the semaphore. 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.
++ *ret_q = q;
++ *ret_timeout = timeout;
++ return 0;
+
-+ 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 (which was not created with the ``WINESYNC_SEM_GETONWAIT``
-+ flag set), and a value of one is posted to it, only one is signaled.
-+ The order in which threads are signaled is not guaranteed.
++err:
++ for (j = 0; j < i; j++)
++ put_obj(q->entries[j].obj);
++ kfree(ids);
++ kfree(q);
++ return -EINVAL;
++}
+
-+ (If two wait operations are queued on the same semaphore, and the
-+ semaphore was created with the ``WINESYNC_SEM_GETONWAIT`` flag set,
-+ and a value of one is posted to it, both threads are signaled, and
-+ the semaphore retains a count of one.)
++static void try_wake_any_obj(struct winesync_obj *obj)
++{
++ switch (obj->type) {
++ case WINESYNC_TYPE_SEM:
++ try_wake_any_sem(obj);
++ break;
++ }
++}
+
-+ 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. ``index`` is still set to the index of the mutex.
++static int winesync_wait_any(struct winesync_device *dev, void __user *argp)
++{
++ struct winesync_wait_args args;
++ struct winesync_q *q;
++ ktime_t timeout;
++ int signaled;
++ __u32 i;
++ int ret;
+
-+ Unlike ``WINESYNC_IOC_WAIT_ALL``, it is valid to pass the same
-+ object more than once. If a wakeup occurs due to that object being
-+ signaled, ``index`` is set to the index of the first instance of the
-+ object.
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
+
-+ Fails with ``ENOMEM`` if not enough memory is available, or
-+ ``EINTR`` if a signal is received.
++ ret = setup_wait(dev, &args, &timeout, &q);
++ if (ret < 0)
++ return ret;
+
-+.. c:macro:: WINESYNC_IOC_WAIT_ALL
++ /* queue ourselves */
+
-+ Poll on a list of objects, atomically acquiring all of them. Takes a
-+ pointer to struct :c:type:`winesync_wait_args`, which is used
-+ identically to ``WINESYNC_IOC_WAIT_ANY``, except that ``index`` is
-+ unused.
++ for (i = 0; i < args.count; i++) {
++ struct winesync_q_entry *entry = &q->entries[i];
++ struct winesync_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.
++ spin_lock(&obj->lock);
++ list_add_tail(&entry->node, &obj->any_waiters);
++ spin_unlock(&obj->lock);
++ }
+
-+ 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 return.
-+ The acquisition is atomic and totally ordered with respect to other
-+ operations on any of the given objects.
++ /* check if we are already signaled */
+
-+ If an inconsistent mutex is acquired, the ioctl fails with
-+ ``EOWNERDEAD``. Similarly to ``WINESYNC_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.
++ for (i = 0; i < args.count; i++) {
++ struct winesync_obj *obj = q->entries[i].obj;
+
-+ Unlike ``WINESYNC_IOC_WAIT_ALL``, it is not valid to pass the same
-+ object more than once. If this is attempted, the function fails with
-+ ``EINVAL``.
++ if (atomic_read(&q->signaled) != -1)
++ break;
+
-+ Fails with ``ENOMEM`` if not enough memory is available, or
-+ ``EINTR`` if a signal is received.
-diff --git a/MAINTAINERS b/MAINTAINERS
-index 3b79fd441dde..0dfd74b56fbe 100644
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -20227,6 +20227,15 @@ M: David Härdeman <david@hardeman.nu>
- S: Maintained
- F: drivers/media/rc/winbond-cir.c
-
-+WINESYNC SYNCHRONIZATION PRIMITIVE DRIVER
-+M: Zebediah Figura <zfigura@codeweavers.com>
-+L: wine-devel@winehq.org
-+S: Supported
-+F: Documentation/userspace-api/winesync.rst
-+F: drivers/misc/winesync.c
-+F: include/uapi/linux/winesync.c
-+F: tools/testing/selftests/drivers/winesync/
++ spin_lock(&obj->lock);
++ try_wake_any_obj(obj);
++ spin_unlock(&obj->lock);
++ }
+
- WINSYSTEMS EBC-C384 WATCHDOG DRIVER
- M: William Breathitt Gray <vilhelm.gray@gmail.com>
- L: linux-watchdog@vger.kernel.org
-diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
-index 0f5a49fc7c9e..e21e4424d6a2 100644
---- a/drivers/misc/Kconfig
-+++ b/drivers/misc/Kconfig
-@@ -470,6 +470,17 @@ config HISI_HIKEY_USB
- switching between the dual-role USB-C port and the USB-A host ports
- using only one USB controller.
-
-+config WINESYNC
-+ tristate "Synchronization primitives for Wine"
-+ help
-+ This module provides kernel support for synchronization primitives
-+ used by Wine. It is not a hardware driver.
++ /* sleep */
+
-+ To compile this driver as a module, choose M here: the
-+ module will be called winesync.
++ ret = winesync_schedule(q, args.timeout ? &timeout : NULL);
+
-+ If unsure, say N.
++ /* and finally, unqueue */
+
- source "drivers/misc/c2port/Kconfig"
- source "drivers/misc/eeprom/Kconfig"
- source "drivers/misc/cb710/Kconfig"
-diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
-index a086197af544..0255d6a05093 100644
---- a/drivers/misc/Makefile
-+++ b/drivers/misc/Makefile
-@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/
- obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
- obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
- obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
-+obj-$(CONFIG_WINESYNC) += winesync.o
-diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
-new file mode 100644
-index 000000000000..ff5749206aa9
---- /dev/null
-+++ b/drivers/misc/winesync.c
-@@ -0,0 +1,1047 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * winesync.c - Kernel driver for Wine synchronization primitives
-+ *
-+ * Copyright (C) 2021 Zebediah Figura
-+ */
++ for (i = 0; i < args.count; i++) {
++ struct winesync_obj *obj = q->entries[i].obj;
+
-+#include <linux/fs.h>
-+#include <linux/idr.h>
-+#include <linux/miscdevice.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/slab.h>
-+#include <uapi/linux/winesync.h>
++ spin_lock(&obj->lock);
++ list_del(&q->entries[i].node);
++ spin_unlock(&obj->lock);
+
-+#define WINESYNC_NAME "winesync"
++ put_obj(obj);
++ }
+
-+enum winesync_type {
-+ WINESYNC_TYPE_SEM,
-+ WINESYNC_TYPE_MUTEX,
-+};
++ signaled = atomic_read(&q->signaled);
++ if (signaled != -1) {
++ struct winesync_wait_args __user *user_args = argp;
+
-+struct winesync_obj {
-+ struct kref refcount;
-+ spinlock_t lock;
++ /* even if we caught a signal, we need to communicate success */
++ ret = 0;
+
++ if (put_user(signaled, &user_args->index))
++ ret = -EFAULT;
++ } else if (!ret) {
++ ret = -ETIMEDOUT;
++ }
++
++ kfree(q);
++ return ret;
++}
++
+ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+ {
+@@ -210,6 +433,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_delete(dev, argp);
+ case WINESYNC_IOC_PUT_SEM:
+ return winesync_put_sem(dev, argp);
++ case WINESYNC_IOC_WAIT_ANY:
++ return winesync_wait_any(dev, argp);
+ default:
+ return -ENOSYS;
+ }
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 7681a168eb92..f57ebfbe1dd9 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -16,6 +16,15 @@ struct winesync_sem_args {
+ __u32 max;
+ };
+
++struct winesync_wait_args {
++ __u64 timeout;
++ __u64 objs;
++ __u32 count;
++ __u32 owner;
++ __u32 index;
++ __u32 pad;
++};
++
+ #define WINESYNC_IOC_BASE 0xf7
+
+ #define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \
+@@ -23,5 +32,7 @@ struct winesync_sem_args {
+ #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 1, __u32)
+ #define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 2, \
+ struct winesync_sem_args)
++#define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 3, \
++ struct winesync_wait_args)
+
+ #endif
+--
+2.34.1
+
+From 1b56ce9253a1dce2f63252e3833a98da353eeb31 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:36:09 -0600
+Subject: [PATCH 06/25] winesync: Introduce WINESYNC_IOC_WAIT_ALL.
+
+---
+ drivers/misc/winesync.c | 242 ++++++++++++++++++++++++++++++++--
+ include/uapi/linux/winesync.h | 2 +
+ 2 files changed, 236 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index e74dba90d525..a0ee4536165e 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -23,7 +23,34 @@ struct winesync_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;
+
+ /*
@@ -517,62 +863,30 @@ index 000000000000..ff5749206aa9
+ * 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 one, and, if it is, we skip trying
++ * first check whether all_hint is zero, and, if it is, we skip trying
+ * to wake "all" waiters.
+ *
-+ * This "refcount" 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.
++ * 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.
+ *
-+ * Use refcount_t rather than atomic_t to take advantage of saturation.
-+ * This does mean that the "no waiters" case is signified by all_hint
-+ * being one, rather than zero (otherwise we would get spurious
-+ * warnings).
++ * Since wait requests must originate from user-space threads, we're
++ * limited here by PID_MAX_LIMIT, so there's no risk of saturation.
+ */
-+ refcount_t all_hint;
-+
-+ enum winesync_type type;
-+
-+ /* The following fields are protected by the object lock. */
-+ union {
-+ struct {
-+ __u32 count;
-+ __u32 max;
-+ __u32 flags;
-+ } sem;
-+ struct {
-+ __u32 count;
-+ __u32 owner;
-+ bool ownerdead;
-+ } mutex;
-+ } u;
-+};
-+
-+struct winesync_q_entry {
-+ struct list_head node;
-+ struct winesync_q *q;
-+ struct winesync_obj *obj;
-+ __u32 index;
-+};
-+
-+struct winesync_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;
-+
++ atomic_t all_hint;
+
+ enum winesync_type type;
+
+@@ -54,11 +81,25 @@ struct winesync_q {
+ */
+ atomic_t signaled;
+
+ bool all;
-+ bool ownerdead;
-+ __u32 count;
-+ struct winesync_q_entry entries[];
-+};
-+
-+struct winesync_device {
+ __u32 count;
+ struct winesync_q_entry entries[];
+ };
+
+ struct winesync_device {
+ /*
+ * Wait-all operations must atomically grab all objects, and be totally
+ * ordered with respect to each other and wait-any operations. If one
@@ -585,77 +899,26 @@ index 000000000000..ff5749206aa9
+ * at the same time.
+ */
+ spinlock_t wait_all_lock;
-+ struct mutex table_lock;
-+ struct idr objects;
-+};
-+
-+static struct winesync_obj *get_obj(struct winesync_device *dev, int id)
-+{
-+ struct winesync_obj *obj;
-+
-+ rcu_read_lock();
-+ obj = idr_find(&dev->objects, id);
-+ if (obj && !kref_get_unless_zero(&obj->refcount))
-+ obj = NULL;
-+ rcu_read_unlock();
-+
-+ return obj;
-+}
-+
-+static void destroy_obj(struct kref *ref)
-+{
-+ struct winesync_obj *obj;
-+
-+ obj = container_of(ref, struct winesync_obj, refcount);
-+ kfree(obj);
-+}
+
-+static void put_obj(struct winesync_obj *obj)
-+{
-+ kref_put(&obj->refcount, destroy_obj);
-+}
-+
-+static int winesync_char_open(struct inode *inode, struct file *file)
-+{
-+ struct winesync_device *dev;
-+
-+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-+ if (!dev)
-+ return -ENOMEM;
-+
-+ idr_init(&dev->objects);
+ struct xarray objects;
+ };
+
+@@ -95,6 +136,8 @@ static int winesync_char_open(struct inode *inode, struct file *file)
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->wait_all_lock);
-+ mutex_init(&dev->table_lock);
-+
-+ file->private_data = dev;
-+ return nonseekable_open(inode, file);
-+}
-+
-+static int winesync_char_release(struct inode *inode, struct file *file)
-+{
-+ struct winesync_device *dev = file->private_data;
-+ struct winesync_obj *obj;
-+ int id;
+
-+ mutex_lock(&dev->table_lock);
-+ idr_for_each_entry(&dev->objects, obj, id) {
-+ idr_remove(&dev->objects, id);
-+ synchronize_rcu();
-+ put_obj(obj);
-+ }
-+ mutex_unlock(&dev->table_lock);
-+
-+ kfree(dev);
-+
-+ return 0;
-+}
-+
-+static void winesync_init_obj(struct winesync_obj *obj)
-+{
-+ kref_init(&obj->refcount);
-+ refcount_set(&obj->all_hint, 1);
-+ spin_lock_init(&obj->lock);
-+ INIT_LIST_HEAD(&obj->any_waiters);
+ xa_init_flags(&dev->objects, XA_FLAGS_ALLOC);
+
+ file->private_data = dev;
+@@ -120,8 +163,82 @@ static int winesync_char_release(struct inode *inode, struct file *file)
+ static void init_obj(struct winesync_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);
+}
+
@@ -666,10 +929,6 @@ index 000000000000..ff5749206aa9
+ switch (obj->type) {
+ case WINESYNC_TYPE_SEM:
+ return !!obj->u.sem.count;
-+ case WINESYNC_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);
@@ -710,15 +969,7 @@ index 000000000000..ff5749206aa9
+
+ switch (obj->type) {
+ case WINESYNC_TYPE_SEM:
-+ if (obj->u.sem.flags & WINESYNC_SEM_GETONWAIT)
-+ obj->u.sem.count--;
-+ break;
-+ case WINESYNC_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;
++ obj->u.sem.count--;
+ break;
+ }
+ }
@@ -741,28 +992,373 @@ index 000000000000..ff5749206aa9
+
+ list_for_each_entry(entry, &obj->all_waiters, node)
+ try_wake_all(dev, entry->q, obj);
-+}
+ }
+
+ static void try_wake_any_sem(struct winesync_obj *sem)
+@@ -226,14 +343,29 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
+ return -EINVAL;
+ }
+
+- spin_lock(&sem->lock);
++ if (atomic_read(&sem->all_hint) > 0) {
++ spin_lock(&dev->wait_all_lock);
++ spin_lock(&sem->lock);
+
-+static void try_wake_any_sem(struct winesync_obj *sem)
++ 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);
+
+@@ -270,7 +402,7 @@ static int winesync_schedule(const struct winesync_q *q, ktime_t *timeout)
+ * Also, calculate the relative timeout.
+ */
+ static int setup_wait(struct winesync_device *dev,
+- const struct winesync_wait_args *args,
++ const struct winesync_wait_args *args, bool all,
+ ktime_t *ret_timeout, struct winesync_q **ret_q)
+ {
+ const __u32 count = args->count;
+@@ -310,6 +442,7 @@ static int setup_wait(struct winesync_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++) {
+@@ -319,6 +452,16 @@ static int setup_wait(struct winesync_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;
+@@ -359,7 +502,7 @@ static int winesync_wait_any(struct winesync_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;
+
+@@ -420,6 +563,87 @@ static int winesync_wait_any(struct winesync_device *dev, void __user *argp)
+ return ret;
+ }
+
++static int winesync_wait_all(struct winesync_device *dev, void __user *argp)
+{
-+ struct winesync_q_entry *entry;
++ struct winesync_wait_args args;
++ struct winesync_q *q;
++ ktime_t timeout;
++ int signaled;
++ __u32 i;
++ int ret;
+
-+ lockdep_assert_held(&sem->lock);
++ if (copy_from_user(&args, argp, sizeof(args)))
++ return -EFAULT;
+
-+ list_for_each_entry(entry, &sem->any_waiters, node) {
-+ struct winesync_q *q = entry->q;
++ ret = setup_wait(dev, &args, true, &timeout, &q);
++ if (ret < 0)
++ return ret;
+
-+ if (!sem->u.sem.count)
-+ break;
++ /* queue ourselves */
+
-+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
-+ if (sem->u.sem.flags & WINESYNC_SEM_GETONWAIT)
-+ sem->u.sem.count--;
-+ wake_up_process(q->task);
-+ }
++ spin_lock(&dev->wait_all_lock);
++
++ for (i = 0; i < args.count; i++) {
++ struct winesync_q_entry *entry = &q->entries[i];
++ struct winesync_obj *obj = q->entries[i].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 = winesync_schedule(q, args.timeout ? &timeout : NULL);
++
++ /* and finally, unqueue */
++
++ spin_lock(&dev->wait_all_lock);
++
++ for (i = 0; i < args.count; i++) {
++ struct winesync_q_entry *entry = &q->entries[i];
++ struct winesync_obj *obj = q->entries[i].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);
++ }
++
++ spin_unlock(&dev->wait_all_lock);
++
++ signaled = atomic_read(&q->signaled);
++ if (signaled != -1) {
++ struct winesync_wait_args __user *user_args = argp;
++
++ /* even if we caught a signal, we need to communicate success */
++ ret = 0;
++
++ if (put_user(signaled, &user_args->index))
++ ret = -EFAULT;
++ } else if (!ret) {
++ ret = -ETIMEDOUT;
+ }
++
++ kfree(q);
++ return ret;
+}
+
+ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+ {
+@@ -435,6 +659,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_put_sem(dev, argp);
+ case WINESYNC_IOC_WAIT_ANY:
+ return winesync_wait_any(dev, argp);
++ case WINESYNC_IOC_WAIT_ALL:
++ return winesync_wait_all(dev, argp);
+ default:
+ return -ENOSYS;
+ }
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index f57ebfbe1dd9..bcd21e53fa04 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -34,5 +34,7 @@ struct winesync_wait_args {
+ struct winesync_sem_args)
+ #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 3, \
+ struct winesync_wait_args)
++#define WINESYNC_IOC_WAIT_ALL _IOW (WINESYNC_IOC_BASE, 4, \
++ struct winesync_wait_args)
+
+ #endif
+--
+2.34.1
+
+From 0a49b2023e8e4ffdafd6e862f3a7e59115dbdc18 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <zfigura@codeweavers.com>
+Date: Tue, 30 Nov 2021 13:32:59 -0600
+Subject: [PATCH 07/25] winesync: Allow atomically changing the signal mask
+ when calling wait ioctls.
+
+Along the lines of pselect(2) et al.
+
+Wine will need, in some cases, to wait for either a winesync primitive to be
+signaled, or for a signal to arrive, i.e. the exact use case that pselect(2)
+was designed for.
+---
+ drivers/misc/winesync.c | 13 +++++++++++++
+ include/uapi/linux/winesync.h | 2 ++
+ kernel/signal.c | 3 +++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index a0ee4536165e..071d611f65a3 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -5,9 +5,11 @@
+ * Copyright (C) 2021 Zebediah Figura
+ */
+
++#include <linux/compat.h>
+ #include <linux/fs.h>
+ #include <linux/miscdevice.h>
+ #include <linux/module.h>
++#include <linux/sched/signal.h>
+ #include <linux/slab.h>
+ #include <linux/xarray.h>
+ #include <uapi/linux/winesync.h>
+@@ -405,11 +407,20 @@ static int setup_wait(struct winesync_device *dev,
+ const struct winesync_wait_args *args, bool all,
+ ktime_t *ret_timeout, struct winesync_q **ret_q)
+ {
++ const void __user *sigmask = u64_to_user_ptr(args->sigmask);
+ const __u32 count = args->count;
+ struct winesync_q *q;
+ ktime_t timeout = 0;
+ __u32 *ids;
+ __u32 i, j;
++ int ret;
++
++ if (in_compat_syscall())
++ ret = set_compat_user_sigmask(sigmask, args->sigsetsize);
++ else
++ ret = set_user_sigmask(sigmask, args->sigsetsize);
++ if (ret < 0)
++ return ret;
+
+ if (!args->owner)
+ return -EINVAL;
+@@ -560,6 +571,7 @@ static int winesync_wait_any(struct winesync_device *dev, void __user *argp)
+ }
+
+ kfree(q);
++ restore_saved_sigmask_unless(ret == -ERESTARTSYS);
+ return ret;
+ }
+
+@@ -641,6 +653,7 @@ static int winesync_wait_all(struct winesync_device *dev, void __user *argp)
+ }
+
+ kfree(q);
++ restore_saved_sigmask_unless(ret == -ERESTARTSYS);
+ return ret;
+ }
+
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index bcd21e53fa04..37a362fa9f1d 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -17,6 +17,8 @@ struct winesync_sem_args {
+ };
+
+ struct winesync_wait_args {
++ __u64 sigmask;
++ __u64 sigsetsize;
+ __u64 timeout;
+ __u64 objs;
+ __u32 count;
+diff --git a/kernel/signal.c b/kernel/signal.c
+index 5892c91696f8..4ef90711610e 100644
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -3064,6 +3064,7 @@ void __set_current_blocked(const sigset_t *newset)
+ __set_task_blocked(tsk, newset);
+ spin_unlock_irq(&tsk->sighand->siglock);
+ }
++EXPORT_SYMBOL_GPL(__set_current_blocked);
+
+ /*
+ * This is also useful for kernel threads that want to temporarily
+@@ -3127,6 +3128,7 @@ int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(set_user_sigmask);
+
+ #ifdef CONFIG_COMPAT
+ int set_compat_user_sigmask(const compat_sigset_t __user *umask,
+@@ -3147,6 +3149,7 @@ int set_compat_user_sigmask(const compat_sigset_t __user *umask,
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(set_compat_user_sigmask);
+ #endif
+
+ /**
+--
+2.34.1
+
+From 839d4c5b7740071251bef01de70e0802df20de7d Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:41:10 -0600
+Subject: [PATCH 08/25] winesync: Introduce WINESYNC_IOC_CREATE_MUTEX.
+
+---
+ drivers/misc/winesync.c | 72 +++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 8 ++++
+ 2 files changed, 80 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 071d611f65a3..f53ca84c39e8 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -18,6 +18,7 @@
+
+ enum winesync_type {
+ WINESYNC_TYPE_SEM,
++ WINESYNC_TYPE_MUTEX,
+ };
+
+ struct winesync_obj {
+@@ -62,6 +63,10 @@ struct winesync_obj {
+ __u32 count;
+ __u32 max;
+ } sem;
++ struct {
++ __u32 count;
++ __u32 owner;
++ } mutex;
+ } u;
+ };
+
+@@ -178,6 +183,10 @@ static bool is_signaled(struct winesync_obj *obj, __u32 owner)
+ switch (obj->type) {
+ case WINESYNC_TYPE_SEM:
+ return !!obj->u.sem.count;
++ case WINESYNC_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);
+@@ -220,6 +229,10 @@ static void try_wake_all(struct winesync_device *dev, struct winesync_q *q,
+ case WINESYNC_TYPE_SEM:
+ obj->u.sem.count--;
+ break;
++ case WINESYNC_TYPE_MUTEX:
++ obj->u.mutex.count++;
++ obj->u.mutex.owner = q->owner;
++ break;
+ }
+ }
+ wake_up_process(q->task);
+@@ -262,6 +275,28 @@ static void try_wake_any_sem(struct winesync_obj *sem)
+ }
+ }
+
+static void try_wake_any_mutex(struct winesync_obj *mutex)
+{
+ struct winesync_q_entry *entry;
@@ -778,9 +1374,6 @@ index 000000000000..ff5749206aa9
+ 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);
@@ -788,49 +1381,19 @@ index 000000000000..ff5749206aa9
+ }
+}
+
-+static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_sem_args __user *user_args = argp;
-+ struct winesync_sem_args args;
-+ struct winesync_obj *sem;
-+ int ret;
-+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
-+
-+ if (args.count > args.max)
-+ return -EINVAL;
-+
-+ if (args.flags & ~WINESYNC_SEM_GETONWAIT)
-+ return -EINVAL;
-+
-+ sem = kzalloc(sizeof(*sem), GFP_KERNEL);
-+ if (!sem)
-+ return -ENOMEM;
-+
-+ winesync_init_obj(sem);
-+ sem->type = WINESYNC_TYPE_SEM;
-+ sem->u.sem.count = args.count;
-+ sem->u.sem.max = args.max;
-+ sem->u.sem.flags = args.flags;
-+
-+ mutex_lock(&dev->table_lock);
-+ ret = idr_alloc(&dev->objects, sem, 0, 0, GFP_KERNEL);
-+ mutex_unlock(&dev->table_lock);
-+
-+ if (ret < 0) {
-+ kfree(sem);
-+ return ret;
-+ }
-+
-+ return put_user(ret, &user_args->sem);
-+}
-+
+ static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
+ {
+ struct winesync_sem_args __user *user_args = argp;
+@@ -294,6 +329,38 @@ static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
+ return put_user(id, &user_args->sem);
+ }
+
+static int winesync_create_mutex(struct winesync_device *dev, void __user *argp)
+{
+ struct winesync_mutex_args __user *user_args = argp;
+ struct winesync_mutex_args args;
+ struct winesync_obj *mutex;
++ __u32 id;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
@@ -843,151 +1406,88 @@ index 000000000000..ff5749206aa9
+ if (!mutex)
+ return -ENOMEM;
+
-+ winesync_init_obj(mutex);
++ init_obj(mutex);
+ mutex->type = WINESYNC_TYPE_MUTEX;
+ mutex->u.mutex.count = args.count;
+ mutex->u.mutex.owner = args.owner;
+
-+ mutex_lock(&dev->table_lock);
-+ ret = idr_alloc(&dev->objects, mutex, 0, 0, GFP_KERNEL);
-+ mutex_unlock(&dev->table_lock);
-+
++ ret = xa_alloc(&dev->objects, &id, mutex, xa_limit_32b, GFP_KERNEL);
+ if (ret < 0) {
+ kfree(mutex);
+ return ret;
+ }
+
-+ return put_user(ret, &user_args->mutex);
-+}
-+
-+static int winesync_delete(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_obj *obj;
-+ __s32 id;
-+
-+ if (get_user(id, (__s32 __user *)argp))
-+ return -EFAULT;
-+
-+ mutex_lock(&dev->table_lock);
-+ obj = idr_remove(&dev->objects, id);
-+ mutex_unlock(&dev->table_lock);
-+
-+ if (!obj)
-+ return -EINVAL;
-+
-+ put_obj(obj);
-+ return 0;
-+}
-+
-+static int winesync_get_sem(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_obj *sem;
-+ int ret = -EWOULDBLOCK;
-+ __s32 id;
-+
-+ if (get_user(id, (__s32 __user *)argp))
-+ return -EFAULT;
-+
-+ sem = get_obj(dev, id);
-+ if (!sem)
-+ return -EINVAL;
-+ if (sem->type != WINESYNC_TYPE_SEM) {
-+ put_obj(sem);
-+ return -EINVAL;
-+ }
-+
-+ spin_lock(&sem->lock);
-+
-+ if (sem->u.sem.count) {
-+ /*
-+ * Decrement the semaphore's count, regardless of whether it
-+ * has the WINESYNC_SEM_GETONWAIT flag set.
-+ */
-+ sem->u.sem.count--;
-+ ret = 0;
-+ }
-+
-+ spin_unlock(&sem->lock);
-+
-+ put_obj(sem);
-+
-+ return ret;
-+}
-+
-+/*
-+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
-+ * invalid.
-+ */
-+static int put_sem_state(struct winesync_obj *sem, __u32 count)
-+{
-+ lockdep_assert_held(&sem->lock);
-+
-+ if (sem->u.sem.count + count < sem->u.sem.count ||
-+ sem->u.sem.count + count > sem->u.sem.max)
-+ return -EOVERFLOW;
-+
-+ sem->u.sem.count += count;
-+ return 0;
++ return put_user(id, &user_args->mutex);
+}
+
-+static int winesync_put_sem(struct winesync_device *dev, void __user *argp,
-+ bool pulse)
-+{
-+ struct winesync_sem_args __user *user_args = argp;
-+ struct winesync_sem_args args;
-+ struct winesync_obj *sem;
-+ __u32 prev_count;
-+ int ret;
-+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
-+
-+ sem = get_obj(dev, args.sem);
-+ if (!sem)
-+ return -EINVAL;
-+ if (sem->type != WINESYNC_TYPE_SEM) {
-+ put_obj(sem);
-+ return -EINVAL;
-+ }
-+
-+ if (refcount_read(&sem->all_hint) > 1) {
-+ spin_lock(&dev->wait_all_lock);
-+ spin_lock(&sem->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);
-+ }
-+
-+ if (pulse)
-+ sem->u.sem.count = 0;
-+
-+ spin_unlock(&sem->lock);
-+ spin_unlock(&dev->wait_all_lock);
-+ } else {
-+ spin_lock(&sem->lock);
-+
-+ prev_count = sem->u.sem.count;
-+ ret = put_sem_state(sem, args.count);
-+ if (!ret)
-+ try_wake_any_sem(sem);
-+
-+ if (pulse)
-+ sem->u.sem.count = 0;
-+
-+ spin_unlock(&sem->lock);
-+ }
-+
-+ put_obj(sem);
-+
-+ if (!ret && put_user(prev_count, &user_args->count))
-+ ret = -EFAULT;
-+
-+ return ret;
-+}
+ static int winesync_delete(struct winesync_device *dev, void __user *argp)
+ {
+ struct winesync_obj *obj;
+@@ -498,6 +565,9 @@ static void try_wake_any_obj(struct winesync_obj *obj)
+ case WINESYNC_TYPE_SEM:
+ try_wake_any_sem(obj);
+ break;
++ case WINESYNC_TYPE_MUTEX:
++ try_wake_any_mutex(obj);
++ break;
+ }
+ }
+
+@@ -666,6 +736,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ switch (cmd) {
+ case WINESYNC_IOC_CREATE_SEM:
+ return winesync_create_sem(dev, argp);
++ case WINESYNC_IOC_CREATE_MUTEX:
++ return winesync_create_mutex(dev, argp);
+ case WINESYNC_IOC_DELETE:
+ return winesync_delete(dev, argp);
+ case WINESYNC_IOC_PUT_SEM:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 37a362fa9f1d..0c58181ae05c 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -16,6 +16,12 @@ struct winesync_sem_args {
+ __u32 max;
+ };
+
++struct winesync_mutex_args {
++ __u32 mutex;
++ __u32 owner;
++ __u32 count;
++};
+
+ struct winesync_wait_args {
+ __u64 sigmask;
+ __u64 sigsetsize;
+@@ -38,5 +44,7 @@ struct winesync_wait_args {
+ struct winesync_wait_args)
+ #define WINESYNC_IOC_WAIT_ALL _IOW (WINESYNC_IOC_BASE, 4, \
+ struct winesync_wait_args)
++#define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 5, \
++ struct winesync_mutex_args)
+
+ #endif
+--
+2.34.1
+
+From 3d4007a2b75f991292d99b4b36159610da602a1b Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:44:41 -0600
+Subject: [PATCH 09/25] winesync: Introduce WINESYNC_IOC_PUT_MUTEX.
+
+---
+ drivers/misc/winesync.c | 71 +++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 2 +
+ 2 files changed, 73 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index f53ca84c39e8..d07ebd4c8c1c 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -444,6 +444,75 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
+ return ret;
+ }
+
+/*
+ * Actually change the mutex state, returning -EPERM if not the owner.
+ */
@@ -1025,7 +1525,7 @@ index 000000000000..ff5749206aa9
+ return -EINVAL;
+ }
+
-+ if (refcount_read(&mutex->all_hint) > 1) {
++ if (atomic_read(&mutex->all_hint) > 0) {
+ spin_lock(&dev->wait_all_lock);
+ spin_lock(&mutex->lock);
+
@@ -1057,12 +1557,228 @@ index 000000000000..ff5749206aa9
+ return ret;
+}
+
+ static int winesync_schedule(const struct winesync_q *q, ktime_t *timeout)
+ {
+ int ret = 0;
+@@ -742,6 +811,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_delete(dev, argp);
+ case WINESYNC_IOC_PUT_SEM:
+ return winesync_put_sem(dev, argp);
++ case WINESYNC_IOC_PUT_MUTEX:
++ return winesync_put_mutex(dev, argp);
+ case WINESYNC_IOC_WAIT_ANY:
+ return winesync_wait_any(dev, argp);
+ case WINESYNC_IOC_WAIT_ALL:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 0c58181ae05c..c72149082828 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -46,5 +46,7 @@ struct winesync_wait_args {
+ struct winesync_wait_args)
+ #define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 5, \
+ struct winesync_mutex_args)
++#define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \
++ struct winesync_mutex_args)
+
+ #endif
+--
+2.34.1
+
+From d24545c3b550a9e05878b8a478c0765f1d41cd82 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:46:46 -0600
+Subject: [PATCH 10/25] winesync: Introduce WINESYNC_IOC_KILL_OWNER.
+
+---
+ drivers/misc/winesync.c | 80 ++++++++++++++++++++++++++++++++++-
+ include/uapi/linux/winesync.h | 1 +
+ 2 files changed, 79 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index d07ebd4c8c1c..e6901ac6d949 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -66,6 +66,7 @@ struct winesync_obj {
+ struct {
+ __u32 count;
+ __u32 owner;
++ bool ownerdead;
+ } mutex;
+ } u;
+ };
+@@ -89,6 +90,7 @@ struct winesync_q {
+ atomic_t signaled;
+
+ bool all;
++ bool ownerdead;
+ __u32 count;
+ struct winesync_q_entry entries[];
+ };
+@@ -230,6 +232,9 @@ static void try_wake_all(struct winesync_device *dev, struct winesync_q *q,
+ obj->u.sem.count--;
+ break;
+ case WINESYNC_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;
+@@ -290,6 +295,9 @@ static void try_wake_any_mutex(struct winesync_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);
+@@ -513,6 +521,71 @@ static int winesync_put_mutex(struct winesync_device *dev, void __user *argp)
+ return ret;
+ }
+
++/*
++ * Actually change the mutex state to mark its owner as dead.
++ */
++static void put_mutex_ownerdead_state(struct winesync_obj *mutex)
++{
++ lockdep_assert_held(&mutex->lock);
++
++ mutex->u.mutex.ownerdead = true;
++ mutex->u.mutex.owner = 0;
++ mutex->u.mutex.count = 0;
++}
++
++static int winesync_kill_owner(struct winesync_device *dev, void __user *argp)
++{
++ struct winesync_obj *obj;
++ unsigned long id;
++ __u32 owner;
++
++ if (get_user(owner, (__u32 __user *)argp))
++ return -EFAULT;
++ if (!owner)
++ return -EINVAL;
++
++ rcu_read_lock();
++
++ xa_for_each(&dev->objects, id, obj) {
++ if (!kref_get_unless_zero(&obj->refcount))
++ continue;
++
++ if (obj->type != WINESYNC_TYPE_MUTEX) {
++ put_obj(obj);
++ continue;
++ }
++
++ if (atomic_read(&obj->all_hint) > 0) {
++ spin_lock(&dev->wait_all_lock);
++ spin_lock(&obj->lock);
++
++ if (obj->u.mutex.owner == owner) {
++ put_mutex_ownerdead_state(obj);
++ try_wake_all_obj(dev, obj);
++ try_wake_any_mutex(obj);
++ }
++
++ spin_unlock(&obj->lock);
++ spin_unlock(&dev->wait_all_lock);
++ } else {
++ spin_lock(&obj->lock);
++
++ if (obj->u.mutex.owner == owner) {
++ put_mutex_ownerdead_state(obj);
++ try_wake_any_mutex(obj);
++ }
++
++ spin_unlock(&obj->lock);
++ }
++
++ put_obj(obj);
++ }
++
++ rcu_read_unlock();
++
++ return 0;
++}
++
+ static int winesync_schedule(const struct winesync_q *q, ktime_t *timeout)
+ {
+ int ret = 0;
+@@ -590,6 +663,7 @@ static int setup_wait(struct winesync_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++) {
+@@ -701,7 +775,7 @@ static int winesync_wait_any(struct winesync_device *dev, void __user *argp)
+ struct winesync_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;
+@@ -783,7 +857,7 @@ static int winesync_wait_all(struct winesync_device *dev, void __user *argp)
+ struct winesync_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;
+@@ -813,6 +887,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_put_sem(dev, argp);
+ case WINESYNC_IOC_PUT_MUTEX:
+ return winesync_put_mutex(dev, argp);
++ case WINESYNC_IOC_KILL_OWNER:
++ return winesync_kill_owner(dev, argp);
+ case WINESYNC_IOC_WAIT_ANY:
+ return winesync_wait_any(dev, argp);
+ case WINESYNC_IOC_WAIT_ALL:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index c72149082828..59b1cfcbf00a 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -48,5 +48,6 @@ struct winesync_wait_args {
+ struct winesync_mutex_args)
+ #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \
+ struct winesync_mutex_args)
++#define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 7, __u32)
+
+ #endif
+--
+2.34.1
+
+From 9826f3a3e702322335cb74e8c648f223a1be1ca6 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:47:55 -0600
+Subject: [PATCH 11/25] winesync: Introduce WINESYNC_IOC_READ_SEM.
+
+---
+ drivers/misc/winesync.c | 33 +++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 2 ++
+ 2 files changed, 35 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index e6901ac6d949..aff9c5d9b48c 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -521,6 +521,37 @@ static int winesync_put_mutex(struct winesync_device *dev, void __user *argp)
+ return ret;
+ }
+
+static int winesync_read_sem(struct winesync_device *dev, void __user *argp)
+{
+ struct winesync_sem_args __user *user_args = argp;
+ struct winesync_sem_args args;
+ struct winesync_obj *sem;
-+ __s32 id;
++ __u32 id;
+
+ if (get_user(id, &user_args->sem))
+ return -EFAULT;
@@ -1079,7 +1795,6 @@ index 000000000000..ff5749206aa9
+ spin_lock(&sem->lock);
+ args.count = sem->u.sem.count;
+ args.max = sem->u.sem.max;
-+ args.flags = sem->u.sem.flags;
+ spin_unlock(&sem->lock);
+
+ put_obj(sem);
@@ -1089,12 +1804,57 @@ index 000000000000..ff5749206aa9
+ return 0;
+}
+
+ /*
+ * Actually change the mutex state to mark its owner as dead.
+ */
+@@ -887,6 +918,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_put_sem(dev, argp);
+ case WINESYNC_IOC_PUT_MUTEX:
+ return winesync_put_mutex(dev, argp);
++ case WINESYNC_IOC_READ_SEM:
++ return winesync_read_sem(dev, argp);
+ case WINESYNC_IOC_KILL_OWNER:
+ return winesync_kill_owner(dev, argp);
+ case WINESYNC_IOC_WAIT_ANY:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 59b1cfcbf00a..f18c42f6596b 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -49,5 +49,7 @@ struct winesync_wait_args {
+ #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \
+ struct winesync_mutex_args)
+ #define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 7, __u32)
++#define WINESYNC_IOC_READ_SEM _IOWR(WINESYNC_IOC_BASE, 8, \
++ struct winesync_sem_args)
+
+ #endif
+--
+2.34.1
+
+From d07e942258dfa43a9785cdab1912e369e0b36e2c Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:48:10 -0600
+Subject: [PATCH 12/25] winesync: Introduce WINESYNC_IOC_READ_MUTEX.
+
+---
+ drivers/misc/winesync.c | 35 +++++++++++++++++++++++++++++++++++
+ include/uapi/linux/winesync.h | 2 ++
+ 2 files changed, 37 insertions(+)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index aff9c5d9b48c..a9a6d1b7970a 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -552,6 +552,39 @@ static int winesync_read_sem(struct winesync_device *dev, void __user *argp)
+ return 0;
+ }
+
+static int winesync_read_mutex(struct winesync_device *dev, void __user *argp)
+{
+ struct winesync_mutex_args __user *user_args = argp;
+ struct winesync_mutex_args args;
+ struct winesync_obj *mutex;
-+ __s32 id;
++ __u32 id;
+ int ret;
+
+ if (get_user(id, &user_args->mutex))
@@ -1122,484 +1882,426 @@ index 000000000000..ff5749206aa9
+ return ret;
+}
+
-+/*
-+ * Actually change the mutex state to mark its owner as dead.
-+ */
-+static void put_mutex_ownerdead_state(struct winesync_obj *mutex)
-+{
-+ lockdep_assert_held(&mutex->lock);
-+
-+ mutex->u.mutex.ownerdead = true;
-+ mutex->u.mutex.owner = 0;
-+ mutex->u.mutex.count = 0;
-+}
-+
-+static int winesync_kill_owner(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_obj *obj;
-+ __u32 owner;
-+ int id;
-+
-+ if (get_user(owner, (__u32 __user *)argp))
-+ return -EFAULT;
-+ if (!owner)
-+ return -EINVAL;
-+
-+ rcu_read_lock();
-+
-+ idr_for_each_entry(&dev->objects, obj, id) {
-+ if (!kref_get_unless_zero(&obj->refcount))
-+ continue;
-+
-+ if (obj->type != WINESYNC_TYPE_MUTEX) {
-+ put_obj(obj);
-+ continue;
-+ }
+ /*
+ * Actually change the mutex state to mark its owner as dead.
+ */
+@@ -920,6 +953,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ return winesync_put_mutex(dev, argp);
+ case WINESYNC_IOC_READ_SEM:
+ return winesync_read_sem(dev, argp);
++ case WINESYNC_IOC_READ_MUTEX:
++ return winesync_read_mutex(dev, argp);
+ case WINESYNC_IOC_KILL_OWNER:
+ return winesync_kill_owner(dev, argp);
+ case WINESYNC_IOC_WAIT_ANY:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index f18c42f6596b..1dccdb3877ec 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -51,5 +51,7 @@ struct winesync_wait_args {
+ #define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 7, __u32)
+ #define WINESYNC_IOC_READ_SEM _IOWR(WINESYNC_IOC_BASE, 8, \
+ struct winesync_sem_args)
++#define WINESYNC_IOC_READ_MUTEX _IOWR(WINESYNC_IOC_BASE, 9, \
++ struct winesync_mutex_args)
+
+ #endif
+--
+2.34.1
+
+From 1782cc3e3647cd8fe39fe6765f106b88d669d374 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 11:50:49 -0600
+Subject: [PATCH 13/25] doc: Add documentation for the winesync uAPI.
+
+---
+ Documentation/userspace-api/index.rst | 1 +
+ Documentation/userspace-api/winesync.rst | 345 +++++++++++++++++++++++
+ 2 files changed, 346 insertions(+)
+ create mode 100644 Documentation/userspace-api/winesync.rst
+
+diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst
+index c432be070f67..fde565a8005c 100644
+--- a/Documentation/userspace-api/index.rst
++++ b/Documentation/userspace-api/index.rst
+@@ -28,6 +28,7 @@ place where this information is gathered.
+ sysfs-platform_profile
+ vduse
+ futex2
++ winesync
+
+ .. only:: subproject and html
+
+diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst
+new file mode 100644
+index 000000000000..009171a187b7
+--- /dev/null
++++ b/Documentation/userspace-api/winesync.rst
+@@ -0,0 +1,345 @@
++=====================================
++Wine synchronization primitive driver
++=====================================
+
-+ if (refcount_read(&obj->all_hint) > 1) {
-+ spin_lock(&dev->wait_all_lock);
-+ spin_lock(&obj->lock);
++This page documents the user-space API for the winesync driver.
+
-+ if (obj->u.mutex.owner == owner) {
-+ put_mutex_ownerdead_state(obj);
-+ try_wake_all_obj(dev, obj);
-+ try_wake_any_mutex(obj);
-+ }
++winesync is a support driver for emulation of NT synchronization
++primitives by the Wine project. It exists because implementation in
++user-space, using existing tools, cannot simultaneously satisfy
++performance, correctness, and security constraints. It is implemented
++entirely in software, and does not drive any hardware device.
+
-+ spin_unlock(&obj->lock);
-+ spin_unlock(&dev->wait_all_lock);
-+ } else {
-+ spin_lock(&obj->lock);
++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).
+
-+ if (obj->u.mutex.owner == owner) {
-+ put_mutex_ownerdead_state(obj);
-+ try_wake_any_mutex(obj);
-+ }
++Synchronization primitives
++==========================
+
-+ spin_unlock(&obj->lock);
-+ }
++The winesync driver exposes two types of synchronization primitives,
++semaphores and mutexes.
+
-+ put_obj(obj);
-+ }
++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.
+
-+ rcu_read_unlock();
++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.
+
-+ return 0;
-+}
++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 ``WINESYNC_IOC_KILL_OWNER``. An inconsistent mutex
++is inherently considered unowned.
+
-+static int winesync_schedule(const struct winesync_q *q, ktime_t *timeout)
-+{
-+ int ret = 0;
++Except for the "unowned" semantics of zero, the actual value of the
++owner identifier is not interpreted by the winesync driver at all. The
++intended use is to store a thread identifier; however, the winesync
++driver does not actually validate that a calling thread provides
++consistent or unique identifiers.
+
-+ do {
-+ if (signal_pending(current)) {
-+ ret = -ERESTARTSYS;
-+ break;
-+ }
++Objects are represented by unsigned 32-bit integers.
+
-+ 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);
++Char device
++===========
+
-+ return ret;
-+}
++The winesync driver creates a single char device /dev/winesync. 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.
+
-+/*
-+ * Allocate and initialize most of the winesync_q structure, but do not queue us
-+ * yet. Also, calculate the relative timeout in jiffies.
-+ */
-+static int setup_wait(struct winesync_device *dev,
-+ const struct winesync_wait_args *args, bool all,
-+ ktime_t *ret_timeout, struct winesync_q **ret_q)
-+{
-+ const __u32 count = args->count;
-+ struct winesync_q *q;
-+ ktime_t timeout = 0;
-+ __s32 *ids;
-+ __u32 i, j;
++ioctl reference
++===============
+
-+ if (args->timeout) {
-+ struct timespec64 to;
++All operations on the device are done through ioctls. There are three
++structures used in ioctl calls::
+
-+ if (get_timespec64(&to, u64_to_user_ptr(args->timeout)))
-+ return -EFAULT;
-+ if (!timespec64_valid(&to))
-+ return -EINVAL;
++ struct winesync_sem_args {
++ __u32 sem;
++ __u32 count;
++ __u32 max;
++ };
+
-+ timeout = timespec64_to_ns(&to);
-+ }
++ struct winesync_mutex_args {
++ __u32 mutex;
++ __u32 owner;
++ __u32 count;
++ };
+
-+ ids = kmalloc_array(args->count, sizeof(*ids), GFP_KERNEL);
-+ if (!ids)
-+ return -ENOMEM;
-+ if (copy_from_user(ids, u64_to_user_ptr(args->objs),
-+ array_size(args->count, sizeof(*ids)))) {
-+ kfree(ids);
-+ return -EFAULT;
-+ }
++ struct winesync_wait_args {
++ __u64 sigmask;
++ __u64 sigsetsize;
++ __u64 timeout;
++ __u64 objs;
++ __u32 count;
++ __u32 owner;
++ __u32 index;
++ __u32 pad;
++ };
+
-+ 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->all = all;
-+ q->ownerdead = false;
-+ q->count = count;
++Depending on the ioctl, members of the structure may be used as input,
++output, or not at all.
+
-+ for (i = 0; i < count; i++) {
-+ struct winesync_q_entry *entry = &q->entries[i];
-+ struct winesync_obj *obj = get_obj(dev, ids[i]);
++All ioctls return 0 on success, and -1 on error, in which case `errno`
++will be set to a nonzero error code.
+
-+ if (!obj)
-+ goto err;
++The ioctls are as follows:
+
-+ 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;
-+ }
-+ }
-+ }
++.. c:macro:: WINESYNC_IOC_CREATE_SEM
+
-+ entry->obj = obj;
-+ entry->q = q;
-+ entry->index = i;
-+ }
++ Create a semaphore object. Takes a pointer to struct
++ :c:type:`winesync_sem_args`, which is used as follows:
+
-+ kfree(ids);
++ ``count`` and ``max`` are input-only arguments, denoting the
++ initial and maximum count of the semaphore.
+
-+ *ret_q = q;
-+ *ret_timeout = timeout;
-+ return 0;
++ ``sem`` is an output-only argument, which will be filled with the
++ identifier of the created semaphore if successful.
+
-+err:
-+ for (j = 0; j < i; j++)
-+ put_obj(q->entries[j].obj);
-+ kfree(ids);
-+ kfree(q);
-+ return -EINVAL;
-+}
++ Fails with ``EINVAL`` if ``count`` is greater than ``max``, or
++ ``ENOMEM`` if not enough memory is available.
+
-+static void try_wake_any_obj(struct winesync_obj *obj)
-+{
-+ switch (obj->type) {
-+ case WINESYNC_TYPE_SEM:
-+ try_wake_any_sem(obj);
-+ break;
-+ case WINESYNC_TYPE_MUTEX:
-+ try_wake_any_mutex(obj);
-+ break;
-+ }
-+}
++.. c:macro:: WINESYNC_IOC_CREATE_MUTEX
+
-+static int winesync_wait_any(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_wait_args args;
-+ struct winesync_q *q;
-+ ktime_t timeout;
-+ __u32 i;
-+ int ret;
++ Create a mutex object. Takes a pointer to struct
++ :c:type:`winesync_mutex_args`, which is used as follows:
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
-+ if (!args.owner)
-+ return -EINVAL;
++ ``owner`` is an input-only argument denoting the initial owner of
++ the mutex.
+
-+ ret = setup_wait(dev, &args, false, &timeout, &q);
-+ if (ret < 0)
-+ return ret;
++ ``count`` is an input-only argument denoting the initial recursion
++ count 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``.
+
-+ /* queue ourselves */
++ ``mutex`` is an output-only argument, which will be filled with
++ the identifier of the created mutex if successful.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct winesync_q_entry *entry = &q->entries[i];
-+ struct winesync_obj *obj = q->entries[i].obj;
++ Fails with ``ENOMEM`` if not enough memory is available.
+
-+ spin_lock(&obj->lock);
-+ list_add_tail(&entry->node, &obj->any_waiters);
-+ spin_unlock(&obj->lock);
-+ }
++.. c:macro:: WINESYNC_IOC_DELETE
+
-+ /* check if we are already signaled */
++ Delete an object of any type. Takes an input-only pointer to a
++ 32-bit integer denoting the object to delete. Fails with ``EINVAL``
++ if the object is not valid. Further ioctls attempting to use the
++ object return ``EINVAL``, unless the object identifier is reused for
++ another object.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct winesync_obj *obj = q->entries[i].obj;
++ Wait ioctls currently in progress are not interrupted, and behave as
++ if the object remains valid.
+
-+ if (atomic_read(&q->signaled) != -1)
-+ break;
++.. c:macro:: WINESYNC_IOC_PUT_SEM
+
-+ spin_lock(&obj->lock);
-+ try_wake_any_obj(obj);
-+ spin_unlock(&obj->lock);
-+ }
++ Post to a semaphore object. Takes a pointer to struct
++ :c:type:`winesync_sem_args`, which is used as follows:
+
-+ /* sleep */
++ ``sem`` is an input-only argument denoting the semaphore object.
++ If ``sem`` does not identify a valid semaphore object, the ioctl
++ fails with ``EINVAL``.
+
-+ ret = winesync_schedule(q, args.timeout ? &timeout : NULL);
++ ``count`` contains on input the count to add to the semaphore, and
++ on output is filled with its previous count.
+
-+ /* and finally, unqueue */
++ ``max`` is not used.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct winesync_obj *obj = q->entries[i].obj;
++ 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.
+
-+ spin_lock(&obj->lock);
-+ list_del(&q->entries[i].node);
-+ spin_unlock(&obj->lock);
++ The operation is atomic and totally ordered with respect to other
++ operations on the same semaphore.
+
-+ put_obj(obj);
-+ }
++.. c:macro:: WINESYNC_IOC_PUT_MUTEX
+
-+ if (atomic_read(&q->signaled) != -1) {
-+ struct winesync_wait_args __user *user_args = argp;
++ Release a mutex object. Takes a pointer to struct
++ :c:type:`winesync_mutex_args`, which is used as follows:
+
-+ /* even if we caught a signal, we need to communicate success */
-+ ret = q->ownerdead ? -EOWNERDEAD : 0;
++ ``mutex`` is an input-only argument denoting the mutex object. If
++ ``mutex`` does not identify a valid mutex object, the ioctl fails
++ with ``EINVAL``.
+
-+ if (put_user(atomic_read(&q->signaled), &user_args->index))
-+ ret = -EFAULT;
-+ } else if (!ret) {
-+ ret = -ETIMEDOUT;
-+ }
++ ``owner`` is an input-only argument denoting the mutex owner. If
++ ``owner`` is zero, the ioctl fails with ``EINVAL``. If ``owner``
++ is not the current owner of the mutex, the ioctl fails with
++ ``EPERM``.
+
-+ kfree(q);
-+ return ret;
-+}
++ ``count`` is an output-only argument which will be filled on
++ success with the mutex's previous recursion count.
+
-+static int winesync_wait_all(struct winesync_device *dev, void __user *argp)
-+{
-+ struct winesync_wait_args args;
-+ struct winesync_q *q;
-+ ktime_t timeout;
-+ __u32 i;
-+ int ret;
++ 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.
+
-+ if (copy_from_user(&args, argp, sizeof(args)))
-+ return -EFAULT;
-+ if (!args.owner)
-+ return -EINVAL;
++ The operation is atomic and totally ordered with respect to other
++ operations on the same mutex.
+
-+ ret = setup_wait(dev, &args, true, &timeout, &q);
-+ if (ret < 0)
-+ return ret;
++.. c:macro:: WINESYNC_IOC_READ_SEM
+
-+ /* queue ourselves */
++ Read the current state of a semaphore object. Takes a pointer to
++ struct :c:type:`winesync_sem_args`, which is used as follows:
+
-+ spin_lock(&dev->wait_all_lock);
++ ``sem`` is an input-only argument denoting the semaphore object.
++ If ``sem`` does not identify a valid semaphore object, the ioctl
++ fails with ``EINVAL``.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct winesync_q_entry *entry = &q->entries[i];
-+ struct winesync_obj *obj = q->entries[i].obj;
++ ``count`` and ``max`` are output-only arguments, which will be
++ filled with the current and maximum count of the given semaphore.
+
-+ refcount_inc(&obj->all_hint);
++ The operation is atomic and totally ordered with respect to other
++ operations on the same semaphore.
+
-+ /*
-+ * 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);
-+ }
++.. c:macro:: WINESYNC_IOC_READ_MUTEX
+
-+ /* check if we are already signaled */
++ Read the current state of a mutex object. Takes a pointer to struct
++ :c:type:`winesync_mutex_args`, which is used as follows:
+
-+ try_wake_all(dev, q, NULL);
++ ``mutex`` is an input-only argument denoting the mutex object. If
++ ``mutex`` does not identify a valid mutex object, the ioctl fails
++ with ``EINVAL``.
+
-+ spin_unlock(&dev->wait_all_lock);
++ ``count`` and ``owner`` are output-only arguments, which will be
++ filled with the current recursion count and owner of the given
++ mutex. If the mutex is not owned, both ``count`` and ``owner`` are
++ set to zero.
+
-+ /* sleep */
++ If the mutex is marked as inconsistent, the function fails with
++ ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
++ zero.
+
-+ ret = winesync_schedule(q, args.timeout ? &timeout : NULL);
++ The operation is atomic and totally ordered with respect to other
++ operations on the same mutex.
+
-+ /* and finally, unqueue */
++.. c:macro:: WINESYNC_IOC_KILL_OWNER
+
-+ spin_lock(&dev->wait_all_lock);
++ 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``.
+
-+ for (i = 0; i < args.count; i++) {
-+ struct winesync_q_entry *entry = &q->entries[i];
-+ struct winesync_obj *obj = q->entries[i].obj;
++ 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).
+
-+ /*
-+ * 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);
++ 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.
+
-+ refcount_dec(&obj->all_hint);
++.. c:macro:: WINESYNC_IOC_WAIT_ANY
+
-+ put_obj(obj);
-+ }
++ Poll on any of a list of objects, atomically acquiring at most one.
++ Takes a pointer to struct :c:type:`winesync_wait_args`, which is
++ used as follows:
+
-+ spin_unlock(&dev->wait_all_lock);
++ ``sigmask`` is an optional input-only pointer to a
++ :c:type:`sigset_t` structure (specified as an integer so that the
++ :c:type:`winesync_wait_args` structure has the same size
++ regardless of architecture). If the pointer is not NULL, it holds
++ a signal mask which will be applied to the current thread for the
++ duration of the call, in the same fashion as ``pselect(2)``.
+
-+ if (atomic_read(&q->signaled) != -1) {
-+ /* even if we caught a signal, we need to communicate success */
-+ ret = q->ownerdead ? -EOWNERDEAD : 0;
-+ } else if (!ret) {
-+ ret = -ETIMEDOUT;
-+ }
++ ``sigsetsize`` specifies the size of the :c:type:`sigset_t`
++ structure passed in ``sigmask``. It is ignored if ``sigmask`` is
++ NULL.
+
-+ kfree(q);
-+ return ret;
-+}
++ ``timeout`` is an optional input-only 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``.
+
-+static long winesync_char_ioctl(struct file *file, unsigned int cmd,
-+ unsigned long parm)
-+{
-+ struct winesync_device *dev = file->private_data;
-+ void __user *argp = (void __user *)parm;
++ ``objs`` is a input-only 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``.
+
-+ switch (cmd) {
-+ case WINESYNC_IOC_CREATE_SEM:
-+ return winesync_create_sem(dev, argp);
-+ case WINESYNC_IOC_CREATE_MUTEX:
-+ return winesync_create_mutex(dev, argp);
-+ case WINESYNC_IOC_DELETE:
-+ return winesync_delete(dev, argp);
-+ case WINESYNC_IOC_GET_SEM:
-+ return winesync_get_sem(dev, argp);
-+ case WINESYNC_IOC_PUT_SEM:
-+ return winesync_put_sem(dev, argp, false);
-+ case WINESYNC_IOC_PULSE_SEM:
-+ return winesync_put_sem(dev, argp, true);
-+ case WINESYNC_IOC_PUT_MUTEX:
-+ return winesync_put_mutex(dev, argp);
-+ case WINESYNC_IOC_READ_SEM:
-+ return winesync_read_sem(dev, argp);
-+ case WINESYNC_IOC_READ_MUTEX:
-+ return winesync_read_mutex(dev, argp);
-+ case WINESYNC_IOC_KILL_OWNER:
-+ return winesync_kill_owner(dev, argp);
-+ case WINESYNC_IOC_WAIT_ANY:
-+ return winesync_wait_any(dev, argp);
-+ case WINESYNC_IOC_WAIT_ALL:
-+ return winesync_wait_all(dev, argp);
-+ default:
-+ return -ENOSYS;
-+ }
-+}
++ ``owner`` is an input-only argument denoting the 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``.
+
-+static const struct file_operations winesync_fops = {
-+ .owner = THIS_MODULE,
-+ .open = winesync_char_open,
-+ .release = winesync_char_release,
-+ .unlocked_ioctl = winesync_char_ioctl,
-+ .compat_ioctl = winesync_char_ioctl,
-+ .llseek = no_llseek,
-+};
++ ``index`` is an output-only argument which, if the ioctl is
++ successful, is filled with the index of the object actually
++ signaled. If unsuccessful, ``index`` is not modified.
+
-+static struct miscdevice winesync_misc = {
-+ .minor = WINESYNC_MINOR,
-+ .name = WINESYNC_NAME,
-+ .fops = &winesync_fops,
-+};
++ ``pad`` is unused, and exists to keep a consistent structure size.
+
-+static int __init winesync_init(void)
-+{
-+ return misc_register(&winesync_misc);
-+}
++ 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.
+
-+static void __exit winesync_exit(void)
-+{
-+ misc_deregister(&winesync_misc);
-+}
++ 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.
+
-+module_init(winesync_init);
-+module_exit(winesync_exit);
++ 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.
+
-+MODULE_AUTHOR("Zebediah Figura");
-+MODULE_DESCRIPTION("Kernel driver for Wine synchronization primitives");
-+MODULE_LICENSE("GPL");
-+MODULE_ALIAS("devname:" WINESYNC_NAME);
-+MODULE_ALIAS_MISCDEV(WINESYNC_MINOR);
-diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
-index 0676f18093f9..350aecfcfb29 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 WINESYNC_MINOR 243
- #define MISC_DYNAMIC_MINOR 255
-
- struct device;
-diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
-new file mode 100644
-index 000000000000..efc591795249
---- /dev/null
-+++ b/include/uapi/linux/winesync.h
-@@ -0,0 +1,61 @@
-+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-+/*
-+ * Kernel support for Wine synchronization primitives
-+ *
-+ * Copyright (C) 2021 Zebediah Figura
-+ */
++ 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.
+
-+#ifndef __LINUX_WINESYNC_H
-+#define __LINUX_WINESYNC_H
++ It is valid to pass the same object more than once. If a wakeup
++ occurs due to that object being signaled, ``index`` is set to the
++ lowest index corresponding to that object.
+
-+#include <linux/types.h>
++ Fails with ``ENOMEM`` if not enough memory is available, or
++ ``EINTR`` if a signal is received.
+
-+#define WINESYNC_SEM_GETONWAIT 1
++.. c:macro:: WINESYNC_IOC_WAIT_ALL
+
-+struct winesync_sem_args {
-+ __s32 sem;
-+ __u32 count;
-+ __u32 max;
-+ __u32 flags;
-+};
++ Poll on a list of objects, atomically acquiring all of them. Takes a
++ pointer to struct :c:type:`winesync_wait_args`, which is used
++ identically to ``WINESYNC_IOC_WAIT_ANY``, except that ``index`` is
++ always filled with zero on success.
+
-+struct winesync_mutex_args {
-+ __s32 mutex;
-+ __u32 owner;
-+ __u32 count;
-+};
++ 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.
+
-+struct winesync_wait_args {
-+ __u64 timeout;
-+ __u64 objs;
-+ __u32 count;
-+ __u32 owner;
-+ __u32 index;
-+ __u32 pad;
-+};
++ 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.
+
-+#define WINESYNC_IOC_BASE 0xf7
++ If an inconsistent mutex is acquired, the ioctl fails with
++ ``EOWNERDEAD``. Similarly to ``WINESYNC_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.
+
-+#define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \
-+ struct winesync_sem_args)
-+#define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 1, __s32)
-+#define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 2, \
-+ struct winesync_sem_args)
-+#define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 3, \
-+ struct winesync_wait_args)
-+#define WINESYNC_IOC_WAIT_ALL _IOW (WINESYNC_IOC_BASE, 4, \
-+ struct winesync_wait_args)
-+#define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 5, \
-+ struct winesync_mutex_args)
-+#define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \
-+ struct winesync_mutex_args)
-+#define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 7, __u32)
-+#define WINESYNC_IOC_READ_SEM _IOWR(WINESYNC_IOC_BASE, 8, \
-+ struct winesync_sem_args)
-+#define WINESYNC_IOC_READ_MUTEX _IOWR(WINESYNC_IOC_BASE, 9, \
-+ struct winesync_mutex_args)
-+#define WINESYNC_IOC_GET_SEM _IOW (WINESYNC_IOC_BASE, 10, __s32)
-+#define WINESYNC_IOC_PULSE_SEM _IOWR(WINESYNC_IOC_BASE, 11, \
-+ struct winesync_sem_args)
++ Unlike ``WINESYNC_IOC_WAIT_ANY``, it is not valid to pass the same
++ object more than once. If this is attempted, the function fails with
++ ``EINVAL``.
+
-+#endif
++ Fails with ``ENOMEM`` if not enough memory is available, or
++ ``EINTR`` if a signal is received.
+--
+2.34.1
+
+From 9453c81c3208b6fddeb80886f5ef7141b897640b Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:06:23 -0600
+Subject: [PATCH 14/25] selftests: winesync: Add some tests for semaphore
+ state.
+
+---
+ tools/testing/selftests/Makefile | 1 +
+ .../selftests/drivers/winesync/Makefile | 8 +
+ .../testing/selftests/drivers/winesync/config | 1 +
+ .../selftests/drivers/winesync/winesync.c | 153 ++++++++++++++++++
+ 4 files changed, 163 insertions(+)
+ create mode 100644 tools/testing/selftests/drivers/winesync/Makefile
+ create mode 100644 tools/testing/selftests/drivers/winesync/config
+ create mode 100644 tools/testing/selftests/drivers/winesync/winesync.c
+
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c852eb40c4f7..a366016d6254 100644
--- a/tools/testing/selftests/Makefile
@@ -1635,10 +2337,10 @@ index 000000000000..60539c826d06
+CONFIG_WINESYNC=y
diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
new file mode 100644
-index 000000000000..52373fcd5c8c
+index 000000000000..da3aa2c24671
--- /dev/null
+++ b/tools/testing/selftests/drivers/winesync/winesync.c
-@@ -0,0 +1,1486 @@
+@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Various unit tests for the "winesync" synchronization primitive driver.
@@ -1657,209 +2359,7 @@ index 000000000000..52373fcd5c8c
+
+TEST(semaphore_state)
+{
-+ struct winesync_wait_args wait_args;
-+ struct winesync_sem_args sem_args;
-+ struct timespec timeout;
-+ int fd, ret;
-+
-+ clock_gettime(CLOCK_MONOTONIC, &timeout);
-+
-+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
-+ ASSERT_LE(0, fd);
-+
-+ sem_args.count = 3;
-+ sem_args.max = 2;
-+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = 0;
-+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
-+ sem_args.count = 2;
-+ sem_args.max = 2;
-+ sem_args.sem = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_NE(0xdeadbeef, sem_args.sem);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ sem_args.flags = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+ EXPECT_EQ(0, sem_args.flags);
-+
-+ sem_args.count = 0;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EOVERFLOW, errno);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ wait_args.timeout = (uintptr_t)&timeout;
-+ wait_args.objs = (uintptr_t)&sem_args.sem;
-+ wait_args.count = 1;
-+ wait_args.owner = 123;
-+ wait_args.index = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, wait_args.index);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EWOULDBLOCK, errno);
-+
-+ sem_args.count = 3;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EOVERFLOW, errno);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ /* Test PULSE. */
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EOVERFLOW, errno);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ close(fd);
-+}
-+
-+TEST(semaphore_state_getonwait)
-+{
-+ struct winesync_wait_args wait_args;
++ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args;
+ struct timespec timeout;
+ int fd, ret;
@@ -1872,7 +2372,6 @@ index 000000000000..52373fcd5c8c
+ sem_args.count = 3;
+ sem_args.max = 2;
+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = WINESYNC_SEM_GETONWAIT;
+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
@@ -1988,108 +2487,36 @@ index 000000000000..52373fcd5c8c
+ EXPECT_EQ(1, sem_args.count);
+ EXPECT_EQ(2, sem_args.max);
+
-+ /* Test GET. */
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EWOULDBLOCK, errno);
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(2, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(0, ret);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ /* Test PULSE. */
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EOVERFLOW, errno);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(1, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 1;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
-+ sem_args.count = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ sem_args.count = 0xdeadbeef;
-+ sem_args.max = 0xdeadbeef;
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+ EXPECT_EQ(2, sem_args.max);
-+
+ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem);
+ EXPECT_EQ(0, ret);
+
+ close(fd);
+}
+
++TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 2d2f5263338184cebd6166cbd9a16ec2484143dd Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:07:04 -0600
+Subject: [PATCH 15/25] selftests: winesync: Add some tests for mutex state.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 250 ++++++++++++++++++
+ 1 file changed, 250 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index da3aa2c24671..f5562a645379 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -150,4 +150,254 @@ TEST(semaphore_state)
+ close(fd);
+ }
+
+TEST(mutex_state)
+{
-+ struct winesync_wait_args wait_args;
++ struct winesync_wait_args wait_args = {0};
+ struct winesync_mutex_args mutex_args;
+ struct timespec timeout;
+ __u32 owner;
@@ -2337,14 +2764,35 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From c5dbac5e814a4b73d98357fb010da08c28556e18 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:07:45 -0600
+Subject: [PATCH 16/25] selftests: winesync: Add some tests for
+ WINESYNC_IOC_WAIT_ANY.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 197 ++++++++++++++++++
+ 1 file changed, 197 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index f5562a645379..1147ebb227da 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -400,4 +400,201 @@ TEST(mutex_state)
+ close(fd);
+ }
+
+TEST(wait_any)
+{
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
+ struct timespec timeout;
-+ __s32 objs[2];
-+ __u32 owner;
++ __u32 objs[2], owner;
+ int fd, ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
@@ -2355,7 +2803,6 @@ index 000000000000..52373fcd5c8c
+ sem_args.count = 2;
+ sem_args.max = 3;
+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = WINESYNC_SEM_GETONWAIT;
+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
@@ -2536,14 +2983,35 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 28fa83f6bb6a5fb7c03cbdc9805b793b7ffa8b54 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:08:25 -0600
+Subject: [PATCH 17/25] selftests: winesync: Add some tests for
+ WINESYNC_IOC_WAIT_ALL.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 151 ++++++++++++++++++
+ 1 file changed, 151 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index 1147ebb227da..3c8ed06946db 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -597,4 +597,155 @@ TEST(wait_any)
+ close(fd);
+ }
+
+TEST(wait_all)
+{
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
+ struct timespec timeout;
-+ __s32 objs[2];
-+ __u32 owner;
++ __u32 objs[2], owner;
+ int fd, ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
@@ -2554,7 +3022,6 @@ index 000000000000..52373fcd5c8c
+ sem_args.count = 2;
+ sem_args.max = 3;
+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = WINESYNC_SEM_GETONWAIT;
+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
@@ -2689,12 +3156,34 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 4da2c162de716164d8461479794391a2c0e042d1 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:08:54 -0600
+Subject: [PATCH 18/25] selftests: winesync: Add some tests for invalid object
+ handling.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 93 +++++++++++++++++++
+ 1 file changed, 93 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index 3c8ed06946db..59ad45f46969 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -748,4 +748,97 @@ TEST(wait_all)
+ close(fd);
+ }
+
+TEST(invalid_objects)
+{
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
-+ __s32 objs[2] = {0};
++ __u32 objs[2] = {0};
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
@@ -2704,18 +3193,10 @@ index 000000000000..52373fcd5c8c
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
-+ ret = ioctl(fd, WINESYNC_IOC_GET_SEM, &sem_args.sem);
-+ EXPECT_EQ(-1, ret);
-+ EXPECT_EQ(EINVAL, errno);
-+
+ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
@@ -2790,10 +3271,32 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 4f0f9ab195cd71122df16c613996088f10432477 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:09:32 -0600
+Subject: [PATCH 19/25] selftests: winesync: Add some tests for wakeup
+ signaling with WINESYNC_IOC_WAIT_ANY.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 166 ++++++++++++++++++
+ 1 file changed, 166 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index 59ad45f46969..cdf69c9ff4a9 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -841,4 +841,170 @@ TEST(invalid_objects)
+ close(fd);
+ }
+
+struct wake_args
+{
+ int fd;
-+ __s32 obj;
++ __u32 obj;
+};
+
+struct wait_args
@@ -2837,9 +3340,8 @@ index 000000000000..52373fcd5c8c
+ struct winesync_sem_args sem_args = {0};
+ struct wait_args thread_args;
+ struct timespec timeout;
++ __u32 objs[2], owner;
+ pthread_t thread;
-+ __s32 objs[2];
-+ __u32 owner;
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
@@ -2848,7 +3350,6 @@ index 000000000000..52373fcd5c8c
+ sem_args.count = 0;
+ sem_args.max = 3;
+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = WINESYNC_SEM_GETONWAIT;
+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
@@ -2894,30 +3395,6 @@ index 000000000000..52373fcd5c8c
+ EXPECT_EQ(0, thread_args.ret);
+ EXPECT_EQ(0, wait_args.index);
+
-+ /* test waking the semaphore via pulse */
-+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.owner = 456;
-+ 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 = 2;
-+ ret = ioctl(fd, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, thread_args.ret);
-+ EXPECT_EQ(0, wait_args.index);
-+
+ /* test waking the mutex */
+
+ /* first grab it again for owner 123 */
@@ -2982,6 +3459,28 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 0721111ee1f1b574f565101638b07952a5c6fe62 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:09:36 -0600
+Subject: [PATCH 20/25] selftests: winesync: Add some tests for wakeup
+ signaling with WINESYNC_IOC_WAIT_ALL.
+
+---
+ .../selftests/drivers/winesync/winesync.c | 121 ++++++++++++++++++
+ 1 file changed, 121 insertions(+)
+
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index cdf69c9ff4a9..19b6bd6e4b9b 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -1007,4 +1007,125 @@ TEST(wake_any)
+ close(fd);
+ }
+
+TEST(wake_all)
+{
+ struct winesync_wait_args wait_args = {0}, wait_args2 = {0};
@@ -2989,9 +3488,8 @@ index 000000000000..52373fcd5c8c
+ struct winesync_sem_args sem_args = {0};
+ struct timespec timeout, timeout2;
+ struct wait_args thread_args;
++ __u32 objs[2], owner;
+ pthread_t thread;
-+ __s32 objs[2];
-+ __u32 owner;
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
@@ -3000,7 +3498,6 @@ index 000000000000..52373fcd5c8c
+ sem_args.count = 0;
+ sem_args.max = 3;
+ sem_args.sem = 0xdeadbeef;
-+ sem_args.flags = WINESYNC_SEM_GETONWAIT;
+ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
@@ -3064,14 +3561,14 @@ index 000000000000..52373fcd5c8c
+ EXPECT_EQ(0, mutex_args.count);
+ EXPECT_EQ(0, mutex_args.owner);
+
-+ sem_args.count = 1;
++ sem_args.count = 2;
+ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, sem_args.count);
+
+ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
++ EXPECT_EQ(1, sem_args.count);
+
+ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args);
+ EXPECT_EQ(0, ret);
@@ -3082,25 +3579,6 @@ index 000000000000..52373fcd5c8c
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
+
-+ /* test waking the semaphore via pulse */
-+
-+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
-+ wait_args.owner = 456;
-+ 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, WINESYNC_IOC_PULSE_SEM, &sem_args);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, sem_args.count);
-+
-+ ret = wait_for_thread(thread, 100);
-+ EXPECT_EQ(0, ret);
-+ EXPECT_EQ(0, thread_args.ret);
-+
+ /* delete an object while it's being waited on */
+
+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 200);
@@ -3124,4 +3602,774 @@ index 000000000000..52373fcd5c8c
+ close(fd);
+}
+
-+TEST_HARNESS_MAIN
+ TEST_HARNESS_MAIN
+--
+2.34.1
+
+From 307a15f378dd5051608d9150dd8d0968a474a278 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 12:22:55 -0600
+Subject: [PATCH 21/25] maintainers: Add an entry for winesync.
+
+---
+ MAINTAINERS | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 3b79fd441dde..4f1b799f8302 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -20227,6 +20227,15 @@ M: David Härdeman <david@hardeman.nu>
+ S: Maintained
+ F: drivers/media/rc/winbond-cir.c
+
++WINESYNC SYNCHRONIZATION PRIMITIVE DRIVER
++M: Zebediah Figura <zfigura@codeweavers.com>
++L: wine-devel@winehq.org
++S: Supported
++F: Documentation/userspace-api/winesync.rst
++F: drivers/misc/winesync.c
++F: include/uapi/linux/winesync.h
++F: tools/testing/selftests/drivers/winesync/
++
+ WINSYSTEMS EBC-C384 WATCHDOG DRIVER
+ M: William Breathitt Gray <vilhelm.gray@gmail.com>
+ L: linux-watchdog@vger.kernel.org
+--
+2.34.1
+
+From de7b97344dd087e85f01b88b31b23173821ddfe6 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Thu, 10 Jun 2021 20:48:58 -0500
+Subject: [PATCH 22/25] winesync: Introduce the WINESYNC_WAIT_FLAG_GET flag.
+
+---
+ drivers/misc/winesync.c | 49 +++++++-----
+ include/uapi/linux/winesync.h | 7 ++
+ .../selftests/drivers/winesync/winesync.c | 80 ++++++++++++-------
+ 3 files changed, 87 insertions(+), 49 deletions(-)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index a9a6d1b7970a..7b7b0807765a 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -75,6 +75,7 @@ struct winesync_q_entry {
+ struct list_head node;
+ struct winesync_q *q;
+ struct winesync_obj *obj;
++ __u32 flags;
+ __u32 index;
+ };
+
+@@ -225,18 +226,23 @@ static void try_wake_all(struct winesync_device *dev, struct winesync_q *q,
+
+ if (can_wake && atomic_cmpxchg(&q->signaled, -1, 0) == -1) {
+ for (i = 0; i < count; i++) {
+- struct winesync_obj *obj = q->entries[i].obj;
++ struct winesync_q_entry *entry = &q->entries[i];
++ struct winesync_obj *obj = entry->obj;
+
+ switch (obj->type) {
+ case WINESYNC_TYPE_SEM:
+- obj->u.sem.count--;
++ if (entry->flags & WINESYNC_WAIT_FLAG_GET)
++ obj->u.sem.count--;
+ break;
+ case WINESYNC_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;
++
++ if (entry->flags & WINESYNC_WAIT_FLAG_GET) {
++ obj->u.mutex.ownerdead = false;
++ obj->u.mutex.count++;
++ obj->u.mutex.owner = q->owner;
++ }
+ break;
+ }
+ }
+@@ -274,7 +280,8 @@ static void try_wake_any_sem(struct winesync_obj *sem)
+ break;
+
+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
+- sem->u.sem.count--;
++ if (entry->flags & WINESYNC_WAIT_FLAG_GET)
++ sem->u.sem.count--;
+ wake_up_process(q->task);
+ }
+ }
+@@ -297,9 +304,12 @@ static void try_wake_any_mutex(struct winesync_obj *mutex)
+ 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;
++
++ if (entry->flags & WINESYNC_WAIT_FLAG_GET) {
++ mutex->u.mutex.ownerdead = false;
++ mutex->u.mutex.count++;
++ mutex->u.mutex.owner = q->owner;
++ }
+ wake_up_process(q->task);
+ }
+ }
+@@ -682,9 +692,9 @@ static int setup_wait(struct winesync_device *dev,
+ {
+ const void __user *sigmask = u64_to_user_ptr(args->sigmask);
+ const __u32 count = args->count;
++ struct winesync_wait_obj *objs;
+ struct winesync_q *q;
+ ktime_t timeout = 0;
+- __u32 *ids;
+ __u32 i, j;
+ int ret;
+
+@@ -709,18 +719,18 @@ static int setup_wait(struct winesync_device *dev,
+ timeout = timespec64_to_ns(&to);
+ }
+
+- ids = kmalloc_array(args->count, sizeof(*ids), GFP_KERNEL);
+- if (!ids)
++ objs = kmalloc_array(args->count, sizeof(*objs), GFP_KERNEL);
++ if (!objs)
+ return -ENOMEM;
+- if (copy_from_user(ids, u64_to_user_ptr(args->objs),
+- array_size(args->count, sizeof(*ids)))) {
+- kfree(ids);
++ if (copy_from_user(objs, u64_to_user_ptr(args->objs),
++ array_size(args->count, sizeof(*objs)))) {
++ kfree(objs);
+ return -EFAULT;
+ }
+
+ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
+ if (!q) {
+- kfree(ids);
++ kfree(objs);
+ return -ENOMEM;
+ }
+ q->task = current;
+@@ -732,7 +742,7 @@ static int setup_wait(struct winesync_device *dev,
+
+ for (i = 0; i < count; i++) {
+ struct winesync_q_entry *entry = &q->entries[i];
+- struct winesync_obj *obj = get_obj(dev, ids[i]);
++ struct winesync_obj *obj = get_obj(dev, objs[i].obj);
+
+ if (!obj)
+ goto err;
+@@ -750,9 +760,10 @@ static int setup_wait(struct winesync_device *dev,
+ entry->obj = obj;
+ entry->q = q;
+ entry->index = i;
++ entry->flags = objs[i].flags;
+ }
+
+- kfree(ids);
++ kfree(objs);
+
+ *ret_q = q;
+ *ret_timeout = timeout;
+@@ -761,7 +772,7 @@ static int setup_wait(struct winesync_device *dev,
+ err:
+ for (j = 0; j < i; j++)
+ put_obj(q->entries[j].obj);
+- kfree(ids);
++ kfree(objs);
+ kfree(q);
+ return -EINVAL;
+ }
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 1dccdb3877ec..04f5006089ca 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -22,6 +22,13 @@ struct winesync_mutex_args {
+ __u32 count;
+ };
+
++#define WINESYNC_WAIT_FLAG_GET (1 << 0)
++
++struct winesync_wait_obj {
++ __u32 obj;
++ __u32 flags;
++};
++
+ struct winesync_wait_args {
+ __u64 sigmask;
+ __u64 sigsetsize;
+diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c
+index 19b6bd6e4b9b..2a7008c9c198 100644
+--- a/tools/testing/selftests/drivers/winesync/winesync.c
++++ b/tools/testing/selftests/drivers/winesync/winesync.c
+@@ -18,6 +18,7 @@ TEST(semaphore_state)
+ {
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args;
++ struct winesync_wait_obj wait_obj;
+ struct timespec timeout;
+ int fd, ret;
+
+@@ -71,8 +72,10 @@ TEST(semaphore_state)
+ EXPECT_EQ(2, sem_args.count);
+ EXPECT_EQ(2, sem_args.max);
+
++ wait_obj.obj = sem_args.sem;
++ wait_obj.flags = WINESYNC_WAIT_FLAG_GET;
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)&sem_args.sem;
++ wait_args.objs = (uintptr_t)&wait_obj;
+ wait_args.count = 1;
+ wait_args.owner = 123;
+ wait_args.index = 0xdeadbeef;
+@@ -154,6 +157,7 @@ TEST(mutex_state)
+ {
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_mutex_args mutex_args;
++ struct winesync_wait_obj wait_obj;
+ struct timespec timeout;
+ __u32 owner;
+ int fd, ret;
+@@ -240,8 +244,10 @@ TEST(mutex_state)
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EPERM, errno);
+
++ wait_obj.obj = mutex_args.mutex;
++ wait_obj.flags = WINESYNC_WAIT_FLAG_GET;
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)&mutex_args.mutex;
++ wait_args.objs = (uintptr_t)&wait_obj;
+ wait_args.count = 1;
+ wait_args.owner = 456;
+ wait_args.index = 0xdeadbeef;
+@@ -405,8 +411,9 @@ TEST(wait_any)
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
++ struct winesync_wait_obj wait_objs[2];
+ struct timespec timeout;
+- __u32 objs[2], owner;
++ __u32 owner;
+ int fd, ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+@@ -428,18 +435,20 @@ TEST(wait_any)
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
+- objs[0] = sem_args.sem;
+- objs[1] = mutex_args.mutex;
++ wait_objs[0].obj = sem_args.sem;
++ wait_objs[0].flags = WINESYNC_WAIT_FLAG_GET;
++ wait_objs[1].obj = mutex_args.mutex;
++ wait_objs[1].flags = WINESYNC_WAIT_FLAG_GET;
+
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)objs;
++ wait_args.objs = (uintptr_t)wait_objs;
+ wait_args.count = 2;
+ wait_args.owner = 123;
+ wait_args.index = 0xdeadbeef;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, wait_args.index);
+- EXPECT_EQ((uintptr_t)objs, wait_args.objs);
++ EXPECT_EQ((uintptr_t)wait_objs, wait_args.objs);
+ EXPECT_EQ(2, wait_args.count);
+ EXPECT_EQ(123, wait_args.owner);
+
+@@ -571,7 +580,7 @@ TEST(wait_any)
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, sem_args.count);
+
+- objs[0] = objs[1] = sem_args.sem;
++ wait_objs[0].obj = wait_objs[1].obj = sem_args.sem;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, wait_args.index);
+@@ -602,8 +611,9 @@ TEST(wait_all)
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
++ struct winesync_wait_obj wait_objs[2];
+ struct timespec timeout;
+- __u32 objs[2], owner;
++ __u32 owner;
+ int fd, ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
+@@ -625,16 +635,18 @@ TEST(wait_all)
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
+- objs[0] = sem_args.sem;
+- objs[1] = mutex_args.mutex;
++ wait_objs[0].obj = sem_args.sem;
++ wait_objs[0].flags = WINESYNC_WAIT_FLAG_GET;
++ wait_objs[1].obj = mutex_args.mutex;
++ wait_objs[1].flags = WINESYNC_WAIT_FLAG_GET;
+
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)objs;
++ wait_args.objs = (uintptr_t)wait_objs;
+ wait_args.count = 2;
+ wait_args.owner = 123;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args);
+ EXPECT_EQ(0, ret);
+- EXPECT_EQ((uintptr_t)objs, wait_args.objs);
++ EXPECT_EQ((uintptr_t)wait_objs, wait_args.objs);
+ EXPECT_EQ(2, wait_args.count);
+ EXPECT_EQ(123, wait_args.owner);
+
+@@ -735,7 +747,7 @@ TEST(wait_all)
+ EXPECT_EQ(123, mutex_args.owner);
+
+ /* test waiting on the same object twice */
+- objs[0] = objs[1] = sem_args.sem;
++ wait_objs[0].obj = wait_objs[1].obj = sem_args.sem;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+@@ -751,9 +763,9 @@ TEST(wait_all)
+ TEST(invalid_objects)
+ {
+ struct winesync_mutex_args mutex_args = {0};
++ struct winesync_wait_obj wait_objs[2] = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
+- __u32 objs[2] = {0};
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
+@@ -775,7 +787,7 @@ TEST(invalid_objects)
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+- wait_args.objs = (uintptr_t)objs;
++ wait_args.objs = (uintptr_t)wait_objs;
+ wait_args.count = 1;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
+ EXPECT_EQ(-1, ret);
+@@ -784,7 +796,7 @@ TEST(invalid_objects)
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+- ret = ioctl(fd, WINESYNC_IOC_DELETE, &objs[0]);
++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+@@ -801,8 +813,8 @@ TEST(invalid_objects)
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+- objs[0] = sem_args.sem;
+- objs[1] = sem_args.sem + 1;
++ wait_objs[0].obj = sem_args.sem;
++ wait_objs[1].obj = sem_args.sem + 1;
+ wait_args.count = 2;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
+ EXPECT_EQ(-1, ret);
+@@ -811,8 +823,8 @@ TEST(invalid_objects)
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+
+- objs[0] = sem_args.sem + 1;
+- objs[1] = sem_args.sem;
++ wait_objs[0].obj = sem_args.sem + 1;
++ wait_objs[1].obj = sem_args.sem;
+ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+@@ -886,10 +898,11 @@ TEST(wake_any)
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_wait_args wait_args = {0};
+ struct winesync_sem_args sem_args = {0};
++ struct winesync_wait_obj wait_objs[2];
+ struct wait_args thread_args;
+ struct timespec timeout;
+- __u32 objs[2], owner;
+ pthread_t thread;
++ __u32 owner;
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
+@@ -909,14 +922,16 @@ TEST(wake_any)
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
+- objs[0] = sem_args.sem;
+- objs[1] = mutex_args.mutex;
++ wait_objs[0].obj = sem_args.sem;
++ wait_objs[0].flags = WINESYNC_WAIT_FLAG_GET;
++ wait_objs[1].obj = mutex_args.mutex;
++ wait_objs[1].flags = WINESYNC_WAIT_FLAG_GET;
+
+ /* test waking the semaphore */
+
+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)objs;
++ wait_args.objs = (uintptr_t)wait_objs;
+ wait_args.count = 2;
+ wait_args.owner = 456;
+ wait_args.index = 0xdeadbeef;
+@@ -1010,12 +1025,13 @@ TEST(wake_any)
+ TEST(wake_all)
+ {
+ struct winesync_wait_args wait_args = {0}, wait_args2 = {0};
++ struct winesync_wait_obj wait_objs[2], wait_obj2;
+ struct winesync_mutex_args mutex_args = {0};
+ struct winesync_sem_args sem_args = {0};
+ struct timespec timeout, timeout2;
+ struct wait_args thread_args;
+- __u32 objs[2], owner;
+ pthread_t thread;
++ __u32 owner;
+ int fd, ret;
+
+ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY);
+@@ -1035,12 +1051,14 @@ TEST(wake_all)
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
+- objs[0] = sem_args.sem;
+- objs[1] = mutex_args.mutex;
++ wait_objs[0].obj = sem_args.sem;
++ wait_objs[0].flags = WINESYNC_WAIT_FLAG_GET;
++ wait_objs[1].obj = mutex_args.mutex;
++ wait_objs[1].flags = WINESYNC_WAIT_FLAG_GET;
+
+ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000);
+ wait_args.timeout = (uintptr_t)&timeout;
+- wait_args.objs = (uintptr_t)objs;
++ wait_args.objs = (uintptr_t)wait_objs;
+ wait_args.count = 2;
+ wait_args.owner = 456;
+ thread_args.fd = fd;
+@@ -1064,9 +1082,11 @@ TEST(wake_all)
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, sem_args.count);
+
++ wait_obj2.obj = sem_args.sem;
++ wait_obj2.flags = WINESYNC_WAIT_FLAG_GET;
+ get_abs_timeout(&timeout2, CLOCK_MONOTONIC, 0);
+ wait_args2.timeout = (uintptr_t)&timeout2;
+- wait_args2.objs = (uintptr_t)&sem_args.sem;
++ wait_args2.objs = (uintptr_t)&wait_obj2;
+ wait_args2.count = 1;
+ wait_args2.owner = 123;
+ wait_args2.index = 0xdeadbeef;
+--
+2.34.1
+
+From fb2424bce2139f69ce38516525021e6288024569 Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Thu, 10 Jun 2021 20:49:21 -0500
+Subject: [PATCH 23/25] doc: Document the WINESYNC_WAIT_FLAG_GET flag.
+
+---
+ Documentation/userspace-api/winesync.rst | 111 ++++++++++++++---------
+ 1 file changed, 70 insertions(+), 41 deletions(-)
+
+diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst
+index 009171a187b7..bd63d8afc969 100644
+--- a/Documentation/userspace-api/winesync.rst
++++ b/Documentation/userspace-api/winesync.rst
+@@ -59,7 +59,7 @@ shared across multiple processes.
+ ioctl reference
+ ===============
+
+-All operations on the device are done through ioctls. There are three
++All operations on the device are done through ioctls. There are four
+ structures used in ioctl calls::
+
+ struct winesync_sem_args {
+@@ -74,6 +74,12 @@ structures used in ioctl calls::
+ __u32 count;
+ };
+
++ /* used in struct winesync_wait_args */
++ struct winesync_wait_obj {
++ __u32 obj;
++ __u32 flags;
++ };
++
+ struct winesync_wait_args {
+ __u64 sigmask;
+ __u64 sigsetsize;
+@@ -238,9 +244,9 @@ The ioctls are as follows:
+
+ .. c:macro:: WINESYNC_IOC_WAIT_ANY
+
+- Poll on any of a list of objects, atomically acquiring at most one.
+- Takes a pointer to struct :c:type:`winesync_wait_args`, which is
+- used as follows:
++ Poll on any of a list of objects, possibly acquiring at most one of
++ them. Takes a pointer to struct :c:type:`winesync_wait_args`, which
++ is used as follows:
+
+ ``sigmask`` is an optional input-only pointer to a
+ :c:type:`sigset_t` structure (specified as an integer so that the
+@@ -262,10 +268,14 @@ The ioctls are as follows:
+ ``timeout`` is zero, i.e. NULL, the function will sleep until an
+ object is signaled, and will not fail with ``ETIMEDOUT``.
+
+- ``objs`` is a input-only 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``.
++ ``objs`` is a input-only pointer to an array of ``count``
++ consecutive ``winesync_wait_obj`` structures (specified as an
++ integer so that the structure has the same size regardless of
++ architecture). In each structure, ``obj`` denotes an object to
++ wait for, and ``flags`` specifies a combination of zero or more
++ ``WINESYNC_WAIT_FLAG_*`` flags modifying the behaviour when
++ waiting for that object. If any identifier is invalid, the
++ function fails with ``EINVAL``.
+
+ ``owner`` is an input-only argument denoting the mutex owner
+ identifier. If any object in ``objs`` is a mutex, the ioctl will
+@@ -278,11 +288,15 @@ The ioctls are as follows:
+
+ ``pad`` is unused, and exists to keep a consistent structure size.
+
+- 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.
++ This function sleeps until one or more of the given objects is
++ signaled, subsequently returning the index of the first signaled
++ object, or until the timeout expires. In the latter case it fails
++ with ``ETIMEDOUT``.
++
++ Each object may optionally be accompanied by the
++ ``WINESYNC_WAIT_FLAG_GET`` flag. If an object marked with this flag
++ becomes signaled, the object will be atomically acquired by the
++ waiter.
+
+ A semaphore is considered to be signaled if its count is nonzero,
+ and is acquired by decrementing its count by one. A mutex is
+@@ -293,16 +307,27 @@ The ioctls are as follows:
+
+ 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 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.
++ different ``owner`` identifiers) are queued on the same mutex, both
++ with the ``WINESYNC_WAIT_FLAG_GET`` flag set, only one is signaled.
++ If two wait operations are queued on the same semaphore, both with
++ the ``WINESYNC_WAIT_FLAG_GET`` flag set, and a value of one is
++ posted to it, only one is signaled. The order in which threads are
++ signaled is not specified.
++
++ On the other hand, if neither waiter specifies
++ ``WINESYNC_WAIT_FLAG_GET``, and the object becomes signaled, both
++ waiters will be woken, and the object will not be modified. If one
++ waiter specifies ``WINESYNC_WAIT_FLAG_GET``, that waiter will be
++ woken and will acquire the object; it is unspecified whether the
++ other waiter will be woken.
++
++ If a mutex is inconsistent (in which case it is unacquired and
++ therefore signaled), the ioctl fails with ``EOWNERDEAD``. Although
++ this is a failure return, the function may otherwise be considered
++ successful, and ``index`` is still set to the index of the mutex. If
++ ``WINESYNC_WAIT_FLAG_GET`` is specified for said mutex, the mutex is
++ marked as owned by the given owner (with a recursion count of 1) and
++ as no longer inconsistent.
+
+ It is valid to pass the same object more than once. If a wakeup
+ occurs due to that object being signaled, ``index`` is set to the
+@@ -313,28 +338,32 @@ The ioctls are as follows:
+
+ .. c:macro:: WINESYNC_IOC_WAIT_ALL
+
+- Poll on a list of objects, atomically acquiring all of them. Takes a
+- pointer to struct :c:type:`winesync_wait_args`, which is used
+- identically to ``WINESYNC_IOC_WAIT_ANY``, except that ``index`` is
+- always filled with zero on success.
++ Poll on a list of objects, waiting until all of them are
++ simultaneously signaled. Takes a pointer to struct
++ :c:type:`winesync_wait_args`, which is used identically to
++ ``WINESYNC_IOC_WAIT_ANY``, except that ``index`` is always filled
++ with zero on success.
+
+- 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.
++ This function sleeps until all of the given objects are signaled. If
++ all objects are not simultaneously signaled at any point before the
++ timeout expires, it fails with ``ETIMEDOUT``.
+
+ 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 ``WINESYNC_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
++ once all objects are simultaneously signaled does the ioctl return.
++
++ The flag ``WINESYNC_WAIT_FLAG_GET`` may optionally be specified for
++ some or all of the objects, in which case the function will also
++ simultaneously acquire every object so marked. The entire
++ acquisition is atomic and totally ordered with respect to other
++ operations on any of the given objects.
++
++ If any mutex waited for is inconsistent at the time the function
++ returns, the ioctl fails with ``EOWNERDEAD``. Similarly to
++ ``WINESYNC_IOC_WAIT_ANY``, the function may be considered to have
++ succeeded, and all objects marked with ``WINESYNC_WIAT_FLAG_GET``
++ are still acquired. Note that if multiple mutex objects are
++ specified, there is no way to know which were marked as
+ inconsistent.
+
+ Unlike ``WINESYNC_IOC_WAIT_ANY``, it is not valid to pass the same
+--
+2.34.1
+
+From 2e364aabcb2fe2d117d00e498288fafee27250db Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 17:21:26 -0600
+Subject: [PATCH 24/25] winesync: Introduce WINESYNC_IOC_PULSE_SEM.
+
+---
+ drivers/misc/winesync.c | 13 +++++++++++--
+ include/uapi/linux/winesync.h | 2 ++
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c
+index 7b7b0807765a..e9db3b199238 100644
+--- a/drivers/misc/winesync.c
++++ b/drivers/misc/winesync.c
+@@ -411,7 +411,8 @@ static int put_sem_state(struct winesync_obj *sem, __u32 count)
+ return 0;
+ }
+
+-static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
++static int winesync_put_sem(struct winesync_device *dev, void __user *argp,
++ bool pulse)
+ {
+ struct winesync_sem_args __user *user_args = argp;
+ struct winesync_sem_args args;
+@@ -441,6 +442,9 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
+ try_wake_any_sem(sem);
+ }
+
++ if (pulse)
++ sem->u.sem.count = 0;
++
+ spin_unlock(&sem->lock);
+ spin_unlock(&dev->wait_all_lock);
+ } else {
+@@ -451,6 +455,9 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp)
+ if (!ret)
+ try_wake_any_sem(sem);
+
++ if (pulse)
++ sem->u.sem.count = 0;
++
+ spin_unlock(&sem->lock);
+ }
+
+@@ -959,7 +966,9 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
+ case WINESYNC_IOC_DELETE:
+ return winesync_delete(dev, argp);
+ case WINESYNC_IOC_PUT_SEM:
+- return winesync_put_sem(dev, argp);
++ return winesync_put_sem(dev, argp, false);
++ case WINESYNC_IOC_PULSE_SEM:
++ return winesync_put_sem(dev, argp, true);
+ case WINESYNC_IOC_PUT_MUTEX:
+ return winesync_put_mutex(dev, argp);
+ case WINESYNC_IOC_READ_SEM:
+diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h
+index 04f5006089ca..f2e1c85befa8 100644
+--- a/include/uapi/linux/winesync.h
++++ b/include/uapi/linux/winesync.h
+@@ -60,5 +60,7 @@ struct winesync_wait_args {
+ struct winesync_sem_args)
+ #define WINESYNC_IOC_READ_MUTEX _IOWR(WINESYNC_IOC_BASE, 9, \
+ struct winesync_mutex_args)
++#define WINESYNC_IOC_PULSE_SEM _IOWR(WINESYNC_IOC_BASE, 10, \
++ struct winesync_sem_args)
+
+ #endif
+--
+2.34.1
+
+From ee18b220dde45003cd7ce7360fe3e633678b97df Mon Sep 17 00:00:00 2001
+From: Zebediah Figura <z.figura12@gmail.com>
+Date: Fri, 5 Mar 2021 17:21:47 -0600
+Subject: [PATCH 25/25] doc: Document WINESYNC_IOC_PULSE_SEM.
+
+---
+ Documentation/userspace-api/winesync.rst | 35 ++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst
+index bd63d8afc969..6e0dde2c5eef 100644
+--- a/Documentation/userspace-api/winesync.rst
++++ b/Documentation/userspace-api/winesync.rst
+@@ -166,6 +166,41 @@ The ioctls are as follows:
+ The operation is atomic and totally ordered with respect to other
+ operations on the same semaphore.
+
++.. c:macro:: WINESYNC_IOC_PULSE_SEM
++
++ This operation is identical to ``WINESYNC_IOC_PUT_SEM``, with one
++ notable exception: the semaphore is always left in an *unsignaled*
++ state, regardless of the initial count or the count added by the
++ ioctl. That is, the count after a pulse operation will always be
++ zero.
++
++ A pulse operation can be thought of as a put operation, followed by
++ clearing the semaphore's current count back to zero. Confer the
++ following examples:
++
++ * If three eligible threads are waiting on a semaphore, all with
++ ``WINESYNC_WAIT_FLAG_GET``, and the semaphore is pulsed with a
++ count of 2, only two of them will be woken, and the third will
++ remain asleep.
++
++ * If only one such thread is waiting, it will be woken up, but the
++ semaphore's count will remain at zero.
++
++ * If three eligible threads are waiting and none of them specify
++ ``WINESYNC_WAIT_FLAG_GET``, all three threads will be woken, and
++ the semaphore's count will remain at zero.
++
++ In either case, a simultaneous ``WINESYNC_IOC_READ_SEM`` ioctl from
++ another thread will always report a count of zero.
++
++ If adding ``count`` to the semaphore's current count would raise the
++ latter past the semaphore's maximum count, the ioctl fails with
++ ``EOVERFLOW``. However, in this case the semaphore's count will
++ still be reset to zero.
++
++ The operation is atomic and totally ordered with respect to other
++ operations on the same semaphore.
++
+ .. c:macro:: WINESYNC_IOC_PUT_MUTEX
+
+ Release a mutex object. Takes a pointer to struct
+--
+2.34.1
+