diff --git a/PKGBUILD b/PKGBUILD index 193abeb..f266f54 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -53,7 +53,7 @@ else fi pkgname=("${pkgbase}" "${pkgbase}-headers") pkgver="${_basekernel}"."${_sub}" -pkgrel=137 +pkgrel=138 pkgdesc='Linux-tkg' arch=('x86_64') # no i686 in here url="http://www.kernel.org/" @@ -371,6 +371,7 @@ case $_basever in 0006-add-acs-overrides_iommu.patch 0007-v5.11-fsync.patch 0007-v5.11-futex2_interface.patch + 0007-v5.11-winesync.patch #0008-5.11-bcachefs.patch 0009-glitched-ondemand-bmq.patch 0009-glitched-bmq.patch @@ -399,6 +400,7 @@ case $_basever in '19661ec0d39f9663452b34433214c755179894528bf73a42f6ba52ccf572832a' 'b302ba6c5bbe8ed19b20207505d513208fae1e678cf4d8e7ac0b154e5fe3f456' '073e7b8ab48aa9abdb5cedb5c729a2f624275ebdbe1769476231c9e712145496' + '2d2d6f03b53339177e81a39a871345f2e1b492f8f0687ba3843eb845a7bc4d4f' '9fad4a40449e09522899955762c8928ae17f4cdaa16e01239fd12592e9d58177' 'a557b342111849a5f920bbe1c129f3ff1fc1eff62c6bd6685e0972fc88e39911' 'e394d4b7721f55837a8364c8311cb06cb5a59484de8aa8731e38d1aff2b7014e' diff --git a/customization.cfg b/customization.cfg index c8cc113..ce8da65 100644 --- a/customization.cfg +++ b/customization.cfg @@ -125,6 +125,9 @@ _fsync="" # https://gitlab.collabora.com/tonyk/linux/-/tree/futex2-dev _futex2="" +# Set to "true" to enable support for winesync, an experimental replacement for esync - requires patched wine - https://repo.or.cz/linux/zf.git/shortlog/refs/heads/winesync +_winesync="" + # Set to "true" to enable the Binder and Ashmem, the kernel modules required to use the android emulator Anbox. _anbox="" diff --git a/linux-tkg-config/prepare b/linux-tkg-config/prepare index 4d72d70..1e4d426 100644 --- a/linux-tkg-config/prepare +++ b/linux-tkg-config/prepare @@ -1100,6 +1100,18 @@ CONFIG_DEBUG_INFO_BTF_MODULES=y\n echo "CONFIG_FUTEX2=y" >> ./.config fi + # winesync support + if [ -z "$_winesync" ]; then + plain "" + plain "Enable support for winesync, an experimental replacement for esync" + plain "https://repo.or.cz/linux/zf.git/shortlog/refs/heads/winesync" + read -rp "`echo $' > N/y : '`" CONDITION_winesync; + fi + if [[ "$CONDITION_winesync" =~ [yY] ]] || [ "$_winesync" = "true" ]; then + msg2 "Patching winesync support" + tkgpatch="$srcdir/0007-v${_basekernel}-winesync.patch" && _tkg_patcher + fi + # ZFS fix if [ "$_basever" = "54" ] || [ "$_basever" = "57" ] || [ "$_basever" = "58" ]; then if [ -z "$_zfsfix" ]; then diff --git a/linux-tkg-patches/5.11/0007-v5.11-winesync.patch b/linux-tkg-patches/5.11/0007-v5.11-winesync.patch new file mode 100644 index 0000000..eb2c70e --- /dev/null +++ b/linux-tkg-patches/5.11/0007-v5.11-winesync.patch @@ -0,0 +1,4402 @@ +From dedf0e7ca660ff6ac3fbc1863207cfd6fba4dbaa Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 10:50:45 -0600 +Subject: [PATCH] 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 fafa8b0d8099..25bf30ab958a 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -466,6 +466,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 d23231e73330..8b1d4ae4ed1b 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -57,3 +57,4 @@ 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 +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 ++#include ++#include ++ ++#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.11.4.GIT + +From c09a117631f9dbd7e0ecd44775e5b73d1ccfd017 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 10:57:06 -0600 +Subject: [PATCH] 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 63fd4e6a014b..dc9a30b5f1a2 100644 +--- a/Documentation/admin-guide/devices.txt ++++ b/Documentation/admin-guide/devices.txt +@@ -376,8 +376,9 @@ + 240 = /dev/userio Serio driver testing device + 241 = /dev/vhost-vsock Host kernel driver for virtio vsock + 242 = /dev/rfkill Turning off radio transmissions (rfkill) ++ 243 = /dev/winesync Wine synchronization primitive device + +- 243-254 Reserved for local use ++ 244-254 Reserved for local use + 255 Reserved for MISC_DYNAMIC_MINOR + + 11 char Raw keyboard device (Linux/SPARC only) +diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst +index a4c75a28c839..e79d6b307e9b 100644 +--- a/Documentation/userspace-api/ioctl/ioctl-number.rst ++++ b/Documentation/userspace-api/ioctl/ioctl-number.rst +@@ -362,6 +362,8 @@ Code Seq# Include File Comments + + 0xF6 all LTTng Linux Trace Toolkit Next Generation + ++0xF7 00-0F uapi/linux/winesync.h Wine synchronization primitives ++ + 0xFD all linux/dm-ioctl.h + 0xFE all linux/isst_if.h + ==== ===== ======================================================= ================================================================ +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.11.4.GIT + +From 9ce65ab0a946055e4586650bef8effaf38b20d45 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:15:39 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_IOC_CREATE_SEM and + WINESYNC_IOC_DELETE. + +--- + drivers/misc/winesync.c | 127 ++++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/winesync.h | 25 +++++++++ + 2 files changed, 152 insertions(+) + create mode 100644 include/uapi/linux/winesync.h + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index 85cb6ccaa077..c59553f4b2ce 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -6,25 +6,152 @@ + */ + + #include ++#include + #include + #include ++#include ++#include + + #define WINESYNC_NAME "winesync" + ++enum winesync_type { ++ WINESYNC_TYPE_SEM, ++}; ++ ++struct winesync_obj { ++ struct kref refcount; ++ ++ enum winesync_type type; ++ ++ union { ++ struct { ++ __u32 count; ++ __u32 max; ++ } sem; ++ } u; ++}; ++ ++struct winesync_device { ++ spinlock_t table_lock; ++ struct idr objects; ++}; ++ ++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); ++ spin_lock_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; ++ ++ spin_lock(&dev->table_lock); ++ idr_for_each_entry(&dev->objects, obj, id) { ++ idr_remove(&dev->objects, id); ++ synchronize_rcu(); ++ put_obj(obj); ++ } ++ spin_unlock(&dev->table_lock); ++ ++ kfree(dev); ++ ++ return 0; ++} ++ ++static void winesync_init_obj(struct winesync_obj *obj) ++{ ++ kref_init(&obj->refcount); ++} ++ ++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; ++ ++ 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; ++ ++ spin_lock(&dev->table_lock); ++ ret = idr_alloc(&dev->objects, sem, 0, 0, GFP_KERNEL); ++ spin_unlock(&dev->table_lock); ++ ++ if (ret < 0) { ++ kfree(sem); ++ return ret; ++ } ++ ++ return put_user(ret, &user_args->sem); ++} ++ ++static int winesync_delete(struct winesync_device *dev, void __user *argp) ++{ ++ struct winesync_obj *obj; ++ __u32 id; ++ ++ if (get_user(id, (__u32 __user *)argp)) ++ return -EFAULT; ++ ++ spin_lock(&dev->table_lock); ++ obj = idr_remove(&dev->objects, id); ++ spin_unlock(&dev->table_lock); ++ ++ if (!obj) ++ return -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; ++ + 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..9096df8e2d99 +--- /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 ++ */ ++ ++#ifndef __LINUX_WINESYNC_H ++#define __LINUX_WINESYNC_H ++ ++#include ++ ++struct winesync_sem_args { ++ __u32 sem; ++ __u32 count; ++ __u32 max; ++}; ++ ++#define WINESYNC_IOC_BASE 0xf7 ++ ++#define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \ ++ struct winesync_sem_args) ++#define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) ++ ++#endif +-- +2.11.4.GIT + +From 43f0eb0633b1c258fbf400495ccbeb78d6a292dd Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:22:42 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_PUT_SEM. + +--- + drivers/misc/winesync.c | 65 +++++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/winesync.h | 2 ++ + 2 files changed, 67 insertions(+) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index c59553f4b2ce..8f105437e7b6 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -20,9 +20,11 @@ enum winesync_type { + + struct winesync_obj { + 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 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; +@@ -86,6 +101,7 @@ static int winesync_char_release(struct inode *inode, struct file *file) + static void winesync_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) +@@ -141,6 +157,53 @@ 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); ++ ++ 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; ++} ++ ++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; ++ ++ 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; ++ } ++ ++ spin_lock(&sem->lock); ++ ret = put_sem_state(sem, args.count); ++ spin_unlock(&sem->lock); ++ ++ put_obj(sem); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static long winesync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -152,6 +215,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 9096df8e2d99..6447c29fc419 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -20,6 +20,8 @@ struct winesync_sem_args { + + #define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \ + struct winesync_sem_args) ++#define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ ++ struct winesync_sem_args) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + + #endif +-- +2.11.4.GIT + +From 1a6f3381fd320e0bddaf6fd8aab9c61ebe828bf5 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:31:44 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_IOC_WAIT_ANY. + +--- + drivers/misc/winesync.c | 229 ++++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/winesync.h | 11 ++ + 2 files changed, 240 insertions(+) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index 8f105437e7b6..c9ef1511342f 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -22,6 +22,8 @@ struct winesync_obj { + struct kref refcount; + spinlock_t lock; + ++ struct list_head any_waiters; ++ + enum winesync_type type; + + /* The following fields are protected by the object lock. */ +@@ -33,6 +35,28 @@ struct winesync_obj { + } 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; ++ ++ __u32 count; ++ struct winesync_q_entry entries[]; ++}; ++ + struct winesync_device { + spinlock_t table_lock; + struct idr objects; +@@ -102,6 +126,26 @@ static void winesync_init_obj(struct winesync_obj *obj) + { + kref_init(&obj->refcount); + spin_lock_init(&obj->lock); ++ INIT_LIST_HEAD(&obj->any_waiters); ++} ++ ++static void try_wake_any_sem(struct winesync_obj *sem) ++{ ++ struct winesync_q_entry *entry; ++ ++ lockdep_assert_held(&sem->lock); ++ ++ list_for_each_entry(entry, &sem->any_waiters, node) { ++ struct winesync_q *q = entry->q; ++ ++ if (!sem->u.sem.count) ++ break; ++ ++ 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) +@@ -194,6 +238,8 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp) + + spin_lock(&sem->lock); + ret = put_sem_state(sem, args.count); ++ if (!ret) ++ try_wake_any_sem(sem); + spin_unlock(&sem->lock); + + put_obj(sem); +@@ -204,6 +250,187 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp) + return ret; + } + ++static int winesync_schedule(const struct winesync_q *q, long timeout) ++{ ++ int ret = 0; ++ ++ if (timeout < 0) ++ return 0; ++ ++ do { ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (atomic_read(&q->signaled) != -1) ++ break; ++ timeout = schedule_timeout(timeout); ++ } while (timeout); ++ __set_current_state(TASK_RUNNING); ++ ++ return ret; ++} ++ ++/* ++ * 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, long *ret_timeout, ++ struct winesync_q **ret_q) ++{ ++ long timeout = MAX_SCHEDULE_TIMEOUT; ++ const __u32 count = args->count; ++ struct winesync_q *q; ++ __u32 *ids; ++ __u32 i, j; ++ ++ if (args->timeout) { ++ struct timespec64 to, ts; ++ ++ if (get_timespec64(&to, u64_to_user_ptr(args->timeout))) ++ return -EFAULT; ++ if (!timespec64_valid(&to)) ++ return -EINVAL; ++ ++ ktime_get_coarse_ts64(&ts); ++ if (timespec64_compare(&to, &ts) <= 0) { ++ timeout = 0; ++ } else { ++ ts = timespec64_sub(to, ts); ++ timeout = timespec64_to_jiffies(&ts); ++ } ++ } ++ ++ 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; ++ } ++ ++ 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; ++ ++ for (i = 0; i < count; i++) { ++ struct winesync_q_entry *entry = &q->entries[i]; ++ struct winesync_obj *obj = get_obj(dev, ids[i]); ++ ++ if (!obj) ++ goto err; ++ ++ entry->obj = obj; ++ entry->q = q; ++ entry->index = i; ++ } ++ ++ kfree(ids); ++ ++ *ret_q = q; ++ *ret_timeout = timeout; ++ return 0; ++ ++err: ++ for (j = 0; j < i; j++) ++ put_obj(q->entries[j].obj); ++ kfree(ids); ++ kfree(q); ++ return -EINVAL; ++} ++ ++static void try_wake_any_obj(struct winesync_obj *obj) ++{ ++ switch (obj->type) { ++ case WINESYNC_TYPE_SEM: ++ try_wake_any_sem(obj); ++ break; ++ } ++} ++ ++static int winesync_wait_any(struct winesync_device *dev, void __user *argp) ++{ ++ struct winesync_wait_args args; ++ struct winesync_q *q; ++ long timeout; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ if (!args.owner) ++ return -EINVAL; ++ ++ ret = setup_wait(dev, &args, &timeout, &q); ++ if (ret < 0) ++ return ret; ++ ++ /* queue ourselves */ ++ ++ for (i = 0; i < args.count; i++) { ++ struct winesync_q_entry *entry = &q->entries[i]; ++ struct winesync_obj *obj = q->entries[i].obj; ++ ++ spin_lock(&obj->lock); ++ list_add_tail(&entry->node, &obj->any_waiters); ++ spin_unlock(&obj->lock); ++ } ++ ++ /* check if we are already signaled */ ++ ++ for (i = 0; i < args.count; i++) { ++ struct winesync_obj *obj = q->entries[i].obj; ++ ++ if (atomic_read(&q->signaled) != -1) ++ break; ++ ++ spin_lock(&obj->lock); ++ try_wake_any_obj(obj); ++ spin_unlock(&obj->lock); ++ } ++ ++ /* sleep */ ++ ++ ret = winesync_schedule(q, timeout); ++ ++ /* and finally, unqueue */ ++ ++ for (i = 0; i < args.count; i++) { ++ struct winesync_obj *obj = q->entries[i].obj; ++ ++ spin_lock(&obj->lock); ++ list_del(&q->entries[i].node); ++ spin_unlock(&obj->lock); ++ ++ put_obj(obj); ++ } ++ ++ if (atomic_read(&q->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(atomic_read(&q->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) + { +@@ -217,6 +444,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 6447c29fc419..c9e18310e77f 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_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ + struct winesync_sem_args) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) ++#define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ ++ struct winesync_wait_args) + + #endif +-- +2.11.4.GIT + +From 7a29da523d518a9b7a27572b1e81642a3adac8be Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:36:09 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_IOC_WAIT_ALL. + +--- + drivers/misc/winesync.c | 240 ++++++++++++++++++++++++++++++++++++++++-- + include/uapi/linux/winesync.h | 2 + + 2 files changed, 234 insertions(+), 8 deletions(-) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index c9ef1511342f..650b688b8bc2 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -22,7 +22,36 @@ 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 all_waiters; ++ ++ /* ++ * Hint describing how many tasks are queued on this object in a ++ * wait-all operation. ++ * ++ * Any time we do a wake, we may need to wake "all" waiters as well as ++ * "any" waiters. In order to atomically wake "all" waiters, we must ++ * lock all of the objects, and that means grabbing the wait_all_lock ++ * below (and, due to lock ordering rules, before locking this object). ++ * However, wait-all is a rare operation, and grabbing the wait-all ++ * lock for every wake would create unnecessary contention. Therefore we ++ * first check whether all_hint is one, 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. ++ * ++ * 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). ++ */ ++ refcount_t all_hint; + + enum winesync_type type; + +@@ -53,11 +82,24 @@ struct winesync_q { + */ + atomic_t signaled; + ++ bool all; + __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 ++ * thread is trying to acquire several objects, another thread cannot ++ * touch the object at the same time. ++ * ++ * We achieve this by grabbing multiple object locks at the same time. ++ * However, this creates a lock ordering problem. To solve that problem, ++ * wait_all_lock is taken first whenever multiple objects must be locked ++ * at the same time. ++ */ ++ spinlock_t wait_all_lock; + spinlock_t table_lock; + struct idr objects; + }; +@@ -97,6 +139,7 @@ static int winesync_char_open(struct inode *inode, struct file *file) + return -ENOMEM; + + idr_init(&dev->objects); ++ spin_lock_init(&dev->wait_all_lock); + spin_lock_init(&dev->table_lock); + + file->private_data = dev; +@@ -125,8 +168,82 @@ static int winesync_char_release(struct inode *inode, struct file *file) + 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); ++ INIT_LIST_HEAD(&obj->all_waiters); ++} ++ ++static bool is_signaled(struct winesync_obj *obj, __u32 owner) ++{ ++ lockdep_assert_held(&obj->lock); ++ ++ switch (obj->type) { ++ case WINESYNC_TYPE_SEM: ++ return !!obj->u.sem.count; ++ } ++ ++ WARN(1, "bad object type %#x\n", obj->type); ++ return false; ++} ++ ++/* ++ * "locked_obj" is an optional pointer to an object which is already locked and ++ * should not be locked again. This is necessary so that changing an object's ++ * state and waking it can be a single atomic operation. ++ */ ++static void try_wake_all(struct winesync_device *dev, struct winesync_q *q, ++ struct winesync_obj *locked_obj) ++{ ++ __u32 count = q->count; ++ bool can_wake = true; ++ __u32 i; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ if (locked_obj) ++ lockdep_assert_held(&locked_obj->lock); ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_lock(&q->entries[i].obj->lock); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (!is_signaled(q->entries[i].obj, q->owner)) { ++ can_wake = false; ++ break; ++ } ++ } ++ ++ if (can_wake && atomic_cmpxchg(&q->signaled, -1, 0) == -1) { ++ for (i = 0; i < count; i++) { ++ struct winesync_obj *obj = q->entries[i].obj; ++ ++ switch (obj->type) { ++ case WINESYNC_TYPE_SEM: ++ obj->u.sem.count--; ++ break; ++ } ++ } ++ wake_up_process(q->task); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_unlock(&q->entries[i].obj->lock); ++ } ++} ++ ++static void try_wake_all_obj(struct winesync_device *dev, ++ struct winesync_obj *obj) ++{ ++ struct winesync_q_entry *entry; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ lockdep_assert_held(&obj->lock); ++ ++ 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) +@@ -236,11 +353,29 @@ static int winesync_put_sem(struct winesync_device *dev, void __user *argp) + return -EINVAL; + } + +- spin_lock(&sem->lock); +- ret = put_sem_state(sem, args.count); +- if (!ret) +- try_wake_any_sem(sem); +- spin_unlock(&sem->lock); ++ 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); ++ } ++ ++ 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); ++ ++ spin_unlock(&sem->lock); ++ } + + put_obj(sem); + +@@ -278,8 +413,8 @@ static int winesync_schedule(const struct winesync_q *q, long timeout) + * yet. Also, calculate the relative timeout in jiffies. + */ + static int setup_wait(struct winesync_device *dev, +- const struct winesync_wait_args *args, long *ret_timeout, +- struct winesync_q **ret_q) ++ const struct winesync_wait_args *args, bool all, ++ long *ret_timeout, struct winesync_q **ret_q) + { + long timeout = MAX_SCHEDULE_TIMEOUT; + const __u32 count = args->count; +@@ -321,6 +456,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++) { +@@ -330,6 +466,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; +@@ -371,7 +517,7 @@ static int winesync_wait_any(struct winesync_device *dev, void __user *argp) + if (!args.owner) + return -EINVAL; + +- ret = setup_wait(dev, &args, &timeout, &q); ++ ret = setup_wait(dev, &args, false, &timeout, &q); + if (ret < 0) + return ret; + +@@ -431,6 +577,82 @@ 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_wait_args args; ++ struct winesync_q *q; ++ long timeout; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ if (!args.owner) ++ return -EINVAL; ++ ++ ret = setup_wait(dev, &args, true, &timeout, &q); ++ if (ret < 0) ++ return ret; ++ ++ /* queue ourselves */ ++ ++ spin_lock(&dev->wait_all_lock); ++ ++ for (i = 0; i < args.count; i++) { ++ struct winesync_q_entry *entry = &q->entries[i]; ++ struct winesync_obj *obj = q->entries[i].obj; ++ ++ refcount_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, timeout); ++ ++ /* 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); ++ ++ refcount_dec(&obj->all_hint); ++ ++ put_obj(obj); ++ } ++ ++ spin_unlock(&dev->wait_all_lock); ++ ++ if (atomic_read(&q->signaled) != -1) { ++ /* even if we caught a signal, we need to communicate success */ ++ ret = 0; ++ } else if (!ret) { ++ ret = -ETIMEDOUT; ++ } ++ ++ kfree(q); ++ return ret; ++} ++ + static long winesync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -446,6 +668,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 c9e18310e77f..f4d776294868 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -34,5 +34,7 @@ struct winesync_wait_args { + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ + struct winesync_wait_args) ++#define WINESYNC_IOC_WAIT_ALL _IOW (WINESYNC_IOC_BASE, 12, \ ++ struct winesync_wait_args) + + #endif +-- +2.11.4.GIT + +From e3658a3c895e4811466afd491bba23dca34320f5 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:41:10 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_IOC_CREATE_MUTEX. + +--- + drivers/misc/winesync.c | 74 +++++++++++++++++++++++++++++++++++++++++++ + include/uapi/linux/winesync.h | 8 +++++ + 2 files changed, 82 insertions(+) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index 650b688b8bc2..9f88f3ec1e62 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -16,6 +16,7 @@ + + enum winesync_type { + WINESYNC_TYPE_SEM, ++ WINESYNC_TYPE_MUTEX, + }; + + struct winesync_obj { +@@ -61,6 +62,10 @@ struct winesync_obj { + __u32 count; + __u32 max; + } sem; ++ struct { ++ __u32 count; ++ __u32 owner; ++ } mutex; + } u; + }; + +@@ -181,6 +186,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); +@@ -223,6 +232,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); +@@ -265,6 +278,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; ++ ++ lockdep_assert_held(&mutex->lock); ++ ++ list_for_each_entry(entry, &mutex->any_waiters, node) { ++ struct winesync_q *q = entry->q; ++ ++ if (mutex->u.mutex.count == UINT_MAX) ++ break; ++ if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner) ++ continue; ++ ++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ mutex->u.mutex.count++; ++ mutex->u.mutex.owner = q->owner; ++ wake_up_process(q->task); ++ } ++ } ++} ++ + static int winesync_create_sem(struct winesync_device *dev, void __user *argp) + { + struct winesync_sem_args __user *user_args = argp; +@@ -299,6 +334,40 @@ static int winesync_create_sem(struct winesync_device *dev, void __user *argp) + return put_user(ret, &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; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ if (!args.owner != !args.count) ++ return -EINVAL; ++ ++ mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); ++ if (!mutex) ++ return -ENOMEM; ++ ++ winesync_init_obj(mutex); ++ mutex->type = WINESYNC_TYPE_MUTEX; ++ mutex->u.mutex.count = args.count; ++ mutex->u.mutex.owner = args.owner; ++ ++ spin_lock(&dev->table_lock); ++ ret = idr_alloc(&dev->objects, mutex, 0, 0, GFP_KERNEL); ++ spin_unlock(&dev->table_lock); ++ ++ 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; +@@ -501,6 +570,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; + } + } + +@@ -662,6 +734,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 f4d776294868..2e16652efe03 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 timeout; + __u64 objs; +@@ -29,6 +35,8 @@ struct winesync_wait_args { + + #define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \ + struct winesync_sem_args) ++#define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 1, \ ++ struct winesync_mutex_args) + #define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ + struct winesync_sem_args) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) +-- +2.11.4.GIT + +From 3eddc4667fb62a3fc9b5d9fc73282a0a5ba63071 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:44:41 -0600 +Subject: [PATCH] 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 9f88f3ec1e62..bba0c7dc0a18 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -454,6 +454,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. ++ */ ++static int put_mutex_state(struct winesync_obj *mutex, ++ const struct winesync_mutex_args *args) ++{ ++ lockdep_assert_held(&mutex->lock); ++ ++ if (mutex->u.mutex.owner != args->owner) ++ return -EPERM; ++ ++ if (!--mutex->u.mutex.count) ++ mutex->u.mutex.owner = 0; ++ return 0; ++} ++ ++static int winesync_put_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 prev_count; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ if (!args.owner) ++ return -EINVAL; ++ ++ mutex = get_obj(dev, args.mutex); ++ if (!mutex) ++ return -EINVAL; ++ if (mutex->type != WINESYNC_TYPE_MUTEX) { ++ put_obj(mutex); ++ return -EINVAL; ++ } ++ ++ if (refcount_read(&mutex->all_hint) > 1) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock(&mutex->lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) { ++ try_wake_all_obj(dev, mutex); ++ try_wake_any_mutex(mutex); ++ } ++ ++ spin_unlock(&mutex->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&mutex->lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) ++ try_wake_any_mutex(mutex); ++ ++ spin_unlock(&mutex->lock); ++ } ++ ++ put_obj(mutex); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static int winesync_schedule(const struct winesync_q *q, long timeout) + { + int ret = 0; +@@ -740,6 +809,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 2e16652efe03..2c8658d5dbe0 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -39,6 +39,8 @@ struct winesync_wait_args { + struct winesync_mutex_args) + #define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ + struct winesync_sem_args) ++#define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \ ++ struct winesync_mutex_args) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ + struct winesync_wait_args) +-- +2.11.4.GIT + +From 4c00fdb452db1797807aff70fc73f5f39e5bc198 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:46:46 -0600 +Subject: [PATCH] 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 bba0c7dc0a18..b64d709cce56 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -65,6 +65,7 @@ struct winesync_obj { + struct { + __u32 count; + __u32 owner; ++ bool ownerdead; + } mutex; + } u; + }; +@@ -88,6 +89,7 @@ struct winesync_q { + atomic_t signaled; + + bool all; ++ bool ownerdead; + __u32 count; + struct winesync_q_entry entries[]; + }; +@@ -233,6 +235,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; +@@ -293,6 +298,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); +@@ -523,6 +531,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; ++ __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; ++ } ++ ++ if (refcount_read(&obj->all_hint) > 1) { ++ 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, long timeout) + { + int ret = 0; +@@ -595,6 +668,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++) { +@@ -706,7 +780,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(atomic_read(&q->signaled), &user_args->index)) + ret = -EFAULT; +@@ -785,7 +859,7 @@ static int winesync_wait_all(struct winesync_device *dev, void __user *argp) + + if (atomic_read(&q->signaled) != -1) { + /* even if we caught a signal, we need to communicate success */ +- ret = 0; ++ ret = q->ownerdead ? -EOWNERDEAD : 0; + } else if (!ret) { + ret = -ETIMEDOUT; + } +@@ -811,6 +885,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 2c8658d5dbe0..d50d76d66705 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -41,6 +41,7 @@ struct winesync_wait_args { + struct winesync_sem_args) + #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \ + struct winesync_mutex_args) ++#define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 9, __u32) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ + struct winesync_wait_args) +-- +2.11.4.GIT + +From 3335e079dd9456186450539b725e8429c7ad017b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:47:55 -0600 +Subject: [PATCH] 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 b64d709cce56..1115f1220955 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -531,6 +531,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; ++ __u32 id; ++ ++ if (get_user(id, &user_args->sem)) ++ return -EFAULT; ++ ++ sem = get_obj(dev, id); ++ if (!sem) ++ return -EINVAL; ++ if (sem->type != WINESYNC_TYPE_SEM) { ++ put_obj(sem); ++ return -EINVAL; ++ } ++ ++ args.sem = id; ++ spin_lock(&sem->lock); ++ args.count = sem->u.sem.count; ++ args.max = sem->u.sem.max; ++ spin_unlock(&sem->lock); ++ ++ put_obj(sem); ++ ++ if (copy_to_user(user_args, &args, sizeof(args))) ++ return -EFAULT; ++ return 0; ++} ++ + /* + * Actually change the mutex state to mark its owner as dead. + */ +@@ -885,6 +916,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 d50d76d66705..15d770683b1a 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -41,6 +41,8 @@ struct winesync_wait_args { + struct winesync_sem_args) + #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \ + struct winesync_mutex_args) ++#define WINESYNC_IOC_READ_SEM _IOWR(WINESYNC_IOC_BASE, 7, \ ++ struct winesync_sem_args) + #define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 9, __u32) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ +-- +2.11.4.GIT + +From e909e6483bbccb780cecb035b61153fd568a1df6 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:48:10 -0600 +Subject: [PATCH] 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 1115f1220955..2e334205abee 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -562,6 +562,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; ++ __u32 id; ++ int ret; ++ ++ if (get_user(id, &user_args->mutex)) ++ return -EFAULT; ++ ++ mutex = get_obj(dev, id); ++ if (!mutex) ++ return -EINVAL; ++ if (mutex->type != WINESYNC_TYPE_MUTEX) { ++ put_obj(mutex); ++ return -EINVAL; ++ } ++ ++ args.mutex = id; ++ spin_lock(&mutex->lock); ++ args.count = mutex->u.mutex.count; ++ args.owner = mutex->u.mutex.owner; ++ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0; ++ spin_unlock(&mutex->lock); ++ ++ put_obj(mutex); ++ ++ if (copy_to_user(user_args, &args, sizeof(args))) ++ return -EFAULT; ++ return ret; ++} ++ + /* + * Actually change the mutex state to mark its owner as dead. + */ +@@ -918,6 +951,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 15d770683b1a..1fdc8801845f 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -43,6 +43,8 @@ struct winesync_wait_args { + struct winesync_mutex_args) + #define WINESYNC_IOC_READ_SEM _IOWR(WINESYNC_IOC_BASE, 7, \ + struct winesync_sem_args) ++#define WINESYNC_IOC_READ_MUTEX _IOWR(WINESYNC_IOC_BASE, 8, \ ++ struct winesync_mutex_args) + #define WINESYNC_IOC_KILL_OWNER _IOW (WINESYNC_IOC_BASE, 9, __u32) + #define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 10, __u32) + #define WINESYNC_IOC_WAIT_ANY _IOWR(WINESYNC_IOC_BASE, 11, \ +-- +2.11.4.GIT + +From 67ccb7505925889de81153d4f9fda2f6a7af8701 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 11:50:49 -0600 +Subject: [PATCH] doc: Add documentation for the winesync uAPI. + +--- + Documentation/userspace-api/index.rst | 1 + + Documentation/userspace-api/winesync.rst | 315 +++++++++++++++++++++++++++++++ + 2 files changed, 316 insertions(+) + create mode 100644 Documentation/userspace-api/winesync.rst + +diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspace-api/index.rst +index acd2cc2a538d..1950883c333e 100644 +--- a/Documentation/userspace-api/index.rst ++++ b/Documentation/userspace-api/index.rst +@@ -24,6 +24,7 @@ place where this information is gathered. + ioctl/index + iommu + media/index ++ 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..7b4e5767c3ec +--- /dev/null ++++ b/Documentation/userspace-api/winesync.rst +@@ -0,0 +1,315 @@ ++===================================== ++Wine synchronization primitive driver ++===================================== ++ ++This page documents the user-space API for the winesync driver. ++ ++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. ++ ++This interface is meant as a compatibility tool only and should not be ++used for general synchronization; instead use generic, versatile ++interfaces such as futex(2) and poll(2). ++ ++Synchronization primitives ++========================== ++ ++The winesync driver exposes two types of synchronization primitives, ++semaphores and mutexes. ++ ++A semaphore holds a single volatile 32-bit counter, and a static ++32-bit integer denoting the maximum value. It is considered signaled ++when the counter is nonzero. The counter is decremented by one when a ++wait is satisfied. Both the initial and maximum count are established ++when the semaphore is created. ++ ++A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit ++identifier denoting its owner. 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 unsigned 32-bit integers. An object ++identifier will always be less than or equal to the value of INT_MAX. ++ ++Char device ++=========== ++ ++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. ++ ++ioctl reference ++=============== ++ ++All operations on the device are done through ioctls. There are three ++structures used in ioctl calls:: ++ ++ struct winesync_sem_args { ++ __u32 sem; ++ __u32 count; ++ __u32 max; ++ }; ++ ++ struct winesync_mutex_args { ++ __u32 mutex; ++ __u32 owner; ++ __u32 count; ++ }; ++ ++ struct winesync_wait_args { ++ __u64 timeout; ++ __u64 objs; ++ __u32 count; ++ __u32 owner; ++ __u32 index; ++ __u32 pad; ++ }; ++ ++Depending on the ioctl, members of the structure may be used as input, ++output, or not at all. ++ ++All ioctls return 0 on success, and -1 on error, in which case `errno` ++will be set to a nonzero error code. ++ ++The ioctls are as follows: ++ ++.. c:macro:: WINESYNC_IOC_CREATE_SEM ++ ++ Create a semaphore object. Takes a pointer to struct ++ :c:type:`winesync_sem_args`, which is used as follows: ++ ++ ``count`` and ``max`` are input-only arguments, denoting the ++ initial and maximum count of the semaphore. ++ ++ ``sem`` is an output-only argument, which will be filled with the ++ allocated identifier if successful. ++ ++ Fails with ``EINVAL`` if ``count`` is greater than ``max``, or ++ ``ENOMEM`` if not enough memory is available. ++ ++.. c:macro:: WINESYNC_IOC_CREATE_MUTEX ++ ++ Create a mutex object. Takes a pointer to struct ++ :c:type:`winesync_mutex_args`, which is used as follows: ++ ++ ``owner`` is an input-only argument denoting the initial owner of ++ the mutex. ++ ++ ``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``. ++ ++ ``mutex`` is an output-only argument, which will be filled with ++ the allocated identifier if successful. ++ ++ Fails with ``ENOMEM`` if not enough memory is available. ++ ++.. c:macro:: WINESYNC_IOC_DELETE ++ ++ 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. ++ ++.. c:macro:: WINESYNC_IOC_PUT_SEM ++ ++ Post to a semaphore object. Takes a pointer to struct ++ :c:type:`winesync_sem_args`, which is used as follows: ++ ++ ``sem`` is an input-only argument denoting the semaphore object. ++ If ``sem`` is not a valid semaphore object, the ioctl fails with ++ ``EINVAL``. ++ ++ ``count`` contains on input the count to add to the semaphore, and ++ on output is filled with its previous count. ++ ++ ``max`` is not used. ++ ++ 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. ++ ++.. c:macro:: WINESYNC_IOC_PUT_MUTEX ++ ++ Release a mutex object. Takes a pointer to struct ++ :c:type:`winesync_mutex_args`, which is used as follows: ++ ++ ``mutex`` is an input-only argument denoting the mutex object. If ++ ``mutex`` is not a valid mutex object, the ioctl fails with ++ ``EINVAL``. ++ ++ ``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``. ++ ++ ``count`` is an output-only argument which will be filled on ++ success with the mutex's previous recursion count. ++ ++ 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. ++ ++.. c:macro:: WINESYNC_IOC_READ_SEM ++ ++ Read the current state of a semaphore object. Takes a pointer to ++ struct :c:type:`winesync_sem_args`, which is used as follows: ++ ++ ``sem`` is an input-only argument denoting the semaphore object. ++ If ``sem`` is not a valid semaphore object, the ioctl fails with ++ ``EINVAL``. ++ ++ ``count`` and ``max`` are output-only arguments, which will be ++ filled with the current and maximum count of the given semaphore. ++ ++ The operation is atomic and totally ordered with respect to other ++ operations on the same semaphore. ++ ++.. c:macro:: WINESYNC_IOC_READ_MUTEX ++ ++ Read the current state of a mutex object. Takes a pointer to struct ++ :c:type:`winesync_mutex_args`, which is used as follows: ++ ++ ``mutex`` is an input-only argument denoting the mutex object. If ++ ``mutex`` is not a valid mutex object, the ioctl fails with ++ ``EINVAL``. ++ ++ ``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. ++ ++ If the mutex is marked as inconsistent, the function fails with ++ ``EOWNERDEAD``. ++ ++ The operation is atomic and totally ordered with respect to other ++ operations on the same mutex. ++ ++.. c:macro:: WINESYNC_IOC_KILL_OWNER ++ ++ 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``. ++ ++.. 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: ++ ++ ``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``. ++ ++ ``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``. ++ ++ ``count`` is an input-only argument denoting the number of ++ elements in ``objs``. ++ ++ ``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``. ++ ++ ``index`` is an output-only argument which, if the ioctl is ++ successful, is filled with the index of the object actually ++ signaled. ++ ++ ``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. ++ ++ 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. ++ ++ 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 guaranteed. ++ ++ 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. ++ ++ 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. ++ ++ Fails with ``ENOMEM`` if not enough memory is available, or ++ ``EINTR`` if a signal is received. ++ ++.. 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 ++ unused. ++ ++ 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. ++ ++ 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. ++ ++ 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. ++ ++ 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``. ++ ++ Fails with ``ENOMEM`` if not enough memory is available, or ++ ``EINTR`` if a signal is received. +-- +2.11.4.GIT + +From 4ee195efbbdac12191bdaa661b942855ede25ef5 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:06:23 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for semaphore state. + +--- + tools/testing/selftests/Makefile | 1 + + tools/testing/selftests/drivers/winesync/Makefile | 8 ++ + tools/testing/selftests/drivers/winesync/config | 1 + + .../testing/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 8a917cb4426a..ed5a7b052ef2 100644 +--- a/tools/testing/selftests/Makefile ++++ b/tools/testing/selftests/Makefile +@@ -9,6 +9,7 @@ TARGETS += core + TARGETS += cpufreq + TARGETS += cpu-hotplug + TARGETS += drivers/dma-buf ++TARGETS += drivers/winesync + TARGETS += efivarfs + TARGETS += exec + TARGETS += filesystems +diff --git a/tools/testing/selftests/drivers/winesync/Makefile b/tools/testing/selftests/drivers/winesync/Makefile +new file mode 100644 +index 000000000000..43b39fdeea10 +--- /dev/null ++++ b/tools/testing/selftests/drivers/winesync/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-LICENSE-IDENTIFIER: GPL-2.0-only ++TEST_GEN_PROGS := winesync ++ ++top_srcdir =../../../../.. ++CFLAGS += -I$(top_srcdir)/usr/include ++LDLIBS += -lpthread ++ ++include ../../lib.mk +diff --git a/tools/testing/selftests/drivers/winesync/config b/tools/testing/selftests/drivers/winesync/config +new file mode 100644 +index 000000000000..60539c826d06 +--- /dev/null ++++ b/tools/testing/selftests/drivers/winesync/config +@@ -0,0 +1 @@ ++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..077d9322923c +--- /dev/null ++++ b/tools/testing/selftests/drivers/winesync/winesync.c +@@ -0,0 +1,153 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Various unit tests for the "winesync" synchronization primitive driver. ++ * ++ * Copyright (C) 2021 Zebediah Figura ++ */ ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../../kselftest_harness.h" ++ ++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; ++ 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; ++ 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 = 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(1, sem_args.count); ++ EXPECT_EQ(2, sem_args.max); ++ ++ 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(0, sem_args.count); ++ EXPECT_EQ(2, sem_args.max); ++ ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, 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_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ 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); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ++ close(fd); ++} ++ ++TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From 7c69a72bd03ca5c69e51e01ad71f83b3a111de50 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:07:04 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for mutex state. + +--- + .../testing/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 077d9322923c..713711dd4bf0 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_mutex_args mutex_args; ++ struct timespec timeout; ++ __u32 owner; ++ int fd, ret; ++ ++ clock_gettime(CLOCK_MONOTONIC, &timeout); ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 0; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ mutex_args.owner = 0; ++ mutex_args.count = 2; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 2; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 456; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EPERM, errno); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EPERM, errno); ++ ++ wait_args.timeout = (uintptr_t)&timeout; ++ wait_args.objs = (uintptr_t)&mutex_args.mutex; ++ wait_args.count = 1; ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, wait_args.index); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, wait_args.index); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 456; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, errno); ++ ++ owner = 0; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(0, ret); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ owner = 456; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(0, ret); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(0, wait_args.index); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(0, ret); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(0, wait_args.index); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ mutex_args.owner = 0; ++ mutex_args.count = 0; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ 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); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From c0dcd10786f2e9eadbc24d76d88e71c06b7e27bd Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:07:45 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for + WINESYNC_IOC_WAIT_ANY. + +--- + .../testing/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 713711dd4bf0..774e7a0b0efe 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; ++ __u32 objs[2], owner; ++ int fd, ret; ++ ++ clock_gettime(CLOCK_MONOTONIC, &timeout); ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ sem_args.count = 2; ++ sem_args.max = 3; ++ sem_args.sem = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, sem_args.sem); ++ ++ mutex_args.owner = 0; ++ mutex_args.count = 0; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ objs[0] = sem_args.sem; ++ objs[1] = mutex_args.mutex; ++ ++ wait_args.timeout = (uintptr_t)&timeout; ++ wait_args.objs = (uintptr_t)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(2, wait_args.count); ++ EXPECT_EQ(123, wait_args.owner); ++ ++ 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ 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(0, sem_args.count); ++ EXPECT_EQ(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, 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(0, sem_args.count); ++ EXPECT_EQ(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ sem_args.count = 1; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, sem_args.count); ++ ++ 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(0, sem_args.count); ++ EXPECT_EQ(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, 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(0, sem_args.count); ++ EXPECT_EQ(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, errno); ++ ++ owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(0, ret); ++ ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, errno); ++ EXPECT_EQ(1, wait_args.index); ++ ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, wait_args.index); ++ ++ /* test waiting on the same object twice */ ++ sem_args.count = 2; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, sem_args.count); ++ ++ objs[0] = objs[1] = sem_args.sem; ++ 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(1, sem_args.count); ++ EXPECT_EQ(3, sem_args.max); ++ ++ wait_args.count = 0; ++ wait_args.objs = (uintptr_t)NULL; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From c2f3db6f08c97254dc815b619052addb2f58aa04 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:08:25 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for + WINESYNC_IOC_WAIT_ALL. + +--- + .../testing/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 774e7a0b0efe..6c1f7fa325fd 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; ++ __u32 objs[2], owner; ++ int fd, ret; ++ ++ clock_gettime(CLOCK_MONOTONIC, &timeout); ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ sem_args.count = 2; ++ sem_args.max = 3; ++ sem_args.sem = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, sem_args.sem); ++ ++ mutex_args.owner = 0; ++ mutex_args.count = 0; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ objs[0] = sem_args.sem; ++ objs[1] = mutex_args.mutex; ++ ++ wait_args.timeout = (uintptr_t)&timeout; ++ wait_args.objs = (uintptr_t)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(2, wait_args.count); ++ EXPECT_EQ(123, wait_args.owner); ++ ++ 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ wait_args.owner = 456; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, errno); ++ ++ 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ wait_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(ETIMEDOUT, 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ sem_args.count = 3; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, sem_args.count); ++ ++ owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_KILL_OWNER, &owner); ++ EXPECT_EQ(0, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EOWNERDEAD, 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(3, sem_args.max); ++ ++ mutex_args.count = 0xdeadbeef; ++ mutex_args.owner = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(123, mutex_args.owner); ++ ++ /* test waiting on the same object twice */ ++ objs[0] = objs[1] = sem_args.sem; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From 239a9fd72767df0d85129ee79aa5bce7aefd8eda Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:08:54 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for invalid object + handling. + +--- + .../testing/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 6c1f7fa325fd..28e6d13afe73 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}; ++ __u32 objs[2] = {0}; ++ int fd, ret; ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ ret = ioctl(fd, WINESYNC_IOC_PUT_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_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ wait_args.objs = (uintptr_t)objs; ++ wait_args.count = 1; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &objs[0]); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ sem_args.max = 1; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ ++ mutex_args.mutex = sem_args.sem; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ objs[0] = sem_args.sem; ++ objs[1] = sem_args.sem + 1; ++ wait_args.count = 2; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ objs[0] = sem_args.sem + 1; ++ objs[1] = sem_args.sem; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ALL, &wait_args); ++ EXPECT_EQ(-1, ret); ++ EXPECT_EQ(EINVAL, errno); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ ++ sem_args.sem = mutex_args.mutex; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_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_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From 926e408383a19d9b68a1f642faedd2820234dac7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:09:32 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for wakeup signaling with + WINESYNC_IOC_WAIT_ANY. + +--- + .../testing/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 28e6d13afe73..ddeaba1bfd2a 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; ++ __u32 obj; ++}; ++ ++struct wait_args ++{ ++ int fd; ++ unsigned long request; ++ struct winesync_wait_args *args; ++ int ret; ++ int err; ++}; ++ ++static void *wait_thread(void *arg) ++{ ++ struct wait_args *args = arg; ++ ++ args->ret = ioctl(args->fd, args->request, args->args); ++ args->err = errno; ++ return NULL; ++} ++ ++static void get_abs_timeout(struct timespec *timeout, clockid_t clock, ++ unsigned int ms) ++{ ++ clock_gettime(clock, timeout); ++ timeout->tv_nsec += ms * 1000000; ++ timeout->tv_sec += (timeout->tv_nsec / 1000000000); ++ timeout->tv_nsec %= 1000000000; ++} ++ ++static int wait_for_thread(pthread_t thread, unsigned int ms) ++{ ++ struct timespec timeout; ++ get_abs_timeout(&timeout, CLOCK_REALTIME, ms); ++ return pthread_timedjoin_np(thread, NULL, &timeout); ++} ++ ++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 wait_args thread_args; ++ struct timespec timeout; ++ __u32 objs[2], owner; ++ pthread_t thread; ++ int fd, ret; ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ sem_args.count = 0; ++ sem_args.max = 3; ++ sem_args.sem = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, sem_args.sem); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 1; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ objs[0] = sem_args.sem; ++ objs[1] = mutex_args.mutex; ++ ++ /* 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.count = 2; ++ wait_args.owner = 456; ++ wait_args.index = 0xdeadbeef; ++ thread_args.fd = fd; ++ thread_args.args = &wait_args; ++ thread_args.request = WINESYNC_IOC_WAIT_ANY; ++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 100); ++ EXPECT_EQ(ETIMEDOUT, ret); ++ ++ sem_args.count = 1; ++ 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); ++ ++ 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 */ ++ wait_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, wait_args.index); ++ ++ 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); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(2, mutex_args.count); ++ ++ ret = pthread_tryjoin_np(thread, NULL); ++ EXPECT_EQ(EBUSY, ret); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ ret = wait_for_thread(thread, 100); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, thread_args.ret); ++ EXPECT_EQ(1, wait_args.index); ++ ++ /* delete an object while it's being waited on */ ++ ++ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 100); ++ wait_args.owner = 123; ++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 100); ++ EXPECT_EQ(ETIMEDOUT, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 200); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(-1, thread_args.ret); ++ EXPECT_EQ(ETIMEDOUT, thread_args.err); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From 6d5c596d46eec421cf8b514770376d36bd0ce517 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:09:36 -0600 +Subject: [PATCH] selftests: winesync: Add some tests for wakeup signaling with + WINESYNC_IOC_WAIT_ALL. + +--- + .../testing/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 ddeaba1bfd2a..c738395b11df 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}; ++ 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; ++ int fd, ret; ++ ++ fd = open("/dev/winesync", O_CLOEXEC | O_RDONLY); ++ ASSERT_LE(0, fd); ++ ++ sem_args.count = 0; ++ sem_args.max = 3; ++ sem_args.sem = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, sem_args.sem); ++ ++ mutex_args.owner = 123; ++ mutex_args.count = 1; ++ mutex_args.mutex = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_CREATE_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_NE(0xdeadbeef, mutex_args.mutex); ++ ++ objs[0] = sem_args.sem; ++ objs[1] = mutex_args.mutex; ++ ++ get_abs_timeout(&timeout, CLOCK_MONOTONIC, 1000); ++ wait_args.timeout = (uintptr_t)&timeout; ++ wait_args.objs = (uintptr_t)objs; ++ wait_args.count = 2; ++ wait_args.owner = 456; ++ thread_args.fd = fd; ++ thread_args.args = &wait_args; ++ thread_args.request = WINESYNC_IOC_WAIT_ALL; ++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 100); ++ EXPECT_EQ(ETIMEDOUT, ret); ++ ++ sem_args.count = 1; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, sem_args.count); ++ ++ ret = pthread_tryjoin_np(thread, NULL); ++ EXPECT_EQ(EBUSY, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_SEM, &sem_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, sem_args.count); ++ ++ get_abs_timeout(&timeout2, CLOCK_MONOTONIC, 0); ++ wait_args2.timeout = (uintptr_t)&timeout2; ++ wait_args2.objs = (uintptr_t)&sem_args.sem; ++ wait_args2.count = 1; ++ wait_args2.owner = 123; ++ wait_args2.index = 0xdeadbeef; ++ ret = ioctl(fd, WINESYNC_IOC_WAIT_ANY, &wait_args2); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, wait_args2.index); ++ ++ mutex_args.owner = 123; ++ ret = ioctl(fd, WINESYNC_IOC_PUT_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ ++ ret = pthread_tryjoin_np(thread, NULL); ++ EXPECT_EQ(EBUSY, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(0, mutex_args.count); ++ EXPECT_EQ(0, mutex_args.owner); ++ ++ 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(1, sem_args.count); ++ ++ ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(1, mutex_args.count); ++ EXPECT_EQ(456, mutex_args.owner); ++ ++ 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, 100); ++ wait_args.owner = 123; ++ ret = pthread_create(&thread, NULL, wait_thread, &thread_args); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 100); ++ EXPECT_EQ(ETIMEDOUT, ret); ++ ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); ++ EXPECT_EQ(0, ret); ++ ret = ioctl(fd, WINESYNC_IOC_DELETE, &mutex_args.mutex); ++ EXPECT_EQ(0, ret); ++ ++ ret = wait_for_thread(thread, 200); ++ EXPECT_EQ(0, ret); ++ EXPECT_EQ(-1, thread_args.ret); ++ EXPECT_EQ(ETIMEDOUT, thread_args.err); ++ ++ close(fd); ++} ++ + TEST_HARNESS_MAIN +-- +2.11.4.GIT + +From ad7507572ed410c3b1747f3e4f6ac261a5464a4b Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 12:22:55 -0600 +Subject: [PATCH] maintainers: Add an entry for winesync. + +--- + MAINTAINERS | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index bfc1b86e3e73..67ffab43fcd3 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -19194,6 +19194,15 @@ M: David Härdeman + S: Maintained + F: drivers/media/rc/winbond-cir.c + ++WINESYNC SYNCHRONIZATION PRIMITIVE DRIVER ++M: Zebediah Figura ++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/ ++ + WINSYSTEMS EBC-C384 WATCHDOG DRIVER + M: William Breathitt Gray + L: linux-watchdog@vger.kernel.org +-- +2.11.4.GIT + +From ba5ee090d130887ae96e1c98a7631e2fd5ecd376 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 16:12:55 -0600 +Subject: [PATCH] winesync: Introduce the WINESYNC_SEM_GETONWAIT flag. + +--- + drivers/misc/winesync.c | 12 ++++++++++-- + include/uapi/linux/winesync.h | 3 +++ + tools/testing/selftests/drivers/winesync/winesync.c | 5 +++++ + 3 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index 2e334205abee..8c352a01010a 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -61,6 +61,7 @@ struct winesync_obj { + struct { + __u32 count; + __u32 max; ++ __u32 flags; + } sem; + struct { + __u32 count; +@@ -232,7 +233,8 @@ static void try_wake_all(struct winesync_device *dev, struct winesync_q *q, + + switch (obj->type) { + case WINESYNC_TYPE_SEM: +- obj->u.sem.count--; ++ if (obj->u.sem.flags & WINESYNC_SEM_GETONWAIT) ++ obj->u.sem.count--; + break; + case WINESYNC_TYPE_MUTEX: + if (obj->u.mutex.ownerdead) +@@ -277,7 +279,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 (sem->u.sem.flags & WINESYNC_SEM_GETONWAIT) ++ sem->u.sem.count--; + wake_up_process(q->task); + } + } +@@ -321,6 +324,9 @@ static int winesync_create_sem(struct winesync_device *dev, void __user *argp) + 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; +@@ -329,6 +335,7 @@ static int winesync_create_sem(struct winesync_device *dev, void __user *argp) + sem->type = WINESYNC_TYPE_SEM; + sem->u.sem.count = args.count; + sem->u.sem.max = args.max; ++ sem->u.sem.flags = args.flags; + + spin_lock(&dev->table_lock); + ret = idr_alloc(&dev->objects, sem, 0, 0, GFP_KERNEL); +@@ -553,6 +560,7 @@ static int winesync_read_sem(struct winesync_device *dev, void __user *argp) + 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); +diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h +index 1fdc8801845f..8acf54e997ba 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -10,10 +10,13 @@ + + #include + ++#define WINESYNC_SEM_GETONWAIT 1 ++ + struct winesync_sem_args { + __u32 sem; + __u32 count; + __u32 max; ++ __u32 flags; + }; + + struct winesync_mutex_args { +diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c +index c738395b11df..59024f99f3f2 100644 +--- a/tools/testing/selftests/drivers/winesync/winesync.c ++++ b/tools/testing/selftests/drivers/winesync/winesync.c +@@ -29,6 +29,7 @@ TEST(semaphore_state) + 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); +@@ -417,6 +418,7 @@ TEST(wait_any) + 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); +@@ -614,6 +616,7 @@ TEST(wait_all) + 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); +@@ -898,6 +901,7 @@ TEST(wake_any) + 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); +@@ -1024,6 +1028,7 @@ TEST(wake_all) + 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); +-- +2.11.4.GIT + +From ab4d16ee32af43072aef760ae69126f21f134999 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 16:13:07 -0600 +Subject: [PATCH] doc: Document the WINESYNC_SEM_GETONWAIT flag. + +--- + Documentation/userspace-api/winesync.rst | 34 ++++++++++++++++++++++++-------- + 1 file changed, 26 insertions(+), 8 deletions(-) + +diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst +index 7b4e5767c3ec..3c5e986ddd8c 100644 +--- a/Documentation/userspace-api/winesync.rst ++++ b/Documentation/userspace-api/winesync.rst +@@ -60,6 +60,7 @@ structures used in ioctl calls:: + __u32 sem; + __u32 count; + __u32 max; ++ __u32 flags; + }; + + struct winesync_mutex_args { +@@ -93,6 +94,13 @@ The ioctls are as follows: + ``count`` and ``max`` are input-only arguments, denoting the + initial and maximum count of the semaphore. + ++ ``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. ++ + ``sem`` is an output-only argument, which will be filled with the + allocated identifier if successful. + +@@ -138,7 +146,7 @@ The ioctls are as follows: + ``count`` contains on input the count to add to the semaphore, and + on output is filled with its previous count. + +- ``max`` is not used. ++ ``max`` and ``flags`` are not used. + + The operation is atomic and totally ordered with respect to other + operations on the same semaphore. If adding ``count`` to the +@@ -184,6 +192,9 @@ The ioctls are as follows: + ``count`` and ``max`` are output-only arguments, which will be + filled with the current and maximum count of the given semaphore. + ++ ``flags`` is an output-only argument, which will be filled with ++ the flags used to create the semaphore. ++ + The operation is atomic and totally ordered with respect to other + operations on the same semaphore. + +@@ -254,20 +265,27 @@ The ioctls are as follows: + case the ioctl fails with ``ETIMEDOUT``. The function only acquires + one object, even if multiple objects are signaled. + +- A semaphore is considered to be signaled if its count is nonzero, +- and is acquired by decrementing its count by one. A mutex is +- considered to be signaled if it is unowned or if its owner matches +- the ``owner`` argument, and is acquired by incrementing its +- recursion count by one and setting its owner to the ``owner`` +- argument. ++ 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. + + 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. ++ 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. + ++ (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.) ++ + 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 +-- +2.11.4.GIT + +From 4e5105d5c39a08d83afee81cb3976f5b72b4ae4d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 16:19:53 -0600 +Subject: [PATCH] winesync: Introduce WINESYNC_IOC_GET_SEM. + +--- + drivers/misc/winesync.c | 37 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/winesync.h | 1 + + 2 files changed, 38 insertions(+) + +diff --git a/drivers/misc/winesync.c b/drivers/misc/winesync.c +index 8c352a01010a..19d3e4c6c84f 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -402,6 +402,41 @@ static int winesync_delete(struct winesync_device *dev, void __user *argp) + return 0; + } + ++static int winesync_get_sem(struct winesync_device *dev, void __user *argp) ++{ ++ struct winesync_obj *sem; ++ int ret = -EWOULDBLOCK; ++ __u32 id; ++ ++ if (get_user(id, (__u32 __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. +@@ -953,6 +988,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd, + 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); + case WINESYNC_IOC_PUT_MUTEX: +diff --git a/include/uapi/linux/winesync.h b/include/uapi/linux/winesync.h +index 8acf54e997ba..96bceacb8091 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -40,6 +40,7 @@ struct winesync_wait_args { + struct winesync_sem_args) + #define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 1, \ + struct winesync_mutex_args) ++#define WINESYNC_IOC_GET_SEM _IOW (WINESYNC_IOC_BASE, 2, __u32) + #define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ + struct winesync_sem_args) + #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \ +-- +2.11.4.GIT + +From 20c2e1da1b97bf73c68f89b35cb67f4a1be37dc7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 17:21:26 -0600 +Subject: [PATCH] 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 19d3e4c6c84f..09d2e6ed6e03 100644 +--- a/drivers/misc/winesync.c ++++ b/drivers/misc/winesync.c +@@ -453,7 +453,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; +@@ -483,6 +484,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 { +@@ -493,6 +497,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); + } + +@@ -991,7 +998,9 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd, + case WINESYNC_IOC_GET_SEM: + return winesync_get_sem(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 96bceacb8091..c16f423dad48 100644 +--- a/include/uapi/linux/winesync.h ++++ b/include/uapi/linux/winesync.h +@@ -41,6 +41,8 @@ struct winesync_wait_args { + #define WINESYNC_IOC_CREATE_MUTEX _IOWR(WINESYNC_IOC_BASE, 1, \ + struct winesync_mutex_args) + #define WINESYNC_IOC_GET_SEM _IOW (WINESYNC_IOC_BASE, 2, __u32) ++#define WINESYNC_IOC_PULSE_SEM _IOWR(WINESYNC_IOC_BASE, 3, \ ++ struct winesync_sem_args) + #define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 5, \ + struct winesync_sem_args) + #define WINESYNC_IOC_PUT_MUTEX _IOWR(WINESYNC_IOC_BASE, 6, \ +-- +2.11.4.GIT + +From 36fac735c4d5a7a0f388f6189f5f86a2b7cf259c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 17:21:47 -0600 +Subject: [PATCH] doc: Document WINESYNC_IOC_GET_SEM and + WINESYNC_IOC_PULSE_SEM. + +--- + Documentation/userspace-api/winesync.rst | 40 ++++++++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +diff --git a/Documentation/userspace-api/winesync.rst b/Documentation/userspace-api/winesync.rst +index 3c5e986ddd8c..c980eab9f2ee 100644 +--- a/Documentation/userspace-api/winesync.rst ++++ b/Documentation/userspace-api/winesync.rst +@@ -157,6 +157,46 @@ The ioctls are as follows: + semaphore will be woken and the semaphore's count decremented + appropriately. + ++.. 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. 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. ++ ++ 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. ++ ++.. c:macro:: WINESYNC_IOC_GET_SEM ++ ++ Attempt to acquire a semaphore object. Takes an input-only pointer ++ to a 32-bit integer denoting the semaphore to acquire. ++ ++ 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. ++ ++ 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.11.4.GIT + +From 10af851acbebd4c82fe1537c2642cf3b9b339e25 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 17:23:16 -0600 +Subject: [PATCH] selftests: winesync: Add tests for semaphores without + WINESYNC_SEM_GETONWAIT and for WINESYNC_IOC_GET_SEM. + +--- + .../testing/selftests/drivers/winesync/winesync.c | 189 +++++++++++++++++++++ + 1 file changed, 189 insertions(+) + +diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c +index 59024f99f3f2..97fdf92e32f9 100644 +--- a/tools/testing/selftests/drivers/winesync/winesync.c ++++ b/tools/testing/selftests/drivers/winesync/winesync.c +@@ -29,6 +29,153 @@ TEST(semaphore_state) + 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); ++ ++ 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_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 = WINESYNC_SEM_GETONWAIT; + ret = ioctl(fd, WINESYNC_IOC_CREATE_SEM, &sem_args); + EXPECT_EQ(-1, ret); +@@ -145,6 +292,44 @@ TEST(semaphore_state) + 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); ++ + ret = ioctl(fd, WINESYNC_IOC_DELETE, &sem_args.sem); + EXPECT_EQ(0, ret); + +@@ -770,6 +955,10 @@ TEST(invalid_objects) + 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); +-- +2.11.4.GIT + +From 369d01fb4c75cb8244443f45cc53e9a2eb90af5d Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Fri, 5 Mar 2021 17:23:35 -0600 +Subject: [PATCH] selftests: winesync: Add tests for WINESYNC_IOC_PULSE_SEM. + +--- + .../testing/selftests/drivers/winesync/winesync.c | 161 ++++++++++++++++++++- + 1 file changed, 159 insertions(+), 2 deletions(-) + +diff --git a/tools/testing/selftests/drivers/winesync/winesync.c b/tools/testing/selftests/drivers/winesync/winesync.c +index 97fdf92e32f9..6510a36e9b80 100644 +--- a/tools/testing/selftests/drivers/winesync/winesync.c ++++ b/tools/testing/selftests/drivers/winesync/winesync.c +@@ -155,6 +155,61 @@ TEST(semaphore_state) + 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); + +@@ -330,6 +385,61 @@ TEST(semaphore_state_getonwait) + 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); + +@@ -951,6 +1061,10 @@ TEST(invalid_objects) + 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); +@@ -1136,6 +1250,30 @@ TEST(wake_any) + 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 */ +@@ -1281,14 +1419,14 @@ TEST(wake_all) + EXPECT_EQ(0, mutex_args.count); + EXPECT_EQ(0, mutex_args.owner); + +- sem_args.count = 2; ++ sem_args.count = 1; + 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(1, sem_args.count); ++ EXPECT_EQ(0, sem_args.count); + + ret = ioctl(fd, WINESYNC_IOC_READ_MUTEX, &mutex_args); + EXPECT_EQ(0, ret); +@@ -1299,6 +1437,25 @@ TEST(wake_all) + 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, 100); +-- +2.11.4.GIT +