Updated for version 6.8.7-zen1 & Remove NTSync patches
This commit is contained in:
parent
9e3ae7d306
commit
60e6566f47
|
@ -1,107 +0,0 @@
|
||||||
ntsync uses a misc device as the simplest and least intrusive uAPI interface.
|
|
||||||
|
|
||||||
Each file description on the device represents an isolated NT instance, intended
|
|
||||||
to correspond to a single NT virtual machine.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/Kconfig | 11 +++++++++
|
|
||||||
drivers/misc/Makefile | 1 +
|
|
||||||
drivers/misc/ntsync.c | 52 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 64 insertions(+)
|
|
||||||
create mode 100644 drivers/misc/ntsync.c
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
|
||||||
index 4fb291f0bf7c..801ed229ed7d 100644
|
|
||||||
--- a/drivers/misc/Kconfig
|
|
||||||
+++ b/drivers/misc/Kconfig
|
|
||||||
@@ -506,6 +506,17 @@ config OPEN_DICE
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
+config NTSYNC
|
|
||||||
+ tristate "NT synchronization primitive emulation"
|
|
||||||
+ help
|
|
||||||
+ This module provides kernel support for emulation of Windows NT
|
|
||||||
+ synchronization primitives. It is not a hardware driver.
|
|
||||||
+
|
|
||||||
+ To compile this driver as a module, choose M here: the
|
|
||||||
+ module will be called ntsync.
|
|
||||||
+
|
|
||||||
+ If unsure, say N.
|
|
||||||
+
|
|
||||||
config VCPU_STALL_DETECTOR
|
|
||||||
tristate "Guest vCPU stall detector"
|
|
||||||
depends on OF && HAS_IOMEM
|
|
||||||
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
|
||||||
index ea6ea5bbbc9c..153a3f4837e8 100644
|
|
||||||
--- a/drivers/misc/Makefile
|
|
||||||
+++ b/drivers/misc/Makefile
|
|
||||||
@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
|
|
||||||
obj-$(CONFIG_UACCE) += uacce/
|
|
||||||
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
|
||||||
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
|
||||||
+obj-$(CONFIG_NTSYNC) += ntsync.o
|
|
||||||
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
|
||||||
obj-$(CONFIG_OPEN_DICE) += open-dice.o
|
|
||||||
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..bd76e653d83e
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -0,0 +1,52 @@
|
|
||||||
+// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
+/*
|
|
||||||
+ * ntsync.c - Kernel driver for NT synchronization primitives
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/miscdevice.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+
|
|
||||||
+#define NTSYNC_NAME "ntsync"
|
|
||||||
+
|
|
||||||
+static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ return nonseekable_open(inode, file);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_char_release(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
+ unsigned long parm)
|
|
||||||
+{
|
|
||||||
+ switch (cmd) {
|
|
||||||
+ default:
|
|
||||||
+ return -ENOIOCTLCMD;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct file_operations ntsync_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .open = ntsync_char_open,
|
|
||||||
+ .release = ntsync_char_release,
|
|
||||||
+ .unlocked_ioctl = ntsync_char_ioctl,
|
|
||||||
+ .compat_ioctl = compat_ptr_ioctl,
|
|
||||||
+ .llseek = no_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct miscdevice ntsync_misc = {
|
|
||||||
+ .minor = MISC_DYNAMIC_MINOR,
|
|
||||||
+ .name = NTSYNC_NAME,
|
|
||||||
+ .fops = &ntsync_fops,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+module_misc_device(ntsync_misc);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Elizabeth Figura <zfigura@codeweavers.com>");
|
|
||||||
+MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,228 +0,0 @@
|
||||||
This corresponds to the NT syscall NtCreateSemaphore().
|
|
||||||
|
|
||||||
Semaphores are one of three types of object to be implemented in this driver,
|
|
||||||
the others being mutexes and events.
|
|
||||||
|
|
||||||
An NT semaphore contains a 32-bit counter, and is signaled and can be acquired
|
|
||||||
when the counter is nonzero. The counter has a maximum value which is specified
|
|
||||||
at creation time. The initial value of the semaphore is also specified at
|
|
||||||
creation time. There are no restrictions on the maximum and initial value.
|
|
||||||
|
|
||||||
Each object is exposed as an file, to which any number of fds may be opened.
|
|
||||||
When all fds are closed, the object is deleted.
|
|
||||||
|
|
||||||
Objects hold a pointer to the ntsync_device that created them. The device's
|
|
||||||
reference count is driven by struct file.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../userspace-api/ioctl/ioctl-number.rst | 2 +
|
|
||||||
drivers/misc/ntsync.c | 131 ++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 21 +++
|
|
||||||
3 files changed, 154 insertions(+)
|
|
||||||
create mode 100644 include/uapi/linux/ntsync.h
|
|
||||||
|
|
||||||
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
index 457e16f06e04..2f5c6994f042 100644
|
|
||||||
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
@@ -173,6 +173,8 @@ Code Seq# Include File Comments
|
|
||||||
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
|
|
||||||
'N' 00-1F drivers/usb/scanner.h
|
|
||||||
'N' 40-7F drivers/block/nvme.c
|
|
||||||
+'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
|
|
||||||
+ <mailto:wine-devel@winehq.org>
|
|
||||||
'O' 00-06 mtd/ubi-user.h UBI
|
|
||||||
'P' all linux/soundcard.h conflict!
|
|
||||||
'P' 60-6F sound/sscape_ioctl.h conflict!
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index bd76e653d83e..20158ec148bc 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -5,26 +5,157 @@
|
|
||||||
* Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
+#include <linux/anon_inodes.h>
|
|
||||||
+#include <linux/file.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <uapi/linux/ntsync.h>
|
|
||||||
|
|
||||||
#define NTSYNC_NAME "ntsync"
|
|
||||||
|
|
||||||
+enum ntsync_type {
|
|
||||||
+ NTSYNC_TYPE_SEM,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Individual synchronization primitives are represented by
|
|
||||||
+ * struct ntsync_obj, and each primitive is backed by a file.
|
|
||||||
+ *
|
|
||||||
+ * The whole namespace is represented by a struct ntsync_device also
|
|
||||||
+ * backed by a file.
|
|
||||||
+ *
|
|
||||||
+ * Both rely on struct file for reference counting. Individual
|
|
||||||
+ * ntsync_obj objects take a reference to the device when created.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+struct ntsync_obj {
|
|
||||||
+ enum ntsync_type type;
|
|
||||||
+
|
|
||||||
+ union {
|
|
||||||
+ struct {
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 max;
|
|
||||||
+ } sem;
|
|
||||||
+ } u;
|
|
||||||
+
|
|
||||||
+ struct file *file;
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_device {
|
|
||||||
+ struct file *file;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj = file->private_data;
|
|
||||||
+
|
|
||||||
+ fput(obj->dev->file);
|
|
||||||
+ kfree(obj);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct file_operations ntsync_obj_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .release = ntsync_obj_release,
|
|
||||||
+ .llseek = no_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
+ enum ntsync_type type)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+
|
|
||||||
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
|
||||||
+ if (!obj)
|
|
||||||
+ return NULL;
|
|
||||||
+ obj->type = type;
|
|
||||||
+ obj->dev = dev;
|
|
||||||
+ get_file(dev->file);
|
|
||||||
+
|
|
||||||
+ return obj;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_obj_get_fd(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ struct file *file;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ fd = get_unused_fd_flags(O_CLOEXEC);
|
|
||||||
+ if (fd < 0)
|
|
||||||
+ return fd;
|
|
||||||
+ file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
|
|
||||||
+ if (IS_ERR(file)) {
|
|
||||||
+ put_unused_fd(fd);
|
|
||||||
+ return PTR_ERR(file);
|
|
||||||
+ }
|
|
||||||
+ obj->file = file;
|
|
||||||
+ fd_install(fd, file);
|
|
||||||
+
|
|
||||||
+ return fd;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+ struct ntsync_obj *sem;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (args.count > args.max)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
|
|
||||||
+ if (!sem)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ sem->u.sem.count = args.count;
|
|
||||||
+ sem->u.sem.max = args.max;
|
|
||||||
+ fd = ntsync_obj_get_fd(sem);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(sem);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->sem);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+
|
|
||||||
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
+ if (!dev)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ file->private_data = dev;
|
|
||||||
+ dev->file = file;
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntsync_char_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = file->private_data;
|
|
||||||
+
|
|
||||||
+ kfree(dev);
|
|
||||||
+
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
unsigned long parm)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = file->private_data;
|
|
||||||
+ void __user *argp = (void __user *)parm;
|
|
||||||
+
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
+ return ntsync_create_sem(dev, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..6a4867a6c97b
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -0,0 +1,21 @@
|
|
||||||
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
|
||||||
+/*
|
|
||||||
+ * Kernel support for NT synchronization primitive emulation
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef __LINUX_NTSYNC_H
|
|
||||||
+#define __LINUX_NTSYNC_H
|
|
||||||
+
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+
|
|
||||||
+struct ntsync_sem_args {
|
|
||||||
+ __u32 sem;
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 max;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
+
|
|
||||||
+#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,147 +0,0 @@
|
||||||
This corresponds to the NT syscall NtReleaseSemaphore().
|
|
||||||
|
|
||||||
This increases the semaphore's internal counter by the given value, and returns
|
|
||||||
the previous value. If the counter would overflow the defined maximum, the
|
|
||||||
function instead fails and returns -EOVERFLOW.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 2 ++
|
|
||||||
2 files changed, 71 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 20158ec148bc..3c2f743c58b0 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -10,7 +10,9 @@
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
+#include <linux/overflow.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
#include <uapi/linux/ntsync.h>
|
|
||||||
|
|
||||||
#define NTSYNC_NAME "ntsync"
|
|
||||||
@@ -31,23 +33,70 @@ enum ntsync_type {
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ntsync_obj {
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+
|
|
||||||
enum ntsync_type type;
|
|
||||||
|
|
||||||
+ struct file *file;
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+
|
|
||||||
+ /* The following fields are protected by the object lock. */
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
__u32 count;
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
-
|
|
||||||
- struct file *file;
|
|
||||||
- struct ntsync_device *dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
+ * invalid.
|
|
||||||
+ */
|
|
||||||
+static int post_sem_state(struct ntsync_obj *sem, __u32 count)
|
|
||||||
+{
|
|
||||||
+ __u32 sum;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (check_add_overflow(sem->u.sem.count, count, &sum) ||
|
|
||||||
+ sum > sem->u.sem.max)
|
|
||||||
+ return -EOVERFLOW;
|
|
||||||
+
|
|
||||||
+ sem->u.sem.count = sum;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ __u32 __user *user_args = argp;
|
|
||||||
+ __u32 prev_count;
|
|
||||||
+ __u32 args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (!ret && put_user(prev_count, user_args))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -58,9 +107,25 @@ static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
+ unsigned long parm)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj = file->private_data;
|
|
||||||
+ void __user *argp = (void __user *)parm;
|
|
||||||
+
|
|
||||||
+ switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_SEM_POST:
|
|
||||||
+ return ntsync_sem_post(obj, argp);
|
|
||||||
+ default:
|
|
||||||
+ return -ENOIOCTLCMD;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static const struct file_operations ntsync_obj_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.release = ntsync_obj_release,
|
|
||||||
+ .unlocked_ioctl = ntsync_obj_ioctl,
|
|
||||||
+ .compat_ioctl = compat_ptr_ioctl,
|
|
||||||
.llseek = no_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -75,6 +140,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
obj->type = type;
|
|
||||||
obj->dev = dev;
|
|
||||||
get_file(dev->file);
|
|
||||||
+ spin_lock_init(&obj->lock);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 6a4867a6c97b..dcfa38fdc93c 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -18,4 +18,6 @@ struct ntsync_sem_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
+#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
+
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,360 +0,0 @@
|
||||||
This corresponds to part of the functionality of the NT syscall
|
|
||||||
NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
|
|
||||||
the third argument (wait_any) is TRUE, and it does not handle alertable waits.
|
|
||||||
Those features have been split out into separate patches to ease review.
|
|
||||||
|
|
||||||
NTSYNC_IOC_WAIT_ANY is a vectored wait function similar to poll(). Unlike
|
|
||||||
poll(), it "consumes" objects when they are signaled. For semaphores, this means
|
|
||||||
decreasing one from the internal counter. At most one object can be consumed by
|
|
||||||
this function.
|
|
||||||
|
|
||||||
Up to 64 objects can be waited on at once. As soon as one is signaled, the
|
|
||||||
object with the lowest index is consumed, and that index is returned via the
|
|
||||||
"index" field.
|
|
||||||
|
|
||||||
A timeout is supported. The timeout is passed as a u64 nanosecond value, which
|
|
||||||
represents absolute time measured against the MONOTONIC clock. If U64_MAX is
|
|
||||||
passed, the ioctl waits indefinitely.
|
|
||||||
|
|
||||||
This ioctl validates that all objects belong to the relevant device. This is not
|
|
||||||
necessary for any technical reason related to NTSYNC_IOC_WAIT_ANY, but will be
|
|
||||||
necessary for NTSYNC_IOC_WAIT_ALL introduced in the following patch.
|
|
||||||
|
|
||||||
Wait ioctls need to take a temporary reference to each object being waited on.
|
|
||||||
As with the device, the reference count of struct file is used for ntsync_obj.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 239 ++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 12 ++
|
|
||||||
2 files changed, 251 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 3c2f743c58b0..ad93ca0f8b84 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -6,11 +6,16 @@
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/anon_inodes.h>
|
|
||||||
+#include <linux/atomic.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
+#include <linux/hrtimer.h>
|
|
||||||
+#include <linux/ktime.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/overflow.h>
|
|
||||||
+#include <linux/sched.h>
|
|
||||||
+#include <linux/sched/signal.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <uapi/linux/ntsync.h>
|
|
||||||
@@ -30,6 +35,8 @@ enum ntsync_type {
|
|
||||||
*
|
|
||||||
* Both rely on struct file for reference counting. Individual
|
|
||||||
* ntsync_obj objects take a reference to the device when created.
|
|
||||||
+ * Wait operations take a reference to each object being waited on for
|
|
||||||
+ * the duration of the wait.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ntsync_obj {
|
|
||||||
@@ -47,12 +54,55 @@ struct ntsync_obj {
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
+
|
|
||||||
+ struct list_head any_waiters;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_q_entry {
|
|
||||||
+ struct list_head node;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+ __u32 index;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_q {
|
|
||||||
+ struct task_struct *task;
|
|
||||||
+ __u32 owner;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Protected via atomic_cmpxchg(). Only the thread that wins the
|
|
||||||
+ * compare-and-swap may actually change object states and wake this
|
|
||||||
+ * task.
|
|
||||||
+ */
|
|
||||||
+ atomic_t signaled;
|
|
||||||
+
|
|
||||||
+ __u32 count;
|
|
||||||
+ struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+static void try_wake_any_sem(struct ntsync_obj *sem)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&sem->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &sem->any_waiters, node) {
|
|
||||||
+ struct ntsync_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);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -88,6 +138,8 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
|
|
||||||
prev_count = sem->u.sem.count;
|
|
||||||
ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
|
|
||||||
spin_unlock(&sem->lock);
|
|
||||||
|
|
||||||
@@ -141,6 +193,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
obj->dev = dev;
|
|
||||||
get_file(dev->file);
|
|
||||||
spin_lock_init(&obj->lock);
|
|
||||||
+ INIT_LIST_HEAD(&obj->any_waiters);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@@ -191,6 +244,190 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
+{
|
|
||||||
+ struct file *file = fget(fd);
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+
|
|
||||||
+ if (file->f_op != &ntsync_obj_fops) {
|
|
||||||
+ fput(file);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ obj = file->private_data;
|
|
||||||
+ if (obj->dev != dev) {
|
|
||||||
+ fput(file);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return obj;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void put_obj(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ fput(obj->file);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
|
|
||||||
+{
|
|
||||||
+ ktime_t timeout = ns_to_ktime(args->timeout);
|
|
||||||
+ ktime_t *timeout_ptr;
|
|
||||||
+ int ret = 0;
|
|
||||||
+
|
|
||||||
+ timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
|
|
||||||
+
|
|
||||||
+ do {
|
|
||||||
+ if (signal_pending(current)) {
|
|
||||||
+ ret = -ERESTARTSYS;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
+ if (atomic_read(&q->signaled) != -1) {
|
|
||||||
+ ret = 0;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ ret = schedule_hrtimeout(timeout_ptr, HRTIMER_MODE_ABS);
|
|
||||||
+ } while (ret < 0);
|
|
||||||
+ __set_current_state(TASK_RUNNING);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Allocate and initialize the ntsync_q structure, but do not queue us yet.
|
|
||||||
+ */
|
|
||||||
+static int setup_wait(struct ntsync_device *dev,
|
|
||||||
+ const struct ntsync_wait_args *args,
|
|
||||||
+ struct ntsync_q **ret_q)
|
|
||||||
+{
|
|
||||||
+ const __u32 count = args->count;
|
|
||||||
+ int fds[NTSYNC_MAX_WAIT_COUNT];
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ __u32 i, j;
|
|
||||||
+
|
|
||||||
+ if (!args->owner || args->pad)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(fds, u64_to_user_ptr(args->objs),
|
|
||||||
+ array_size(count, sizeof(*fds))))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
|
|
||||||
+ if (!q)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ q->task = current;
|
|
||||||
+ q->owner = args->owner;
|
|
||||||
+ atomic_set(&q->signaled, -1);
|
|
||||||
+ q->count = count;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = get_obj(dev, fds[i]);
|
|
||||||
+
|
|
||||||
+ if (!obj)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ entry->obj = obj;
|
|
||||||
+ entry->q = q;
|
|
||||||
+ entry->index = i;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *ret_q = q;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ for (j = 0; j < i; j++)
|
|
||||||
+ put_obj(q->entries[j].obj);
|
|
||||||
+ kfree(q);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_TYPE_SEM:
|
|
||||||
+ try_wake_any_sem(obj);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ int signaled;
|
|
||||||
+ __u32 i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ ret = setup_wait(dev, &args, &q);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ /* queue ourselves */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ list_add_tail(&entry->node, &obj->any_waiters);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check if we are already signaled */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&q->signaled) != -1)
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ try_wake_any_obj(obj);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* sleep */
|
|
||||||
+
|
|
||||||
+ ret = ntsync_schedule(q, &args);
|
|
||||||
+
|
|
||||||
+ /* and finally, unqueue */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ signaled = atomic_read(&q->signaled);
|
|
||||||
+ if (signaled != -1) {
|
|
||||||
+ struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
+
|
|
||||||
+ /* even if we caught a signal, we need to communicate success */
|
|
||||||
+ ret = 0;
|
|
||||||
+
|
|
||||||
+ if (put_user(signaled, &user_args->index))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+ } else if (!ret) {
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kfree(q);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev;
|
|
||||||
@@ -222,6 +459,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
+ case NTSYNC_IOC_WAIT_ANY:
|
|
||||||
+ return ntsync_wait_any(dev, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index dcfa38fdc93c..56b643fab611 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -16,7 +16,19 @@ struct ntsync_sem_args {
|
|
||||||
__u32 max;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_wait_args {
|
|
||||||
+ __u64 timeout;
|
|
||||||
+ __u64 objs;
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ __u32 index;
|
|
||||||
+ __u32 pad;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
+
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,365 +0,0 @@
|
||||||
This is similar to NTSYNC_IOC_WAIT_ANY, but waits until all of the objects are
|
|
||||||
simultaneously signaled, and then acquires all of them as a single atomic
|
|
||||||
operation.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 242 ++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 235 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index ad93ca0f8b84..d5759e9a3a8e 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -55,7 +55,34 @@ struct ntsync_obj {
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * 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 zero, and, if it is,
|
|
||||||
+ * we skip trying to wake "all" waiters.
|
|
||||||
+ *
|
|
||||||
+ * This hint isn't protected by any lock. It might change during the
|
|
||||||
+ * course of a wake, but there's no meaningful race there; it's only a
|
|
||||||
+ * hint.
|
|
||||||
+ *
|
|
||||||
+ * Since wait requests must originate from user-space threads, we're
|
|
||||||
+ * limited here by PID_MAX_LIMIT, so there's no risk of overflow.
|
|
||||||
+ */
|
|
||||||
+ atomic_t all_hint;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_q_entry {
|
|
||||||
@@ -76,14 +103,99 @@ struct ntsync_q {
|
|
||||||
*/
|
|
||||||
atomic_t signaled;
|
|
||||||
|
|
||||||
+ bool all;
|
|
||||||
__u32 count;
|
|
||||||
struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
+ /*
|
|
||||||
+ * Wait-all operations must atomically grab all objects, and be totally
|
|
||||||
+ * ordered with respect to each other and wait-any operations.
|
|
||||||
+ * If one thread is trying to acquire several objects, another thread
|
|
||||||
+ * cannot touch the object at the same time.
|
|
||||||
+ *
|
|
||||||
+ * 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;
|
|
||||||
+
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
+{
|
|
||||||
+ lockdep_assert_held(&obj->lock);
|
|
||||||
+
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_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 ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
+ struct ntsync_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_nest_lock(&q->entries[i].obj->lock, &dev->wait_all_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 ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
+
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_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 ntsync_device *dev, struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_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 ntsync_obj *sem)
|
|
||||||
{
|
|
||||||
struct ntsync_q_entry *entry;
|
|
||||||
@@ -123,6 +235,7 @@ static int post_sem_state(struct ntsync_obj *sem, __u32 count)
|
|
||||||
|
|
||||||
static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = sem->dev;
|
|
||||||
__u32 __user *user_args = argp;
|
|
||||||
__u32 prev_count;
|
|
||||||
__u32 args;
|
|
||||||
@@ -134,14 +247,29 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
- spin_lock(&sem->lock);
|
|
||||||
+ if (atomic_read(&sem->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock);
|
|
||||||
|
|
||||||
- prev_count = sem->u.sem.count;
|
|
||||||
- ret = post_sem_state(sem, args);
|
|
||||||
- if (!ret)
|
|
||||||
- try_wake_any_sem(sem);
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret) {
|
|
||||||
+ try_wake_all_obj(dev, sem);
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- spin_unlock(&sem->lock);
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+ } else {
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (!ret && put_user(prev_count, user_args))
|
|
||||||
ret = -EFAULT;
|
|
||||||
@@ -194,6 +322,8 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
get_file(dev->file);
|
|
||||||
spin_lock_init(&obj->lock);
|
|
||||||
INIT_LIST_HEAD(&obj->any_waiters);
|
|
||||||
+ INIT_LIST_HEAD(&obj->all_waiters);
|
|
||||||
+ atomic_set(&obj->all_hint, 0);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@@ -298,7 +428,7 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar
|
|
||||||
* Allocate and initialize the ntsync_q structure, but do not queue us yet.
|
|
||||||
*/
|
|
||||||
static int setup_wait(struct ntsync_device *dev,
|
|
||||||
- const struct ntsync_wait_args *args,
|
|
||||||
+ const struct ntsync_wait_args *args, bool all,
|
|
||||||
struct ntsync_q **ret_q)
|
|
||||||
{
|
|
||||||
const __u32 count = args->count;
|
|
||||||
@@ -322,6 +452,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->task = current;
|
|
||||||
q->owner = args->owner;
|
|
||||||
atomic_set(&q->signaled, -1);
|
|
||||||
+ q->all = all;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
@@ -331,6 +462,16 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
if (!obj)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
+ if (all) {
|
|
||||||
+ /* Check that the objects are all distinct. */
|
|
||||||
+ for (j = 0; j < i; j++) {
|
|
||||||
+ if (obj == q->entries[j].obj) {
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
entry->obj = obj;
|
|
||||||
entry->q = q;
|
|
||||||
entry->index = i;
|
|
||||||
@@ -366,7 +507,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
- ret = setup_wait(dev, &args, &q);
|
|
||||||
+ ret = setup_wait(dev, &args, false, &q);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
@@ -428,6 +569,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ int signaled;
|
|
||||||
+ __u32 i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ ret = setup_wait(dev, &args, true, &q);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ /* queue ourselves */
|
|
||||||
+
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ atomic_inc(&obj->all_hint);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * obj->all_waiters is protected by dev->wait_all_lock rather
|
|
||||||
+ * than obj->lock, so there is no need to acquire obj->lock
|
|
||||||
+ * here.
|
|
||||||
+ */
|
|
||||||
+ list_add_tail(&entry->node, &obj->all_waiters);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check if we are already signaled */
|
|
||||||
+
|
|
||||||
+ try_wake_all(dev, q, NULL);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ /* sleep */
|
|
||||||
+
|
|
||||||
+ ret = ntsync_schedule(q, &args);
|
|
||||||
+
|
|
||||||
+ /* and finally, unqueue */
|
|
||||||
+
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * obj->all_waiters is protected by dev->wait_all_lock rather
|
|
||||||
+ * than obj->lock, so there is no need to acquire it here.
|
|
||||||
+ */
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+
|
|
||||||
+ atomic_dec(&obj->all_hint);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ signaled = atomic_read(&q->signaled);
|
|
||||||
+ if (signaled != -1) {
|
|
||||||
+ struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
+
|
|
||||||
+ /* even if we caught a signal, we need to communicate success */
|
|
||||||
+ ret = 0;
|
|
||||||
+
|
|
||||||
+ if (put_user(signaled, &user_args->index))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+ } else if (!ret) {
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kfree(q);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev;
|
|
||||||
@@ -436,6 +658,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
if (!dev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
+ spin_lock_init(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
file->private_data = dev;
|
|
||||||
dev->file = file;
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
@@ -459,6 +683,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
+ case NTSYNC_IOC_WAIT_ALL:
|
|
||||||
+ return ntsync_wait_all(dev, argp);
|
|
||||||
case NTSYNC_IOC_WAIT_ANY:
|
|
||||||
return ntsync_wait_any(dev, argp);
|
|
||||||
default:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 56b643fab611..19c37e27a4f8 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -29,6 +29,7 @@ struct ntsync_wait_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
+#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,172 +0,0 @@
|
||||||
This corresponds to the NT syscall NtCreateMutant().
|
|
||||||
|
|
||||||
An NT mutex is recursive, with a 32-bit recursion counter. When acquired via
|
|
||||||
NtWaitForMultipleObjects(), the recursion counter is incremented by one.
|
|
||||||
|
|
||||||
The OS records the thread which acquired it. However, in order to keep this
|
|
||||||
driver self-contained, the owning thread ID is managed by user-space, and passed
|
|
||||||
as a parameter to all relevant ioctls.
|
|
||||||
|
|
||||||
The initial owner and recursion count, if any, are specified when the mutex is
|
|
||||||
created.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 67 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 7 ++++
|
|
||||||
2 files changed, 74 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index d5759e9a3a8e..6f7086d0440a 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -24,6 +24,7 @@
|
|
||||||
|
|
||||||
enum ntsync_type {
|
|
||||||
NTSYNC_TYPE_SEM,
|
|
||||||
+ NTSYNC_TYPE_MUTEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -53,6 +54,10 @@ struct ntsync_obj {
|
|
||||||
__u32 count;
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
+ struct {
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ } mutex;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -132,6 +137,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
switch (obj->type) {
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
return !!obj->u.sem.count;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
|
|
||||||
+ return false;
|
|
||||||
+ return obj->u.mutex.count < UINT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN(1, "bad object type %#x\n", obj->type);
|
|
||||||
@@ -174,6 +183,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
obj->u.sem.count--;
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ obj->u.mutex.count++;
|
|
||||||
+ obj->u.mutex.owner = q->owner;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -215,6 +228,28 @@ static void try_wake_any_sem(struct ntsync_obj *sem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &mutex->any_waiters, node) {
|
|
||||||
+ struct ntsync_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);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -374,6 +409,33 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ struct ntsync_obj *mutex;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (!args.owner != !args.count)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX);
|
|
||||||
+ if (!mutex)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ mutex->u.mutex.count = args.count;
|
|
||||||
+ mutex->u.mutex.owner = args.owner;
|
|
||||||
+ fd = ntsync_obj_get_fd(mutex);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(mutex);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->mutex);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
{
|
|
||||||
struct file *file = fget(fd);
|
|
||||||
@@ -493,6 +555,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
try_wake_any_sem(obj);
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ try_wake_any_mutex(obj);
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -681,6 +746,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
void __user *argp = (void __user *)parm;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_MUTEX:
|
|
||||||
+ return ntsync_create_mutex(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
case NTSYNC_IOC_WAIT_ALL:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 19c37e27a4f8..8ac9d419c360 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -16,6 +16,12 @@ struct ntsync_sem_args {
|
|
||||||
__u32 max;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_mutex_args {
|
|
||||||
+ __u32 mutex;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ __u32 count;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -30,6 +36,7 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
+#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,107 +0,0 @@
|
||||||
This corresponds to the NT syscall NtReleaseMutant().
|
|
||||||
|
|
||||||
This syscall decrements the mutex's recursion count by one, and returns the
|
|
||||||
previous value. If the mutex is not owned by the given owner ID, the function
|
|
||||||
instead fails and returns -EPERM.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 64 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 65 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 6f7086d0440a..222ebead8eba 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -312,6 +312,68 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the mutex state, returning -EPERM if not the owner.
|
|
||||||
+ */
|
|
||||||
+static int unlock_mutex_state(struct ntsync_obj *mutex,
|
|
||||||
+ const struct ntsync_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 ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_device *dev = mutex->dev;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ __u32 prev_count;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ if (!args.owner)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&mutex->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ prev_count = mutex->u.mutex.count;
|
|
||||||
+ ret = unlock_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 = unlock_mutex_state(mutex, &args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_mutex(mutex);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!ret && put_user(prev_count, &user_args->count))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -331,6 +393,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_SEM_POST:
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
+ return ntsync_mutex_unlock(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 8ac9d419c360..265503d441b1 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -39,5 +39,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,166 +0,0 @@
|
||||||
This does not correspond to any NT syscall. Rather, when a thread dies, it
|
|
||||||
should be called by the NT emulator for each mutex.
|
|
||||||
|
|
||||||
NT mutexes are robust (in the pthread sense). When an NT thread dies, any
|
|
||||||
mutexes it owned are immediately released. Acquisition of those mutexes by other
|
|
||||||
threads will return a special value indicating that the mutex was abandoned,
|
|
||||||
like EOWNERDEAD returned from pthread_mutex_lock(), and EOWNERDEAD is indeed
|
|
||||||
used here for that purpose.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 71 +++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 70 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 222ebead8eba..a3466be50c45 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -57,6 +57,7 @@ struct ntsync_obj {
|
|
||||||
struct {
|
|
||||||
__u32 count;
|
|
||||||
__u32 owner;
|
|
||||||
+ bool ownerdead;
|
|
||||||
} mutex;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ struct ntsync_q {
|
|
||||||
atomic_t signaled;
|
|
||||||
|
|
||||||
bool all;
|
|
||||||
+ bool ownerdead;
|
|
||||||
__u32 count;
|
|
||||||
struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
@@ -184,6 +186,9 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
obj->u.sem.count--;
|
|
||||||
break;
|
|
||||||
case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ if (obj->u.mutex.ownerdead)
|
|
||||||
+ q->ownerdead = true;
|
|
||||||
+ obj->u.mutex.ownerdead = false;
|
|
||||||
obj->u.mutex.count++;
|
|
||||||
obj->u.mutex.owner = q->owner;
|
|
||||||
break;
|
|
||||||
@@ -243,6 +248,9 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
|
|
||||||
+ if (mutex->u.mutex.ownerdead)
|
|
||||||
+ q->ownerdead = true;
|
|
||||||
+ mutex->u.mutex.ownerdead = false;
|
|
||||||
mutex->u.mutex.count++;
|
|
||||||
mutex->u.mutex.owner = q->owner;
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -374,6 +382,62 @@ static int ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the mutex state to mark its owner as dead,
|
|
||||||
+ * returning -EPERM if not the owner.
|
|
||||||
+ */
|
|
||||||
+static int kill_mutex_state(struct ntsync_obj *mutex, __u32 owner)
|
|
||||||
+{
|
|
||||||
+ lockdep_assert_held(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ if (mutex->u.mutex.owner != owner)
|
|
||||||
+ return -EPERM;
|
|
||||||
+
|
|
||||||
+ mutex->u.mutex.ownerdead = true;
|
|
||||||
+ mutex->u.mutex.owner = 0;
|
|
||||||
+ mutex->u.mutex.count = 0;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_device *dev = mutex->dev;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (get_user(owner, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ if (!owner)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&mutex->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ ret = kill_mutex_state(mutex, owner);
|
|
||||||
+ 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);
|
|
||||||
+
|
|
||||||
+ ret = kill_mutex_state(mutex, owner);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_mutex(mutex);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -395,6 +459,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
+ return ntsync_mutex_kill(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
@@ -579,6 +645,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->owner = args->owner;
|
|
||||||
atomic_set(&q->signaled, -1);
|
|
||||||
q->all = all;
|
|
||||||
+ q->ownerdead = false;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
@@ -686,7 +753,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
|
|
||||||
/* even if we caught a signal, we need to communicate success */
|
|
||||||
- ret = 0;
|
|
||||||
+ ret = q->ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
|
|
||||||
if (put_user(signaled, &user_args->index))
|
|
||||||
ret = -EFAULT;
|
|
||||||
@@ -767,7 +834,7 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
|
|
||||||
/* even if we caught a signal, we need to communicate success */
|
|
||||||
- ret = 0;
|
|
||||||
+ ret = q->ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
|
|
||||||
if (put_user(signaled, &user_args->index))
|
|
||||||
ret = -EFAULT;
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 265503d441b1..4800941fcbda 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -40,5 +40,6 @@ struct ntsync_wait_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,165 +0,0 @@
|
||||||
This correspond to the NT syscall NtCreateEvent().
|
|
||||||
|
|
||||||
An NT event holds a single bit of state denoting whether it is signaled or
|
|
||||||
unsignaled.
|
|
||||||
|
|
||||||
There are two types of events: manual-reset and automatic-reset. When an
|
|
||||||
automatic-reset event is acquired via a wait function, its state is reset to
|
|
||||||
unsignaled. Manual-reset events are not affected by wait functions.
|
|
||||||
|
|
||||||
Whether the event is manual-reset, and its initial state, are specified at
|
|
||||||
creation time.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 60 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 7 +++++
|
|
||||||
2 files changed, 67 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index a3466be50c45..17dd47d06e0a 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -25,6 +25,7 @@
|
|
||||||
enum ntsync_type {
|
|
||||||
NTSYNC_TYPE_SEM,
|
|
||||||
NTSYNC_TYPE_MUTEX,
|
|
||||||
+ NTSYNC_TYPE_EVENT,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -59,6 +60,10 @@ struct ntsync_obj {
|
|
||||||
__u32 owner;
|
|
||||||
bool ownerdead;
|
|
||||||
} mutex;
|
|
||||||
+ struct {
|
|
||||||
+ bool manual;
|
|
||||||
+ bool signaled;
|
|
||||||
+ } event;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -143,6 +148,8 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
|
|
||||||
return false;
|
|
||||||
return obj->u.mutex.count < UINT_MAX;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ return obj->u.event.signaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN(1, "bad object type %#x\n", obj->type);
|
|
||||||
@@ -192,6 +199,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
obj->u.mutex.count++;
|
|
||||||
obj->u.mutex.owner = q->owner;
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ if (!obj->u.event.manual)
|
|
||||||
+ obj->u.event.signaled = false;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -258,6 +269,26 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void try_wake_any_event(struct ntsync_obj *event)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&event->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &event->any_waiters, node) {
|
|
||||||
+ struct ntsync_q *q = entry->q;
|
|
||||||
+
|
|
||||||
+ if (!event->u.event.signaled)
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
|
|
||||||
+ if (!event->u.event.manual)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
+ wake_up_process(q->task);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -566,6 +597,30 @@ static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_create_event(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+ struct ntsync_obj *event;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ event = ntsync_alloc_obj(dev, NTSYNC_TYPE_EVENT);
|
|
||||||
+ if (!event)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ event->u.event.manual = args.manual;
|
|
||||||
+ event->u.event.signaled = args.signaled;
|
|
||||||
+ fd = ntsync_obj_get_fd(event);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(event);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->event);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
{
|
|
||||||
struct file *file = fget(fd);
|
|
||||||
@@ -689,6 +744,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
case NTSYNC_TYPE_MUTEX:
|
|
||||||
try_wake_any_mutex(obj);
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ try_wake_any_event(obj);
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -877,6 +935,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
void __user *argp = (void __user *)parm;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_EVENT:
|
|
||||||
+ return ntsync_create_event(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_MUTEX:
|
|
||||||
return ntsync_create_mutex(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 4800941fcbda..040cbdb39033 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -22,6 +22,12 @@ struct ntsync_mutex_args {
|
|
||||||
__u32 count;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_event_args {
|
|
||||||
+ __u32 event;
|
|
||||||
+ __u32 manual;
|
|
||||||
+ __u32 signaled;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -37,6 +43,7 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_CREATE_EVENT _IOWR('N', 0x87, struct ntsync_event_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,78 +0,0 @@
|
||||||
This corresponds to the NT syscall NtSetEvent().
|
|
||||||
|
|
||||||
This sets the event to the signaled state, and returns its previous state.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 37 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 38 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 17dd47d06e0a..edfbf11cafe0 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -469,6 +469,41 @@ static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_device *dev = event->dev;
|
|
||||||
+ __u32 prev_state;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&event->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&event->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = true;
|
|
||||||
+ try_wake_all_obj(dev, event);
|
|
||||||
+ try_wake_any_event(event);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+ } else {
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = true;
|
|
||||||
+ try_wake_any_event(event);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (put_user(prev_state, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -492,6 +527,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_SET:
|
|
||||||
+ return ntsync_event_set(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 040cbdb39033..af518530bffd 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -48,5 +48,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,63 +0,0 @@
|
||||||
This corresponds to the NT syscall NtResetEvent().
|
|
||||||
|
|
||||||
This sets the event to the unsignaled state, and returns its previous state.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 22 ++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 23 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index edfbf11cafe0..fa4c3fa1e496 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -504,6 +504,26 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ __u32 prev_state;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+
|
|
||||||
+ if (put_user(prev_state, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -529,6 +549,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
return ntsync_event_set(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
+ return ntsync_event_reset(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index af518530bffd..6963356ee3f7 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -49,5 +49,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,70 +0,0 @@
|
||||||
This corresponds to the NT syscall NtPulseEvent().
|
|
||||||
|
|
||||||
This wakes up any waiters as if the event had been set, but does not set the
|
|
||||||
event, instead resetting it if it had been signalled. Thus, for a manual-reset
|
|
||||||
event, all waiters are woken, whereas for an auto-reset event, at most one
|
|
||||||
waiter is woken.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 10 ++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 9 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index fa4c3fa1e496..b9b4127a6c9f 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -469,7 +469,7 @@ static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+static int ntsync_event_set(struct ntsync_obj *event, void __user *argp, bool pulse)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev = event->dev;
|
|
||||||
__u32 prev_state;
|
|
||||||
@@ -485,6 +485,8 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
event->u.event.signaled = true;
|
|
||||||
try_wake_all_obj(dev, event);
|
|
||||||
try_wake_any_event(event);
|
|
||||||
+ if (pulse)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
|
|
||||||
spin_unlock(&event->lock);
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
@@ -494,6 +496,8 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
prev_state = event->u.event.signaled;
|
|
||||||
event->u.event.signaled = true;
|
|
||||||
try_wake_any_event(event);
|
|
||||||
+ if (pulse)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
|
|
||||||
spin_unlock(&event->lock);
|
|
||||||
}
|
|
||||||
@@ -548,9 +552,11 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
- return ntsync_event_set(obj, argp);
|
|
||||||
+ return ntsync_event_set(obj, argp, false);
|
|
||||||
case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
return ntsync_event_reset(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_PULSE:
|
|
||||||
+ return ntsync_event_set(obj, argp, true);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 6963356ee3f7..72047f36c45d 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -50,5 +50,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,62 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQuerySemaphore().
|
|
||||||
|
|
||||||
This returns the current count and maximum count of the semaphore.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 22 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index b9b4127a6c9f..0daaeeeba051 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -528,6 +528,25 @@ static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+
|
|
||||||
+ if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.sem = 0;
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+ args.count = sem->u.sem.count;
|
|
||||||
+ args.max = sem->u.sem.max;
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -547,6 +566,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_SEM_POST:
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_SEM_READ:
|
|
||||||
+ return ntsync_sem_read(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 72047f36c45d..42f51dc4e57e 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -51,5 +51,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
+#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,64 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQueryMutant().
|
|
||||||
|
|
||||||
This returns the recursion count, owner, and abandoned state of the mutex.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 23 +++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 24 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 0daaeeeba051..b07510035c1f 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -547,6 +547,27 @@ static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.mutex = 0;
|
|
||||||
+ spin_lock(&mutex->lock);
|
|
||||||
+ args.count = mutex->u.mutex.count;
|
|
||||||
+ args.owner = mutex->u.mutex.owner;
|
|
||||||
+ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -572,6 +593,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_READ:
|
|
||||||
+ return ntsync_mutex_read(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
return ntsync_event_set(obj, argp, false);
|
|
||||||
case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 42f51dc4e57e..25f3296cfabf 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -52,5 +52,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,62 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQueryEvent().
|
|
||||||
|
|
||||||
This returns the signaled state of the event and whether it is manual-reset.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 22 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index b07510035c1f..981a1545192c 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -568,6 +568,25 @@ static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.event = 0;
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+ args.manual = event->u.event.manual;
|
|
||||||
+ args.signaled = event->u.event.signaled;
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -601,6 +620,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_event_reset(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_PULSE:
|
|
||||||
return ntsync_event_set(obj, argp, true);
|
|
||||||
+ case NTSYNC_IOC_EVENT_READ:
|
|
||||||
+ return ntsync_event_read(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 25f3296cfabf..03c95e5a398f 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -53,5 +53,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
|
|
||||||
+#define NTSYNC_IOC_EVENT_READ _IOR ('N', 0x8d, struct ntsync_event_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,184 +0,0 @@
|
||||||
NT waits can optionally be made "alertable". This is a special channel for
|
|
||||||
thread wakeup that is mildly similar to SIGIO. A thread has an internal single
|
|
||||||
bit of "alerted" state, and if a thread is made alerted while an alertable wait,
|
|
||||||
the wait will return a special value, consume the "alerted" state, and will not
|
|
||||||
consume any of its objects.
|
|
||||||
|
|
||||||
Alerts are implemented using events; the user-space NT emulator is expected to
|
|
||||||
create an internal ntsync event for each thread and pass that event to wait
|
|
||||||
functions.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 68 ++++++++++++++++++++++++++++++++-----
|
|
||||||
include/uapi/linux/ntsync.h | 2 +-
|
|
||||||
2 files changed, 60 insertions(+), 10 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 981a1545192c..0055b4671808 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -808,22 +808,29 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
const struct ntsync_wait_args *args, bool all,
|
|
||||||
struct ntsync_q **ret_q)
|
|
||||||
{
|
|
||||||
+ int fds[NTSYNC_MAX_WAIT_COUNT + 1];
|
|
||||||
const __u32 count = args->count;
|
|
||||||
- int fds[NTSYNC_MAX_WAIT_COUNT];
|
|
||||||
struct ntsync_q *q;
|
|
||||||
+ __u32 total_count;
|
|
||||||
__u32 i, j;
|
|
||||||
|
|
||||||
- if (!args->owner || args->pad)
|
|
||||||
+ if (!args->owner)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
+ total_count = count;
|
|
||||||
+ if (args->alert)
|
|
||||||
+ total_count++;
|
|
||||||
+
|
|
||||||
if (copy_from_user(fds, u64_to_user_ptr(args->objs),
|
|
||||||
array_size(count, sizeof(*fds))))
|
|
||||||
return -EFAULT;
|
|
||||||
+ if (args->alert)
|
|
||||||
+ fds[count] = args->alert;
|
|
||||||
|
|
||||||
- q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
|
|
||||||
+ q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL);
|
|
||||||
if (!q)
|
|
||||||
return -ENOMEM;
|
|
||||||
q->task = current;
|
|
||||||
@@ -833,7 +840,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->ownerdead = false;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
- for (i = 0; i < count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = get_obj(dev, fds[i]);
|
|
||||||
|
|
||||||
@@ -883,9 +890,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args;
|
|
||||||
+ __u32 i, total_count;
|
|
||||||
struct ntsync_q *q;
|
|
||||||
int signaled;
|
|
||||||
- __u32 i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
@@ -895,9 +902,13 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
+ total_count = args.count;
|
|
||||||
+ if (args.alert)
|
|
||||||
+ total_count++;
|
|
||||||
+
|
|
||||||
/* queue ourselves */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = entry->obj;
|
|
||||||
|
|
||||||
@@ -906,9 +917,15 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
spin_unlock(&obj->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
- /* check if we are already signaled */
|
|
||||||
+ /*
|
|
||||||
+ * Check if we are already signaled.
|
|
||||||
+ *
|
|
||||||
+ * Note that the API requires that normal objects are checked before
|
|
||||||
+ * the alert event. Hence we queue the alert event last, and check
|
|
||||||
+ * objects in order.
|
|
||||||
+ */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
|
|
||||||
if (atomic_read(&q->signaled) != -1)
|
|
||||||
@@ -925,7 +942,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
/* and finally, unqueue */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = entry->obj;
|
|
||||||
|
|
||||||
@@ -985,6 +1002,14 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
*/
|
|
||||||
list_add_tail(&entry->node, &obj->all_waiters);
|
|
||||||
}
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[args.count];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
|
|
||||||
+ list_add_tail(&entry->node, &obj->any_waiters);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
/* check if we are already signaled */
|
|
||||||
|
|
||||||
@@ -992,6 +1017,21 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * Check if the alert event is signaled, making sure to do so only
|
|
||||||
+ * after checking if the other objects are signaled.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_obj *obj = q->entries[args.count].obj;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&q->signaled) == -1) {
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ try_wake_any_obj(obj);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
/* sleep */
|
|
||||||
|
|
||||||
ret = ntsync_schedule(q, &args);
|
|
||||||
@@ -1014,6 +1054,16 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
put_obj(obj);
|
|
||||||
}
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[args.count];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 03c95e5a398f..555ae81b479a 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -34,7 +34,7 @@ struct ntsync_wait_args {
|
|
||||||
__u32 count;
|
|
||||||
__u32 owner;
|
|
||||||
__u32 index;
|
|
||||||
- __u32 pad;
|
|
||||||
+ __u32 alert;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,79 +0,0 @@
|
||||||
NtWaitForMultipleObjects() can receive a timeout in two forms, relative or
|
|
||||||
absolute. Relative timeouts are unaffected by changes to the system time and do
|
|
||||||
not count down while the system suspends; for absolute timeouts the opposite is
|
|
||||||
true.
|
|
||||||
|
|
||||||
In order to make the interface and implementation simpler, the ntsync driver
|
|
||||||
only deals in absolute timeouts. However, we need to be able to emulate both
|
|
||||||
behaviours apropos suspension and time adjustment, which is achieved by allowing
|
|
||||||
either the MONOTONIC or REALTIME clock to be used.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 9 ++++++++-
|
|
||||||
include/uapi/linux/ntsync.h | 4 ++++
|
|
||||||
2 files changed, 12 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 0055b4671808..f54c81dada3d 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -778,11 +778,15 @@ static void put_obj(struct ntsync_obj *obj)
|
|
||||||
static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
|
|
||||||
{
|
|
||||||
ktime_t timeout = ns_to_ktime(args->timeout);
|
|
||||||
+ clockid_t clock = CLOCK_MONOTONIC;
|
|
||||||
ktime_t *timeout_ptr;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
|
|
||||||
|
|
||||||
+ if (args->flags & NTSYNC_WAIT_REALTIME)
|
|
||||||
+ clock = CLOCK_REALTIME;
|
|
||||||
+
|
|
||||||
do {
|
|
||||||
if (signal_pending(current)) {
|
|
||||||
ret = -ERESTARTSYS;
|
|
||||||
@@ -794,7 +798,7 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
- ret = schedule_hrtimeout(timeout_ptr, HRTIMER_MODE_ABS);
|
|
||||||
+ ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
|
|
||||||
} while (ret < 0);
|
|
||||||
__set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
@@ -817,6 +821,9 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
if (!args->owner)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
+ if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 555ae81b479a..b5e835d8dba8 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -28,6 +28,8 @@ struct ntsync_event_args {
|
|
||||||
__u32 signaled;
|
|
||||||
};
|
|
||||||
|
|
||||||
+#define NTSYNC_WAIT_REALTIME 0x1
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -35,6 +37,8 @@ struct ntsync_wait_args {
|
|
||||||
__u32 owner;
|
|
||||||
__u32 index;
|
|
||||||
__u32 alert;
|
|
||||||
+ __u32 flags;
|
|
||||||
+ __u32 pad;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,208 +0,0 @@
|
||||||
Wine has tests for its synchronization primitives, but these are more accessible
|
|
||||||
to kernel developers, and also allow us to test some edge cases that Wine does
|
|
||||||
not care about.
|
|
||||||
|
|
||||||
This patch adds tests for semaphore-specific ioctls NTSYNC_IOC_SEM_POST and
|
|
||||||
NTSYNC_IOC_SEM_READ, and waiting on semaphores.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
tools/testing/selftests/Makefile | 1 +
|
|
||||||
.../testing/selftests/drivers/ntsync/Makefile | 8 +
|
|
||||||
tools/testing/selftests/drivers/ntsync/config | 1 +
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 149 ++++++++++++++++++
|
|
||||||
4 files changed, 159 insertions(+)
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/config
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
|
|
||||||
index 15b6a111c3be..6c714a4e6478 100644
|
|
||||||
--- a/tools/testing/selftests/Makefile
|
|
||||||
+++ b/tools/testing/selftests/Makefile
|
|
||||||
@@ -15,6 +15,7 @@ TARGETS += cpu-hotplug
|
|
||||||
TARGETS += damon
|
|
||||||
TARGETS += dmabuf-heaps
|
|
||||||
TARGETS += drivers/dma-buf
|
|
||||||
+TARGETS += drivers/ntsync
|
|
||||||
TARGETS += drivers/s390x/uvdevice
|
|
||||||
TARGETS += drivers/net/bonding
|
|
||||||
TARGETS += drivers/net/team
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/Makefile b/tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..a34da5ccacf0
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
@@ -0,0 +1,8 @@
|
|
||||||
+# SPDX-LICENSE-IDENTIFIER: GPL-2.0-only
|
|
||||||
+TEST_GEN_PROGS := ntsync
|
|
||||||
+
|
|
||||||
+top_srcdir =../../../../..
|
|
||||||
+CFLAGS += -I$(top_srcdir)/usr/include
|
|
||||||
+LDLIBS += -lpthread
|
|
||||||
+
|
|
||||||
+include ../../lib.mk
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..60539c826d06
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/config
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+CONFIG_WINESYNC=y
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..1e145c6dfded
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -0,0 +1,149 @@
|
|
||||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
+/*
|
|
||||||
+ * Various unit tests for the "ntsync" synchronization primitive driver.
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#define _GNU_SOURCE
|
|
||||||
+#include <sys/ioctl.h>
|
|
||||||
+#include <sys/stat.h>
|
|
||||||
+#include <fcntl.h>
|
|
||||||
+#include <time.h>
|
|
||||||
+#include <pthread.h>
|
|
||||||
+#include <linux/ntsync.h>
|
|
||||||
+#include "../../kselftest_harness.h"
|
|
||||||
+
|
|
||||||
+static int read_sem_state(int sem, __u32 *count, __u32 *max)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(sem, NTSYNC_IOC_SEM_READ, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ *max = args.max;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_sem_state(sem, count, max) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __count, __max; \
|
|
||||||
+ int ret = read_sem_state((sem), &__count, &__max); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((count), __count); \
|
|
||||||
+ EXPECT_EQ((max), __max); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+static int post_sem(int sem, __u32 *count)
|
|
||||||
+{
|
|
||||||
+ return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args = {0};
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ args.timeout = timeout.tv_sec * 1000000000 + timeout.tv_nsec;
|
|
||||||
+ args.count = count;
|
|
||||||
+ args.objs = (uintptr_t)objs;
|
|
||||||
+ args.owner = owner;
|
|
||||||
+ args.index = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
|
|
||||||
+ *index = args.index;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(semaphore_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args sem_args;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ int fd, ret, sem;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 3;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ sem = sem_args.sem;
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ count = 0;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem, 0, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ count = 3;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 0, 2);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ count = ~0u;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ close(sem);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,222 +0,0 @@
|
||||||
Test mutex-specific ioctls NTSYNC_IOC_MUTEX_UNLOCK and NTSYNC_IOC_MUTEX_READ,
|
|
||||||
and waiting on mutexes.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 196 ++++++++++++++++++
|
|
||||||
1 file changed, 196 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 1e145c6dfded..7cd0f40594fd 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -40,6 +40,39 @@ static int post_sem(int sem, __u32 *count)
|
|
||||||
return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int read_mutex_state(int mutex, __u32 *count, __u32 *owner)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ *owner = args.owner;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_mutex_state(mutex, count, owner) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __count, __owner; \
|
|
||||||
+ int ret = read_mutex_state((mutex), &__count, &__owner); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((count), __count); \
|
|
||||||
+ EXPECT_EQ((owner), __owner); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ args.owner = owner;
|
|
||||||
+ args.count = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_UNLOCK, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
@@ -146,4 +179,167 @@ TEST(semaphore_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(mutex_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args;
|
|
||||||
+ __u32 owner, count, index;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ int fd, ret, mutex;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 2;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 0, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 456, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+ check_mutex_state(mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, count);
|
|
||||||
+ check_mutex_state(mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 2, 456);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 456, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ owner = 0;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ owner = 456;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = ~0u;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, ~0u, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,139 +0,0 @@
|
||||||
Test basic synchronous functionality of NTSYNC_IOC_WAIT_ANY, when objects are
|
|
||||||
considered signaled or not signaled, and how they are affected by a successful
|
|
||||||
wait.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 119 ++++++++++++++++++
|
|
||||||
1 file changed, 119 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 7cd0f40594fd..40ad8cbd3138 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -342,4 +342,123 @@ TEST(mutex_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(test_wait_any)
|
|
||||||
+{
|
|
||||||
+ int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 owner, index, count, i;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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;
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ /* test waiting on the same object twice */
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ objs[0] = objs[1] = sem_args.sem;
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 0, NULL, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < NTSYNC_MAX_WAIT_COUNT + 1; ++i)
|
|
||||||
+ objs[i] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT + 1, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, -1, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,136 +0,0 @@
|
||||||
Test basic synchronous functionality of NTSYNC_IOC_WAIT_ALL, and when objects
|
|
||||||
are considered simultaneously signaled.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 99 ++++++++++++++++++-
|
|
||||||
1 file changed, 97 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 40ad8cbd3138..c0f372167557 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -73,7 +73,8 @@ static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
+ const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
struct timespec timeout;
|
|
||||||
@@ -86,11 +87,21 @@ static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *in
|
|
||||||
args.objs = (uintptr_t)objs;
|
|
||||||
args.owner = owner;
|
|
||||||
args.index = 0xdeadbeef;
|
|
||||||
- ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
|
|
||||||
+ ret = ioctl(fd, request, &args);
|
|
||||||
*index = args.index;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(semaphore_state)
|
|
||||||
{
|
|
||||||
struct ntsync_sem_args sem_args;
|
|
||||||
@@ -461,4 +472,88 @@ TEST(test_wait_any)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(test_wait_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 owner, index, count;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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;
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ count = 3;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 2, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 3, 123);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ /* test waiting on the same object twice */
|
|
||||||
+ objs[0] = objs[1] = sem_args.sem;
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,169 +0,0 @@
|
||||||
Test contended "wait-for-any" waits, to make sure that scheduling and wakeup
|
|
||||||
logic works correctly.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 150 ++++++++++++++++++
|
|
||||||
1 file changed, 150 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index c0f372167557..993f5db23768 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -556,4 +556,154 @@ TEST(test_wait_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+struct wake_args {
|
|
||||||
+ int fd;
|
|
||||||
+ int obj;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct wait_args {
|
|
||||||
+ int fd;
|
|
||||||
+ unsigned long request;
|
|
||||||
+ struct ntsync_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 __u64 get_abs_timeout(unsigned int ms)
|
|
||||||
+{
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+ return (timeout.tv_sec * 1000000000) + timeout.tv_nsec + (ms * 1000000);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_for_thread(pthread_t thread, unsigned int ms)
|
|
||||||
+{
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_REALTIME, &timeout);
|
|
||||||
+ timeout.tv_nsec += ms * 1000000;
|
|
||||||
+ timeout.tv_sec += (timeout.tv_nsec / 1000000000);
|
|
||||||
+ timeout.tv_nsec %= 1000000000;
|
|
||||||
+ return pthread_timedjoin_np(thread, NULL, &timeout);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(wake_any)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ pthread_t thread;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 1;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ /* test waking the semaphore */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ 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 = NTSYNC_IOC_WAIT_ANY;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+
|
|
||||||
+ 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 */
|
|
||||||
+ ret = wait_any(fd, 1, &mutex_args.mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(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);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, mutex_args.count);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ /* delete an object while it's being waited on */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(200);
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 200);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(-1, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,118 +0,0 @@
|
||||||
Test contended "wait-for-all" waits, to make sure that scheduling and wakeup
|
|
||||||
logic works correctly, and that the wait only exits once objects are all
|
|
||||||
simultaneously signaled.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 98 +++++++++++++++++++
|
|
||||||
1 file changed, 98 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 993f5db23768..b77fb0b2c4b1 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -706,4 +706,102 @@ TEST(wake_any)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(wake_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ pthread_t thread;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 1;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 456;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ 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 */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(200);
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 200);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(-1, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,118 +0,0 @@
|
||||||
Test event-specific ioctls NTSYNC_IOC_EVENT_SET, NTSYNC_IOC_EVENT_RESET,
|
|
||||||
NTSYNC_IOC_EVENT_PULSE, NTSYNC_IOC_EVENT_READ for manual-reset events, and
|
|
||||||
waiting on manual-reset events.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 89 +++++++++++++++++++
|
|
||||||
1 file changed, 89 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index b77fb0b2c4b1..b6481c2b85cc 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -73,6 +73,27 @@ static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int read_event_state(int event, __u32 *signaled, __u32 *manual)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_READ, &args);
|
|
||||||
+ *signaled = args.signaled;
|
|
||||||
+ *manual = args.manual;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_event_state(event, signaled, manual) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __signaled, __manual; \
|
|
||||||
+ int ret = read_event_state((event), &__signaled, &__manual); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((signaled), __signaled); \
|
|
||||||
+ EXPECT_EQ((manual), __manual); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
@@ -353,6 +374,74 @@ TEST(mutex_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(manual_event_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ __u32 index, signaled;
|
|
||||||
+ int fd, event, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ event_args.manual = 1;
|
|
||||||
+ event_args.signaled = 0;
|
|
||||||
+ event_args.event = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, event_args.event);
|
|
||||||
+ event = event_args.event;
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ close(event);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(test_wait_any)
|
|
||||||
{
|
|
||||||
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,81 +0,0 @@
|
||||||
Test event-specific ioctls NTSYNC_IOC_EVENT_SET, NTSYNC_IOC_EVENT_RESET,
|
|
||||||
NTSYNC_IOC_EVENT_PULSE, NTSYNC_IOC_EVENT_READ for auto-reset events, and
|
|
||||||
waiting on auto-reset events.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 59 +++++++++++++++++++
|
|
||||||
1 file changed, 59 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index b6481c2b85cc..12ccb4ec28e4 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -442,6 +442,65 @@ TEST(manual_event_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(auto_event_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ __u32 index, signaled;
|
|
||||||
+ int fd, event, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ event_args.manual = 0;
|
|
||||||
+ event_args.signaled = 1;
|
|
||||||
+ event_args.event = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, event_args.event);
|
|
||||||
+ event = event_args.event;
|
|
||||||
+
|
|
||||||
+ check_event_state(event, 1, 0);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 1, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ close(event);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(test_wait_any)
|
|
||||||
{
|
|
||||||
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,259 +0,0 @@
|
||||||
Expand the contended wait tests, which previously only covered events and
|
|
||||||
semaphores, to cover events as well.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 151 +++++++++++++++++-
|
|
||||||
1 file changed, 147 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 12ccb4ec28e4..5d17eff6a370 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -622,6 +622,7 @@ TEST(test_wait_any)
|
|
||||||
|
|
||||||
TEST(test_wait_all)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
__u32 owner, index, count;
|
|
||||||
@@ -644,6 +645,11 @@ TEST(test_wait_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
objs[0] = sem_args.sem;
|
|
||||||
objs[1] = mutex_args.mutex;
|
|
||||||
|
|
||||||
@@ -692,6 +698,14 @@ TEST(test_wait_all)
|
|
||||||
check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_event_state(event_args.event, 1, 1);
|
|
||||||
+
|
|
||||||
/* test waiting on the same object twice */
|
|
||||||
objs[0] = objs[1] = sem_args.sem;
|
|
||||||
ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
@@ -700,6 +714,7 @@ TEST(test_wait_all)
|
|
||||||
|
|
||||||
close(sem_args.sem);
|
|
||||||
close(mutex_args.mutex);
|
|
||||||
+ close(event_args.event);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
@@ -746,12 +761,13 @@ static int wait_for_thread(pthread_t thread, unsigned int ms)
|
|
||||||
|
|
||||||
TEST(wake_any)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
struct wait_args thread_args;
|
|
||||||
+ __u32 count, index, signaled;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
- __u32 count, index;
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
@@ -833,10 +849,101 @@ TEST(wake_any)
|
|
||||||
EXPECT_EQ(0, thread_args.ret);
|
|
||||||
EXPECT_EQ(1, wait_args.index);
|
|
||||||
|
|
||||||
+ /* test waking events */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = false;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = false;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
/* delete an object while it's being waited on */
|
|
||||||
|
|
||||||
wait_args.timeout = get_abs_timeout(200);
|
|
||||||
wait_args.owner = 123;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
|
|
||||||
@@ -856,12 +963,14 @@ TEST(wake_any)
|
|
||||||
|
|
||||||
TEST(wake_all)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args manual_event_args = {0};
|
|
||||||
+ struct ntsync_event_args auto_event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
struct wait_args thread_args;
|
|
||||||
- int objs[2], fd, ret;
|
|
||||||
- __u32 count, index;
|
|
||||||
+ __u32 count, index, signaled;
|
|
||||||
+ int objs[4], fd, ret;
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
@@ -881,12 +990,24 @@ TEST(wake_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
|
|
||||||
+ manual_event_args.manual = true;
|
|
||||||
+ manual_event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &manual_event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ auto_event_args.manual = false;
|
|
||||||
+ auto_event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
objs[0] = sem_args.sem;
|
|
||||||
objs[1] = mutex_args.mutex;
|
|
||||||
+ objs[2] = manual_event_args.event;
|
|
||||||
+ objs[3] = auto_event_args.event;
|
|
||||||
|
|
||||||
wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
wait_args.objs = (uintptr_t)objs;
|
|
||||||
- wait_args.count = 2;
|
|
||||||
+ wait_args.count = 4;
|
|
||||||
wait_args.owner = 456;
|
|
||||||
thread_args.fd = fd;
|
|
||||||
thread_args.args = &wait_args;
|
|
||||||
@@ -920,12 +1041,32 @@ TEST(wake_all)
|
|
||||||
|
|
||||||
check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
|
|
||||||
+ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
count = 2;
|
|
||||||
ret = post_sem(sem_args.sem, &count);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 2, 3);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+ check_event_state(manual_event_args.event, 1, 1);
|
|
||||||
+ check_event_state(auto_event_args.event, 0, 0);
|
|
||||||
|
|
||||||
ret = wait_for_thread(thread, 100);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
@@ -943,6 +1084,8 @@ TEST(wake_all)
|
|
||||||
|
|
||||||
close(sem_args.sem);
|
|
||||||
close(mutex_args.mutex);
|
|
||||||
+ close(manual_event_args.event);
|
|
||||||
+ close(auto_event_args.event);
|
|
||||||
|
|
||||||
ret = wait_for_thread(thread, 200);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,223 +0,0 @@
|
||||||
Test the "alert" functionality of NTSYNC_IOC_WAIT_ALL and NTSYNC_IOC_WAIT_ANY,
|
|
||||||
when a wait is woken with an alert and when it is woken by an object.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 179 +++++++++++++++++-
|
|
||||||
1 file changed, 176 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 5d17eff6a370..5465a16d38b3 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -95,7 +95,7 @@ static int read_event_state(int event, __u32 *signaled, __u32 *manual)
|
|
||||||
})
|
|
||||||
|
|
||||||
static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
- const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+ const int *objs, __u32 owner, int alert, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
struct timespec timeout;
|
|
||||||
@@ -108,6 +108,7 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
args.objs = (uintptr_t)objs;
|
|
||||||
args.owner = owner;
|
|
||||||
args.index = 0xdeadbeef;
|
|
||||||
+ args.alert = alert;
|
|
||||||
ret = ioctl(fd, request, &args);
|
|
||||||
*index = args.index;
|
|
||||||
return ret;
|
|
||||||
@@ -115,12 +116,26 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
|
|
||||||
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
- return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
- return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_any_alert(int fd, __u32 count, const int *objs,
|
|
||||||
+ __u32 owner, int alert, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
|
|
||||||
+ count, objs, owner, alert, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_all_alert(int fd, __u32 count, const int *objs,
|
|
||||||
+ __u32 owner, int alert, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
|
|
||||||
+ count, objs, owner, alert, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(semaphore_state)
|
|
||||||
@@ -1095,4 +1110,162 @@ TEST(wake_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(alert_any)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 index, count, signaled;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ sem_args.count = 1;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[1] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ /* test with an auto-reset event */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(objs[0], &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ close(objs[0]);
|
|
||||||
+ close(objs[1]);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(alert_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 index, count, signaled;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ sem_args.count = 1;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[1] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ /* test with an auto-reset event */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(objs[1], &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ close(objs[0]);
|
|
||||||
+ close(objs[1]);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,110 +0,0 @@
|
||||||
Expand the alert tests to cover alerting a thread mid-wait, to test that the
|
|
||||||
relevant scheduling logic works correctly.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 62 +++++++++++++++++++
|
|
||||||
1 file changed, 62 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 5465a16d38b3..968874d7e325 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -1113,9 +1113,12 @@ TEST(wake_all)
|
|
||||||
TEST(alert_any)
|
|
||||||
{
|
|
||||||
struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
__u32 index, count, signaled;
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
+ pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
ASSERT_LE(0, fd);
|
|
||||||
@@ -1163,6 +1166,34 @@ TEST(alert_any)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(2, index);
|
|
||||||
|
|
||||||
+ /* test wakeup via alert */
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+ wait_args.alert = event_args.event;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ANY;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(2, wait_args.index);
|
|
||||||
+
|
|
||||||
close(event_args.event);
|
|
||||||
|
|
||||||
/* test with an auto-reset event */
|
|
||||||
@@ -1199,9 +1230,12 @@ TEST(alert_any)
|
|
||||||
TEST(alert_all)
|
|
||||||
{
|
|
||||||
struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
__u32 index, count, signaled;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
+ pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
ASSERT_LE(0, fd);
|
|
||||||
@@ -1235,6 +1269,34 @@ TEST(alert_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(2, index);
|
|
||||||
|
|
||||||
+ /* test wakeup via alert */
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+ wait_args.alert = event_args.event;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(2, wait_args.index);
|
|
||||||
+
|
|
||||||
close(event_args.event);
|
|
||||||
|
|
||||||
/* test with an auto-reset event */
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,97 +0,0 @@
|
||||||
Test a more realistic usage pattern, and one with heavy contention, in order to
|
|
||||||
actually exercise ntsync's internal synchronization.
|
|
||||||
|
|
||||||
This test has several threads in a tight loop acquiring a mutex, modifying some
|
|
||||||
shared data, and then releasing the mutex. At the end we check if the data is
|
|
||||||
consistent.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 74 +++++++++++++++++++
|
|
||||||
1 file changed, 74 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 968874d7e325..5fa2c9a0768c 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -1330,4 +1330,78 @@ TEST(alert_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#define STRESS_LOOPS 10000
|
|
||||||
+#define STRESS_THREADS 4
|
|
||||||
+
|
|
||||||
+static unsigned int stress_counter;
|
|
||||||
+static int stress_device, stress_start_event, stress_mutex;
|
|
||||||
+
|
|
||||||
+static void *stress_thread(void *arg)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ __u32 index, count, i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = UINT64_MAX;
|
|
||||||
+ wait_args.count = 1;
|
|
||||||
+ wait_args.objs = (uintptr_t)&stress_start_event;
|
|
||||||
+ wait_args.owner = gettid();
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+
|
|
||||||
+ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
||||||
+
|
|
||||||
+ wait_args.objs = (uintptr_t)&stress_mutex;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_LOOPS; ++i) {
|
|
||||||
+ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
||||||
+
|
|
||||||
+ ++stress_counter;
|
|
||||||
+
|
|
||||||
+ unlock_mutex(stress_mutex, wait_args.owner, &count);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return NULL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(stress_wait)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ struct ntsync_mutex_args mutex_args;
|
|
||||||
+ pthread_t threads[STRESS_THREADS];
|
|
||||||
+ __u32 signaled, i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ stress_device = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, stress_device);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ stress_mutex = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ event_args.manual = 1;
|
|
||||||
+ event_args.signaled = 0;
|
|
||||||
+ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ stress_start_event = event_args.event;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_THREADS; ++i)
|
|
||||||
+ pthread_create(&threads[i], NULL, stress_thread, NULL);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(stress_start_event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_THREADS; ++i) {
|
|
||||||
+ ret = pthread_join(threads[i], NULL);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ EXPECT_EQ(STRESS_LOOPS * STRESS_THREADS, stress_counter);
|
|
||||||
+
|
|
||||||
+ close(stress_start_event);
|
|
||||||
+ close(stress_mutex);
|
|
||||||
+ close(stress_device);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,29 +0,0 @@
|
||||||
Add myself as maintainer, supported by CodeWeavers.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
MAINTAINERS | 9 +++++++++
|
|
||||||
1 file changed, 9 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
||||||
index 9ed4d3868539..d83dd35d9f73 100644
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -15595,6 +15595,15 @@ T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
|
|
||||||
F: Documentation/filesystems/ntfs3.rst
|
|
||||||
F: fs/ntfs3/
|
|
||||||
|
|
||||||
+NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
|
|
||||||
+M: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+L: wine-devel@winehq.org
|
|
||||||
+S: Supported
|
|
||||||
+F: Documentation/userspace-api/ntsync.rst
|
|
||||||
+F: drivers/misc/ntsync.c
|
|
||||||
+F: include/uapi/linux/ntsync.h
|
|
||||||
+F: tools/testing/selftests/drivers/ntsync/
|
|
||||||
+
|
|
||||||
NUBUS SUBSYSTEM
|
|
||||||
M: Finn Thain <fthain@linux-m68k.org>
|
|
||||||
L: linux-m68k@lists.linux-m68k.org
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,5 +1,5 @@
|
||||||
pkgname=linux6.8-zen
|
pkgname=linux6.8-zen
|
||||||
version=6.8.6
|
version=6.8.7
|
||||||
revision=1
|
revision=1
|
||||||
zen=1
|
zen=1
|
||||||
wrksrc="linux-${version}-zen${zen}"
|
wrksrc="linux-${version}-zen${zen}"
|
||||||
|
@ -8,7 +8,7 @@ maintainer="Wizzard <retard@deadzone.lol>"
|
||||||
license="GPL-2.0-only"
|
license="GPL-2.0-only"
|
||||||
homepage="http://www.zen-kernel.org/"
|
homepage="http://www.zen-kernel.org/"
|
||||||
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
||||||
checksum="a6f2cd6b3ed6555161b688d41d04275c2bf05e893778a8e5b2c52a3ff627db1f"
|
checksum="5ab6ad1d26925f02a74b41d485e5faf25056efec123d0a1433aa3a4108534aae"
|
||||||
python_version=3
|
python_version=3
|
||||||
patch_args="-Np1"
|
patch_args="-Np1"
|
||||||
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
ntsync uses a misc device as the simplest and least intrusive uAPI interface.
|
|
||||||
|
|
||||||
Each file description on the device represents an isolated NT instance, intended
|
|
||||||
to correspond to a single NT virtual machine.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/Kconfig | 11 +++++++++
|
|
||||||
drivers/misc/Makefile | 1 +
|
|
||||||
drivers/misc/ntsync.c | 52 +++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
3 files changed, 64 insertions(+)
|
|
||||||
create mode 100644 drivers/misc/ntsync.c
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
|
||||||
index 4fb291f0bf7c..801ed229ed7d 100644
|
|
||||||
--- a/drivers/misc/Kconfig
|
|
||||||
+++ b/drivers/misc/Kconfig
|
|
||||||
@@ -506,6 +506,17 @@ config OPEN_DICE
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
+config NTSYNC
|
|
||||||
+ tristate "NT synchronization primitive emulation"
|
|
||||||
+ help
|
|
||||||
+ This module provides kernel support for emulation of Windows NT
|
|
||||||
+ synchronization primitives. It is not a hardware driver.
|
|
||||||
+
|
|
||||||
+ To compile this driver as a module, choose M here: the
|
|
||||||
+ module will be called ntsync.
|
|
||||||
+
|
|
||||||
+ If unsure, say N.
|
|
||||||
+
|
|
||||||
config VCPU_STALL_DETECTOR
|
|
||||||
tristate "Guest vCPU stall detector"
|
|
||||||
depends on OF && HAS_IOMEM
|
|
||||||
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
|
||||||
index ea6ea5bbbc9c..153a3f4837e8 100644
|
|
||||||
--- a/drivers/misc/Makefile
|
|
||||||
+++ b/drivers/misc/Makefile
|
|
||||||
@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
|
|
||||||
obj-$(CONFIG_UACCE) += uacce/
|
|
||||||
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
|
||||||
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
|
||||||
+obj-$(CONFIG_NTSYNC) += ntsync.o
|
|
||||||
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
|
||||||
obj-$(CONFIG_OPEN_DICE) += open-dice.o
|
|
||||||
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..bd76e653d83e
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -0,0 +1,52 @@
|
|
||||||
+// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
+/*
|
|
||||||
+ * ntsync.c - Kernel driver for NT synchronization primitives
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/fs.h>
|
|
||||||
+#include <linux/miscdevice.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+
|
|
||||||
+#define NTSYNC_NAME "ntsync"
|
|
||||||
+
|
|
||||||
+static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ return nonseekable_open(inode, file);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_char_release(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
+ unsigned long parm)
|
|
||||||
+{
|
|
||||||
+ switch (cmd) {
|
|
||||||
+ default:
|
|
||||||
+ return -ENOIOCTLCMD;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct file_operations ntsync_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .open = ntsync_char_open,
|
|
||||||
+ .release = ntsync_char_release,
|
|
||||||
+ .unlocked_ioctl = ntsync_char_ioctl,
|
|
||||||
+ .compat_ioctl = compat_ptr_ioctl,
|
|
||||||
+ .llseek = no_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct miscdevice ntsync_misc = {
|
|
||||||
+ .minor = MISC_DYNAMIC_MINOR,
|
|
||||||
+ .name = NTSYNC_NAME,
|
|
||||||
+ .fops = &ntsync_fops,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+module_misc_device(ntsync_misc);
|
|
||||||
+
|
|
||||||
+MODULE_AUTHOR("Elizabeth Figura <zfigura@codeweavers.com>");
|
|
||||||
+MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
|
|
||||||
+MODULE_LICENSE("GPL");
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,228 +0,0 @@
|
||||||
This corresponds to the NT syscall NtCreateSemaphore().
|
|
||||||
|
|
||||||
Semaphores are one of three types of object to be implemented in this driver,
|
|
||||||
the others being mutexes and events.
|
|
||||||
|
|
||||||
An NT semaphore contains a 32-bit counter, and is signaled and can be acquired
|
|
||||||
when the counter is nonzero. The counter has a maximum value which is specified
|
|
||||||
at creation time. The initial value of the semaphore is also specified at
|
|
||||||
creation time. There are no restrictions on the maximum and initial value.
|
|
||||||
|
|
||||||
Each object is exposed as an file, to which any number of fds may be opened.
|
|
||||||
When all fds are closed, the object is deleted.
|
|
||||||
|
|
||||||
Objects hold a pointer to the ntsync_device that created them. The device's
|
|
||||||
reference count is driven by struct file.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../userspace-api/ioctl/ioctl-number.rst | 2 +
|
|
||||||
drivers/misc/ntsync.c | 131 ++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 21 +++
|
|
||||||
3 files changed, 154 insertions(+)
|
|
||||||
create mode 100644 include/uapi/linux/ntsync.h
|
|
||||||
|
|
||||||
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
index 457e16f06e04..2f5c6994f042 100644
|
|
||||||
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
|
|
||||||
@@ -173,6 +173,8 @@ Code Seq# Include File Comments
|
|
||||||
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
|
|
||||||
'N' 00-1F drivers/usb/scanner.h
|
|
||||||
'N' 40-7F drivers/block/nvme.c
|
|
||||||
+'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
|
|
||||||
+ <mailto:wine-devel@winehq.org>
|
|
||||||
'O' 00-06 mtd/ubi-user.h UBI
|
|
||||||
'P' all linux/soundcard.h conflict!
|
|
||||||
'P' 60-6F sound/sscape_ioctl.h conflict!
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index bd76e653d83e..20158ec148bc 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -5,26 +5,157 @@
|
|
||||||
* Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
+#include <linux/anon_inodes.h>
|
|
||||||
+#include <linux/file.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
+#include <linux/slab.h>
|
|
||||||
+#include <uapi/linux/ntsync.h>
|
|
||||||
|
|
||||||
#define NTSYNC_NAME "ntsync"
|
|
||||||
|
|
||||||
+enum ntsync_type {
|
|
||||||
+ NTSYNC_TYPE_SEM,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Individual synchronization primitives are represented by
|
|
||||||
+ * struct ntsync_obj, and each primitive is backed by a file.
|
|
||||||
+ *
|
|
||||||
+ * The whole namespace is represented by a struct ntsync_device also
|
|
||||||
+ * backed by a file.
|
|
||||||
+ *
|
|
||||||
+ * Both rely on struct file for reference counting. Individual
|
|
||||||
+ * ntsync_obj objects take a reference to the device when created.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+struct ntsync_obj {
|
|
||||||
+ enum ntsync_type type;
|
|
||||||
+
|
|
||||||
+ union {
|
|
||||||
+ struct {
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 max;
|
|
||||||
+ } sem;
|
|
||||||
+ } u;
|
|
||||||
+
|
|
||||||
+ struct file *file;
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_device {
|
|
||||||
+ struct file *file;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj = file->private_data;
|
|
||||||
+
|
|
||||||
+ fput(obj->dev->file);
|
|
||||||
+ kfree(obj);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct file_operations ntsync_obj_fops = {
|
|
||||||
+ .owner = THIS_MODULE,
|
|
||||||
+ .release = ntsync_obj_release,
|
|
||||||
+ .llseek = no_llseek,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
+ enum ntsync_type type)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+
|
|
||||||
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
|
||||||
+ if (!obj)
|
|
||||||
+ return NULL;
|
|
||||||
+ obj->type = type;
|
|
||||||
+ obj->dev = dev;
|
|
||||||
+ get_file(dev->file);
|
|
||||||
+
|
|
||||||
+ return obj;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_obj_get_fd(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ struct file *file;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ fd = get_unused_fd_flags(O_CLOEXEC);
|
|
||||||
+ if (fd < 0)
|
|
||||||
+ return fd;
|
|
||||||
+ file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
|
|
||||||
+ if (IS_ERR(file)) {
|
|
||||||
+ put_unused_fd(fd);
|
|
||||||
+ return PTR_ERR(file);
|
|
||||||
+ }
|
|
||||||
+ obj->file = file;
|
|
||||||
+ fd_install(fd, file);
|
|
||||||
+
|
|
||||||
+ return fd;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+ struct ntsync_obj *sem;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (args.count > args.max)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
|
|
||||||
+ if (!sem)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ sem->u.sem.count = args.count;
|
|
||||||
+ sem->u.sem.max = args.max;
|
|
||||||
+ fd = ntsync_obj_get_fd(sem);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(sem);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->sem);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+
|
|
||||||
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
||||||
+ if (!dev)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ file->private_data = dev;
|
|
||||||
+ dev->file = file;
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ntsync_char_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = file->private_data;
|
|
||||||
+
|
|
||||||
+ kfree(dev);
|
|
||||||
+
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
unsigned long parm)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = file->private_data;
|
|
||||||
+ void __user *argp = (void __user *)parm;
|
|
||||||
+
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
+ return ntsync_create_sem(dev, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..6a4867a6c97b
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -0,0 +1,21 @@
|
|
||||||
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
|
||||||
+/*
|
|
||||||
+ * Kernel support for NT synchronization primitive emulation
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef __LINUX_NTSYNC_H
|
|
||||||
+#define __LINUX_NTSYNC_H
|
|
||||||
+
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+
|
|
||||||
+struct ntsync_sem_args {
|
|
||||||
+ __u32 sem;
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 max;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
+
|
|
||||||
+#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,147 +0,0 @@
|
||||||
This corresponds to the NT syscall NtReleaseSemaphore().
|
|
||||||
|
|
||||||
This increases the semaphore's internal counter by the given value, and returns
|
|
||||||
the previous value. If the counter would overflow the defined maximum, the
|
|
||||||
function instead fails and returns -EOVERFLOW.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 2 ++
|
|
||||||
2 files changed, 71 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 20158ec148bc..3c2f743c58b0 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -10,7 +10,9 @@
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
+#include <linux/overflow.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
+#include <linux/spinlock.h>
|
|
||||||
#include <uapi/linux/ntsync.h>
|
|
||||||
|
|
||||||
#define NTSYNC_NAME "ntsync"
|
|
||||||
@@ -31,23 +33,70 @@ enum ntsync_type {
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ntsync_obj {
|
|
||||||
+ spinlock_t lock;
|
|
||||||
+
|
|
||||||
enum ntsync_type type;
|
|
||||||
|
|
||||||
+ struct file *file;
|
|
||||||
+ struct ntsync_device *dev;
|
|
||||||
+
|
|
||||||
+ /* The following fields are protected by the object lock. */
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
__u32 count;
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
-
|
|
||||||
- struct file *file;
|
|
||||||
- struct ntsync_device *dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
+ * invalid.
|
|
||||||
+ */
|
|
||||||
+static int post_sem_state(struct ntsync_obj *sem, __u32 count)
|
|
||||||
+{
|
|
||||||
+ __u32 sum;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (check_add_overflow(sem->u.sem.count, count, &sum) ||
|
|
||||||
+ sum > sem->u.sem.max)
|
|
||||||
+ return -EOVERFLOW;
|
|
||||||
+
|
|
||||||
+ sem->u.sem.count = sum;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ __u32 __user *user_args = argp;
|
|
||||||
+ __u32 prev_count;
|
|
||||||
+ __u32 args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (!ret && put_user(prev_count, user_args))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -58,9 +107,25 @@ static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
+ unsigned long parm)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_obj *obj = file->private_data;
|
|
||||||
+ void __user *argp = (void __user *)parm;
|
|
||||||
+
|
|
||||||
+ switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_SEM_POST:
|
|
||||||
+ return ntsync_sem_post(obj, argp);
|
|
||||||
+ default:
|
|
||||||
+ return -ENOIOCTLCMD;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static const struct file_operations ntsync_obj_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.release = ntsync_obj_release,
|
|
||||||
+ .unlocked_ioctl = ntsync_obj_ioctl,
|
|
||||||
+ .compat_ioctl = compat_ptr_ioctl,
|
|
||||||
.llseek = no_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -75,6 +140,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
obj->type = type;
|
|
||||||
obj->dev = dev;
|
|
||||||
get_file(dev->file);
|
|
||||||
+ spin_lock_init(&obj->lock);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 6a4867a6c97b..dcfa38fdc93c 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -18,4 +18,6 @@ struct ntsync_sem_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
+#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
+
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,360 +0,0 @@
|
||||||
This corresponds to part of the functionality of the NT syscall
|
|
||||||
NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
|
|
||||||
the third argument (wait_any) is TRUE, and it does not handle alertable waits.
|
|
||||||
Those features have been split out into separate patches to ease review.
|
|
||||||
|
|
||||||
NTSYNC_IOC_WAIT_ANY is a vectored wait function similar to poll(). Unlike
|
|
||||||
poll(), it "consumes" objects when they are signaled. For semaphores, this means
|
|
||||||
decreasing one from the internal counter. At most one object can be consumed by
|
|
||||||
this function.
|
|
||||||
|
|
||||||
Up to 64 objects can be waited on at once. As soon as one is signaled, the
|
|
||||||
object with the lowest index is consumed, and that index is returned via the
|
|
||||||
"index" field.
|
|
||||||
|
|
||||||
A timeout is supported. The timeout is passed as a u64 nanosecond value, which
|
|
||||||
represents absolute time measured against the MONOTONIC clock. If U64_MAX is
|
|
||||||
passed, the ioctl waits indefinitely.
|
|
||||||
|
|
||||||
This ioctl validates that all objects belong to the relevant device. This is not
|
|
||||||
necessary for any technical reason related to NTSYNC_IOC_WAIT_ANY, but will be
|
|
||||||
necessary for NTSYNC_IOC_WAIT_ALL introduced in the following patch.
|
|
||||||
|
|
||||||
Wait ioctls need to take a temporary reference to each object being waited on.
|
|
||||||
As with the device, the reference count of struct file is used for ntsync_obj.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 239 ++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 12 ++
|
|
||||||
2 files changed, 251 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 3c2f743c58b0..ad93ca0f8b84 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -6,11 +6,16 @@
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/anon_inodes.h>
|
|
||||||
+#include <linux/atomic.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
+#include <linux/hrtimer.h>
|
|
||||||
+#include <linux/ktime.h>
|
|
||||||
#include <linux/miscdevice.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/overflow.h>
|
|
||||||
+#include <linux/sched.h>
|
|
||||||
+#include <linux/sched/signal.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <uapi/linux/ntsync.h>
|
|
||||||
@@ -30,6 +35,8 @@ enum ntsync_type {
|
|
||||||
*
|
|
||||||
* Both rely on struct file for reference counting. Individual
|
|
||||||
* ntsync_obj objects take a reference to the device when created.
|
|
||||||
+ * Wait operations take a reference to each object being waited on for
|
|
||||||
+ * the duration of the wait.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ntsync_obj {
|
|
||||||
@@ -47,12 +54,55 @@ struct ntsync_obj {
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
+
|
|
||||||
+ struct list_head any_waiters;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_q_entry {
|
|
||||||
+ struct list_head node;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+ __u32 index;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct ntsync_q {
|
|
||||||
+ struct task_struct *task;
|
|
||||||
+ __u32 owner;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Protected via atomic_cmpxchg(). Only the thread that wins the
|
|
||||||
+ * compare-and-swap may actually change object states and wake this
|
|
||||||
+ * task.
|
|
||||||
+ */
|
|
||||||
+ atomic_t signaled;
|
|
||||||
+
|
|
||||||
+ __u32 count;
|
|
||||||
+ struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+static void try_wake_any_sem(struct ntsync_obj *sem)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&sem->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &sem->any_waiters, node) {
|
|
||||||
+ struct ntsync_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);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -88,6 +138,8 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
|
|
||||||
prev_count = sem->u.sem.count;
|
|
||||||
ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
|
|
||||||
spin_unlock(&sem->lock);
|
|
||||||
|
|
||||||
@@ -141,6 +193,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
obj->dev = dev;
|
|
||||||
get_file(dev->file);
|
|
||||||
spin_lock_init(&obj->lock);
|
|
||||||
+ INIT_LIST_HEAD(&obj->any_waiters);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@@ -191,6 +244,190 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
+{
|
|
||||||
+ struct file *file = fget(fd);
|
|
||||||
+ struct ntsync_obj *obj;
|
|
||||||
+
|
|
||||||
+ if (file->f_op != &ntsync_obj_fops) {
|
|
||||||
+ fput(file);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ obj = file->private_data;
|
|
||||||
+ if (obj->dev != dev) {
|
|
||||||
+ fput(file);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return obj;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void put_obj(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ fput(obj->file);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
|
|
||||||
+{
|
|
||||||
+ ktime_t timeout = ns_to_ktime(args->timeout);
|
|
||||||
+ ktime_t *timeout_ptr;
|
|
||||||
+ int ret = 0;
|
|
||||||
+
|
|
||||||
+ timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
|
|
||||||
+
|
|
||||||
+ do {
|
|
||||||
+ if (signal_pending(current)) {
|
|
||||||
+ ret = -ERESTARTSYS;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
+ if (atomic_read(&q->signaled) != -1) {
|
|
||||||
+ ret = 0;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ ret = schedule_hrtimeout(timeout_ptr, HRTIMER_MODE_ABS);
|
|
||||||
+ } while (ret < 0);
|
|
||||||
+ __set_current_state(TASK_RUNNING);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Allocate and initialize the ntsync_q structure, but do not queue us yet.
|
|
||||||
+ */
|
|
||||||
+static int setup_wait(struct ntsync_device *dev,
|
|
||||||
+ const struct ntsync_wait_args *args,
|
|
||||||
+ struct ntsync_q **ret_q)
|
|
||||||
+{
|
|
||||||
+ const __u32 count = args->count;
|
|
||||||
+ int fds[NTSYNC_MAX_WAIT_COUNT];
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ __u32 i, j;
|
|
||||||
+
|
|
||||||
+ if (!args->owner || args->pad)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(fds, u64_to_user_ptr(args->objs),
|
|
||||||
+ array_size(count, sizeof(*fds))))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
|
|
||||||
+ if (!q)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ q->task = current;
|
|
||||||
+ q->owner = args->owner;
|
|
||||||
+ atomic_set(&q->signaled, -1);
|
|
||||||
+ q->count = count;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = get_obj(dev, fds[i]);
|
|
||||||
+
|
|
||||||
+ if (!obj)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ entry->obj = obj;
|
|
||||||
+ entry->q = q;
|
|
||||||
+ entry->index = i;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *ret_q = q;
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ for (j = 0; j < i; j++)
|
|
||||||
+ put_obj(q->entries[j].obj);
|
|
||||||
+ kfree(q);
|
|
||||||
+ return -EINVAL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_TYPE_SEM:
|
|
||||||
+ try_wake_any_sem(obj);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ int signaled;
|
|
||||||
+ __u32 i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ ret = setup_wait(dev, &args, &q);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ /* queue ourselves */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ list_add_tail(&entry->node, &obj->any_waiters);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check if we are already signaled */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&q->signaled) != -1)
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ try_wake_any_obj(obj);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* sleep */
|
|
||||||
+
|
|
||||||
+ ret = ntsync_schedule(q, &args);
|
|
||||||
+
|
|
||||||
+ /* and finally, unqueue */
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ signaled = atomic_read(&q->signaled);
|
|
||||||
+ if (signaled != -1) {
|
|
||||||
+ struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
+
|
|
||||||
+ /* even if we caught a signal, we need to communicate success */
|
|
||||||
+ ret = 0;
|
|
||||||
+
|
|
||||||
+ if (put_user(signaled, &user_args->index))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+ } else if (!ret) {
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kfree(q);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev;
|
|
||||||
@@ -222,6 +459,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
+ case NTSYNC_IOC_WAIT_ANY:
|
|
||||||
+ return ntsync_wait_any(dev, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index dcfa38fdc93c..56b643fab611 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -16,7 +16,19 @@ struct ntsync_sem_args {
|
|
||||||
__u32 max;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_wait_args {
|
|
||||||
+ __u64 timeout;
|
|
||||||
+ __u64 objs;
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ __u32 index;
|
|
||||||
+ __u32 pad;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
+
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,365 +0,0 @@
|
||||||
This is similar to NTSYNC_IOC_WAIT_ANY, but waits until all of the objects are
|
|
||||||
simultaneously signaled, and then acquires all of them as a single atomic
|
|
||||||
operation.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 242 ++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 235 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index ad93ca0f8b84..d5759e9a3a8e 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -55,7 +55,34 @@ struct ntsync_obj {
|
|
||||||
} sem;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * 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 zero, and, if it is,
|
|
||||||
+ * we skip trying to wake "all" waiters.
|
|
||||||
+ *
|
|
||||||
+ * This hint isn't protected by any lock. It might change during the
|
|
||||||
+ * course of a wake, but there's no meaningful race there; it's only a
|
|
||||||
+ * hint.
|
|
||||||
+ *
|
|
||||||
+ * Since wait requests must originate from user-space threads, we're
|
|
||||||
+ * limited here by PID_MAX_LIMIT, so there's no risk of overflow.
|
|
||||||
+ */
|
|
||||||
+ atomic_t all_hint;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_q_entry {
|
|
||||||
@@ -76,14 +103,99 @@ struct ntsync_q {
|
|
||||||
*/
|
|
||||||
atomic_t signaled;
|
|
||||||
|
|
||||||
+ bool all;
|
|
||||||
__u32 count;
|
|
||||||
struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ntsync_device {
|
|
||||||
+ /*
|
|
||||||
+ * Wait-all operations must atomically grab all objects, and be totally
|
|
||||||
+ * ordered with respect to each other and wait-any operations.
|
|
||||||
+ * If one thread is trying to acquire several objects, another thread
|
|
||||||
+ * cannot touch the object at the same time.
|
|
||||||
+ *
|
|
||||||
+ * 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;
|
|
||||||
+
|
|
||||||
struct file *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
+static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
+{
|
|
||||||
+ lockdep_assert_held(&obj->lock);
|
|
||||||
+
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_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 ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
+ struct ntsync_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_nest_lock(&q->entries[i].obj->lock, &dev->wait_all_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 ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
+
|
|
||||||
+ switch (obj->type) {
|
|
||||||
+ case NTSYNC_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 ntsync_device *dev, struct ntsync_obj *obj)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_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 ntsync_obj *sem)
|
|
||||||
{
|
|
||||||
struct ntsync_q_entry *entry;
|
|
||||||
@@ -123,6 +235,7 @@ static int post_sem_state(struct ntsync_obj *sem, __u32 count)
|
|
||||||
|
|
||||||
static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
{
|
|
||||||
+ struct ntsync_device *dev = sem->dev;
|
|
||||||
__u32 __user *user_args = argp;
|
|
||||||
__u32 prev_count;
|
|
||||||
__u32 args;
|
|
||||||
@@ -134,14 +247,29 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
- spin_lock(&sem->lock);
|
|
||||||
+ if (atomic_read(&sem->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock);
|
|
||||||
|
|
||||||
- prev_count = sem->u.sem.count;
|
|
||||||
- ret = post_sem_state(sem, args);
|
|
||||||
- if (!ret)
|
|
||||||
- try_wake_any_sem(sem);
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret) {
|
|
||||||
+ try_wake_all_obj(dev, sem);
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- spin_unlock(&sem->lock);
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+ } else {
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ prev_count = sem->u.sem.count;
|
|
||||||
+ ret = post_sem_state(sem, args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_sem(sem);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (!ret && put_user(prev_count, user_args))
|
|
||||||
ret = -EFAULT;
|
|
||||||
@@ -194,6 +322,8 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
|
|
||||||
get_file(dev->file);
|
|
||||||
spin_lock_init(&obj->lock);
|
|
||||||
INIT_LIST_HEAD(&obj->any_waiters);
|
|
||||||
+ INIT_LIST_HEAD(&obj->all_waiters);
|
|
||||||
+ atomic_set(&obj->all_hint, 0);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
@@ -298,7 +428,7 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar
|
|
||||||
* Allocate and initialize the ntsync_q structure, but do not queue us yet.
|
|
||||||
*/
|
|
||||||
static int setup_wait(struct ntsync_device *dev,
|
|
||||||
- const struct ntsync_wait_args *args,
|
|
||||||
+ const struct ntsync_wait_args *args, bool all,
|
|
||||||
struct ntsync_q **ret_q)
|
|
||||||
{
|
|
||||||
const __u32 count = args->count;
|
|
||||||
@@ -322,6 +452,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->task = current;
|
|
||||||
q->owner = args->owner;
|
|
||||||
atomic_set(&q->signaled, -1);
|
|
||||||
+ q->all = all;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
@@ -331,6 +462,16 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
if (!obj)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
+ if (all) {
|
|
||||||
+ /* Check that the objects are all distinct. */
|
|
||||||
+ for (j = 0; j < i; j++) {
|
|
||||||
+ if (obj == q->entries[j].obj) {
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
entry->obj = obj;
|
|
||||||
entry->q = q;
|
|
||||||
entry->index = i;
|
|
||||||
@@ -366,7 +507,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
- ret = setup_wait(dev, &args, &q);
|
|
||||||
+ ret = setup_wait(dev, &args, false, &q);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
@@ -428,6 +569,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args;
|
|
||||||
+ struct ntsync_q *q;
|
|
||||||
+ int signaled;
|
|
||||||
+ __u32 i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ ret = setup_wait(dev, &args, true, &q);
|
|
||||||
+ if (ret < 0)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ /* queue ourselves */
|
|
||||||
+
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ atomic_inc(&obj->all_hint);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * obj->all_waiters is protected by dev->wait_all_lock rather
|
|
||||||
+ * than obj->lock, so there is no need to acquire obj->lock
|
|
||||||
+ * here.
|
|
||||||
+ */
|
|
||||||
+ list_add_tail(&entry->node, &obj->all_waiters);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* check if we are already signaled */
|
|
||||||
+
|
|
||||||
+ try_wake_all(dev, q, NULL);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ /* sleep */
|
|
||||||
+
|
|
||||||
+ ret = ntsync_schedule(q, &args);
|
|
||||||
+
|
|
||||||
+ /* and finally, unqueue */
|
|
||||||
+
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < args.count; i++) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * obj->all_waiters is protected by dev->wait_all_lock rather
|
|
||||||
+ * than obj->lock, so there is no need to acquire it here.
|
|
||||||
+ */
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+
|
|
||||||
+ atomic_dec(&obj->all_hint);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ signaled = atomic_read(&q->signaled);
|
|
||||||
+ if (signaled != -1) {
|
|
||||||
+ struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
+
|
|
||||||
+ /* even if we caught a signal, we need to communicate success */
|
|
||||||
+ ret = 0;
|
|
||||||
+
|
|
||||||
+ if (put_user(signaled, &user_args->index))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+ } else if (!ret) {
|
|
||||||
+ ret = -ETIMEDOUT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kfree(q);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev;
|
|
||||||
@@ -436,6 +658,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file)
|
|
||||||
if (!dev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
+ spin_lock_init(&dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
file->private_data = dev;
|
|
||||||
dev->file = file;
|
|
||||||
return nonseekable_open(inode, file);
|
|
||||||
@@ -459,6 +683,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
+ case NTSYNC_IOC_WAIT_ALL:
|
|
||||||
+ return ntsync_wait_all(dev, argp);
|
|
||||||
case NTSYNC_IOC_WAIT_ANY:
|
|
||||||
return ntsync_wait_any(dev, argp);
|
|
||||||
default:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 56b643fab611..19c37e27a4f8 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -29,6 +29,7 @@ struct ntsync_wait_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
+#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,172 +0,0 @@
|
||||||
This corresponds to the NT syscall NtCreateMutant().
|
|
||||||
|
|
||||||
An NT mutex is recursive, with a 32-bit recursion counter. When acquired via
|
|
||||||
NtWaitForMultipleObjects(), the recursion counter is incremented by one.
|
|
||||||
|
|
||||||
The OS records the thread which acquired it. However, in order to keep this
|
|
||||||
driver self-contained, the owning thread ID is managed by user-space, and passed
|
|
||||||
as a parameter to all relevant ioctls.
|
|
||||||
|
|
||||||
The initial owner and recursion count, if any, are specified when the mutex is
|
|
||||||
created.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 67 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 7 ++++
|
|
||||||
2 files changed, 74 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index d5759e9a3a8e..6f7086d0440a 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -24,6 +24,7 @@
|
|
||||||
|
|
||||||
enum ntsync_type {
|
|
||||||
NTSYNC_TYPE_SEM,
|
|
||||||
+ NTSYNC_TYPE_MUTEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -53,6 +54,10 @@ struct ntsync_obj {
|
|
||||||
__u32 count;
|
|
||||||
__u32 max;
|
|
||||||
} sem;
|
|
||||||
+ struct {
|
|
||||||
+ __u32 count;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ } mutex;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -132,6 +137,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
switch (obj->type) {
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
return !!obj->u.sem.count;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
|
|
||||||
+ return false;
|
|
||||||
+ return obj->u.mutex.count < UINT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN(1, "bad object type %#x\n", obj->type);
|
|
||||||
@@ -174,6 +183,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
obj->u.sem.count--;
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ obj->u.mutex.count++;
|
|
||||||
+ obj->u.mutex.owner = q->owner;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -215,6 +228,28 @@ static void try_wake_any_sem(struct ntsync_obj *sem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &mutex->any_waiters, node) {
|
|
||||||
+ struct ntsync_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);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -374,6 +409,33 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ struct ntsync_obj *mutex;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ if (!args.owner != !args.count)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX);
|
|
||||||
+ if (!mutex)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ mutex->u.mutex.count = args.count;
|
|
||||||
+ mutex->u.mutex.owner = args.owner;
|
|
||||||
+ fd = ntsync_obj_get_fd(mutex);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(mutex);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->mutex);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
{
|
|
||||||
struct file *file = fget(fd);
|
|
||||||
@@ -493,6 +555,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
case NTSYNC_TYPE_SEM:
|
|
||||||
try_wake_any_sem(obj);
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ try_wake_any_mutex(obj);
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -681,6 +746,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
void __user *argp = (void __user *)parm;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_MUTEX:
|
|
||||||
+ return ntsync_create_mutex(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
return ntsync_create_sem(dev, argp);
|
|
||||||
case NTSYNC_IOC_WAIT_ALL:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 19c37e27a4f8..8ac9d419c360 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -16,6 +16,12 @@ struct ntsync_sem_args {
|
|
||||||
__u32 max;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_mutex_args {
|
|
||||||
+ __u32 mutex;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ __u32 count;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -30,6 +36,7 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
+#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,107 +0,0 @@
|
||||||
This corresponds to the NT syscall NtReleaseMutant().
|
|
||||||
|
|
||||||
This syscall decrements the mutex's recursion count by one, and returns the
|
|
||||||
previous value. If the mutex is not owned by the given owner ID, the function
|
|
||||||
instead fails and returns -EPERM.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 64 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 65 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 6f7086d0440a..222ebead8eba 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -312,6 +312,68 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the mutex state, returning -EPERM if not the owner.
|
|
||||||
+ */
|
|
||||||
+static int unlock_mutex_state(struct ntsync_obj *mutex,
|
|
||||||
+ const struct ntsync_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 ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_device *dev = mutex->dev;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ __u32 prev_count;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ if (!args.owner)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&mutex->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ prev_count = mutex->u.mutex.count;
|
|
||||||
+ ret = unlock_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 = unlock_mutex_state(mutex, &args);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_mutex(mutex);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!ret && put_user(prev_count, &user_args->count))
|
|
||||||
+ ret = -EFAULT;
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -331,6 +393,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_SEM_POST:
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
+ return ntsync_mutex_unlock(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 8ac9d419c360..265503d441b1 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -39,5 +39,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,166 +0,0 @@
|
||||||
This does not correspond to any NT syscall. Rather, when a thread dies, it
|
|
||||||
should be called by the NT emulator for each mutex.
|
|
||||||
|
|
||||||
NT mutexes are robust (in the pthread sense). When an NT thread dies, any
|
|
||||||
mutexes it owned are immediately released. Acquisition of those mutexes by other
|
|
||||||
threads will return a special value indicating that the mutex was abandoned,
|
|
||||||
like EOWNERDEAD returned from pthread_mutex_lock(), and EOWNERDEAD is indeed
|
|
||||||
used here for that purpose.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 71 +++++++++++++++++++++++++++++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 70 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 222ebead8eba..a3466be50c45 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -57,6 +57,7 @@ struct ntsync_obj {
|
|
||||||
struct {
|
|
||||||
__u32 count;
|
|
||||||
__u32 owner;
|
|
||||||
+ bool ownerdead;
|
|
||||||
} mutex;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ struct ntsync_q {
|
|
||||||
atomic_t signaled;
|
|
||||||
|
|
||||||
bool all;
|
|
||||||
+ bool ownerdead;
|
|
||||||
__u32 count;
|
|
||||||
struct ntsync_q_entry entries[];
|
|
||||||
};
|
|
||||||
@@ -184,6 +186,9 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
obj->u.sem.count--;
|
|
||||||
break;
|
|
||||||
case NTSYNC_TYPE_MUTEX:
|
|
||||||
+ if (obj->u.mutex.ownerdead)
|
|
||||||
+ q->ownerdead = true;
|
|
||||||
+ obj->u.mutex.ownerdead = false;
|
|
||||||
obj->u.mutex.count++;
|
|
||||||
obj->u.mutex.owner = q->owner;
|
|
||||||
break;
|
|
||||||
@@ -243,6 +248,9 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
|
|
||||||
+ if (mutex->u.mutex.ownerdead)
|
|
||||||
+ q->ownerdead = true;
|
|
||||||
+ mutex->u.mutex.ownerdead = false;
|
|
||||||
mutex->u.mutex.count++;
|
|
||||||
mutex->u.mutex.owner = q->owner;
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -374,6 +382,62 @@ static int ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/*
|
|
||||||
+ * Actually change the mutex state to mark its owner as dead,
|
|
||||||
+ * returning -EPERM if not the owner.
|
|
||||||
+ */
|
|
||||||
+static int kill_mutex_state(struct ntsync_obj *mutex, __u32 owner)
|
|
||||||
+{
|
|
||||||
+ lockdep_assert_held(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ if (mutex->u.mutex.owner != owner)
|
|
||||||
+ return -EPERM;
|
|
||||||
+
|
|
||||||
+ mutex->u.mutex.ownerdead = true;
|
|
||||||
+ mutex->u.mutex.owner = 0;
|
|
||||||
+ mutex->u.mutex.count = 0;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_device *dev = mutex->dev;
|
|
||||||
+ __u32 owner;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (get_user(owner, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ if (!owner)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&mutex->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ ret = kill_mutex_state(mutex, owner);
|
|
||||||
+ 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);
|
|
||||||
+
|
|
||||||
+ ret = kill_mutex_state(mutex, owner);
|
|
||||||
+ if (!ret)
|
|
||||||
+ try_wake_any_mutex(mutex);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -395,6 +459,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
+ return ntsync_mutex_kill(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
@@ -579,6 +645,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->owner = args->owner;
|
|
||||||
atomic_set(&q->signaled, -1);
|
|
||||||
q->all = all;
|
|
||||||
+ q->ownerdead = false;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
@@ -686,7 +753,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
|
|
||||||
/* even if we caught a signal, we need to communicate success */
|
|
||||||
- ret = 0;
|
|
||||||
+ ret = q->ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
|
|
||||||
if (put_user(signaled, &user_args->index))
|
|
||||||
ret = -EFAULT;
|
|
||||||
@@ -767,7 +834,7 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
struct ntsync_wait_args __user *user_args = argp;
|
|
||||||
|
|
||||||
/* even if we caught a signal, we need to communicate success */
|
|
||||||
- ret = 0;
|
|
||||||
+ ret = q->ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
|
|
||||||
if (put_user(signaled, &user_args->index))
|
|
||||||
ret = -EFAULT;
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 265503d441b1..4800941fcbda 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -40,5 +40,6 @@ struct ntsync_wait_args {
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,165 +0,0 @@
|
||||||
This correspond to the NT syscall NtCreateEvent().
|
|
||||||
|
|
||||||
An NT event holds a single bit of state denoting whether it is signaled or
|
|
||||||
unsignaled.
|
|
||||||
|
|
||||||
There are two types of events: manual-reset and automatic-reset. When an
|
|
||||||
automatic-reset event is acquired via a wait function, its state is reset to
|
|
||||||
unsignaled. Manual-reset events are not affected by wait functions.
|
|
||||||
|
|
||||||
Whether the event is manual-reset, and its initial state, are specified at
|
|
||||||
creation time.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 60 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 7 +++++
|
|
||||||
2 files changed, 67 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index a3466be50c45..17dd47d06e0a 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -25,6 +25,7 @@
|
|
||||||
enum ntsync_type {
|
|
||||||
NTSYNC_TYPE_SEM,
|
|
||||||
NTSYNC_TYPE_MUTEX,
|
|
||||||
+ NTSYNC_TYPE_EVENT,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -59,6 +60,10 @@ struct ntsync_obj {
|
|
||||||
__u32 owner;
|
|
||||||
bool ownerdead;
|
|
||||||
} mutex;
|
|
||||||
+ struct {
|
|
||||||
+ bool manual;
|
|
||||||
+ bool signaled;
|
|
||||||
+ } event;
|
|
||||||
} u;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -143,6 +148,8 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
|
||||||
if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
|
|
||||||
return false;
|
|
||||||
return obj->u.mutex.count < UINT_MAX;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ return obj->u.event.signaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN(1, "bad object type %#x\n", obj->type);
|
|
||||||
@@ -192,6 +199,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
|
|
||||||
obj->u.mutex.count++;
|
|
||||||
obj->u.mutex.owner = q->owner;
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ if (!obj->u.event.manual)
|
|
||||||
+ obj->u.event.signaled = false;
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wake_up_process(q->task);
|
|
||||||
@@ -258,6 +269,26 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void try_wake_any_event(struct ntsync_obj *event)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_q_entry *entry;
|
|
||||||
+
|
|
||||||
+ lockdep_assert_held(&event->lock);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(entry, &event->any_waiters, node) {
|
|
||||||
+ struct ntsync_q *q = entry->q;
|
|
||||||
+
|
|
||||||
+ if (!event->u.event.signaled)
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) {
|
|
||||||
+ if (!event->u.event.manual)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
+ wake_up_process(q->task);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
|
||||||
* invalid.
|
|
||||||
@@ -566,6 +597,30 @@ static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
|
|
||||||
return put_user(fd, &user_args->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_create_event(struct ntsync_device *dev, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+ struct ntsync_obj *event;
|
|
||||||
+ int fd;
|
|
||||||
+
|
|
||||||
+ if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ event = ntsync_alloc_obj(dev, NTSYNC_TYPE_EVENT);
|
|
||||||
+ if (!event)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ event->u.event.manual = args.manual;
|
|
||||||
+ event->u.event.signaled = args.signaled;
|
|
||||||
+ fd = ntsync_obj_get_fd(event);
|
|
||||||
+ if (fd < 0) {
|
|
||||||
+ kfree(event);
|
|
||||||
+ return fd;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return put_user(fd, &user_args->event);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
|
||||||
{
|
|
||||||
struct file *file = fget(fd);
|
|
||||||
@@ -689,6 +744,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
case NTSYNC_TYPE_MUTEX:
|
|
||||||
try_wake_any_mutex(obj);
|
|
||||||
break;
|
|
||||||
+ case NTSYNC_TYPE_EVENT:
|
|
||||||
+ try_wake_any_event(obj);
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -877,6 +935,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
void __user *argp = (void __user *)parm;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
+ case NTSYNC_IOC_CREATE_EVENT:
|
|
||||||
+ return ntsync_create_event(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_MUTEX:
|
|
||||||
return ntsync_create_mutex(dev, argp);
|
|
||||||
case NTSYNC_IOC_CREATE_SEM:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 4800941fcbda..040cbdb39033 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -22,6 +22,12 @@ struct ntsync_mutex_args {
|
|
||||||
__u32 count;
|
|
||||||
};
|
|
||||||
|
|
||||||
+struct ntsync_event_args {
|
|
||||||
+ __u32 event;
|
|
||||||
+ __u32 manual;
|
|
||||||
+ __u32 signaled;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -37,6 +43,7 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
|
|
||||||
#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_CREATE_EVENT _IOWR('N', 0x87, struct ntsync_event_args)
|
|
||||||
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,78 +0,0 @@
|
||||||
This corresponds to the NT syscall NtSetEvent().
|
|
||||||
|
|
||||||
This sets the event to the signaled state, and returns its previous state.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 37 +++++++++++++++++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 38 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 17dd47d06e0a..edfbf11cafe0 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -469,6 +469,41 @@ static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_device *dev = event->dev;
|
|
||||||
+ __u32 prev_state;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&event->all_hint) > 0) {
|
|
||||||
+ spin_lock(&dev->wait_all_lock);
|
|
||||||
+ spin_lock_nest_lock(&event->lock, &dev->wait_all_lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = true;
|
|
||||||
+ try_wake_all_obj(dev, event);
|
|
||||||
+ try_wake_any_event(event);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+ spin_unlock(&dev->wait_all_lock);
|
|
||||||
+ } else {
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = true;
|
|
||||||
+ try_wake_any_event(event);
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (put_user(prev_state, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -492,6 +527,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_SET:
|
|
||||||
+ return ntsync_event_set(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 040cbdb39033..af518530bffd 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -48,5 +48,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,63 +0,0 @@
|
||||||
This corresponds to the NT syscall NtResetEvent().
|
|
||||||
|
|
||||||
This sets the event to the unsignaled state, and returns its previous state.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 22 ++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 23 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index edfbf11cafe0..fa4c3fa1e496 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -504,6 +504,26 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ __u32 prev_state;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+
|
|
||||||
+ prev_state = event->u.event.signaled;
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
+
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+
|
|
||||||
+ if (put_user(prev_state, (__u32 __user *)argp))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -529,6 +549,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
return ntsync_event_set(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
+ return ntsync_event_reset(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index af518530bffd..6963356ee3f7 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -49,5 +49,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,70 +0,0 @@
|
||||||
This corresponds to the NT syscall NtPulseEvent().
|
|
||||||
|
|
||||||
This wakes up any waiters as if the event had been set, but does not set the
|
|
||||||
event, instead resetting it if it had been signalled. Thus, for a manual-reset
|
|
||||||
event, all waiters are woken, whereas for an auto-reset event, at most one
|
|
||||||
waiter is woken.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 10 ++++++++--
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 9 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index fa4c3fa1e496..b9b4127a6c9f 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -469,7 +469,7 @@ static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+static int ntsync_event_set(struct ntsync_obj *event, void __user *argp, bool pulse)
|
|
||||||
{
|
|
||||||
struct ntsync_device *dev = event->dev;
|
|
||||||
__u32 prev_state;
|
|
||||||
@@ -485,6 +485,8 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
event->u.event.signaled = true;
|
|
||||||
try_wake_all_obj(dev, event);
|
|
||||||
try_wake_any_event(event);
|
|
||||||
+ if (pulse)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
|
|
||||||
spin_unlock(&event->lock);
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
@@ -494,6 +496,8 @@ static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
|
||||||
prev_state = event->u.event.signaled;
|
|
||||||
event->u.event.signaled = true;
|
|
||||||
try_wake_any_event(event);
|
|
||||||
+ if (pulse)
|
|
||||||
+ event->u.event.signaled = false;
|
|
||||||
|
|
||||||
spin_unlock(&event->lock);
|
|
||||||
}
|
|
||||||
@@ -548,9 +552,11 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
- return ntsync_event_set(obj, argp);
|
|
||||||
+ return ntsync_event_set(obj, argp, false);
|
|
||||||
case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
return ntsync_event_reset(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_EVENT_PULSE:
|
|
||||||
+ return ntsync_event_set(obj, argp, true);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 6963356ee3f7..72047f36c45d 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -50,5 +50,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_MUTEX_KILL _IOW ('N', 0x86, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
+#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,62 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQuerySemaphore().
|
|
||||||
|
|
||||||
This returns the current count and maximum count of the semaphore.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 22 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index b9b4127a6c9f..0daaeeeba051 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -528,6 +528,25 @@ static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+
|
|
||||||
+ if (sem->type != NTSYNC_TYPE_SEM)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.sem = 0;
|
|
||||||
+ spin_lock(&sem->lock);
|
|
||||||
+ args.count = sem->u.sem.count;
|
|
||||||
+ args.max = sem->u.sem.max;
|
|
||||||
+ spin_unlock(&sem->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -547,6 +566,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
switch (cmd) {
|
|
||||||
case NTSYNC_IOC_SEM_POST:
|
|
||||||
return ntsync_sem_post(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_SEM_READ:
|
|
||||||
+ return ntsync_sem_read(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_UNLOCK:
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 72047f36c45d..42f51dc4e57e 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -51,5 +51,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
+#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,64 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQueryMutant().
|
|
||||||
|
|
||||||
This returns the recursion count, owner, and abandoned state of the mutex.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 23 +++++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 24 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 0daaeeeba051..b07510035c1f 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -547,6 +547,27 @@ static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.mutex = 0;
|
|
||||||
+ spin_lock(&mutex->lock);
|
|
||||||
+ args.count = mutex->u.mutex.count;
|
|
||||||
+ args.owner = mutex->u.mutex.owner;
|
|
||||||
+ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
|
|
||||||
+ spin_unlock(&mutex->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -572,6 +593,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_mutex_unlock(obj, argp);
|
|
||||||
case NTSYNC_IOC_MUTEX_KILL:
|
|
||||||
return ntsync_mutex_kill(obj, argp);
|
|
||||||
+ case NTSYNC_IOC_MUTEX_READ:
|
|
||||||
+ return ntsync_mutex_read(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_SET:
|
|
||||||
return ntsync_event_set(obj, argp, false);
|
|
||||||
case NTSYNC_IOC_EVENT_RESET:
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 42f51dc4e57e..25f3296cfabf 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -52,5 +52,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
+#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,62 +0,0 @@
|
||||||
This corresponds to the NT syscall NtQueryEvent().
|
|
||||||
|
|
||||||
This returns the signaled state of the event and whether it is manual-reset.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
|
|
||||||
include/uapi/linux/ntsync.h | 1 +
|
|
||||||
2 files changed, 22 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index b07510035c1f..981a1545192c 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -568,6 +568,25 @@ static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args __user *user_args = argp;
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+
|
|
||||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
+ args.event = 0;
|
|
||||||
+ spin_lock(&event->lock);
|
|
||||||
+ args.manual = event->u.event.manual;
|
|
||||||
+ args.signaled = event->u.event.signaled;
|
|
||||||
+ spin_unlock(&event->lock);
|
|
||||||
+
|
|
||||||
+ if (copy_to_user(user_args, &args, sizeof(args)))
|
|
||||||
+ return -EFAULT;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct ntsync_obj *obj = file->private_data;
|
|
||||||
@@ -601,6 +620,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
|
|
||||||
return ntsync_event_reset(obj, argp);
|
|
||||||
case NTSYNC_IOC_EVENT_PULSE:
|
|
||||||
return ntsync_event_set(obj, argp, true);
|
|
||||||
+ case NTSYNC_IOC_EVENT_READ:
|
|
||||||
+ return ntsync_event_read(obj, argp);
|
|
||||||
default:
|
|
||||||
return -ENOIOCTLCMD;
|
|
||||||
}
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 25f3296cfabf..03c95e5a398f 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -53,5 +53,6 @@ struct ntsync_wait_args {
|
|
||||||
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
|
|
||||||
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
|
|
||||||
#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
|
|
||||||
+#define NTSYNC_IOC_EVENT_READ _IOR ('N', 0x8d, struct ntsync_event_args)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,184 +0,0 @@
|
||||||
NT waits can optionally be made "alertable". This is a special channel for
|
|
||||||
thread wakeup that is mildly similar to SIGIO. A thread has an internal single
|
|
||||||
bit of "alerted" state, and if a thread is made alerted while an alertable wait,
|
|
||||||
the wait will return a special value, consume the "alerted" state, and will not
|
|
||||||
consume any of its objects.
|
|
||||||
|
|
||||||
Alerts are implemented using events; the user-space NT emulator is expected to
|
|
||||||
create an internal ntsync event for each thread and pass that event to wait
|
|
||||||
functions.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 68 ++++++++++++++++++++++++++++++++-----
|
|
||||||
include/uapi/linux/ntsync.h | 2 +-
|
|
||||||
2 files changed, 60 insertions(+), 10 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 981a1545192c..0055b4671808 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -808,22 +808,29 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
const struct ntsync_wait_args *args, bool all,
|
|
||||||
struct ntsync_q **ret_q)
|
|
||||||
{
|
|
||||||
+ int fds[NTSYNC_MAX_WAIT_COUNT + 1];
|
|
||||||
const __u32 count = args->count;
|
|
||||||
- int fds[NTSYNC_MAX_WAIT_COUNT];
|
|
||||||
struct ntsync_q *q;
|
|
||||||
+ __u32 total_count;
|
|
||||||
__u32 i, j;
|
|
||||||
|
|
||||||
- if (!args->owner || args->pad)
|
|
||||||
+ if (!args->owner)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
+ total_count = count;
|
|
||||||
+ if (args->alert)
|
|
||||||
+ total_count++;
|
|
||||||
+
|
|
||||||
if (copy_from_user(fds, u64_to_user_ptr(args->objs),
|
|
||||||
array_size(count, sizeof(*fds))))
|
|
||||||
return -EFAULT;
|
|
||||||
+ if (args->alert)
|
|
||||||
+ fds[count] = args->alert;
|
|
||||||
|
|
||||||
- q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
|
|
||||||
+ q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL);
|
|
||||||
if (!q)
|
|
||||||
return -ENOMEM;
|
|
||||||
q->task = current;
|
|
||||||
@@ -833,7 +840,7 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
q->ownerdead = false;
|
|
||||||
q->count = count;
|
|
||||||
|
|
||||||
- for (i = 0; i < count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = get_obj(dev, fds[i]);
|
|
||||||
|
|
||||||
@@ -883,9 +890,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
|
|
||||||
static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args;
|
|
||||||
+ __u32 i, total_count;
|
|
||||||
struct ntsync_q *q;
|
|
||||||
int signaled;
|
|
||||||
- __u32 i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (copy_from_user(&args, argp, sizeof(args)))
|
|
||||||
@@ -895,9 +902,13 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
+ total_count = args.count;
|
|
||||||
+ if (args.alert)
|
|
||||||
+ total_count++;
|
|
||||||
+
|
|
||||||
/* queue ourselves */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = entry->obj;
|
|
||||||
|
|
||||||
@@ -906,9 +917,15 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
spin_unlock(&obj->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
- /* check if we are already signaled */
|
|
||||||
+ /*
|
|
||||||
+ * Check if we are already signaled.
|
|
||||||
+ *
|
|
||||||
+ * Note that the API requires that normal objects are checked before
|
|
||||||
+ * the alert event. Hence we queue the alert event last, and check
|
|
||||||
+ * objects in order.
|
|
||||||
+ */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_obj *obj = q->entries[i].obj;
|
|
||||||
|
|
||||||
if (atomic_read(&q->signaled) != -1)
|
|
||||||
@@ -925,7 +942,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
/* and finally, unqueue */
|
|
||||||
|
|
||||||
- for (i = 0; i < args.count; i++) {
|
|
||||||
+ for (i = 0; i < total_count; i++) {
|
|
||||||
struct ntsync_q_entry *entry = &q->entries[i];
|
|
||||||
struct ntsync_obj *obj = entry->obj;
|
|
||||||
|
|
||||||
@@ -985,6 +1002,14 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
*/
|
|
||||||
list_add_tail(&entry->node, &obj->all_waiters);
|
|
||||||
}
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[args.count];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
|
|
||||||
+ list_add_tail(&entry->node, &obj->any_waiters);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
/* check if we are already signaled */
|
|
||||||
|
|
||||||
@@ -992,6 +1017,21 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * Check if the alert event is signaled, making sure to do so only
|
|
||||||
+ * after checking if the other objects are signaled.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_obj *obj = q->entries[args.count].obj;
|
|
||||||
+
|
|
||||||
+ if (atomic_read(&q->signaled) == -1) {
|
|
||||||
+ spin_lock(&obj->lock);
|
|
||||||
+ try_wake_any_obj(obj);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
/* sleep */
|
|
||||||
|
|
||||||
ret = ntsync_schedule(q, &args);
|
|
||||||
@@ -1014,6 +1054,16 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
|
|
||||||
|
|
||||||
put_obj(obj);
|
|
||||||
}
|
|
||||||
+ if (args.alert) {
|
|
||||||
+ struct ntsync_q_entry *entry = &q->entries[args.count];
|
|
||||||
+ struct ntsync_obj *obj = entry->obj;
|
|
||||||
+
|
|
||||||
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
|
|
||||||
+ list_del(&entry->node);
|
|
||||||
+ spin_unlock(&obj->lock);
|
|
||||||
+
|
|
||||||
+ put_obj(obj);
|
|
||||||
+ }
|
|
||||||
|
|
||||||
spin_unlock(&dev->wait_all_lock);
|
|
||||||
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 03c95e5a398f..555ae81b479a 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -34,7 +34,7 @@ struct ntsync_wait_args {
|
|
||||||
__u32 count;
|
|
||||||
__u32 owner;
|
|
||||||
__u32 index;
|
|
||||||
- __u32 pad;
|
|
||||||
+ __u32 alert;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,79 +0,0 @@
|
||||||
NtWaitForMultipleObjects() can receive a timeout in two forms, relative or
|
|
||||||
absolute. Relative timeouts are unaffected by changes to the system time and do
|
|
||||||
not count down while the system suspends; for absolute timeouts the opposite is
|
|
||||||
true.
|
|
||||||
|
|
||||||
In order to make the interface and implementation simpler, the ntsync driver
|
|
||||||
only deals in absolute timeouts. However, we need to be able to emulate both
|
|
||||||
behaviours apropos suspension and time adjustment, which is achieved by allowing
|
|
||||||
either the MONOTONIC or REALTIME clock to be used.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
drivers/misc/ntsync.c | 9 ++++++++-
|
|
||||||
include/uapi/linux/ntsync.h | 4 ++++
|
|
||||||
2 files changed, 12 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
|
|
||||||
index 0055b4671808..f54c81dada3d 100644
|
|
||||||
--- a/drivers/misc/ntsync.c
|
|
||||||
+++ b/drivers/misc/ntsync.c
|
|
||||||
@@ -778,11 +778,15 @@ static void put_obj(struct ntsync_obj *obj)
|
|
||||||
static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
|
|
||||||
{
|
|
||||||
ktime_t timeout = ns_to_ktime(args->timeout);
|
|
||||||
+ clockid_t clock = CLOCK_MONOTONIC;
|
|
||||||
ktime_t *timeout_ptr;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
|
|
||||||
|
|
||||||
+ if (args->flags & NTSYNC_WAIT_REALTIME)
|
|
||||||
+ clock = CLOCK_REALTIME;
|
|
||||||
+
|
|
||||||
do {
|
|
||||||
if (signal_pending(current)) {
|
|
||||||
ret = -ERESTARTSYS;
|
|
||||||
@@ -794,7 +798,7 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
- ret = schedule_hrtimeout(timeout_ptr, HRTIMER_MODE_ABS);
|
|
||||||
+ ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
|
|
||||||
} while (ret < 0);
|
|
||||||
__set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
@@ -817,6 +821,9 @@ static int setup_wait(struct ntsync_device *dev,
|
|
||||||
if (!args->owner)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
+ if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
|
||||||
+ return -EINVAL;
|
|
||||||
+
|
|
||||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
|
|
||||||
index 555ae81b479a..b5e835d8dba8 100644
|
|
||||||
--- a/include/uapi/linux/ntsync.h
|
|
||||||
+++ b/include/uapi/linux/ntsync.h
|
|
||||||
@@ -28,6 +28,8 @@ struct ntsync_event_args {
|
|
||||||
__u32 signaled;
|
|
||||||
};
|
|
||||||
|
|
||||||
+#define NTSYNC_WAIT_REALTIME 0x1
|
|
||||||
+
|
|
||||||
struct ntsync_wait_args {
|
|
||||||
__u64 timeout;
|
|
||||||
__u64 objs;
|
|
||||||
@@ -35,6 +37,8 @@ struct ntsync_wait_args {
|
|
||||||
__u32 owner;
|
|
||||||
__u32 index;
|
|
||||||
__u32 alert;
|
|
||||||
+ __u32 flags;
|
|
||||||
+ __u32 pad;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,208 +0,0 @@
|
||||||
Wine has tests for its synchronization primitives, but these are more accessible
|
|
||||||
to kernel developers, and also allow us to test some edge cases that Wine does
|
|
||||||
not care about.
|
|
||||||
|
|
||||||
This patch adds tests for semaphore-specific ioctls NTSYNC_IOC_SEM_POST and
|
|
||||||
NTSYNC_IOC_SEM_READ, and waiting on semaphores.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
tools/testing/selftests/Makefile | 1 +
|
|
||||||
.../testing/selftests/drivers/ntsync/Makefile | 8 +
|
|
||||||
tools/testing/selftests/drivers/ntsync/config | 1 +
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 149 ++++++++++++++++++
|
|
||||||
4 files changed, 159 insertions(+)
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/config
|
|
||||||
create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
|
|
||||||
index 15b6a111c3be..6c714a4e6478 100644
|
|
||||||
--- a/tools/testing/selftests/Makefile
|
|
||||||
+++ b/tools/testing/selftests/Makefile
|
|
||||||
@@ -15,6 +15,7 @@ TARGETS += cpu-hotplug
|
|
||||||
TARGETS += damon
|
|
||||||
TARGETS += dmabuf-heaps
|
|
||||||
TARGETS += drivers/dma-buf
|
|
||||||
+TARGETS += drivers/ntsync
|
|
||||||
TARGETS += drivers/s390x/uvdevice
|
|
||||||
TARGETS += drivers/net/bonding
|
|
||||||
TARGETS += drivers/net/team
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/Makefile b/tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..a34da5ccacf0
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/Makefile
|
|
||||||
@@ -0,0 +1,8 @@
|
|
||||||
+# SPDX-LICENSE-IDENTIFIER: GPL-2.0-only
|
|
||||||
+TEST_GEN_PROGS := ntsync
|
|
||||||
+
|
|
||||||
+top_srcdir =../../../../..
|
|
||||||
+CFLAGS += -I$(top_srcdir)/usr/include
|
|
||||||
+LDLIBS += -lpthread
|
|
||||||
+
|
|
||||||
+include ../../lib.mk
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..60539c826d06
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/config
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+CONFIG_WINESYNC=y
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..1e145c6dfded
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -0,0 +1,149 @@
|
|
||||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
+/*
|
|
||||||
+ * Various unit tests for the "ntsync" synchronization primitive driver.
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#define _GNU_SOURCE
|
|
||||||
+#include <sys/ioctl.h>
|
|
||||||
+#include <sys/stat.h>
|
|
||||||
+#include <fcntl.h>
|
|
||||||
+#include <time.h>
|
|
||||||
+#include <pthread.h>
|
|
||||||
+#include <linux/ntsync.h>
|
|
||||||
+#include "../../kselftest_harness.h"
|
|
||||||
+
|
|
||||||
+static int read_sem_state(int sem, __u32 *count, __u32 *max)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(sem, NTSYNC_IOC_SEM_READ, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ *max = args.max;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_sem_state(sem, count, max) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __count, __max; \
|
|
||||||
+ int ret = read_sem_state((sem), &__count, &__max); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((count), __count); \
|
|
||||||
+ EXPECT_EQ((max), __max); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+static int post_sem(int sem, __u32 *count)
|
|
||||||
+{
|
|
||||||
+ return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args args = {0};
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ args.timeout = timeout.tv_sec * 1000000000 + timeout.tv_nsec;
|
|
||||||
+ args.count = count;
|
|
||||||
+ args.objs = (uintptr_t)objs;
|
|
||||||
+ args.owner = owner;
|
|
||||||
+ args.index = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
|
|
||||||
+ *index = args.index;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(semaphore_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_sem_args sem_args;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ int fd, ret, sem;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 3;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ sem = sem_args.sem;
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ count = 0;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem, 0, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ count = 3;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 0, 2);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem, 2, 2);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ ret = wait_any(fd, 1, &sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ count = ~0u;
|
|
||||||
+ ret = post_sem(sem, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOVERFLOW, errno);
|
|
||||||
+ check_sem_state(sem, 1, 2);
|
|
||||||
+
|
|
||||||
+ close(sem);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,222 +0,0 @@
|
||||||
Test mutex-specific ioctls NTSYNC_IOC_MUTEX_UNLOCK and NTSYNC_IOC_MUTEX_READ,
|
|
||||||
and waiting on mutexes.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 196 ++++++++++++++++++
|
|
||||||
1 file changed, 196 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 1e145c6dfded..7cd0f40594fd 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -40,6 +40,39 @@ static int post_sem(int sem, __u32 *count)
|
|
||||||
return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int read_mutex_state(int mutex, __u32 *count, __u32 *owner)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ *owner = args.owner;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_mutex_state(mutex, count, owner) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __count, __owner; \
|
|
||||||
+ int ret = read_mutex_state((mutex), &__count, &__owner); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((count), __count); \
|
|
||||||
+ EXPECT_EQ((owner), __owner); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ args.owner = owner;
|
|
||||||
+ args.count = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_UNLOCK, &args);
|
|
||||||
+ *count = args.count;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
@@ -146,4 +179,167 @@ TEST(semaphore_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(mutex_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args;
|
|
||||||
+ __u32 owner, count, index;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ int fd, ret, mutex;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 2;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 0, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 456, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+ check_mutex_state(mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, count);
|
|
||||||
+ check_mutex_state(mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 2, 456);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex, 456, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ owner = 0;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EPERM, errno);
|
|
||||||
+ check_mutex_state(mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ owner = 456;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
||||||
+ ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.count);
|
|
||||||
+ EXPECT_EQ(0, mutex_args.owner);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_mutex_state(mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = ~0u;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+ mutex = mutex_args.mutex;
|
|
||||||
+ check_mutex_state(mutex, ~0u, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,139 +0,0 @@
|
||||||
Test basic synchronous functionality of NTSYNC_IOC_WAIT_ANY, when objects are
|
|
||||||
considered signaled or not signaled, and how they are affected by a successful
|
|
||||||
wait.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 119 ++++++++++++++++++
|
|
||||||
1 file changed, 119 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 7cd0f40594fd..40ad8cbd3138 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -342,4 +342,123 @@ TEST(mutex_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(test_wait_any)
|
|
||||||
+{
|
|
||||||
+ int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 owner, index, count, i;
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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;
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ /* test waiting on the same object twice */
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ objs[0] = objs[1] = sem_args.sem;
|
|
||||||
+ ret = wait_any(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 0, NULL, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < NTSYNC_MAX_WAIT_COUNT + 1; ++i)
|
|
||||||
+ objs[i] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT + 1, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, -1, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,136 +0,0 @@
|
||||||
Test basic synchronous functionality of NTSYNC_IOC_WAIT_ALL, and when objects
|
|
||||||
are considered simultaneously signaled.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 99 ++++++++++++++++++-
|
|
||||||
1 file changed, 97 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 40ad8cbd3138..c0f372167557 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -73,7 +73,8 @@ static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
+ const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
struct timespec timeout;
|
|
||||||
@@ -86,11 +87,21 @@ static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *in
|
|
||||||
args.objs = (uintptr_t)objs;
|
|
||||||
args.owner = owner;
|
|
||||||
args.index = 0xdeadbeef;
|
|
||||||
- ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
|
|
||||||
+ ret = ioctl(fd, request, &args);
|
|
||||||
*index = args.index;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(semaphore_state)
|
|
||||||
{
|
|
||||||
struct ntsync_sem_args sem_args;
|
|
||||||
@@ -461,4 +472,88 @@ TEST(test_wait_any)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(test_wait_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 owner, index, count;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_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;
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 456, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 2, 123);
|
|
||||||
+
|
|
||||||
+ count = 3;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 2, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 3, 123);
|
|
||||||
+
|
|
||||||
+ owner = 123;
|
|
||||||
+ ret = ioctl(mutex_args.mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EOWNERDEAD, errno);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
+
|
|
||||||
+ /* test waiting on the same object twice */
|
|
||||||
+ objs[0] = objs[1] = sem_args.sem;
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(EINVAL, errno);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,169 +0,0 @@
|
||||||
Test contended "wait-for-any" waits, to make sure that scheduling and wakeup
|
|
||||||
logic works correctly.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 150 ++++++++++++++++++
|
|
||||||
1 file changed, 150 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index c0f372167557..993f5db23768 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -556,4 +556,154 @@ TEST(test_wait_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+struct wake_args {
|
|
||||||
+ int fd;
|
|
||||||
+ int obj;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+struct wait_args {
|
|
||||||
+ int fd;
|
|
||||||
+ unsigned long request;
|
|
||||||
+ struct ntsync_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 __u64 get_abs_timeout(unsigned int ms)
|
|
||||||
+{
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
||||||
+ return (timeout.tv_sec * 1000000000) + timeout.tv_nsec + (ms * 1000000);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_for_thread(pthread_t thread, unsigned int ms)
|
|
||||||
+{
|
|
||||||
+ struct timespec timeout;
|
|
||||||
+
|
|
||||||
+ clock_gettime(CLOCK_REALTIME, &timeout);
|
|
||||||
+ timeout.tv_nsec += ms * 1000000;
|
|
||||||
+ timeout.tv_sec += (timeout.tv_nsec / 1000000000);
|
|
||||||
+ timeout.tv_nsec %= 1000000000;
|
|
||||||
+ return pthread_timedjoin_np(thread, NULL, &timeout);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(wake_any)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ pthread_t thread;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 1;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ /* test waking the semaphore */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ 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 = NTSYNC_IOC_WAIT_ANY;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+
|
|
||||||
+ 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 */
|
|
||||||
+ ret = wait_any(fd, 1, &mutex_args.mutex, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(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);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, mutex_args.count);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ /* delete an object while it's being waited on */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(200);
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 200);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(-1, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,118 +0,0 @@
|
||||||
Test contended "wait-for-all" waits, to make sure that scheduling and wakeup
|
|
||||||
logic works correctly, and that the wait only exits once objects are all
|
|
||||||
simultaneously signaled.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 98 +++++++++++++++++++
|
|
||||||
1 file changed, 98 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 993f5db23768..b77fb0b2c4b1 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -706,4 +706,102 @@ TEST(wake_any)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(wake_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+ __u32 count, index;
|
|
||||||
+ pthread_t thread;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 3;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 123;
|
|
||||||
+ mutex_args.count = 1;
|
|
||||||
+ mutex_args.mutex = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 456;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, count);
|
|
||||||
+
|
|
||||||
+ ret = pthread_tryjoin_np(thread, NULL);
|
|
||||||
+ EXPECT_EQ(EBUSY, ret);
|
|
||||||
+
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(sem_args.sem, &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
+ check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+
|
|
||||||
+ 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 */
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(200);
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ close(sem_args.sem);
|
|
||||||
+ close(mutex_args.mutex);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 200);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(-1, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,118 +0,0 @@
|
||||||
Test event-specific ioctls NTSYNC_IOC_EVENT_SET, NTSYNC_IOC_EVENT_RESET,
|
|
||||||
NTSYNC_IOC_EVENT_PULSE, NTSYNC_IOC_EVENT_READ for manual-reset events, and
|
|
||||||
waiting on manual-reset events.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 89 +++++++++++++++++++
|
|
||||||
1 file changed, 89 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index b77fb0b2c4b1..b6481c2b85cc 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -73,6 +73,27 @@ static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int read_event_state(int event, __u32 *signaled, __u32 *manual)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args args;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ memset(&args, 0xcc, sizeof(args));
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_READ, &args);
|
|
||||||
+ *signaled = args.signaled;
|
|
||||||
+ *manual = args.manual;
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#define check_event_state(event, signaled, manual) \
|
|
||||||
+ ({ \
|
|
||||||
+ __u32 __signaled, __manual; \
|
|
||||||
+ int ret = read_event_state((event), &__signaled, &__manual); \
|
|
||||||
+ EXPECT_EQ(0, ret); \
|
|
||||||
+ EXPECT_EQ((signaled), __signaled); \
|
|
||||||
+ EXPECT_EQ((manual), __manual); \
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
@@ -353,6 +374,74 @@ TEST(mutex_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(manual_event_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ __u32 index, signaled;
|
|
||||||
+ int fd, event, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ event_args.manual = 1;
|
|
||||||
+ event_args.signaled = 0;
|
|
||||||
+ event_args.event = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, event_args.event);
|
|
||||||
+ event = event_args.event;
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_event_state(event, 1, 1);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 1);
|
|
||||||
+
|
|
||||||
+ close(event);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(test_wait_any)
|
|
||||||
{
|
|
||||||
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,81 +0,0 @@
|
||||||
Test event-specific ioctls NTSYNC_IOC_EVENT_SET, NTSYNC_IOC_EVENT_RESET,
|
|
||||||
NTSYNC_IOC_EVENT_PULSE, NTSYNC_IOC_EVENT_READ for auto-reset events, and
|
|
||||||
waiting on auto-reset events.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 59 +++++++++++++++++++
|
|
||||||
1 file changed, 59 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index b6481c2b85cc..12ccb4ec28e4 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -442,6 +442,65 @@ TEST(manual_event_state)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(auto_event_state)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ __u32 index, signaled;
|
|
||||||
+ int fd, event, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ event_args.manual = 0;
|
|
||||||
+ event_args.signaled = 1;
|
|
||||||
+ event_args.event = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, event_args.event);
|
|
||||||
+ event = event_args.event;
|
|
||||||
+
|
|
||||||
+ check_event_state(event, 1, 0);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 1, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ signaled = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_any(fd, 1, &event, 123, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event, 0, 0);
|
|
||||||
+
|
|
||||||
+ close(event);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST(test_wait_any)
|
|
||||||
{
|
|
||||||
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,259 +0,0 @@
|
||||||
Expand the contended wait tests, which previously only covered events and
|
|
||||||
semaphores, to cover events as well.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 151 +++++++++++++++++-
|
|
||||||
1 file changed, 147 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 12ccb4ec28e4..5d17eff6a370 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -622,6 +622,7 @@ TEST(test_wait_any)
|
|
||||||
|
|
||||||
TEST(test_wait_all)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
__u32 owner, index, count;
|
|
||||||
@@ -644,6 +645,11 @@ TEST(test_wait_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
objs[0] = sem_args.sem;
|
|
||||||
objs[1] = mutex_args.mutex;
|
|
||||||
|
|
||||||
@@ -692,6 +698,14 @@ TEST(test_wait_all)
|
|
||||||
check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
check_mutex_state(mutex_args.mutex, 1, 123);
|
|
||||||
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+ check_sem_state(sem_args.sem, 0, 3);
|
|
||||||
+ check_event_state(event_args.event, 1, 1);
|
|
||||||
+
|
|
||||||
/* test waiting on the same object twice */
|
|
||||||
objs[0] = objs[1] = sem_args.sem;
|
|
||||||
ret = wait_all(fd, 2, objs, 123, &index);
|
|
||||||
@@ -700,6 +714,7 @@ TEST(test_wait_all)
|
|
||||||
|
|
||||||
close(sem_args.sem);
|
|
||||||
close(mutex_args.mutex);
|
|
||||||
+ close(event_args.event);
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
@@ -746,12 +761,13 @@ static int wait_for_thread(pthread_t thread, unsigned int ms)
|
|
||||||
|
|
||||||
TEST(wake_any)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
struct wait_args thread_args;
|
|
||||||
+ __u32 count, index, signaled;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
- __u32 count, index;
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
@@ -833,10 +849,101 @@ TEST(wake_any)
|
|
||||||
EXPECT_EQ(0, thread_args.ret);
|
|
||||||
EXPECT_EQ(1, wait_args.index);
|
|
||||||
|
|
||||||
+ /* test waking events */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = false;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 0);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = false;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ objs[1] = event_args.event;
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 1, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+ check_event_state(event_args.event, 0, 1);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(1, wait_args.index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
/* delete an object while it's being waited on */
|
|
||||||
|
|
||||||
wait_args.timeout = get_abs_timeout(200);
|
|
||||||
wait_args.owner = 123;
|
|
||||||
+ objs[1] = mutex_args.mutex;
|
|
||||||
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
|
|
||||||
@@ -856,12 +963,14 @@ TEST(wake_any)
|
|
||||||
|
|
||||||
TEST(wake_all)
|
|
||||||
{
|
|
||||||
+ struct ntsync_event_args manual_event_args = {0};
|
|
||||||
+ struct ntsync_event_args auto_event_args = {0};
|
|
||||||
struct ntsync_mutex_args mutex_args = {0};
|
|
||||||
struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
struct wait_args thread_args;
|
|
||||||
- int objs[2], fd, ret;
|
|
||||||
- __u32 count, index;
|
|
||||||
+ __u32 count, index, signaled;
|
|
||||||
+ int objs[4], fd, ret;
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
@@ -881,12 +990,24 @@ TEST(wake_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_NE(0xdeadbeef, mutex_args.mutex);
|
|
||||||
|
|
||||||
+ manual_event_args.manual = true;
|
|
||||||
+ manual_event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &manual_event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ auto_event_args.manual = false;
|
|
||||||
+ auto_event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
objs[0] = sem_args.sem;
|
|
||||||
objs[1] = mutex_args.mutex;
|
|
||||||
+ objs[2] = manual_event_args.event;
|
|
||||||
+ objs[3] = auto_event_args.event;
|
|
||||||
|
|
||||||
wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
wait_args.objs = (uintptr_t)objs;
|
|
||||||
- wait_args.count = 2;
|
|
||||||
+ wait_args.count = 4;
|
|
||||||
wait_args.owner = 456;
|
|
||||||
thread_args.fd = fd;
|
|
||||||
thread_args.args = &wait_args;
|
|
||||||
@@ -920,12 +1041,32 @@ TEST(wake_all)
|
|
||||||
|
|
||||||
check_mutex_state(mutex_args.mutex, 0, 0);
|
|
||||||
|
|
||||||
+ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
count = 2;
|
|
||||||
ret = post_sem(sem_args.sem, &count);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(0, count);
|
|
||||||
+ check_sem_state(sem_args.sem, 2, 3);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(manual_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(auto_event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, signaled);
|
|
||||||
+
|
|
||||||
check_sem_state(sem_args.sem, 1, 3);
|
|
||||||
check_mutex_state(mutex_args.mutex, 1, 456);
|
|
||||||
+ check_event_state(manual_event_args.event, 1, 1);
|
|
||||||
+ check_event_state(auto_event_args.event, 0, 0);
|
|
||||||
|
|
||||||
ret = wait_for_thread(thread, 100);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
@@ -943,6 +1084,8 @@ TEST(wake_all)
|
|
||||||
|
|
||||||
close(sem_args.sem);
|
|
||||||
close(mutex_args.mutex);
|
|
||||||
+ close(manual_event_args.event);
|
|
||||||
+ close(auto_event_args.event);
|
|
||||||
|
|
||||||
ret = wait_for_thread(thread, 200);
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,223 +0,0 @@
|
||||||
Test the "alert" functionality of NTSYNC_IOC_WAIT_ALL and NTSYNC_IOC_WAIT_ANY,
|
|
||||||
when a wait is woken with an alert and when it is woken by an object.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 179 +++++++++++++++++-
|
|
||||||
1 file changed, 176 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 5d17eff6a370..5465a16d38b3 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -95,7 +95,7 @@ static int read_event_state(int event, __u32 *signaled, __u32 *manual)
|
|
||||||
})
|
|
||||||
|
|
||||||
static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
- const int *objs, __u32 owner, __u32 *index)
|
|
||||||
+ const int *objs, __u32 owner, int alert, __u32 *index)
|
|
||||||
{
|
|
||||||
struct ntsync_wait_args args = {0};
|
|
||||||
struct timespec timeout;
|
|
||||||
@@ -108,6 +108,7 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
args.objs = (uintptr_t)objs;
|
|
||||||
args.owner = owner;
|
|
||||||
args.index = 0xdeadbeef;
|
|
||||||
+ args.alert = alert;
|
|
||||||
ret = ioctl(fd, request, &args);
|
|
||||||
*index = args.index;
|
|
||||||
return ret;
|
|
||||||
@@ -115,12 +116,26 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
||||||
|
|
||||||
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
- return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
||||||
{
|
|
||||||
- return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_any_alert(int fd, __u32 count, const int *objs,
|
|
||||||
+ __u32 owner, int alert, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
|
|
||||||
+ count, objs, owner, alert, index);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int wait_all_alert(int fd, __u32 count, const int *objs,
|
|
||||||
+ __u32 owner, int alert, __u32 *index)
|
|
||||||
+{
|
|
||||||
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
|
|
||||||
+ count, objs, owner, alert, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(semaphore_state)
|
|
||||||
@@ -1095,4 +1110,162 @@ TEST(wake_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+TEST(alert_any)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 index, count, signaled;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 0;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ sem_args.count = 1;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[1] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(1, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ /* test with an auto-reset event */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 1;
|
|
||||||
+ ret = post_sem(objs[0], &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ close(objs[0]);
|
|
||||||
+ close(objs[1]);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(alert_all)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ __u32 index, count, signaled;
|
|
||||||
+ int objs[2], fd, ret;
|
|
||||||
+
|
|
||||||
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, fd);
|
|
||||||
+
|
|
||||||
+ sem_args.count = 2;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[0] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ sem_args.count = 1;
|
|
||||||
+ sem_args.max = 2;
|
|
||||||
+ sem_args.sem = 0xdeadbeef;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
|
|
||||||
+ objs[1] = sem_args.sem;
|
|
||||||
+
|
|
||||||
+ event_args.manual = true;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ /* test with an auto-reset event */
|
|
||||||
+
|
|
||||||
+ event_args.manual = false;
|
|
||||||
+ event_args.signaled = true;
|
|
||||||
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ count = 2;
|
|
||||||
+ ret = post_sem(objs[1], &count);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(2, index);
|
|
||||||
+
|
|
||||||
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
|
|
||||||
+ EXPECT_EQ(-1, ret);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, errno);
|
|
||||||
+
|
|
||||||
+ close(event_args.event);
|
|
||||||
+
|
|
||||||
+ close(objs[0]);
|
|
||||||
+ close(objs[1]);
|
|
||||||
+
|
|
||||||
+ close(fd);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,110 +0,0 @@
|
||||||
Expand the alert tests to cover alerting a thread mid-wait, to test that the
|
|
||||||
relevant scheduling logic works correctly.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 62 +++++++++++++++++++
|
|
||||||
1 file changed, 62 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 5465a16d38b3..968874d7e325 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -1113,9 +1113,12 @@ TEST(wake_all)
|
|
||||||
TEST(alert_any)
|
|
||||||
{
|
|
||||||
struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
__u32 index, count, signaled;
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
+ pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
ASSERT_LE(0, fd);
|
|
||||||
@@ -1163,6 +1166,34 @@ TEST(alert_any)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(2, index);
|
|
||||||
|
|
||||||
+ /* test wakeup via alert */
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+ wait_args.alert = event_args.event;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ANY;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(2, wait_args.index);
|
|
||||||
+
|
|
||||||
close(event_args.event);
|
|
||||||
|
|
||||||
/* test with an auto-reset event */
|
|
||||||
@@ -1199,9 +1230,12 @@ TEST(alert_any)
|
|
||||||
TEST(alert_all)
|
|
||||||
{
|
|
||||||
struct ntsync_event_args event_args = {0};
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
struct ntsync_sem_args sem_args = {0};
|
|
||||||
+ struct wait_args thread_args;
|
|
||||||
__u32 index, count, signaled;
|
|
||||||
int objs[2], fd, ret;
|
|
||||||
+ pthread_t thread;
|
|
||||||
|
|
||||||
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
ASSERT_LE(0, fd);
|
|
||||||
@@ -1235,6 +1269,34 @@ TEST(alert_all)
|
|
||||||
EXPECT_EQ(0, ret);
|
|
||||||
EXPECT_EQ(2, index);
|
|
||||||
|
|
||||||
+ /* test wakeup via alert */
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = get_abs_timeout(1000);
|
|
||||||
+ wait_args.objs = (uintptr_t)objs;
|
|
||||||
+ wait_args.count = 2;
|
|
||||||
+ wait_args.owner = 123;
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+ wait_args.alert = event_args.event;
|
|
||||||
+ thread_args.fd = fd;
|
|
||||||
+ thread_args.args = &wait_args;
|
|
||||||
+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
||||||
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(ETIMEDOUT, ret);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ ret = wait_for_thread(thread, 100);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ EXPECT_EQ(0, thread_args.ret);
|
|
||||||
+ EXPECT_EQ(2, wait_args.index);
|
|
||||||
+
|
|
||||||
close(event_args.event);
|
|
||||||
|
|
||||||
/* test with an auto-reset event */
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,97 +0,0 @@
|
||||||
Test a more realistic usage pattern, and one with heavy contention, in order to
|
|
||||||
actually exercise ntsync's internal synchronization.
|
|
||||||
|
|
||||||
This test has several threads in a tight loop acquiring a mutex, modifying some
|
|
||||||
shared data, and then releasing the mutex. At the end we check if the data is
|
|
||||||
consistent.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
.../testing/selftests/drivers/ntsync/ntsync.c | 74 +++++++++++++++++++
|
|
||||||
1 file changed, 74 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
index 968874d7e325..5fa2c9a0768c 100644
|
|
||||||
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
|
|
||||||
@@ -1330,4 +1330,78 @@ TEST(alert_all)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
+#define STRESS_LOOPS 10000
|
|
||||||
+#define STRESS_THREADS 4
|
|
||||||
+
|
|
||||||
+static unsigned int stress_counter;
|
|
||||||
+static int stress_device, stress_start_event, stress_mutex;
|
|
||||||
+
|
|
||||||
+static void *stress_thread(void *arg)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_wait_args wait_args = {0};
|
|
||||||
+ __u32 index, count, i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ wait_args.timeout = UINT64_MAX;
|
|
||||||
+ wait_args.count = 1;
|
|
||||||
+ wait_args.objs = (uintptr_t)&stress_start_event;
|
|
||||||
+ wait_args.owner = gettid();
|
|
||||||
+ wait_args.index = 0xdeadbeef;
|
|
||||||
+
|
|
||||||
+ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
||||||
+
|
|
||||||
+ wait_args.objs = (uintptr_t)&stress_mutex;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_LOOPS; ++i) {
|
|
||||||
+ ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
||||||
+
|
|
||||||
+ ++stress_counter;
|
|
||||||
+
|
|
||||||
+ unlock_mutex(stress_mutex, wait_args.owner, &count);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return NULL;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+TEST(stress_wait)
|
|
||||||
+{
|
|
||||||
+ struct ntsync_event_args event_args;
|
|
||||||
+ struct ntsync_mutex_args mutex_args;
|
|
||||||
+ pthread_t threads[STRESS_THREADS];
|
|
||||||
+ __u32 signaled, i;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ stress_device = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
||||||
+ ASSERT_LE(0, stress_device);
|
|
||||||
+
|
|
||||||
+ mutex_args.owner = 0;
|
|
||||||
+ mutex_args.count = 0;
|
|
||||||
+ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ stress_mutex = mutex_args.mutex;
|
|
||||||
+
|
|
||||||
+ event_args.manual = 1;
|
|
||||||
+ event_args.signaled = 0;
|
|
||||||
+ ret = ioctl(stress_device, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ stress_start_event = event_args.event;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_THREADS; ++i)
|
|
||||||
+ pthread_create(&threads[i], NULL, stress_thread, NULL);
|
|
||||||
+
|
|
||||||
+ ret = ioctl(stress_start_event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < STRESS_THREADS; ++i) {
|
|
||||||
+ ret = pthread_join(threads[i], NULL);
|
|
||||||
+ EXPECT_EQ(0, ret);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ EXPECT_EQ(STRESS_LOOPS * STRESS_THREADS, stress_counter);
|
|
||||||
+
|
|
||||||
+ close(stress_start_event);
|
|
||||||
+ close(stress_mutex);
|
|
||||||
+ close(stress_device);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
TEST_HARNESS_MAIN
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,29 +0,0 @@
|
||||||
Add myself as maintainer, supported by CodeWeavers.
|
|
||||||
|
|
||||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
---
|
|
||||||
MAINTAINERS | 9 +++++++++
|
|
||||||
1 file changed, 9 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
||||||
index 9ed4d3868539..d83dd35d9f73 100644
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -15595,6 +15595,15 @@ T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
|
|
||||||
F: Documentation/filesystems/ntfs3.rst
|
|
||||||
F: fs/ntfs3/
|
|
||||||
|
|
||||||
+NTSYNC SYNCHRONIZATION PRIMITIVE DRIVER
|
|
||||||
+M: Elizabeth Figura <zfigura@codeweavers.com>
|
|
||||||
+L: wine-devel@winehq.org
|
|
||||||
+S: Supported
|
|
||||||
+F: Documentation/userspace-api/ntsync.rst
|
|
||||||
+F: drivers/misc/ntsync.c
|
|
||||||
+F: include/uapi/linux/ntsync.h
|
|
||||||
+F: tools/testing/selftests/drivers/ntsync/
|
|
||||||
+
|
|
||||||
NUBUS SUBSYSTEM
|
|
||||||
M: Finn Thain <fthain@linux-m68k.org>
|
|
||||||
L: linux-m68k@lists.linux-m68k.org
|
|
||||||
--
|
|
||||||
2.43.0
|
|
|
@ -1,5 +1,5 @@
|
||||||
pkgname=linux6.8-zen
|
pkgname=linux6.8-zen
|
||||||
version=6.8.6
|
version=6.8.7
|
||||||
revision=1
|
revision=1
|
||||||
zen=1
|
zen=1
|
||||||
python_version=3
|
python_version=3
|
||||||
|
@ -8,7 +8,7 @@ maintainer="Wizzard <retard@deadzone.lol>"
|
||||||
license="GPL-2.0-only"
|
license="GPL-2.0-only"
|
||||||
homepage="https://github.com/zen-kernel/zen-kernel"
|
homepage="https://github.com/zen-kernel/zen-kernel"
|
||||||
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
||||||
checksum="a6f2cd6b3ed6555161b688d41d04275c2bf05e893778a8e5b2c52a3ff627db1f"
|
checksum="5ab6ad1d26925f02a74b41d485e5faf25056efec123d0a1433aa3a4108534aae"
|
||||||
archs="x86_64* i686* ppc* aarch64*"
|
archs="x86_64* i686* ppc* aarch64*"
|
||||||
patch_args="-Np1"
|
patch_args="-Np1"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue