release 6.12.5
This commit is contained in:
55
debian/patches/misc-ntsync7/0001-ntsync-Return-the-fd-from-NTSYNC_IOC_CREATE_SEM.patch
vendored
Normal file
55
debian/patches/misc-ntsync7/0001-ntsync-Return-the-fd-from-NTSYNC_IOC_CREATE_SEM.patch
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
From e50ffd43b88d64b8063a9fce59f1d03b56f6144c Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:42 -0600
|
||||
Subject: ntsync: Return the fd from NTSYNC_IOC_CREATE_SEM.
|
||||
|
||||
Simplify the user API a bit by returning the fd as return value from the ioctl
|
||||
instead of through the argument pointer.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/ntsync.c | 7 ++-----
|
||||
include/uapi/linux/ntsync.h | 3 +--
|
||||
2 files changed, 3 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -165,7 +165,6 @@ static int ntsync_obj_get_fd(struct ntsy
|
||||
|
||||
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;
|
||||
@@ -182,12 +181,10 @@ static int ntsync_create_sem(struct ntsy
|
||||
sem->u.sem.count = args.count;
|
||||
sem->u.sem.max = args.max;
|
||||
fd = ntsync_obj_get_fd(sem);
|
||||
- if (fd < 0) {
|
||||
+ if (fd < 0)
|
||||
kfree(sem);
|
||||
- return fd;
|
||||
- }
|
||||
|
||||
- return put_user(fd, &user_args->sem);
|
||||
+ return fd;
|
||||
}
|
||||
|
||||
static int ntsync_char_open(struct inode *inode, struct file *file)
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -11,12 +11,11 @@
|
||||
#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)
|
||||
+#define NTSYNC_IOC_CREATE_SEM _IOW ('N', 0x80, struct ntsync_sem_args)
|
||||
|
||||
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
||||
|
64
debian/patches/misc-ntsync7/0002-ntsync-Rename-NTSYNC_IOC_SEM_POST-to-NTSYNC_IOC_SEM_.patch
vendored
Normal file
64
debian/patches/misc-ntsync7/0002-ntsync-Rename-NTSYNC_IOC_SEM_POST-to-NTSYNC_IOC_SEM_.patch
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
From 160e9bf7826da868ae4de261753a03cce2208ff6 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:43 -0600
|
||||
Subject: ntsync: Rename NTSYNC_IOC_SEM_POST to NTSYNC_IOC_SEM_RELEASE.
|
||||
|
||||
Use the more common "release" terminology, which is also the term used by NT,
|
||||
instead of "post" (which is used by POSIX).
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/ntsync.c | 10 +++++-----
|
||||
include/uapi/linux/ntsync.h | 2 +-
|
||||
2 files changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -57,7 +57,7 @@ struct ntsync_device {
|
||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
||||
* invalid.
|
||||
*/
|
||||
-static int post_sem_state(struct ntsync_obj *sem, __u32 count)
|
||||
+static int release_sem_state(struct ntsync_obj *sem, __u32 count)
|
||||
{
|
||||
__u32 sum;
|
||||
|
||||
@@ -71,7 +71,7 @@ static int post_sem_state(struct ntsync_
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
|
||||
+static int ntsync_sem_release(struct ntsync_obj *sem, void __user *argp)
|
||||
{
|
||||
__u32 __user *user_args = argp;
|
||||
__u32 prev_count;
|
||||
@@ -87,7 +87,7 @@ static int ntsync_sem_post(struct ntsync
|
||||
spin_lock(&sem->lock);
|
||||
|
||||
prev_count = sem->u.sem.count;
|
||||
- ret = post_sem_state(sem, args);
|
||||
+ ret = release_sem_state(sem, args);
|
||||
|
||||
spin_unlock(&sem->lock);
|
||||
|
||||
@@ -114,8 +114,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
void __user *argp = (void __user *)parm;
|
||||
|
||||
switch (cmd) {
|
||||
- case NTSYNC_IOC_SEM_POST:
|
||||
- return ntsync_sem_post(obj, argp);
|
||||
+ case NTSYNC_IOC_SEM_RELEASE:
|
||||
+ return ntsync_sem_release(obj, argp);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -17,6 +17,6 @@ struct ntsync_sem_args {
|
||||
|
||||
#define NTSYNC_IOC_CREATE_SEM _IOW ('N', 0x80, struct ntsync_sem_args)
|
||||
|
||||
-#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
|
||||
+#define NTSYNC_IOC_SEM_RELEASE _IOWR('N', 0x81, __u32)
|
||||
|
||||
#endif
|
377
debian/patches/misc-ntsync7/0003-ntsync-Introduce-NTSYNC_IOC_WAIT_ANY.patch
vendored
Normal file
377
debian/patches/misc-ntsync7/0003-ntsync-Introduce-NTSYNC_IOC_WAIT_ANY.patch
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
From e855a17ec837cdee9047e6e23e47ed7b4312a265 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:44 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
|
||||
|
||||
This corresponds to part of the functionality of the NT syscall
|
||||
NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
|
||||
the third argument (wait_any) is TRUE, and it does not handle alertable waits.
|
||||
Those features have been split out into separate patches to ease review.
|
||||
|
||||
This patch therefore implements the wait/wake infrastructure which comprises the
|
||||
core of ntsync's functionality.
|
||||
|
||||
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.
|
||||
|
||||
This wait/wake model is fundamentally different from that used anywhere else in
|
||||
the kernel, and for that reason ntsync does not use any existing infrastructure,
|
||||
such as futexes, kernel mutexes or semaphores, or wait_event().
|
||||
|
||||
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 either the MONOTONIC or REALTIME clock
|
||||
(controlled by the flags argument). 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.
|
||||
|
||||
Some padding fields are added for alignment and for fields which will be added
|
||||
in future patches (split out to ease review).
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/ntsync.c | 247 +++++++++++++++++++++++++++++++++++-
|
||||
include/uapi/linux/ntsync.h | 14 ++
|
||||
2 files changed, 260 insertions(+), 1 deletion(-)
|
||||
|
||||
--- 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;
|
||||
+
|
||||
+ /*
|
||||
+ * Protected via atomic_try_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;
|
||||
+ int signaled = -1;
|
||||
+
|
||||
+ if (!sem->u.sem.count)
|
||||
+ break;
|
||||
+
|
||||
+ if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
|
||||
+ sem->u.sem.count--;
|
||||
+ wake_up_process(q->task);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Actually change the semaphore state, returning -EOVERFLOW if it is made
|
||||
* invalid.
|
||||
@@ -87,7 +137,9 @@ static int ntsync_sem_release(struct nts
|
||||
spin_lock(&sem->lock);
|
||||
|
||||
prev_count = sem->u.sem.count;
|
||||
- ret = release_sem_state(sem, args);
|
||||
+ ret = post_sem_state(sem, args);
|
||||
+ if (!ret)
|
||||
+ try_wake_any_sem(sem);
|
||||
|
||||
spin_unlock(&sem->lock);
|
||||
|
||||
@@ -140,6 +192,7 @@ static struct ntsync_obj *ntsync_alloc_o
|
||||
obj->dev = dev;
|
||||
get_file(dev->file);
|
||||
spin_lock_init(&obj->lock);
|
||||
+ INIT_LIST_HEAD(&obj->any_waiters);
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -187,6 +240,196 @@ static int ntsync_create_sem(struct ntsy
|
||||
return fd;
|
||||
}
|
||||
|
||||
+static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
||||
+{
|
||||
+ struct file *file = fget(fd);
|
||||
+ struct ntsync_obj *obj;
|
||||
+
|
||||
+ if (!file)
|
||||
+ return NULL;
|
||||
+
|
||||
+ 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);
|
||||
+ 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;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
||||
+ if (atomic_read(&q->signaled) != -1) {
|
||||
+ ret = 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
|
||||
+ } while (ret < 0);
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+
|
||||
+ 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->pad[0] || args->pad[1] || args->pad[2] || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
||||
+ 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;
|
||||
+ 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;
|
||||
@@ -218,6 +461,8 @@ static long ntsync_char_ioctl(struct fil
|
||||
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;
|
||||
}
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -15,7 +15,21 @@ struct ntsync_sem_args {
|
||||
__u32 max;
|
||||
};
|
||||
|
||||
+#define NTSYNC_WAIT_REALTIME 0x1
|
||||
+
|
||||
+struct ntsync_wait_args {
|
||||
+ __u64 timeout;
|
||||
+ __u64 objs;
|
||||
+ __u32 count;
|
||||
+ __u32 index;
|
||||
+ __u32 flags;
|
||||
+ __u32 pad[3];
|
||||
+};
|
||||
+
|
||||
+#define NTSYNC_MAX_WAIT_COUNT 64
|
||||
+
|
||||
#define NTSYNC_IOC_CREATE_SEM _IOW ('N', 0x80, struct ntsync_sem_args)
|
||||
+#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
|
||||
|
||||
#define NTSYNC_IOC_SEM_RELEASE _IOWR('N', 0x81, __u32)
|
||||
|
533
debian/patches/misc-ntsync7/0004-ntsync-Introduce-NTSYNC_IOC_WAIT_ALL.patch
vendored
Normal file
533
debian/patches/misc-ntsync7/0004-ntsync-Introduce-NTSYNC_IOC_WAIT_ALL.patch
vendored
Normal file
@@ -0,0 +1,533 @@
|
||||
From 6c1dac87ff835a48a067fe75bd0a6965921dac78 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:45 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
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.
|
||||
|
||||
Because acquisition of multiple objects is atomic, some complex locking is
|
||||
required. We cannot simply spin-lock multiple objects simultaneously, as that
|
||||
may disable preëmption for a problematically long time.
|
||||
|
||||
Instead, modifying any object which may be involved in a wait-all operation takes
|
||||
a device-wide sleeping mutex, "wait_all_lock", instead of the normal object
|
||||
spinlock.
|
||||
|
||||
Because wait-for-all is a rare operation, in order to optimize wait-for-any,
|
||||
this lock is only taken when necessary. "all_hint" is used to mark objects which
|
||||
are involved in a wait-for-all operation, and if an object is not, only its
|
||||
spinlock is taken.
|
||||
|
||||
The locking scheme used here was written by Peter Zijlstra.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/ntsync.c | 336 ++++++++++++++++++++++++++++++++++--
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 323 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/signal.h>
|
||||
@@ -41,6 +42,7 @@ enum ntsync_type {
|
||||
|
||||
struct ntsync_obj {
|
||||
spinlock_t lock;
|
||||
+ int dev_locked;
|
||||
|
||||
enum ntsync_type type;
|
||||
|
||||
@@ -55,7 +57,30 @@ 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.
|
||||
+ *
|
||||
+ * 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 {
|
||||
@@ -75,19 +100,198 @@ 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.
|
||||
+ *
|
||||
+ * This device-wide lock is used to serialize wait-for-all
|
||||
+ * operations, and operations on an object that is involved in a
|
||||
+ * wait-for-all.
|
||||
+ */
|
||||
+ struct mutex wait_all_lock;
|
||||
+
|
||||
struct file *file;
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * Single objects are locked using obj->lock.
|
||||
+ *
|
||||
+ * Multiple objects are 'locked' while holding dev->wait_all_lock.
|
||||
+ * In this case however, individual objects are not locked by holding
|
||||
+ * obj->lock, but by setting obj->dev_locked.
|
||||
+ *
|
||||
+ * This means that in order to lock a single object, the sequence is slightly
|
||||
+ * more complicated than usual. Specifically it needs to check obj->dev_locked
|
||||
+ * after acquiring obj->lock, if set, it needs to drop the lock and acquire
|
||||
+ * dev->wait_all_lock in order to serialize against the multi-object operation.
|
||||
+ */
|
||||
+
|
||||
+static void dev_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
|
||||
+{
|
||||
+ lockdep_assert_held(&dev->wait_all_lock);
|
||||
+ lockdep_assert(obj->dev == dev);
|
||||
+ spin_lock(&obj->lock);
|
||||
+ /*
|
||||
+ * By setting obj->dev_locked inside obj->lock, it is ensured that
|
||||
+ * anyone holding obj->lock must see the value.
|
||||
+ */
|
||||
+ obj->dev_locked = 1;
|
||||
+ spin_unlock(&obj->lock);
|
||||
+}
|
||||
+
|
||||
+static void dev_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
|
||||
+{
|
||||
+ lockdep_assert_held(&dev->wait_all_lock);
|
||||
+ lockdep_assert(obj->dev == dev);
|
||||
+ spin_lock(&obj->lock);
|
||||
+ obj->dev_locked = 0;
|
||||
+ spin_unlock(&obj->lock);
|
||||
+}
|
||||
+
|
||||
+static void obj_lock(struct ntsync_obj *obj)
|
||||
+{
|
||||
+ struct ntsync_device *dev = obj->dev;
|
||||
+
|
||||
+ for (;;) {
|
||||
+ spin_lock(&obj->lock);
|
||||
+ if (likely(!obj->dev_locked))
|
||||
+ break;
|
||||
+
|
||||
+ spin_unlock(&obj->lock);
|
||||
+ mutex_lock(&dev->wait_all_lock);
|
||||
+ spin_lock(&obj->lock);
|
||||
+ /*
|
||||
+ * obj->dev_locked should be set and released under the same
|
||||
+ * wait_all_lock section, since we now own this lock, it should
|
||||
+ * be clear.
|
||||
+ */
|
||||
+ lockdep_assert(!obj->dev_locked);
|
||||
+ spin_unlock(&obj->lock);
|
||||
+ mutex_unlock(&dev->wait_all_lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void obj_unlock(struct ntsync_obj *obj)
|
||||
+{
|
||||
+ spin_unlock(&obj->lock);
|
||||
+}
|
||||
+
|
||||
+static bool ntsync_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
|
||||
+{
|
||||
+ bool all;
|
||||
+
|
||||
+ obj_lock(obj);
|
||||
+ all = atomic_read(&obj->all_hint);
|
||||
+ if (unlikely(all)) {
|
||||
+ obj_unlock(obj);
|
||||
+ mutex_lock(&dev->wait_all_lock);
|
||||
+ dev_lock_obj(dev, obj);
|
||||
+ }
|
||||
+
|
||||
+ return all;
|
||||
+}
|
||||
+
|
||||
+static void ntsync_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj, bool all)
|
||||
+{
|
||||
+ if (all) {
|
||||
+ dev_unlock_obj(dev, obj);
|
||||
+ mutex_unlock(&dev->wait_all_lock);
|
||||
+ } else {
|
||||
+ obj_unlock(obj);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#define ntsync_assert_held(obj) \
|
||||
+ lockdep_assert((lockdep_is_held(&(obj)->lock) != LOCK_STATE_NOT_HELD) || \
|
||||
+ ((lockdep_is_held(&(obj)->dev->wait_all_lock) != LOCK_STATE_NOT_HELD) && \
|
||||
+ (obj)->dev_locked))
|
||||
+
|
||||
+static bool is_signaled(struct ntsync_obj *obj)
|
||||
+{
|
||||
+ ntsync_assert_held(obj);
|
||||
+
|
||||
+ 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;
|
||||
+ int signaled = -1;
|
||||
+ __u32 i;
|
||||
+
|
||||
+ lockdep_assert_held(&dev->wait_all_lock);
|
||||
+ if (locked_obj)
|
||||
+ lockdep_assert(locked_obj->dev_locked);
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (q->entries[i].obj != locked_obj)
|
||||
+ dev_lock_obj(dev, q->entries[i].obj);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (!is_signaled(q->entries[i].obj)) {
|
||||
+ can_wake = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (can_wake && atomic_try_cmpxchg(&q->signaled, &signaled, 0)) {
|
||||
+ 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)
|
||||
+ dev_unlock_obj(dev, q->entries[i].obj);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+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(obj->dev_locked);
|
||||
+
|
||||
+ 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;
|
||||
|
||||
- lockdep_assert_held(&sem->lock);
|
||||
+ ntsync_assert_held(sem);
|
||||
+ lockdep_assert(sem->type == NTSYNC_TYPE_SEM);
|
||||
|
||||
list_for_each_entry(entry, &sem->any_waiters, node) {
|
||||
struct ntsync_q *q = entry->q;
|
||||
@@ -111,7 +315,7 @@ static int release_sem_state(struct ntsy
|
||||
{
|
||||
__u32 sum;
|
||||
|
||||
- lockdep_assert_held(&sem->lock);
|
||||
+ ntsync_assert_held(sem);
|
||||
|
||||
if (check_add_overflow(sem->u.sem.count, count, &sum) ||
|
||||
sum > sem->u.sem.max)
|
||||
@@ -123,9 +327,11 @@ static int release_sem_state(struct ntsy
|
||||
|
||||
static int ntsync_sem_release(struct ntsync_obj *sem, void __user *argp)
|
||||
{
|
||||
+ struct ntsync_device *dev = sem->dev;
|
||||
__u32 __user *user_args = argp;
|
||||
__u32 prev_count;
|
||||
__u32 args;
|
||||
+ bool all;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
@@ -134,14 +340,17 @@ static int ntsync_sem_release(struct nts
|
||||
if (sem->type != NTSYNC_TYPE_SEM)
|
||||
return -EINVAL;
|
||||
|
||||
- spin_lock(&sem->lock);
|
||||
+ all = ntsync_lock_obj(dev, sem);
|
||||
|
||||
prev_count = sem->u.sem.count;
|
||||
- ret = post_sem_state(sem, args);
|
||||
- if (!ret)
|
||||
+ ret = release_sem_state(sem, args);
|
||||
+ if (!ret) {
|
||||
+ if (all)
|
||||
+ try_wake_all_obj(dev, sem);
|
||||
try_wake_any_sem(sem);
|
||||
+ }
|
||||
|
||||
- spin_unlock(&sem->lock);
|
||||
+ ntsync_unlock_obj(dev, sem, all);
|
||||
|
||||
if (!ret && put_user(prev_count, user_args))
|
||||
ret = -EFAULT;
|
||||
@@ -193,6 +402,8 @@ static struct ntsync_obj *ntsync_alloc_o
|
||||
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;
|
||||
}
|
||||
@@ -301,7 +512,7 @@ static int ntsync_schedule(const struct
|
||||
* 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;
|
||||
@@ -324,6 +535,7 @@ static int setup_wait(struct ntsync_devi
|
||||
return -ENOMEM;
|
||||
q->task = current;
|
||||
atomic_set(&q->signaled, -1);
|
||||
+ q->all = all;
|
||||
q->count = count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -333,6 +545,16 @@ static int setup_wait(struct ntsync_devi
|
||||
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;
|
||||
@@ -362,13 +584,14 @@ static int ntsync_wait_any(struct ntsync
|
||||
struct ntsync_wait_args args;
|
||||
struct ntsync_q *q;
|
||||
int signaled;
|
||||
+ bool all;
|
||||
__u32 i;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
||||
@@ -378,9 +601,9 @@ static int ntsync_wait_any(struct ntsync
|
||||
struct ntsync_q_entry *entry = &q->entries[i];
|
||||
struct ntsync_obj *obj = entry->obj;
|
||||
|
||||
- spin_lock(&obj->lock);
|
||||
+ all = ntsync_lock_obj(dev, obj);
|
||||
list_add_tail(&entry->node, &obj->any_waiters);
|
||||
- spin_unlock(&obj->lock);
|
||||
+ ntsync_unlock_obj(dev, obj, all);
|
||||
}
|
||||
|
||||
/* check if we are already signaled */
|
||||
@@ -391,9 +614,9 @@ static int ntsync_wait_any(struct ntsync
|
||||
if (atomic_read(&q->signaled) != -1)
|
||||
break;
|
||||
|
||||
- spin_lock(&obj->lock);
|
||||
+ all = ntsync_lock_obj(dev, obj);
|
||||
try_wake_any_obj(obj);
|
||||
- spin_unlock(&obj->lock);
|
||||
+ ntsync_unlock_obj(dev, obj, all);
|
||||
}
|
||||
|
||||
/* sleep */
|
||||
@@ -406,13 +629,94 @@ static int ntsync_wait_any(struct ntsync
|
||||
struct ntsync_q_entry *entry = &q->entries[i];
|
||||
struct ntsync_obj *obj = entry->obj;
|
||||
|
||||
- spin_lock(&obj->lock);
|
||||
+ all = ntsync_lock_obj(dev, obj);
|
||||
list_del(&entry->node);
|
||||
- spin_unlock(&obj->lock);
|
||||
+ ntsync_unlock_obj(dev, obj, all);
|
||||
+
|
||||
+ 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_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 */
|
||||
+
|
||||
+ mutex_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);
|
||||
+
|
||||
+ mutex_unlock(&dev->wait_all_lock);
|
||||
+
|
||||
+ /* sleep */
|
||||
+
|
||||
+ ret = ntsync_schedule(q, &args);
|
||||
+
|
||||
+ /* and finally, unqueue */
|
||||
+
|
||||
+ mutex_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);
|
||||
}
|
||||
|
||||
+ mutex_unlock(&dev->wait_all_lock);
|
||||
+
|
||||
signaled = atomic_read(&q->signaled);
|
||||
if (signaled != -1) {
|
||||
struct ntsync_wait_args __user *user_args = argp;
|
||||
@@ -438,6 +742,8 @@ static int ntsync_char_open(struct inode
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
+ mutex_init(&dev->wait_all_lock);
|
||||
+
|
||||
file->private_data = dev;
|
||||
dev->file = file;
|
||||
return nonseekable_open(inode, file);
|
||||
@@ -461,6 +767,8 @@ static long ntsync_char_ioctl(struct fil
|
||||
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:
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -30,6 +30,7 @@ struct ntsync_wait_args {
|
||||
|
||||
#define NTSYNC_IOC_CREATE_SEM _IOW ('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_RELEASE _IOWR('N', 0x81, __u32)
|
||||
|
222
debian/patches/misc-ntsync7/0005-ntsync-Introduce-NTSYNC_IOC_CREATE_MUTEX.patch
vendored
Normal file
222
debian/patches/misc-ntsync7/0005-ntsync-Introduce-NTSYNC_IOC_CREATE_MUTEX.patch
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
From bcdeaefdc4b60e7845232c201427717df3a83277 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:46 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
|
||||
|
||||
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.
|
||||
|
||||
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 | 74 +++++++++++++++++++++++++++++++++++--
|
||||
include/uapi/linux/ntsync.h | 9 ++++-
|
||||
2 files changed, 79 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
enum ntsync_type {
|
||||
NTSYNC_TYPE_SEM,
|
||||
+ NTSYNC_TYPE_MUTEX,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -55,6 +56,10 @@ struct ntsync_obj {
|
||||
__u32 count;
|
||||
__u32 max;
|
||||
} sem;
|
||||
+ struct {
|
||||
+ __u32 count;
|
||||
+ pid_t owner;
|
||||
+ } mutex;
|
||||
} u;
|
||||
|
||||
/*
|
||||
@@ -92,6 +97,7 @@ struct ntsync_q_entry {
|
||||
|
||||
struct ntsync_q {
|
||||
struct task_struct *task;
|
||||
+ __u32 owner;
|
||||
|
||||
/*
|
||||
* Protected via atomic_try_cmpxchg(). Only the thread that wins the
|
||||
@@ -214,13 +220,17 @@ static void ntsync_unlock_obj(struct nts
|
||||
((lockdep_is_held(&(obj)->dev->wait_all_lock) != LOCK_STATE_NOT_HELD) && \
|
||||
(obj)->dev_locked))
|
||||
|
||||
-static bool is_signaled(struct ntsync_obj *obj)
|
||||
+static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
|
||||
{
|
||||
ntsync_assert_held(obj);
|
||||
|
||||
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);
|
||||
@@ -250,7 +260,7 @@ static void try_wake_all(struct ntsync_d
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
- if (!is_signaled(q->entries[i].obj)) {
|
||||
+ if (!is_signaled(q->entries[i].obj, q->owner)) {
|
||||
can_wake = false;
|
||||
break;
|
||||
}
|
||||
@@ -264,6 +274,10 @@ static void try_wake_all(struct ntsync_d
|
||||
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);
|
||||
@@ -307,6 +321,30 @@ static void try_wake_any_sem(struct ntsy
|
||||
}
|
||||
}
|
||||
|
||||
+static void try_wake_any_mutex(struct ntsync_obj *mutex)
|
||||
+{
|
||||
+ struct ntsync_q_entry *entry;
|
||||
+
|
||||
+ ntsync_assert_held(mutex);
|
||||
+ lockdep_assert(mutex->type == NTSYNC_TYPE_MUTEX);
|
||||
+
|
||||
+ list_for_each_entry(entry, &mutex->any_waiters, node) {
|
||||
+ struct ntsync_q *q = entry->q;
|
||||
+ int signaled = -1;
|
||||
+
|
||||
+ if (mutex->u.mutex.count == UINT_MAX)
|
||||
+ break;
|
||||
+ if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner)
|
||||
+ continue;
|
||||
+
|
||||
+ if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
|
||||
+ 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.
|
||||
@@ -451,6 +489,30 @@ static int ntsync_create_sem(struct ntsy
|
||||
return fd;
|
||||
}
|
||||
|
||||
+static int ntsync_create_mutex(struct ntsync_device *dev, void __user *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;
|
||||
+}
|
||||
+
|
||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
||||
{
|
||||
struct file *file = fget(fd);
|
||||
@@ -520,7 +582,7 @@ static int setup_wait(struct ntsync_devi
|
||||
struct ntsync_q *q;
|
||||
__u32 i, j;
|
||||
|
||||
- if (args->pad[0] || args->pad[1] || args->pad[2] || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
||||
+ if (args->pad[0] || args->pad[1] || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
||||
return -EINVAL;
|
||||
|
||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
||||
@@ -534,6 +596,7 @@ static int setup_wait(struct ntsync_devi
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
q->task = current;
|
||||
+ q->owner = args->owner;
|
||||
atomic_set(&q->signaled, -1);
|
||||
q->all = all;
|
||||
q->count = count;
|
||||
@@ -576,6 +639,9 @@ static void try_wake_any_obj(struct ntsy
|
||||
case NTSYNC_TYPE_SEM:
|
||||
try_wake_any_sem(obj);
|
||||
break;
|
||||
+ case NTSYNC_TYPE_MUTEX:
|
||||
+ try_wake_any_mutex(obj);
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,6 +831,8 @@ static long ntsync_char_ioctl(struct fil
|
||||
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:
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -15,6 +15,11 @@ struct ntsync_sem_args {
|
||||
__u32 max;
|
||||
};
|
||||
|
||||
+struct ntsync_mutex_args {
|
||||
+ __u32 owner;
|
||||
+ __u32 count;
|
||||
+};
|
||||
+
|
||||
#define NTSYNC_WAIT_REALTIME 0x1
|
||||
|
||||
struct ntsync_wait_args {
|
||||
@@ -23,7 +28,8 @@ struct ntsync_wait_args {
|
||||
__u32 count;
|
||||
__u32 index;
|
||||
__u32 flags;
|
||||
- __u32 pad[3];
|
||||
+ __u32 owner;
|
||||
+ __u32 pad[2];
|
||||
};
|
||||
|
||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
||||
@@ -31,6 +37,7 @@ struct ntsync_wait_args {
|
||||
#define NTSYNC_IOC_CREATE_SEM _IOW ('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 _IOW ('N', 0x84, struct ntsync_mutex_args)
|
||||
|
||||
#define NTSYNC_IOC_SEM_RELEASE _IOWR('N', 0x81, __u32)
|
||||
|
95
debian/patches/misc-ntsync7/0006-ntsync-Introduce-NTSYNC_IOC_MUTEX_UNLOCK.patch
vendored
Normal file
95
debian/patches/misc-ntsync7/0006-ntsync-Introduce-NTSYNC_IOC_MUTEX_UNLOCK.patch
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
From e349279c9dc7fc2136a764a16074a90ef3039f38 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:47 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_MUTEX_UNLOCK.
|
||||
|
||||
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 current task, the function
|
||||
instead fails and returns -EPERM.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/ntsync.c | 53 +++++++++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 54 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -396,6 +396,57 @@ static int ntsync_sem_release(struct nts
|
||||
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)
|
||||
+{
|
||||
+ ntsync_assert_held(mutex);
|
||||
+
|
||||
+ 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;
|
||||
+ bool all;
|
||||
+ 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;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, mutex);
|
||||
+
|
||||
+ prev_count = mutex->u.mutex.count;
|
||||
+ ret = unlock_mutex_state(mutex, &args);
|
||||
+ if (!ret) {
|
||||
+ if (all)
|
||||
+ try_wake_all_obj(dev, mutex);
|
||||
+ try_wake_any_mutex(mutex);
|
||||
+ }
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, mutex, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -415,6 +466,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
switch (cmd) {
|
||||
case NTSYNC_IOC_SEM_RELEASE:
|
||||
return ntsync_sem_release(obj, argp);
|
||||
+ case NTSYNC_IOC_MUTEX_UNLOCK:
|
||||
+ return ntsync_mutex_unlock(obj, argp);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -40,5 +40,6 @@ struct ntsync_wait_args {
|
||||
#define NTSYNC_IOC_CREATE_MUTEX _IOW ('N', 0x84, struct ntsync_mutex_args)
|
||||
|
||||
#define NTSYNC_IOC_SEM_RELEASE _IOWR('N', 0x81, __u32)
|
||||
+#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
||||
|
||||
#endif
|
156
debian/patches/misc-ntsync7/0007-ntsync-Introduce-NTSYNC_IOC_MUTEX_KILL.patch
vendored
Normal file
156
debian/patches/misc-ntsync7/0007-ntsync-Introduce-NTSYNC_IOC_MUTEX_KILL.patch
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
From ebb60a10ac3c6b28ba7a46aa67b279d41ad9356d Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:48 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_MUTEX_KILL.
|
||||
|
||||
This does not correspond to any NT syscall. Rather, when a thread dies, it
|
||||
should be called by the NT emulator for each mutex, with the TID of the dying
|
||||
thread.
|
||||
|
||||
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 | 61 +++++++++++++++++++++++++++++++++++--
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 60 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -59,6 +59,7 @@ struct ntsync_obj {
|
||||
struct {
|
||||
__u32 count;
|
||||
pid_t owner;
|
||||
+ bool ownerdead;
|
||||
} mutex;
|
||||
} u;
|
||||
|
||||
@@ -107,6 +108,7 @@ struct ntsync_q {
|
||||
atomic_t signaled;
|
||||
|
||||
bool all;
|
||||
+ bool ownerdead;
|
||||
__u32 count;
|
||||
struct ntsync_q_entry entries[];
|
||||
};
|
||||
@@ -275,6 +277,9 @@ static void try_wake_all(struct ntsync_d
|
||||
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;
|
||||
@@ -338,6 +343,9 @@ static void try_wake_any_mutex(struct nt
|
||||
continue;
|
||||
|
||||
if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
|
||||
+ 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);
|
||||
@@ -447,6 +455,52 @@ static int ntsync_mutex_unlock(struct nt
|
||||
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)
|
||||
+{
|
||||
+ ntsync_assert_held(mutex);
|
||||
+
|
||||
+ 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;
|
||||
+ bool all;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (get_user(owner, (__u32 __user *)argp))
|
||||
+ return -EFAULT;
|
||||
+ if (!owner)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, mutex);
|
||||
+
|
||||
+ ret = kill_mutex_state(mutex, owner);
|
||||
+ if (!ret) {
|
||||
+ if (all)
|
||||
+ try_wake_all_obj(dev, mutex);
|
||||
+ try_wake_any_mutex(mutex);
|
||||
+ }
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, mutex, all);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int ntsync_obj_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct ntsync_obj *obj = file->private_data;
|
||||
@@ -468,6 +522,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
return ntsync_sem_release(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;
|
||||
}
|
||||
@@ -652,6 +708,7 @@ static int setup_wait(struct ntsync_devi
|
||||
q->owner = args->owner;
|
||||
atomic_set(&q->signaled, -1);
|
||||
q->all = all;
|
||||
+ q->ownerdead = false;
|
||||
q->count = count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -760,7 +817,7 @@ static int ntsync_wait_any(struct ntsync
|
||||
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;
|
||||
@@ -841,7 +898,7 @@ static int ntsync_wait_all(struct ntsync
|
||||
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;
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -41,5 +41,6 @@ struct ntsync_wait_args {
|
||||
|
||||
#define NTSYNC_IOC_SEM_RELEASE _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
|
162
debian/patches/misc-ntsync7/0008-ntsync-Introduce-NTSYNC_IOC_CREATE_EVENT.patch
vendored
Normal file
162
debian/patches/misc-ntsync7/0008-ntsync-Introduce-NTSYNC_IOC_CREATE_EVENT.patch
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
From f74c8259d49ea4c0e679902da9c7c95ec06ae65c Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:49 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_CREATE_EVENT.
|
||||
|
||||
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 | 59 +++++++++++++++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 6 ++++
|
||||
2 files changed, 65 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -26,6 +26,7 @@
|
||||
enum ntsync_type {
|
||||
NTSYNC_TYPE_SEM,
|
||||
NTSYNC_TYPE_MUTEX,
|
||||
+ NTSYNC_TYPE_EVENT,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -61,6 +62,10 @@ struct ntsync_obj {
|
||||
pid_t owner;
|
||||
bool ownerdead;
|
||||
} mutex;
|
||||
+ struct {
|
||||
+ bool manual;
|
||||
+ bool signaled;
|
||||
+ } event;
|
||||
} u;
|
||||
|
||||
/*
|
||||
@@ -233,6 +238,8 @@ static bool is_signaled(struct ntsync_ob
|
||||
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);
|
||||
@@ -283,6 +290,10 @@ static void try_wake_all(struct ntsync_d
|
||||
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);
|
||||
@@ -353,6 +364,28 @@ static void try_wake_any_mutex(struct nt
|
||||
}
|
||||
}
|
||||
|
||||
+static void try_wake_any_event(struct ntsync_obj *event)
|
||||
+{
|
||||
+ struct ntsync_q_entry *entry;
|
||||
+
|
||||
+ ntsync_assert_held(event);
|
||||
+ lockdep_assert(event->type == NTSYNC_TYPE_EVENT);
|
||||
+
|
||||
+ list_for_each_entry(entry, &event->any_waiters, node) {
|
||||
+ struct ntsync_q *q = entry->q;
|
||||
+ int signaled = -1;
|
||||
+
|
||||
+ if (!event->u.event.signaled)
|
||||
+ break;
|
||||
+
|
||||
+ if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
|
||||
+ 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.
|
||||
@@ -622,6 +655,27 @@ static int ntsync_create_mutex(struct nt
|
||||
return fd;
|
||||
}
|
||||
|
||||
+static int ntsync_create_event(struct ntsync_device *dev, void __user *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;
|
||||
+}
|
||||
+
|
||||
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
|
||||
{
|
||||
struct file *file = fget(fd);
|
||||
@@ -752,6 +806,9 @@ static void try_wake_any_obj(struct ntsy
|
||||
case NTSYNC_TYPE_MUTEX:
|
||||
try_wake_any_mutex(obj);
|
||||
break;
|
||||
+ case NTSYNC_TYPE_EVENT:
|
||||
+ try_wake_any_event(obj);
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,6 +998,8 @@ static long ntsync_char_ioctl(struct fil
|
||||
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:
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -20,6 +20,11 @@ struct ntsync_mutex_args {
|
||||
__u32 count;
|
||||
};
|
||||
|
||||
+struct ntsync_event_args {
|
||||
+ __u32 manual;
|
||||
+ __u32 signaled;
|
||||
+};
|
||||
+
|
||||
#define NTSYNC_WAIT_REALTIME 0x1
|
||||
|
||||
struct ntsync_wait_args {
|
||||
@@ -38,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 _IOW ('N', 0x84, struct ntsync_mutex_args)
|
||||
+#define NTSYNC_IOC_CREATE_EVENT _IOW ('N', 0x87, struct ntsync_event_args)
|
||||
|
||||
#define NTSYNC_IOC_SEM_RELEASE _IOWR('N', 0x81, __u32)
|
||||
#define NTSYNC_IOC_MUTEX_UNLOCK _IOWR('N', 0x85, struct ntsync_mutex_args)
|
67
debian/patches/misc-ntsync7/0009-ntsync-Introduce-NTSYNC_IOC_EVENT_SET.patch
vendored
Normal file
67
debian/patches/misc-ntsync7/0009-ntsync-Introduce-NTSYNC_IOC_EVENT_SET.patch
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
From bf60db9cfeccc8f92636b6dcf2eccd7fcd8d84f3 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:50 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_EVENT_SET.
|
||||
|
||||
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 | 27 +++++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 28 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -534,6 +534,31 @@ static int ntsync_mutex_kill(struct ntsy
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int ntsync_event_set(struct ntsync_obj *event, void __user *argp)
|
||||
+{
|
||||
+ struct ntsync_device *dev = event->dev;
|
||||
+ __u32 prev_state;
|
||||
+ bool all;
|
||||
+
|
||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, event);
|
||||
+
|
||||
+ prev_state = event->u.event.signaled;
|
||||
+ event->u.event.signaled = true;
|
||||
+ if (all)
|
||||
+ try_wake_all_obj(dev, event);
|
||||
+ try_wake_any_event(event);
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, event, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -557,6 +582,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
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;
|
||||
}
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -48,5 +48,6 @@ struct ntsync_wait_args {
|
||||
#define NTSYNC_IOC_SEM_RELEASE _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
|
64
debian/patches/misc-ntsync7/0010-ntsync-Introduce-NTSYNC_IOC_EVENT_RESET.patch
vendored
Normal file
64
debian/patches/misc-ntsync7/0010-ntsync-Introduce-NTSYNC_IOC_EVENT_RESET.patch
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
From f2de3c99a840cac45446515dd268cb9d64f9f892 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:51 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_EVENT_RESET.
|
||||
|
||||
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 | 24 ++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 25 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -559,6 +559,28 @@ static int ntsync_event_set(struct ntsyn
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
|
||||
+{
|
||||
+ struct ntsync_device *dev = event->dev;
|
||||
+ __u32 prev_state;
|
||||
+ bool all;
|
||||
+
|
||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, event);
|
||||
+
|
||||
+ prev_state = event->u.event.signaled;
|
||||
+ event->u.event.signaled = false;
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, event, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -584,6 +606,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
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;
|
||||
}
|
||||
--- 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
|
60
debian/patches/misc-ntsync7/0011-ntsync-Introduce-NTSYNC_IOC_EVENT_PULSE.patch
vendored
Normal file
60
debian/patches/misc-ntsync7/0011-ntsync-Introduce-NTSYNC_IOC_EVENT_PULSE.patch
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
From 50c791dde217f9fdc1785de77fa2ae888d6bdb4e Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:52 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_EVENT_PULSE.
|
||||
|
||||
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 | 8 ++++++--
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -534,7 +534,7 @@ static int ntsync_mutex_kill(struct ntsy
|
||||
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;
|
||||
@@ -550,6 +550,8 @@ static int ntsync_event_set(struct ntsyn
|
||||
if (all)
|
||||
try_wake_all_obj(dev, event);
|
||||
try_wake_any_event(event);
|
||||
+ if (pulse)
|
||||
+ event->u.event.signaled = false;
|
||||
|
||||
ntsync_unlock_obj(dev, event, all);
|
||||
|
||||
@@ -605,9 +607,11 @@ static long ntsync_obj_ioctl(struct file
|
||||
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;
|
||||
}
|
||||
--- 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
|
64
debian/patches/misc-ntsync7/0012-ntsync-Introduce-NTSYNC_IOC_SEM_READ.patch
vendored
Normal file
64
debian/patches/misc-ntsync7/0012-ntsync-Introduce-NTSYNC_IOC_SEM_READ.patch
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
From 248013d9877d47dc5219344268c10b62de1f52f2 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:53 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_SEM_READ.
|
||||
|
||||
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 | 24 ++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 25 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -583,6 +583,28 @@ static int ntsync_event_reset(struct nts
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
|
||||
+{
|
||||
+ struct ntsync_sem_args __user *user_args = argp;
|
||||
+ struct ntsync_device *dev = sem->dev;
|
||||
+ struct ntsync_sem_args args;
|
||||
+ bool all;
|
||||
+
|
||||
+ if (sem->type != NTSYNC_TYPE_SEM)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, sem);
|
||||
+
|
||||
+ args.count = sem->u.sem.count;
|
||||
+ args.max = sem->u.sem.max;
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, sem, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -602,6 +624,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
switch (cmd) {
|
||||
case NTSYNC_IOC_SEM_RELEASE:
|
||||
return ntsync_sem_release(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:
|
||||
--- 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
|
66
debian/patches/misc-ntsync7/0013-ntsync-Introduce-NTSYNC_IOC_MUTEX_READ.patch
vendored
Normal file
66
debian/patches/misc-ntsync7/0013-ntsync-Introduce-NTSYNC_IOC_MUTEX_READ.patch
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
From 8fc7a993fd8bc6b1a09b4b965bee7d16bb2156cc Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:54 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_MUTEX_READ.
|
||||
|
||||
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 | 26 ++++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 27 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -605,6 +605,30 @@ static int ntsync_sem_read(struct ntsync
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int ntsync_mutex_read(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;
|
||||
+ bool all;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, mutex);
|
||||
+
|
||||
+ args.count = mutex->u.mutex.count;
|
||||
+ args.owner = mutex->u.mutex.owner;
|
||||
+ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, mutex, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -630,6 +654,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
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:
|
||||
--- 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
|
64
debian/patches/misc-ntsync7/0014-ntsync-Introduce-NTSYNC_IOC_EVENT_READ.patch
vendored
Normal file
64
debian/patches/misc-ntsync7/0014-ntsync-Introduce-NTSYNC_IOC_EVENT_READ.patch
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
From aed34cc9c28dba5e3735d7c59e1970a32eefc5f4 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:55 -0600
|
||||
Subject: ntsync: Introduce NTSYNC_IOC_EVENT_READ.
|
||||
|
||||
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 | 24 ++++++++++++++++++++++++
|
||||
include/uapi/linux/ntsync.h | 1 +
|
||||
2 files changed, 25 insertions(+)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -629,6 +629,28 @@ static int ntsync_mutex_read(struct ntsy
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
|
||||
+{
|
||||
+ struct ntsync_event_args __user *user_args = argp;
|
||||
+ struct ntsync_device *dev = event->dev;
|
||||
+ struct ntsync_event_args args;
|
||||
+ bool all;
|
||||
+
|
||||
+ if (event->type != NTSYNC_TYPE_EVENT)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, event);
|
||||
+
|
||||
+ args.manual = event->u.event.manual;
|
||||
+ args.signaled = event->u.event.signaled;
|
||||
+
|
||||
+ ntsync_unlock_obj(dev, event, all);
|
||||
+
|
||||
+ 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;
|
||||
@@ -662,6 +684,8 @@ static long ntsync_obj_ioctl(struct file
|
||||
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;
|
||||
}
|
||||
--- 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
|
187
debian/patches/misc-ntsync7/0015-ntsync-Introduce-alertable-waits.patch
vendored
Normal file
187
debian/patches/misc-ntsync7/0015-ntsync-Introduce-alertable-waits.patch
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
From 361a7fb848ba9cac87855cb68f9ab000ed1027be Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:34:56 -0600
|
||||
Subject: ntsync: Introduce alertable waits.
|
||||
|
||||
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 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 | 70 ++++++++++++++++++++++++++++++++-----
|
||||
include/uapi/linux/ntsync.h | 3 +-
|
||||
2 files changed, 63 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/misc/ntsync.c
|
||||
+++ b/drivers/misc/ntsync.c
|
||||
@@ -869,22 +869,29 @@ static int setup_wait(struct ntsync_devi
|
||||
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->pad[0] || args->pad[1] || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
||||
+ if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
|
||||
return -EINVAL;
|
||||
|
||||
if (args->count > NTSYNC_MAX_WAIT_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
+ total_count = count;
|
||||
+ if (args->alert)
|
||||
+ total_count++;
|
||||
+
|
||||
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;
|
||||
@@ -894,7 +901,7 @@ static int setup_wait(struct ntsync_devi
|
||||
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]);
|
||||
|
||||
@@ -944,10 +951,10 @@ static void try_wake_any_obj(struct ntsy
|
||||
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;
|
||||
bool all;
|
||||
- __u32 i;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
@@ -957,9 +964,13 @@ static int ntsync_wait_any(struct ntsync
|
||||
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;
|
||||
|
||||
@@ -968,9 +979,15 @@ static int ntsync_wait_any(struct ntsync
|
||||
ntsync_unlock_obj(dev, obj, all);
|
||||
}
|
||||
|
||||
- /* 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)
|
||||
@@ -987,7 +1004,7 @@ static int ntsync_wait_any(struct ntsync
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -1047,6 +1064,14 @@ static int ntsync_wait_all(struct ntsync
|
||||
*/
|
||||
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;
|
||||
+
|
||||
+ dev_lock_obj(dev, obj);
|
||||
+ list_add_tail(&entry->node, &obj->any_waiters);
|
||||
+ dev_unlock_obj(dev, obj);
|
||||
+ }
|
||||
|
||||
/* check if we are already signaled */
|
||||
|
||||
@@ -1054,6 +1079,21 @@ static int ntsync_wait_all(struct ntsync
|
||||
|
||||
mutex_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) {
|
||||
+ bool all = ntsync_lock_obj(dev, obj);
|
||||
+ try_wake_any_obj(obj);
|
||||
+ ntsync_unlock_obj(dev, obj, all);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* sleep */
|
||||
|
||||
ret = ntsync_schedule(q, &args);
|
||||
@@ -1079,6 +1119,18 @@ static int ntsync_wait_all(struct ntsync
|
||||
|
||||
mutex_unlock(&dev->wait_all_lock);
|
||||
|
||||
+ if (args.alert) {
|
||||
+ struct ntsync_q_entry *entry = &q->entries[args.count];
|
||||
+ struct ntsync_obj *obj = entry->obj;
|
||||
+ bool all;
|
||||
+
|
||||
+ all = ntsync_lock_obj(dev, obj);
|
||||
+ list_del(&entry->node);
|
||||
+ ntsync_unlock_obj(dev, obj, all);
|
||||
+
|
||||
+ put_obj(obj);
|
||||
+ }
|
||||
+
|
||||
signaled = atomic_read(&q->signaled);
|
||||
if (signaled != -1) {
|
||||
struct ntsync_wait_args __user *user_args = argp;
|
||||
--- a/include/uapi/linux/ntsync.h
|
||||
+++ b/include/uapi/linux/ntsync.h
|
||||
@@ -34,7 +34,8 @@ struct ntsync_wait_args {
|
||||
__u32 index;
|
||||
__u32 flags;
|
||||
__u32 owner;
|
||||
- __u32 pad[2];
|
||||
+ __u32 alert;
|
||||
+ __u32 pad;
|
||||
};
|
||||
|
||||
#define NTSYNC_MAX_WAIT_COUNT 64
|
30
debian/patches/misc-ntsync7/0016-maintainers-Add-an-entry-for-ntsync.patch
vendored
Normal file
30
debian/patches/misc-ntsync7/0016-maintainers-Add-an-entry-for-ntsync.patch
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
From b240b27e5348d38acbc4a12f1dc762dd1845f391 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:35:09 -0600
|
||||
Subject: maintainers: Add an entry for ntsync.
|
||||
|
||||
Add myself as maintainer, supported by CodeWeavers.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
MAINTAINERS | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/MAINTAINERS
|
||||
+++ b/MAINTAINERS
|
||||
@@ -16486,6 +16486,15 @@ T: git https://github.com/Paragon-Softwa
|
||||
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
|
413
debian/patches/misc-ntsync7/0017-docs-ntsync-Add-documentation-for-the-ntsync-uAPI.patch
vendored
Normal file
413
debian/patches/misc-ntsync7/0017-docs-ntsync-Add-documentation-for-the-ntsync-uAPI.patch
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
From 733e310bb840117593a0eb4726fa63b34fea9cc3 Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:35:10 -0600
|
||||
Subject: docs: ntsync: Add documentation for the ntsync uAPI.
|
||||
|
||||
Add an overall explanation of the driver architecture, and complete and precise
|
||||
specification for its intended behaviour.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
Documentation/userspace-api/index.rst | 1 +
|
||||
Documentation/userspace-api/ntsync.rst | 385 +++++++++++++++++++++++++
|
||||
2 files changed, 386 insertions(+)
|
||||
create mode 100644 Documentation/userspace-api/ntsync.rst
|
||||
|
||||
--- a/Documentation/userspace-api/index.rst
|
||||
+++ b/Documentation/userspace-api/index.rst
|
||||
@@ -63,6 +63,7 @@ Everything else
|
||||
vduse
|
||||
futex2
|
||||
perf_ring_buffer
|
||||
+ ntsync
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/userspace-api/ntsync.rst
|
||||
@@ -0,0 +1,385 @@
|
||||
+===================================
|
||||
+NT synchronization primitive driver
|
||||
+===================================
|
||||
+
|
||||
+This page documents the user-space API for the ntsync driver.
|
||||
+
|
||||
+ntsync is a support driver for emulation of NT synchronization
|
||||
+primitives by user-space NT emulators. It exists because implementation
|
||||
+in user-space, using existing tools, cannot match Windows performance
|
||||
+while offering accurate semantics. It is implemented entirely in
|
||||
+software, and does not drive any hardware device.
|
||||
+
|
||||
+This interface is meant as a compatibility tool only, and should not
|
||||
+be used for general synchronization. Instead use generic, versatile
|
||||
+interfaces such as futex(2) and poll(2).
|
||||
+
|
||||
+Synchronization primitives
|
||||
+==========================
|
||||
+
|
||||
+The ntsync driver exposes three types of synchronization primitives:
|
||||
+semaphores, mutexes, and events.
|
||||
+
|
||||
+A semaphore holds a single volatile 32-bit counter, and a static 32-bit
|
||||
+integer denoting the maximum value. It is considered signaled (that is,
|
||||
+can be acquired without contention, or will wake up a waiting thread)
|
||||
+when the counter is nonzero. The counter is decremented by one when a
|
||||
+wait is satisfied. Both the initial and maximum count are established
|
||||
+when the semaphore is created.
|
||||
+
|
||||
+A mutex holds a volatile 32-bit recursion count, and a volatile 32-bit
|
||||
+identifier denoting its owner. A mutex is considered signaled when its
|
||||
+owner is zero (indicating that it is not owned). The recursion count is
|
||||
+incremented when a wait is satisfied, and ownership is set to the given
|
||||
+identifier.
|
||||
+
|
||||
+A mutex also holds an internal flag denoting whether its previous owner
|
||||
+has died; such a mutex is said to be abandoned. Owner death is not
|
||||
+tracked automatically based on thread death, but rather must be
|
||||
+communicated using ``NTSYNC_IOC_MUTEX_KILL``. An abandoned mutex is
|
||||
+inherently considered unowned.
|
||||
+
|
||||
+Except for the "unowned" semantics of zero, the actual value of the
|
||||
+owner identifier is not interpreted by the ntsync driver at all. The
|
||||
+intended use is to store a thread identifier; however, the ntsync
|
||||
+driver does not actually validate that a calling thread provides
|
||||
+consistent or unique identifiers.
|
||||
+
|
||||
+An event is similar to a semaphore with a maximum count of one. It holds
|
||||
+a volatile boolean state denoting whether it is signaled or not. There
|
||||
+are two types of events, auto-reset and manual-reset. An auto-reset
|
||||
+event is designaled when a wait is satisfied; a manual-reset event is
|
||||
+not. The event type is specified when the event is created.
|
||||
+
|
||||
+Unless specified otherwise, all operations on an object are atomic and
|
||||
+totally ordered with respect to other operations on the same object.
|
||||
+
|
||||
+Objects are represented by files. When all file descriptors to an
|
||||
+object are closed, that object is deleted.
|
||||
+
|
||||
+Char device
|
||||
+===========
|
||||
+
|
||||
+The ntsync driver creates a single char device /dev/ntsync. Each file
|
||||
+description opened on the device represents a unique instance intended
|
||||
+to back an individual NT virtual machine. Objects created by one ntsync
|
||||
+instance may only be used with other objects created by the same
|
||||
+instance.
|
||||
+
|
||||
+ioctl reference
|
||||
+===============
|
||||
+
|
||||
+All operations on the device are done through ioctls. There are four
|
||||
+structures used in ioctl calls::
|
||||
+
|
||||
+ struct ntsync_sem_args {
|
||||
+ __u32 count;
|
||||
+ __u32 max;
|
||||
+ };
|
||||
+
|
||||
+ struct ntsync_mutex_args {
|
||||
+ __u32 owner;
|
||||
+ __u32 count;
|
||||
+ };
|
||||
+
|
||||
+ struct ntsync_event_args {
|
||||
+ __u32 signaled;
|
||||
+ __u32 manual;
|
||||
+ };
|
||||
+
|
||||
+ struct ntsync_wait_args {
|
||||
+ __u64 timeout;
|
||||
+ __u64 objs;
|
||||
+ __u32 count;
|
||||
+ __u32 owner;
|
||||
+ __u32 index;
|
||||
+ __u32 alert;
|
||||
+ __u32 flags;
|
||||
+ __u32 pad;
|
||||
+ };
|
||||
+
|
||||
+Depending on the ioctl, members of the structure may be used as input,
|
||||
+output, or not at all.
|
||||
+
|
||||
+The ioctls on the device file are as follows:
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_CREATE_SEM
|
||||
+
|
||||
+ Create a semaphore object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_sem_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``count``
|
||||
+ - Initial count of the semaphore.
|
||||
+ * - ``max``
|
||||
+ - Maximum count of the semaphore.
|
||||
+
|
||||
+ Fails with ``EINVAL`` if ``count`` is greater than ``max``.
|
||||
+ On success, returns a file descriptor the created semaphore.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_CREATE_MUTEX
|
||||
+
|
||||
+ Create a mutex object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_mutex_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``count``
|
||||
+ - Initial recursion count of the mutex.
|
||||
+ * - ``owner``
|
||||
+ - Initial owner of the mutex.
|
||||
+
|
||||
+ If ``owner`` is nonzero and ``count`` is zero, or if ``owner`` is
|
||||
+ zero and ``count`` is nonzero, the function fails with ``EINVAL``.
|
||||
+ On success, returns a file descriptor the created mutex.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_CREATE_EVENT
|
||||
+
|
||||
+ Create an event object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_event_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``signaled``
|
||||
+ - If nonzero, the event is initially signaled, otherwise
|
||||
+ nonsignaled.
|
||||
+ * - ``manual``
|
||||
+ - If nonzero, the event is a manual-reset event, otherwise
|
||||
+ auto-reset.
|
||||
+
|
||||
+ On success, returns a file descriptor the created event.
|
||||
+
|
||||
+The ioctls on the individual objects are as follows:
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_SEM_POST
|
||||
+
|
||||
+ Post to a semaphore object. Takes a pointer to a 32-bit integer,
|
||||
+ which on input holds the count to be added to the semaphore, and on
|
||||
+ output contains its previous count.
|
||||
+
|
||||
+ If adding to the semaphore's current count would raise the latter
|
||||
+ past the semaphore's maximum count, the ioctl fails with
|
||||
+ ``EOVERFLOW`` and the semaphore is not affected. If raising the
|
||||
+ semaphore's count causes it to become signaled, eligible threads
|
||||
+ waiting on this semaphore will be woken and the semaphore's count
|
||||
+ decremented appropriately.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_MUTEX_UNLOCK
|
||||
+
|
||||
+ Release a mutex object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_mutex_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``owner``
|
||||
+ - Specifies the owner trying to release this mutex.
|
||||
+ * - ``count``
|
||||
+ - On output, contains the previous recursion count.
|
||||
+
|
||||
+ If ``owner`` is zero, the ioctl fails with ``EINVAL``. If ``owner``
|
||||
+ is not the current owner of the mutex, the ioctl fails with
|
||||
+ ``EPERM``.
|
||||
+
|
||||
+ The mutex's count will be decremented by one. If decrementing the
|
||||
+ mutex's count causes it to become zero, the mutex is marked as
|
||||
+ unowned and signaled, and eligible threads waiting on it will be
|
||||
+ woken as appropriate.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_SET_EVENT
|
||||
+
|
||||
+ Signal an event object. Takes a pointer to a 32-bit integer, which on
|
||||
+ output contains the previous state of the event.
|
||||
+
|
||||
+ Eligible threads will be woken, and auto-reset events will be
|
||||
+ designaled appropriately.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_RESET_EVENT
|
||||
+
|
||||
+ Designal an event object. Takes a pointer to a 32-bit integer, which
|
||||
+ on output contains the previous state of the event.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_PULSE_EVENT
|
||||
+
|
||||
+ Wake threads waiting on an event object while leaving it in an
|
||||
+ unsignaled state. Takes a pointer to a 32-bit integer, which on
|
||||
+ output contains the previous state of the event.
|
||||
+
|
||||
+ A pulse operation can be thought of as a set followed by a reset,
|
||||
+ performed as a single atomic operation. If two threads are waiting on
|
||||
+ an auto-reset event which is pulsed, only one will be woken. If two
|
||||
+ threads are waiting a manual-reset event which is pulsed, both will
|
||||
+ be woken. However, in both cases, the event will be unsignaled
|
||||
+ afterwards, and a simultaneous read operation will always report the
|
||||
+ event as unsignaled.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_READ_SEM
|
||||
+
|
||||
+ Read the current state of a semaphore object. Takes a pointer to
|
||||
+ struct :c:type:`ntsync_sem_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``count``
|
||||
+ - On output, contains the current count of the semaphore.
|
||||
+ * - ``max``
|
||||
+ - On output, contains the maximum count of the semaphore.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_READ_MUTEX
|
||||
+
|
||||
+ Read the current state of a mutex object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_mutex_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``owner``
|
||||
+ - On output, contains the current owner of the mutex, or zero
|
||||
+ if the mutex is not currently owned.
|
||||
+ * - ``count``
|
||||
+ - On output, contains the current recursion count of the mutex.
|
||||
+
|
||||
+ If the mutex is marked as abandoned, the function fails with
|
||||
+ ``EOWNERDEAD``. In this case, ``count`` and ``owner`` are set to
|
||||
+ zero.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_READ_EVENT
|
||||
+
|
||||
+ Read the current state of an event object. Takes a pointer to struct
|
||||
+ :c:type:`ntsync_event_args`, which is used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``signaled``
|
||||
+ - On output, contains the current state of the event.
|
||||
+ * - ``manual``
|
||||
+ - On output, contains 1 if the event is a manual-reset event,
|
||||
+ and 0 otherwise.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_KILL_OWNER
|
||||
+
|
||||
+ Mark a mutex as unowned and abandoned if it is owned by the given
|
||||
+ owner. Takes an input-only pointer to a 32-bit integer denoting the
|
||||
+ owner. If the owner is zero, the ioctl fails with ``EINVAL``. If the
|
||||
+ owner does not own the mutex, the function fails with ``EPERM``.
|
||||
+
|
||||
+ Eligible threads waiting on the mutex will be woken as appropriate
|
||||
+ (and such waits will fail with ``EOWNERDEAD``, as described below).
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_WAIT_ANY
|
||||
+
|
||||
+ Poll on any of a list of objects, atomically acquiring at most one.
|
||||
+ Takes a pointer to struct :c:type:`ntsync_wait_args`, which is
|
||||
+ used as follows:
|
||||
+
|
||||
+ .. list-table::
|
||||
+
|
||||
+ * - ``timeout``
|
||||
+ - Absolute timeout in nanoseconds. If ``NTSYNC_WAIT_REALTIME``
|
||||
+ is set, the timeout is measured against the REALTIME clock;
|
||||
+ otherwise it is measured against the MONOTONIC clock. If the
|
||||
+ timeout is equal to or earlier than the current time, the
|
||||
+ function returns immediately without sleeping. If ``timeout``
|
||||
+ is U64_MAX, the function will sleep until an object is
|
||||
+ signaled, and will not fail with ``ETIMEDOUT``.
|
||||
+ * - ``objs``
|
||||
+ - Pointer to an array of ``count`` file descriptors
|
||||
+ (specified as an integer so that the structure has the same
|
||||
+ size regardless of architecture). If any object is
|
||||
+ invalid, the function fails with ``EINVAL``.
|
||||
+ * - ``count``
|
||||
+ - Number of objects specified in the ``objs`` array.
|
||||
+ If greater than ``NTSYNC_MAX_WAIT_COUNT``, the function fails
|
||||
+ with ``EINVAL``.
|
||||
+ * - ``owner``
|
||||
+ - Mutex owner identifier. If any object in ``objs`` is a mutex,
|
||||
+ the ioctl will attempt to acquire that mutex on behalf of
|
||||
+ ``owner``. If ``owner`` is zero, the ioctl fails with
|
||||
+ ``EINVAL``.
|
||||
+ * - ``index``
|
||||
+ - On success, contains the index (into ``objs``) of the object
|
||||
+ which was signaled. If ``alert`` was signaled instead,
|
||||
+ this contains ``count``.
|
||||
+ * - ``alert``
|
||||
+ - Optional event object file descriptor. If nonzero, this
|
||||
+ specifies an "alert" event object which, if signaled, will
|
||||
+ terminate the wait. If nonzero, the identifier must point to a
|
||||
+ valid event.
|
||||
+ * - ``flags``
|
||||
+ - Zero or more flags. Currently the only flag is
|
||||
+ ``NTSYNC_WAIT_REALTIME``, which causes the timeout to be
|
||||
+ measured against the REALTIME clock instead of MONOTONIC.
|
||||
+ * - ``pad``
|
||||
+ - Unused, must be set to zero.
|
||||
+
|
||||
+ This function attempts to acquire one of the given objects. If unable
|
||||
+ to do so, it sleeps until an object becomes signaled, subsequently
|
||||
+ acquiring it, or the timeout expires. In the latter case the ioctl
|
||||
+ fails with ``ETIMEDOUT``. The function only acquires one object, even
|
||||
+ if multiple objects are signaled.
|
||||
+
|
||||
+ A semaphore is considered to be signaled if its count is nonzero, and
|
||||
+ is acquired by decrementing its count by one. A mutex is considered
|
||||
+ to be signaled if it is unowned or if its owner matches the ``owner``
|
||||
+ argument, and is acquired by incrementing its recursion count by one
|
||||
+ and setting its owner to the ``owner`` argument. An auto-reset event
|
||||
+ is acquired by designaling it; a manual-reset event is not affected
|
||||
+ by acquisition.
|
||||
+
|
||||
+ Acquisition is atomic and totally ordered with respect to other
|
||||
+ operations on the same object. If two wait operations (with different
|
||||
+ ``owner`` identifiers) are queued on the same mutex, only one is
|
||||
+ signaled. If two wait operations are queued on the same semaphore,
|
||||
+ and a value of one is posted to it, only one is signaled.
|
||||
+
|
||||
+ If an abandoned mutex is acquired, the ioctl fails with
|
||||
+ ``EOWNERDEAD``. Although this is a failure return, the function may
|
||||
+ otherwise be considered successful. The mutex is marked as owned by
|
||||
+ the given owner (with a recursion count of 1) and as no longer
|
||||
+ abandoned, and ``index`` is still set to the index of the mutex.
|
||||
+
|
||||
+ The ``alert`` argument is an "extra" event which can terminate the
|
||||
+ wait, independently of all other objects.
|
||||
+
|
||||
+ It is valid to pass the same object more than once, including by
|
||||
+ passing the same event in the ``objs`` array and in ``alert``. If a
|
||||
+ wakeup occurs due to that object being signaled, ``index`` is set to
|
||||
+ the lowest index corresponding to that object.
|
||||
+
|
||||
+ The function may fail with ``EINTR`` if a signal is received.
|
||||
+
|
||||
+.. c:macro:: NTSYNC_IOC_WAIT_ALL
|
||||
+
|
||||
+ Poll on a list of objects, atomically acquiring all of them. Takes a
|
||||
+ pointer to struct :c:type:`ntsync_wait_args`, which is used
|
||||
+ identically to ``NTSYNC_IOC_WAIT_ANY``, except that ``index`` is
|
||||
+ always filled with zero on success if not woken via alert.
|
||||
+
|
||||
+ This function attempts to simultaneously acquire all of the given
|
||||
+ objects. If unable to do so, it sleeps until all objects become
|
||||
+ simultaneously signaled, subsequently acquiring them, or the timeout
|
||||
+ expires. In the latter case the ioctl fails with ``ETIMEDOUT`` and no
|
||||
+ objects are modified.
|
||||
+
|
||||
+ Objects may become signaled and subsequently designaled (through
|
||||
+ acquisition by other threads) while this thread is sleeping. Only
|
||||
+ once all objects are simultaneously signaled does the ioctl acquire
|
||||
+ them and return. The entire acquisition is atomic and totally ordered
|
||||
+ with respect to other operations on any of the given objects.
|
||||
+
|
||||
+ If an abandoned mutex is acquired, the ioctl fails with
|
||||
+ ``EOWNERDEAD``. Similarly to ``NTSYNC_IOC_WAIT_ANY``, all objects are
|
||||
+ nevertheless marked as acquired. Note that if multiple mutex objects
|
||||
+ are specified, there is no way to know which were marked as
|
||||
+ abandoned.
|
||||
+
|
||||
+ As with "any" waits, the ``alert`` argument is an "extra" event which
|
||||
+ can terminate the wait. Critically, however, an "all" wait will
|
||||
+ succeed if all members in ``objs`` are signaled, *or* if ``alert`` is
|
||||
+ signaled. In the latter case ``index`` will be set to ``count``. As
|
||||
+ with "any" waits, if both conditions are filled, the former takes
|
||||
+ priority, and objects in ``objs`` will be acquired.
|
||||
+
|
||||
+ Unlike ``NTSYNC_IOC_WAIT_ANY``, it is not valid to pass the same
|
||||
+ object more than once, nor is it valid to pass the same object in
|
||||
+ ``objs`` and in ``alert``. If this is attempted, the function fails
|
||||
+ with ``EINVAL``.
|
25
debian/patches/misc-ntsync7/0018-ntsync-No-longer-depend-on-BROKEN.patch
vendored
Normal file
25
debian/patches/misc-ntsync7/0018-ntsync-No-longer-depend-on-BROKEN.patch
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
From 4871bb89577d78a3d55b44e47c3a4f677dbdc89b Mon Sep 17 00:00:00 2001
|
||||
From: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
Date: Fri, 13 Dec 2024 13:35:11 -0600
|
||||
Subject: ntsync: No longer depend on BROKEN.
|
||||
|
||||
f5b335dc025cfee90957efa90dc72fada0d5abb4 ("misc: ntsync: mark driver as "broken"
|
||||
to prevent from building") was committed to avoid the driver being used while
|
||||
only part of its functionality was released. Since the rest of the functionality
|
||||
has now been committed, revert this.
|
||||
|
||||
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||||
---
|
||||
drivers/misc/Kconfig | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/drivers/misc/Kconfig
|
||||
+++ b/drivers/misc/Kconfig
|
||||
@@ -517,7 +517,6 @@ config OPEN_DICE
|
||||
|
||||
config NTSYNC
|
||||
tristate "NT synchronization primitive emulation"
|
||||
- depends on BROKEN
|
||||
help
|
||||
This module provides kernel support for emulation of Windows NT
|
||||
synchronization primitives. It is not a hardware driver.
|
Reference in New Issue
Block a user