Updated for version 6.8-zen1
This commit is contained in:
parent
e5757b04c8
commit
1e1c248524
|
@ -0,0 +1 @@
|
||||||
|
linux6.8-zen
|
|
@ -0,0 +1 @@
|
||||||
|
linux6.8-zen
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
mod=$1
|
||||||
|
mkdir -p usr/lib/debug/${mod%/*}
|
||||||
|
$OBJCOPY --only-keep-debug --compress-debug-sections $mod usr/lib/debug/$mod
|
||||||
|
$OBJCOPY --add-gnu-debuglink=${DESTDIR}/usr/lib/debug/$mod $mod
|
||||||
|
/usr/bin/$STRIP --strip-debug $mod
|
||||||
|
gzip -9 $mod
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
mod=$1
|
||||||
|
mkdir -p usr/lib/debug/${mod%/*}
|
||||||
|
$OBJCOPY --only-keep-debug --compress-debug-sections $mod usr/lib/debug/$mod
|
||||||
|
$OBJCOPY --add-gnu-debuglink=${DESTDIR}/usr/lib/debug/$mod $mod
|
||||||
|
/usr/bin/$STRIP --strip-debug $mod
|
||||||
|
gzip -9 $mod
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,228 @@
|
||||||
|
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
|
|
@ -0,0 +1,147 @@
|
||||||
|
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
|
|
@ -0,0 +1,360 @@
|
||||||
|
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
|
|
@ -0,0 +1,365 @@
|
||||||
|
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
|
|
@ -0,0 +1,172 @@
|
||||||
|
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
|
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,166 @@
|
||||||
|
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
|
|
@ -0,0 +1,165 @@
|
||||||
|
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
|
|
@ -0,0 +1,78 @@
|
||||||
|
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
|
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
|
@ -0,0 +1,70 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,64 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,184 @@
|
||||||
|
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
|
|
@ -0,0 +1,79 @@
|
||||||
|
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
|
|
@ -0,0 +1,208 @@
|
||||||
|
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
|
|
@ -0,0 +1,222 @@
|
||||||
|
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
|
|
@ -0,0 +1,139 @@
|
||||||
|
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
|
|
@ -0,0 +1,136 @@
|
||||||
|
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
|
|
@ -0,0 +1,169 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
|
@ -0,0 +1,81 @@
|
||||||
|
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
|
|
@ -0,0 +1,259 @@
|
||||||
|
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
|
|
@ -0,0 +1,223 @@
|
||||||
|
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
|
|
@ -0,0 +1,110 @@
|
||||||
|
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
|
|
@ -0,0 +1,97 @@
|
||||||
|
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
|
|
@ -0,0 +1,29 @@
|
||||||
|
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
|
|
@ -0,0 +1,19 @@
|
||||||
|
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
|
||||||
|
index 4102108..72474d8 100644
|
||||||
|
--- a/kernel/rcu/tree_plugin.h
|
||||||
|
+++ b/kernel/rcu/tree_plugin.h
|
||||||
|
@@ -406,7 +406,7 @@ void __rcu_read_lock(void)
|
||||||
|
WRITE_ONCE(current->rcu_read_unlock_special.b.need_qs, true);
|
||||||
|
barrier(); /* critical section after entry code. */
|
||||||
|
}
|
||||||
|
-EXPORT_SYMBOL_GPL(__rcu_read_lock);
|
||||||
|
+EXPORT_SYMBOL(__rcu_read_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preemptible RCU implementation for rcu_read_unlock().
|
||||||
|
@@ -431,7 +431,7 @@ void __rcu_read_unlock(void)
|
||||||
|
WARN_ON_ONCE(rrln < 0 || rrln > RCU_NEST_PMAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-EXPORT_SYMBOL_GPL(__rcu_read_unlock);
|
||||||
|
+EXPORT_SYMBOL(__rcu_read_unlock);
|
|
@ -0,0 +1,324 @@
|
||||||
|
pkgname=linux6.7-zen
|
||||||
|
version=6.7.9
|
||||||
|
revision=1
|
||||||
|
zen=1
|
||||||
|
wrksrc="linux-${version}-zen${zen}"
|
||||||
|
short_desc="Linux kernel and modules with Zen patches (${version%.*} series) compiled with Clang"
|
||||||
|
maintainer="Wizzard <retard@deadzone.lol>"
|
||||||
|
license="GPL-2.0-only"
|
||||||
|
homepage="http://www.zen-kernel.org/"
|
||||||
|
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
||||||
|
checksum="a4bf7246a632c856294c252598738fd39de3e87aa4f0bbb137ae518f832c51a2"
|
||||||
|
python_version=3
|
||||||
|
patch_args="-Np1"
|
||||||
|
|
||||||
|
nodebug=yes # -dbg package is generated below manually
|
||||||
|
nostrip=yes
|
||||||
|
noverifyrdeps=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
preserve=yes
|
||||||
|
|
||||||
|
hostmakedepends="tar xz lz4 bc-gh elfutils-devel flex gmp-devel kmod libmpc-devel
|
||||||
|
openssl-devel perl uboot-mkimage cpio clang llvm lld pahole python3 which"
|
||||||
|
|
||||||
|
if [[ $version =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
_kernver="${version}.0-zen${zen}_${revision}"
|
||||||
|
else
|
||||||
|
_kernver="${version}-zen${zen}_${revision}"
|
||||||
|
fi
|
||||||
|
triggers="kernel-hooks"
|
||||||
|
kernel_hooks_version="${_kernver}"
|
||||||
|
|
||||||
|
# These files could be modified when an external module is built.
|
||||||
|
mutable_files="
|
||||||
|
/usr/lib/modules/${_kernver}/modules.builtin.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.builtin.alias.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.softdep
|
||||||
|
/usr/lib/modules/${_kernver}/modules.dep
|
||||||
|
/usr/lib/modules/${_kernver}/modules.dep.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.symbols
|
||||||
|
/usr/lib/modules/${_kernver}/modules.symbols.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.alias
|
||||||
|
/usr/lib/modules/${_kernver}/modules.alias.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.devname"
|
||||||
|
|
||||||
|
# reproducible build
|
||||||
|
export KBUILD_BUILD_TIMESTAMP=${SOURCE_DATE_EPOCH:-0}
|
||||||
|
export KBUILD_BUILD_USER=voidlinux
|
||||||
|
export KBUILD_BUILD_HOST=zen
|
||||||
|
|
||||||
|
if [ "$CROSS_BUILD" ]; then
|
||||||
|
_cross="CROSS_COMPILE=${XBPS_CROSS_TRIPLET}-"
|
||||||
|
fi
|
||||||
|
if [ "${_patchver}" ]; then
|
||||||
|
_version="EXTRAVERSION=${_patchver}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_configure() {
|
||||||
|
# If there's a file called <arch>-dotconfig, use it to
|
||||||
|
# configure the kernel; otherwise use arch defaults and all stuff
|
||||||
|
# as modules (allmodconfig).
|
||||||
|
local arch subarch
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) arch=i386;;
|
||||||
|
x86_64*) arch=x86_64;;
|
||||||
|
arm*) arch=arm;;
|
||||||
|
aarch64*) arch=arm64;;
|
||||||
|
ppc64le*) arch=powerpc; subarch=ppc64le;;
|
||||||
|
ppc64*) arch=powerpc; subarch=ppc64;;
|
||||||
|
ppc*) arch=powerpc;;
|
||||||
|
mips*) arch=mips;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -f ${FILESDIR}/${subarch:-$arch}-dotconfig-custom ]; then
|
||||||
|
msg_normal "Detected a custom .config file for your arch, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${subarch:-$arch}-dotconfig-custom .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
elif [ -f ${FILESDIR}/${subarch:-$arch}-dotconfig ]; then
|
||||||
|
msg_normal "Detected a .config file for your arch, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${subarch:-$arch}-dotconfig .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
elif [ -f ${FILESDIR}/${XBPS_TARGET_MACHINE%%-musl}-dotconfig ]; then
|
||||||
|
msg_normal "Detected a .config file for your cpu family, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${XBPS_TARGET_MACHINE%%-musl}-dotconfig .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
else
|
||||||
|
msg_normal "Defaulting to 'allmodconfig'.\n"
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} allmodconfig
|
||||||
|
fi
|
||||||
|
# Always use our revision to CONFIG_LOCALVERSION to match our pkg version.
|
||||||
|
sed -i -e "s|^\(CONFIG_LOCALVERSION=\).*|\1\"_${revision}\"|" .config
|
||||||
|
}
|
||||||
|
|
||||||
|
do_build() {
|
||||||
|
local arch _cross _args
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) _args="bzImage modules"; arch=i386;;
|
||||||
|
x86_64*) _args="bzImage modules"; arch=x86_64;;
|
||||||
|
arm*) _args="zImage modules dtbs"; arch=arm;;
|
||||||
|
aarch64*) _args="Image modules dtbs"; arch=arm64;;
|
||||||
|
ppc*) _args="zImage modules"; arch=powerpc;;
|
||||||
|
mips*) _args="uImage modules dtbs"; arch=mips;;
|
||||||
|
esac
|
||||||
|
export LDFLAGS=
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ARCH=$arch ${_version} ${_cross} ${makejobs} prepare
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ARCH=$arch ${_version} ${_cross} ${makejobs} ${_args}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_install() {
|
||||||
|
local arch subarch _args hdrdest
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) arch=x86; subarch=i386;;
|
||||||
|
x86_64*) arch=x86; subarch=x86_64;;
|
||||||
|
arm*) arch=arm;;
|
||||||
|
aarch64*) arch=arm64;;
|
||||||
|
ppc*) arch=powerpc;;
|
||||||
|
mips*) arch=mips;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run depmod after compressing modules.
|
||||||
|
sed -i '2iexit 0' scripts/depmod.sh
|
||||||
|
|
||||||
|
# Install kernel, firmware and modules
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_MOD_PATH=${DESTDIR} modules_install
|
||||||
|
|
||||||
|
hdrdest=${DESTDIR}/usr/src/kernel-headers-${_kernver}
|
||||||
|
|
||||||
|
vinstall .config 644 boot config-${_kernver}
|
||||||
|
vinstall System.map 644 boot System.map-${_kernver}
|
||||||
|
|
||||||
|
case "$arch" in
|
||||||
|
x86)
|
||||||
|
vinstall arch/x86/boot/bzImage 644 boot vmlinuz-${_kernver}
|
||||||
|
;;
|
||||||
|
arm)
|
||||||
|
vinstall arch/arm/boot/zImage 644 boot
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
vinstall arch/arm64/boot/Image 644 boot vmlinux-${_kernver}
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
powerpc)
|
||||||
|
# zImage on powerpc is useless as it won't load initramfs
|
||||||
|
# raw vmlinux is huge, and this is nostrip, so do it manually
|
||||||
|
vinstall vmlinux 644 boot vmlinux-${_kernver}
|
||||||
|
/usr/bin/$STRIP ${DESTDIR}/boot/vmlinux-${_kernver}
|
||||||
|
;;
|
||||||
|
mips)
|
||||||
|
vinstall arch/mips/boot/uImage.bin 644 boot uImage-${_kernver}
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Switch to /usr.
|
||||||
|
vmkdir usr
|
||||||
|
mv ${DESTDIR}/lib ${DESTDIR}/usr
|
||||||
|
|
||||||
|
cd ${DESTDIR}/usr/lib/modules/${_kernver}
|
||||||
|
rm -f source build
|
||||||
|
ln -sf ../../../src/kernel-headers-${_kernver} build
|
||||||
|
|
||||||
|
cd ${wrksrc}
|
||||||
|
# Install required headers to build external modules
|
||||||
|
install -Dm644 Makefile ${hdrdest}/Makefile
|
||||||
|
install -Dm644 kernel/Makefile ${hdrdest}/kernel/Makefile
|
||||||
|
install -Dm644 .config ${hdrdest}/.config
|
||||||
|
for file in $(find . -name Kconfig\*); do
|
||||||
|
mkdir -p ${hdrdest}/$(dirname $file)
|
||||||
|
install -Dm644 $file ${hdrdest}/${file}
|
||||||
|
done
|
||||||
|
for file in $(find arch/${subarch:-$arch} -name module.lds -o -name Kbuild.platforms -o -name Platform); do
|
||||||
|
mkdir -p ${hdrdest}/$(dirname $file)
|
||||||
|
install -Dm644 $file ${hdrdest}/${file}
|
||||||
|
done
|
||||||
|
mkdir -p ${hdrdest}/include
|
||||||
|
# Remove firmware stuff provided by the "linux-firmware" pkg.
|
||||||
|
rm -rf ${DESTDIR}/usr/lib/firmware
|
||||||
|
|
||||||
|
for i in acpi asm-generic clocksource config crypto drm generated linux vdso \
|
||||||
|
math-emu media net pcmcia scsi sound trace uapi video xen dt-bindings; do
|
||||||
|
if [ -d include/$i ]; then
|
||||||
|
cp -a include/$i ${hdrdest}/include
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cd ${wrksrc}
|
||||||
|
mkdir -p ${hdrdest}/arch/${arch}
|
||||||
|
cp -a arch/${arch}/include ${hdrdest}/arch/${arch}
|
||||||
|
|
||||||
|
# Remove helper binaries built for host,
|
||||||
|
# if generated files from the scripts/ directory need to be included,
|
||||||
|
# they need to be copied to ${hdrdest} before this step
|
||||||
|
if [ "$CROSS_BUILD" ]; then
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} _mrproper_scripts
|
||||||
|
# remove host specific objects as well
|
||||||
|
find scripts -name '*.o' -delete
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy files necessary for later builds, like nvidia and vmware
|
||||||
|
cp Module.symvers ${hdrdest}
|
||||||
|
cp -a scripts ${hdrdest}
|
||||||
|
mkdir -p ${hdrdest}/security/selinux
|
||||||
|
cp -a security/selinux/include ${hdrdest}/security/selinux
|
||||||
|
mkdir -p ${hdrdest}/tools/include
|
||||||
|
cp -a tools/include/tools ${hdrdest}/tools/include
|
||||||
|
|
||||||
|
mkdir -p ${hdrdest}/arch/${arch}/kernel
|
||||||
|
cp arch/${arch}/Makefile ${hdrdest}/arch/${arch}
|
||||||
|
if [ "$subarch" = "i386" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/x86
|
||||||
|
cp arch/x86/Makefile_32.cpu ${hdrdest}/arch/x86
|
||||||
|
fi
|
||||||
|
if [ "$arch" = "x86" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/x86/kernel
|
||||||
|
cp arch/x86/kernel/asm-offsets.s ${hdrdest}/arch/x86/kernel
|
||||||
|
elif [ "$arch" = "arm64" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/arm64/kernel
|
||||||
|
cp -a arch/arm64/kernel/vdso ${hdrdest}/arch/arm64/kernel/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# add headers for lirc package
|
||||||
|
# pci
|
||||||
|
for i in bt8xx cx88 saa7134; do
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/pci/${i}
|
||||||
|
cp -a drivers/media/pci/${i}/*.h ${hdrdest}/drivers/media/pci/${i}
|
||||||
|
done
|
||||||
|
# i2c
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/i2c
|
||||||
|
cp drivers/media/i2c/*.h ${hdrdest}/drivers/media/i2c
|
||||||
|
for i in cx25840; do
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/i2c/${i}
|
||||||
|
cp -a drivers/media/i2c/${i}/*.h ${hdrdest}/drivers/media/i2c/${i}
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add md headers
|
||||||
|
mkdir -p ${hdrdest}/drivers/md
|
||||||
|
cp drivers/md/*.h ${hdrdest}/drivers/md
|
||||||
|
|
||||||
|
# Add inotify.h
|
||||||
|
mkdir -p ${hdrdest}/include/linux
|
||||||
|
cp include/linux/inotify.h ${hdrdest}/include/linux
|
||||||
|
|
||||||
|
# Add wireless headers
|
||||||
|
mkdir -p ${hdrdest}/net/mac80211/
|
||||||
|
cp net/mac80211/*.h ${hdrdest}/net/mac80211
|
||||||
|
|
||||||
|
# add dvb headers for http://mcentral.de/hg/~mrec/em28xx-new
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/dvb-frontends
|
||||||
|
cp drivers/media/dvb-frontends/lgdt330x.h \
|
||||||
|
${hdrdest}/drivers/media/dvb-frontends/
|
||||||
|
cp drivers/media/i2c/msp3400-driver.h ${hdrdest}/drivers/media/i2c/
|
||||||
|
|
||||||
|
# add dvb headers
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/usb/dvb-usb
|
||||||
|
cp drivers/media/usb/dvb-usb/*.h ${hdrdest}/drivers/media/usb/dvb-usb/
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/dvb-frontends
|
||||||
|
cp drivers/media/dvb-frontends/*.h ${hdrdest}/drivers/media/dvb-frontends/
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/tuners
|
||||||
|
cp drivers/media/tuners/*.h ${hdrdest}/drivers/media/tuners/
|
||||||
|
|
||||||
|
# Add xfs and shmem for aufs building
|
||||||
|
mkdir -p ${hdrdest}/fs/xfs/libxfs
|
||||||
|
mkdir -p ${hdrdest}/mm
|
||||||
|
cp fs/xfs/libxfs/xfs_sb.h ${hdrdest}/fs/xfs/libxfs/xfs_sb.h
|
||||||
|
|
||||||
|
# Add objtool binary, needed to build external modules with dkms
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
x86_64*)
|
||||||
|
mkdir -p ${hdrdest}/tools/objtool
|
||||||
|
cp tools/objtool/objtool ${hdrdest}/tools/objtool
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Remove unneeded architectures
|
||||||
|
case "$arch" in
|
||||||
|
i386|x86_64) _args="arm* m* p*";;
|
||||||
|
arm|arm64) _args="x86* m* p*";;
|
||||||
|
powerpc) _args="arm* m* x86* parisc";;
|
||||||
|
mips) _args="arm* x86* p*";;
|
||||||
|
esac
|
||||||
|
for arch in alpha avr32 blackfin cris frv h8300 \
|
||||||
|
ia64 s* um v850 xtensa ${_args}; do
|
||||||
|
rm -rf ${hdrdest}/arch/${arch}
|
||||||
|
done
|
||||||
|
# Keep arch/x86/ras/Kconfig as it is needed by drivers/ras/Kconfig
|
||||||
|
mkdir -p ${hdrdest}/arch/x86/ras
|
||||||
|
cp -a arch/x86/ras/Kconfig ${hdrdest}/arch/x86/ras/Kconfig
|
||||||
|
|
||||||
|
# Extract debugging symbols and compress modules
|
||||||
|
msg_normal "$pkgver: extracting debug info and compressing modules, please wait...\n"
|
||||||
|
install -Dm644 vmlinux ${DESTDIR}/usr/lib/debug/boot/vmlinux-${_kernver}
|
||||||
|
(
|
||||||
|
cd ${DESTDIR}
|
||||||
|
export DESTDIR
|
||||||
|
find ./ -name '*.ko' -print0 | \
|
||||||
|
xargs -0r -n1 -P ${XBPS_MAKEJOBS} ${FILESDIR}/mv-debug
|
||||||
|
)
|
||||||
|
# ... and run depmod again.
|
||||||
|
depmod -b ${DESTDIR}/usr -F System.map ${_kernver}
|
||||||
|
}
|
||||||
|
linux6.7-zen-headers_package() {
|
||||||
|
preserve=yes
|
||||||
|
nostrip=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
short_desc+=" - source headers for 3rd party modules"
|
||||||
|
pkg_install() {
|
||||||
|
vmove usr/src
|
||||||
|
vmove usr/lib/modules/${_kernver}/build
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linux6.7-zen-dbg_package() {
|
||||||
|
preserve=yes
|
||||||
|
nostrip=yes
|
||||||
|
noverifyrdeps=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
repository=debug
|
||||||
|
short_desc+=" - debugging symbols"
|
||||||
|
pkg_install() {
|
||||||
|
vmove usr/lib/debug
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,228 @@
|
||||||
|
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
|
|
@ -0,0 +1,147 @@
|
||||||
|
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
|
|
@ -0,0 +1,360 @@
|
||||||
|
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
|
|
@ -0,0 +1,365 @@
|
||||||
|
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
|
|
@ -0,0 +1,172 @@
|
||||||
|
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
|
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,166 @@
|
||||||
|
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
|
|
@ -0,0 +1,165 @@
|
||||||
|
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
|
|
@ -0,0 +1,78 @@
|
||||||
|
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
|
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
|
@ -0,0 +1,70 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,64 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,184 @@
|
||||||
|
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
|
|
@ -0,0 +1,79 @@
|
||||||
|
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
|
|
@ -0,0 +1,208 @@
|
||||||
|
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
|
|
@ -0,0 +1,222 @@
|
||||||
|
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
|
|
@ -0,0 +1,139 @@
|
||||||
|
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
|
|
@ -0,0 +1,136 @@
|
||||||
|
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
|
|
@ -0,0 +1,169 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
|
@ -0,0 +1,81 @@
|
||||||
|
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
|
|
@ -0,0 +1,259 @@
|
||||||
|
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
|
|
@ -0,0 +1,223 @@
|
||||||
|
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
|
|
@ -0,0 +1,110 @@
|
||||||
|
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
|
|
@ -0,0 +1,97 @@
|
||||||
|
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
|
|
@ -0,0 +1,29 @@
|
||||||
|
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
|
|
@ -0,0 +1,19 @@
|
||||||
|
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
|
||||||
|
index 4102108..72474d8 100644
|
||||||
|
--- a/kernel/rcu/tree_plugin.h
|
||||||
|
+++ b/kernel/rcu/tree_plugin.h
|
||||||
|
@@ -406,7 +406,7 @@ void __rcu_read_lock(void)
|
||||||
|
WRITE_ONCE(current->rcu_read_unlock_special.b.need_qs, true);
|
||||||
|
barrier(); /* critical section after entry code. */
|
||||||
|
}
|
||||||
|
-EXPORT_SYMBOL_GPL(__rcu_read_lock);
|
||||||
|
+EXPORT_SYMBOL(__rcu_read_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preemptible RCU implementation for rcu_read_unlock().
|
||||||
|
@@ -431,7 +431,7 @@ void __rcu_read_unlock(void)
|
||||||
|
WARN_ON_ONCE(rrln < 0 || rrln > RCU_NEST_PMAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-EXPORT_SYMBOL_GPL(__rcu_read_unlock);
|
||||||
|
+EXPORT_SYMBOL(__rcu_read_unlock);
|
|
@ -0,0 +1,324 @@
|
||||||
|
pkgname=linux6.8-zen
|
||||||
|
version=6.8
|
||||||
|
revision=1
|
||||||
|
zen=1
|
||||||
|
wrksrc="linux-${version}-zen${zen}"
|
||||||
|
short_desc="Linux kernel and modules with Zen patches (${version%.*} series) compiled with Clang"
|
||||||
|
maintainer="Wizzard <retard@deadzone.lol>"
|
||||||
|
license="GPL-2.0-only"
|
||||||
|
homepage="http://www.zen-kernel.org/"
|
||||||
|
distfiles="https://github.com/zen-kernel/zen-kernel/archive/refs/tags/v${version}-zen${zen}.tar.gz"
|
||||||
|
checksum="274cb5096ef9e38fef510bec78ba4b6b61935adf5bed00fa89711b0138732474"
|
||||||
|
python_version=3
|
||||||
|
patch_args="-Np1"
|
||||||
|
|
||||||
|
nodebug=yes # -dbg package is generated below manually
|
||||||
|
nostrip=yes
|
||||||
|
noverifyrdeps=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
preserve=yes
|
||||||
|
|
||||||
|
hostmakedepends="tar xz lz4 bc-gh elfutils-devel flex gmp-devel kmod libmpc-devel
|
||||||
|
openssl-devel perl uboot-mkimage cpio clang llvm lld pahole python3 which"
|
||||||
|
|
||||||
|
if [[ $version =~ ^[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
_kernver="${version}.0-zen${zen}_${revision}"
|
||||||
|
else
|
||||||
|
_kernver="${version}-zen${zen}_${revision}"
|
||||||
|
fi
|
||||||
|
triggers="kernel-hooks"
|
||||||
|
kernel_hooks_version="${_kernver}"
|
||||||
|
|
||||||
|
# These files could be modified when an external module is built.
|
||||||
|
mutable_files="
|
||||||
|
/usr/lib/modules/${_kernver}/modules.builtin.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.builtin.alias.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.softdep
|
||||||
|
/usr/lib/modules/${_kernver}/modules.dep
|
||||||
|
/usr/lib/modules/${_kernver}/modules.dep.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.symbols
|
||||||
|
/usr/lib/modules/${_kernver}/modules.symbols.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.alias
|
||||||
|
/usr/lib/modules/${_kernver}/modules.alias.bin
|
||||||
|
/usr/lib/modules/${_kernver}/modules.devname"
|
||||||
|
|
||||||
|
# reproducible build
|
||||||
|
export KBUILD_BUILD_TIMESTAMP=${SOURCE_DATE_EPOCH:-0}
|
||||||
|
export KBUILD_BUILD_USER=voidlinux
|
||||||
|
export KBUILD_BUILD_HOST=zen
|
||||||
|
|
||||||
|
if [ "$CROSS_BUILD" ]; then
|
||||||
|
_cross="CROSS_COMPILE=${XBPS_CROSS_TRIPLET}-"
|
||||||
|
fi
|
||||||
|
if [ "${_patchver}" ]; then
|
||||||
|
_version="EXTRAVERSION=${_patchver}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_configure() {
|
||||||
|
# If there's a file called <arch>-dotconfig, use it to
|
||||||
|
# configure the kernel; otherwise use arch defaults and all stuff
|
||||||
|
# as modules (allmodconfig).
|
||||||
|
local arch subarch
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) arch=i386;;
|
||||||
|
x86_64*) arch=x86_64;;
|
||||||
|
arm*) arch=arm;;
|
||||||
|
aarch64*) arch=arm64;;
|
||||||
|
ppc64le*) arch=powerpc; subarch=ppc64le;;
|
||||||
|
ppc64*) arch=powerpc; subarch=ppc64;;
|
||||||
|
ppc*) arch=powerpc;;
|
||||||
|
mips*) arch=mips;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -f ${FILESDIR}/${subarch:-$arch}-dotconfig-custom ]; then
|
||||||
|
msg_normal "Detected a custom .config file for your arch, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${subarch:-$arch}-dotconfig-custom .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
elif [ -f ${FILESDIR}/${subarch:-$arch}-dotconfig ]; then
|
||||||
|
msg_normal "Detected a .config file for your arch, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${subarch:-$arch}-dotconfig .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
elif [ -f ${FILESDIR}/${XBPS_TARGET_MACHINE%%-musl}-dotconfig ]; then
|
||||||
|
msg_normal "Detected a .config file for your cpu family, using it.\n"
|
||||||
|
cp -f ${FILESDIR}/${XBPS_TARGET_MACHINE%%-musl}-dotconfig .config
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} oldconfig
|
||||||
|
else
|
||||||
|
msg_normal "Defaulting to 'allmodconfig'.\n"
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=$arch ${_args} allmodconfig
|
||||||
|
fi
|
||||||
|
# Always use our revision to CONFIG_LOCALVERSION to match our pkg version.
|
||||||
|
sed -i -e "s|^\(CONFIG_LOCALVERSION=\).*|\1\"_${revision}\"|" .config
|
||||||
|
}
|
||||||
|
|
||||||
|
do_build() {
|
||||||
|
local arch _cross _args
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) _args="bzImage modules"; arch=i386;;
|
||||||
|
x86_64*) _args="bzImage modules"; arch=x86_64;;
|
||||||
|
arm*) _args="zImage modules dtbs"; arch=arm;;
|
||||||
|
aarch64*) _args="Image modules dtbs"; arch=arm64;;
|
||||||
|
ppc*) _args="zImage modules"; arch=powerpc;;
|
||||||
|
mips*) _args="uImage modules dtbs"; arch=mips;;
|
||||||
|
esac
|
||||||
|
export LDFLAGS=
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ARCH=$arch ${_version} ${_cross} ${makejobs} prepare
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ARCH=$arch ${_version} ${_cross} ${makejobs} ${_args}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_install() {
|
||||||
|
local arch subarch _args hdrdest
|
||||||
|
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
i686*) arch=x86; subarch=i386;;
|
||||||
|
x86_64*) arch=x86; subarch=x86_64;;
|
||||||
|
arm*) arch=arm;;
|
||||||
|
aarch64*) arch=arm64;;
|
||||||
|
ppc*) arch=powerpc;;
|
||||||
|
mips*) arch=mips;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run depmod after compressing modules.
|
||||||
|
sed -i '2iexit 0' scripts/depmod.sh
|
||||||
|
|
||||||
|
# Install kernel, firmware and modules
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_MOD_PATH=${DESTDIR} modules_install
|
||||||
|
|
||||||
|
hdrdest=${DESTDIR}/usr/src/kernel-headers-${_kernver}
|
||||||
|
|
||||||
|
vinstall .config 644 boot config-${_kernver}
|
||||||
|
vinstall System.map 644 boot System.map-${_kernver}
|
||||||
|
|
||||||
|
case "$arch" in
|
||||||
|
x86)
|
||||||
|
vinstall arch/x86/boot/bzImage 644 boot vmlinuz-${_kernver}
|
||||||
|
;;
|
||||||
|
arm)
|
||||||
|
vinstall arch/arm/boot/zImage 644 boot
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
vinstall arch/arm64/boot/Image 644 boot vmlinux-${_kernver}
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
powerpc)
|
||||||
|
# zImage on powerpc is useless as it won't load initramfs
|
||||||
|
# raw vmlinux is huge, and this is nostrip, so do it manually
|
||||||
|
vinstall vmlinux 644 boot vmlinux-${_kernver}
|
||||||
|
/usr/bin/$STRIP ${DESTDIR}/boot/vmlinux-${_kernver}
|
||||||
|
;;
|
||||||
|
mips)
|
||||||
|
vinstall arch/mips/boot/uImage.bin 644 boot uImage-${_kernver}
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} INSTALL_DTBS_PATH=${DESTDIR}/boot/dtbs/dtbs-${_kernver} dtbs_install
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Switch to /usr.
|
||||||
|
vmkdir usr
|
||||||
|
mv ${DESTDIR}/lib ${DESTDIR}/usr
|
||||||
|
|
||||||
|
cd ${DESTDIR}/usr/lib/modules/${_kernver}
|
||||||
|
rm -f source build
|
||||||
|
ln -sf ../../../src/kernel-headers-${_kernver} build
|
||||||
|
|
||||||
|
cd ${wrksrc}
|
||||||
|
# Install required headers to build external modules
|
||||||
|
install -Dm644 Makefile ${hdrdest}/Makefile
|
||||||
|
install -Dm644 kernel/Makefile ${hdrdest}/kernel/Makefile
|
||||||
|
install -Dm644 .config ${hdrdest}/.config
|
||||||
|
for file in $(find . -name Kconfig\*); do
|
||||||
|
mkdir -p ${hdrdest}/$(dirname $file)
|
||||||
|
install -Dm644 $file ${hdrdest}/${file}
|
||||||
|
done
|
||||||
|
for file in $(find arch/${subarch:-$arch} -name module.lds -o -name Kbuild.platforms -o -name Platform); do
|
||||||
|
mkdir -p ${hdrdest}/$(dirname $file)
|
||||||
|
install -Dm644 $file ${hdrdest}/${file}
|
||||||
|
done
|
||||||
|
mkdir -p ${hdrdest}/include
|
||||||
|
# Remove firmware stuff provided by the "linux-firmware" pkg.
|
||||||
|
rm -rf ${DESTDIR}/usr/lib/firmware
|
||||||
|
|
||||||
|
for i in acpi asm-generic clocksource config crypto drm generated linux vdso \
|
||||||
|
math-emu media net pcmcia scsi sound trace uapi video xen dt-bindings; do
|
||||||
|
if [ -d include/$i ]; then
|
||||||
|
cp -a include/$i ${hdrdest}/include
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cd ${wrksrc}
|
||||||
|
mkdir -p ${hdrdest}/arch/${arch}
|
||||||
|
cp -a arch/${arch}/include ${hdrdest}/arch/${arch}
|
||||||
|
|
||||||
|
# Remove helper binaries built for host,
|
||||||
|
# if generated files from the scripts/ directory need to be included,
|
||||||
|
# they need to be copied to ${hdrdest} before this step
|
||||||
|
if [ "$CROSS_BUILD" ]; then
|
||||||
|
make CC=clang AS=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump HOSTCC=clang HOSTLD=ld.lld HOSTAR=llvm-ar ${makejobs} ARCH=${subarch:-$arch} _mrproper_scripts
|
||||||
|
# remove host specific objects as well
|
||||||
|
find scripts -name '*.o' -delete
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy files necessary for later builds, like nvidia and vmware
|
||||||
|
cp Module.symvers ${hdrdest}
|
||||||
|
cp -a scripts ${hdrdest}
|
||||||
|
mkdir -p ${hdrdest}/security/selinux
|
||||||
|
cp -a security/selinux/include ${hdrdest}/security/selinux
|
||||||
|
mkdir -p ${hdrdest}/tools/include
|
||||||
|
cp -a tools/include/tools ${hdrdest}/tools/include
|
||||||
|
|
||||||
|
mkdir -p ${hdrdest}/arch/${arch}/kernel
|
||||||
|
cp arch/${arch}/Makefile ${hdrdest}/arch/${arch}
|
||||||
|
if [ "$subarch" = "i386" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/x86
|
||||||
|
cp arch/x86/Makefile_32.cpu ${hdrdest}/arch/x86
|
||||||
|
fi
|
||||||
|
if [ "$arch" = "x86" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/x86/kernel
|
||||||
|
cp arch/x86/kernel/asm-offsets.s ${hdrdest}/arch/x86/kernel
|
||||||
|
elif [ "$arch" = "arm64" ]; then
|
||||||
|
mkdir -p ${hdrdest}/arch/arm64/kernel
|
||||||
|
cp -a arch/arm64/kernel/vdso ${hdrdest}/arch/arm64/kernel/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# add headers for lirc package
|
||||||
|
# pci
|
||||||
|
for i in bt8xx cx88 saa7134; do
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/pci/${i}
|
||||||
|
cp -a drivers/media/pci/${i}/*.h ${hdrdest}/drivers/media/pci/${i}
|
||||||
|
done
|
||||||
|
# i2c
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/i2c
|
||||||
|
cp drivers/media/i2c/*.h ${hdrdest}/drivers/media/i2c
|
||||||
|
for i in cx25840; do
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/i2c/${i}
|
||||||
|
cp -a drivers/media/i2c/${i}/*.h ${hdrdest}/drivers/media/i2c/${i}
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add md headers
|
||||||
|
mkdir -p ${hdrdest}/drivers/md
|
||||||
|
cp drivers/md/*.h ${hdrdest}/drivers/md
|
||||||
|
|
||||||
|
# Add inotify.h
|
||||||
|
mkdir -p ${hdrdest}/include/linux
|
||||||
|
cp include/linux/inotify.h ${hdrdest}/include/linux
|
||||||
|
|
||||||
|
# Add wireless headers
|
||||||
|
mkdir -p ${hdrdest}/net/mac80211/
|
||||||
|
cp net/mac80211/*.h ${hdrdest}/net/mac80211
|
||||||
|
|
||||||
|
# add dvb headers for http://mcentral.de/hg/~mrec/em28xx-new
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/dvb-frontends
|
||||||
|
cp drivers/media/dvb-frontends/lgdt330x.h \
|
||||||
|
${hdrdest}/drivers/media/dvb-frontends/
|
||||||
|
cp drivers/media/i2c/msp3400-driver.h ${hdrdest}/drivers/media/i2c/
|
||||||
|
|
||||||
|
# add dvb headers
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/usb/dvb-usb
|
||||||
|
cp drivers/media/usb/dvb-usb/*.h ${hdrdest}/drivers/media/usb/dvb-usb/
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/dvb-frontends
|
||||||
|
cp drivers/media/dvb-frontends/*.h ${hdrdest}/drivers/media/dvb-frontends/
|
||||||
|
mkdir -p ${hdrdest}/drivers/media/tuners
|
||||||
|
cp drivers/media/tuners/*.h ${hdrdest}/drivers/media/tuners/
|
||||||
|
|
||||||
|
# Add xfs and shmem for aufs building
|
||||||
|
mkdir -p ${hdrdest}/fs/xfs/libxfs
|
||||||
|
mkdir -p ${hdrdest}/mm
|
||||||
|
cp fs/xfs/libxfs/xfs_sb.h ${hdrdest}/fs/xfs/libxfs/xfs_sb.h
|
||||||
|
|
||||||
|
# Add objtool binary, needed to build external modules with dkms
|
||||||
|
case "$XBPS_TARGET_MACHINE" in
|
||||||
|
x86_64*)
|
||||||
|
mkdir -p ${hdrdest}/tools/objtool
|
||||||
|
cp tools/objtool/objtool ${hdrdest}/tools/objtool
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Remove unneeded architectures
|
||||||
|
case "$arch" in
|
||||||
|
i386|x86_64) _args="arm* m* p*";;
|
||||||
|
arm|arm64) _args="x86* m* p*";;
|
||||||
|
powerpc) _args="arm* m* x86* parisc";;
|
||||||
|
mips) _args="arm* x86* p*";;
|
||||||
|
esac
|
||||||
|
for arch in alpha avr32 blackfin cris frv h8300 \
|
||||||
|
ia64 s* um v850 xtensa ${_args}; do
|
||||||
|
rm -rf ${hdrdest}/arch/${arch}
|
||||||
|
done
|
||||||
|
# Keep arch/x86/ras/Kconfig as it is needed by drivers/ras/Kconfig
|
||||||
|
mkdir -p ${hdrdest}/arch/x86/ras
|
||||||
|
cp -a arch/x86/ras/Kconfig ${hdrdest}/arch/x86/ras/Kconfig
|
||||||
|
|
||||||
|
# Extract debugging symbols and compress modules
|
||||||
|
msg_normal "$pkgver: extracting debug info and compressing modules, please wait...\n"
|
||||||
|
install -Dm644 vmlinux ${DESTDIR}/usr/lib/debug/boot/vmlinux-${_kernver}
|
||||||
|
(
|
||||||
|
cd ${DESTDIR}
|
||||||
|
export DESTDIR
|
||||||
|
find ./ -name '*.ko' -print0 | \
|
||||||
|
xargs -0r -n1 -P ${XBPS_MAKEJOBS} ${FILESDIR}/mv-debug
|
||||||
|
)
|
||||||
|
# ... and run depmod again.
|
||||||
|
depmod -b ${DESTDIR}/usr -F System.map ${_kernver}
|
||||||
|
}
|
||||||
|
linux6.8-zen-headers_package() {
|
||||||
|
preserve=yes
|
||||||
|
nostrip=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
short_desc+=" - source headers for 3rd party modules"
|
||||||
|
pkg_install() {
|
||||||
|
vmove usr/src
|
||||||
|
vmove usr/lib/modules/${_kernver}/build
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linux6.8-zen-dbg_package() {
|
||||||
|
preserve=yes
|
||||||
|
nostrip=yes
|
||||||
|
noverifyrdeps=yes
|
||||||
|
noshlibprovides=yes
|
||||||
|
repository=debug
|
||||||
|
short_desc+=" - debugging symbols"
|
||||||
|
pkg_install() {
|
||||||
|
vmove usr/lib/debug
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
linux6.8-zen
|
|
@ -0,0 +1 @@
|
||||||
|
linux6.8-zen
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
mod=$1
|
||||||
|
mkdir -p usr/lib/debug/${mod%/*}
|
||||||
|
$OBJCOPY --only-keep-debug --compress-debug-sections $mod usr/lib/debug/$mod
|
||||||
|
$OBJCOPY --add-gnu-debuglink=${DESTDIR}/usr/lib/debug/$mod $mod
|
||||||
|
/usr/bin/$STRIP --strip-debug $mod
|
||||||
|
gzip -9 $mod
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/sh
|
||||||
|
mod=$1
|
||||||
|
mkdir -p usr/lib/debug/${mod%/*}
|
||||||
|
$OBJCOPY --only-keep-debug --compress-debug-sections $mod usr/lib/debug/$mod
|
||||||
|
$OBJCOPY --add-gnu-debuglink=${DESTDIR}/usr/lib/debug/$mod $mod
|
||||||
|
/usr/bin/$STRIP --strip-debug $mod
|
||||||
|
gzip -9 $mod
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,228 @@
|
||||||
|
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
|
|
@ -0,0 +1,147 @@
|
||||||
|
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
|
|
@ -0,0 +1,360 @@
|
||||||
|
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
|
|
@ -0,0 +1,365 @@
|
||||||
|
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
|
|
@ -0,0 +1,172 @@
|
||||||
|
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
|
|
@ -0,0 +1,107 @@
|
||||||
|
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
|
|
@ -0,0 +1,166 @@
|
||||||
|
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
|
|
@ -0,0 +1,165 @@
|
||||||
|
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
|
|
@ -0,0 +1,78 @@
|
||||||
|
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
|
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
|
@ -0,0 +1,70 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,64 @@
|
||||||
|
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
|
|
@ -0,0 +1,62 @@
|
||||||
|
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
|
|
@ -0,0 +1,184 @@
|
||||||
|
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
|
|
@ -0,0 +1,79 @@
|
||||||
|
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
|
|
@ -0,0 +1,208 @@
|
||||||
|
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
|
|
@ -0,0 +1,222 @@
|
||||||
|
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
|
|
@ -0,0 +1,139 @@
|
||||||
|
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
|
|
@ -0,0 +1,136 @@
|
||||||
|
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
|
|
@ -0,0 +1,169 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
|
@ -0,0 +1,118 @@
|
||||||
|
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue