diff --git "a/linux-clang/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" "b/linux-clang/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" new file mode 100644 index 0000000..580a595 --- /dev/null +++ "b/linux-clang/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" @@ -0,0 +1,106 @@ +ntsync uses a misc device as the simplest and least intrusive uAPI interface. + +Each file description on the device represents an isolated NT instance, intended +to correspond to a single NT virtual machine. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/Kconfig | 9 ++++++++ + drivers/misc/Makefile | 1 + + drivers/misc/ntsync.c | 53 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 63 insertions(+) + create mode 100644 drivers/misc/ntsync.c + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 4fb291f0bf7c..bdd8a71bd853 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -504,6 +504,15 @@ config OPEN_DICE + measured boot flow. Userspace can use CDIs for remote attestation + and sealing. + ++config NTSYNC ++ tristate "NT synchronization primitive emulation" ++ help ++ This module provides kernel support for emulation of Windows NT ++ synchronization primitives. It is not a hardware driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ntsync. ++ + If unsure, say N. + + config VCPU_STALL_DETECTOR +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index ea6ea5bbbc9c..153a3f4837e8 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/ + obj-$(CONFIG_UACCE) += uacce/ + obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o + obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o ++obj-$(CONFIG_NTSYNC) += ntsync.o + obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o + obj-$(CONFIG_OPEN_DICE) += open-dice.o + obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +new file mode 100644 +index 000000000000..9424c6210e51 +--- /dev/null ++++ b/drivers/misc/ntsync.c +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * ntsync.c - Kernel driver for NT synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Elizabeth Figura ++ */ ++ ++#include ++#include ++#include ++ ++#define NTSYNC_NAME "ntsync" ++ ++static int ntsync_char_open(struct inode *inode, struct file *file) ++{ ++ return nonseekable_open(inode, file); ++} ++ ++static int ntsync_char_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static long ntsync_char_ioctl(struct file *file, unsigned int cmd, ++ unsigned long parm) ++{ ++ switch (cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++static const struct file_operations ntsync_fops = { ++ .owner = THIS_MODULE, ++ .open = ntsync_char_open, ++ .release = ntsync_char_release, ++ .unlocked_ioctl = ntsync_char_ioctl, ++ .compat_ioctl = ntsync_char_ioctl, ++ .llseek = no_llseek, ++}; ++ ++static struct miscdevice ntsync_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = NTSYNC_NAME, ++ .fops = &ntsync_fops, ++}; ++ ++module_misc_device(ntsync_misc); ++ ++MODULE_AUTHOR("Elizabeth Figura"); ++MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("devname:" NTSYNC_NAME); +-- +2.43.0 diff --git "a/linux-clang/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" "b/linux-clang/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" new file mode 100644 index 0000000..0e03db0 --- /dev/null +++ "b/linux-clang/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" @@ -0,0 +1,68 @@ +Signed-off-by: Elizabeth Figura +--- + Documentation/admin-guide/devices.txt | 3 ++- + Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++ + drivers/misc/ntsync.c | 3 ++- + include/linux/miscdevice.h | 1 + + 4 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt +index 94c98be1329a..041404397ee5 100644 +--- a/Documentation/admin-guide/devices.txt ++++ b/Documentation/admin-guide/devices.txt +@@ -376,8 +376,9 @@ + 240 = /dev/userio Serio driver testing device + 241 = /dev/vhost-vsock Host kernel driver for virtio vsock + 242 = /dev/rfkill Turning off radio transmissions (rfkill) ++ 243 = /dev/ntsync NT synchronization primitive device + +- 243-254 Reserved for local use ++ 244-254 Reserved for local use + 255 Reserved for MISC_DYNAMIC_MINOR + + 11 char Raw keyboard device (Linux/SPARC only) +diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst +index 457e16f06e04..a1326a5bc2e0 100644 +--- a/Documentation/userspace-api/ioctl/ioctl-number.rst ++++ b/Documentation/userspace-api/ioctl/ioctl-number.rst +@@ -378,6 +378,8 @@ Code Seq# Include File Comments + + 0xF6 all LTTng Linux Trace Toolkit Next Generation + ++0xF7 00-1F uapi/linux/ntsync.h NT synchronization primitives ++ + 0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver + + 0xFD all linux/dm-ioctl.h +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 9424c6210e51..84b498e2b2d5 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -40,7 +40,7 @@ static const struct file_operations ntsync_fops = { + }; + + static struct miscdevice ntsync_misc = { +- .minor = MISC_DYNAMIC_MINOR, ++ .minor = NTSYNC_MINOR, + .name = NTSYNC_NAME, + .fops = &ntsync_fops, + }; +@@ -51,3 +51,4 @@ MODULE_AUTHOR("Elizabeth Figura"); + MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS("devname:" NTSYNC_NAME); ++MODULE_ALIAS_MISCDEV(NTSYNC_MINOR); +diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h +index c0fea6ca5076..fe5d9366fdf7 100644 +--- a/include/linux/miscdevice.h ++++ b/include/linux/miscdevice.h +@@ -71,6 +71,7 @@ + #define USERIO_MINOR 240 + #define VHOST_VSOCK_MINOR 241 + #define RFKILL_MINOR 242 ++#define NTSYNC_MINOR 243 + #define MISC_DYNAMIC_MINOR 255 + + struct device; +-- +2.43.0 diff --git "a/linux-clang/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" "b/linux-clang/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" new file mode 100644 index 0000000..c233e43 --- /dev/null +++ "b/linux-clang/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" @@ -0,0 +1,191 @@ +These correspond to the NT syscalls NtCreateSemaphore() and NtClose(). +Unlike those functions, however, these ioctls do not handle object names, or +lookup of existing objects, or handle reference counting, but simply create the +underlying primitive. The user space emulator is expected to implement those +functions if they are required. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 117 ++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 25 ++++++++ + 2 files changed, 142 insertions(+) + create mode 100644 include/uapi/linux/ntsync.h + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 84b498e2b2d5..3287b94be351 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -8,23 +8,140 @@ + #include + #include + #include ++#include ++#include ++#include + + #define NTSYNC_NAME "ntsync" + ++enum ntsync_type { ++ NTSYNC_TYPE_SEM, ++}; ++ ++struct ntsync_obj { ++ struct rcu_head rhead; ++ struct kref refcount; ++ ++ enum ntsync_type type; ++ ++ union { ++ struct { ++ __u32 count; ++ __u32 max; ++ } sem; ++ } u; ++}; ++ ++struct ntsync_device { ++ struct xarray objects; ++}; ++ ++static void destroy_obj(struct kref *ref) ++{ ++ struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount); ++ ++ kfree_rcu(obj, rhead); ++} ++ ++static void put_obj(struct ntsync_obj *obj) ++{ ++ kref_put(&obj->refcount, destroy_obj); ++} ++ + static int ntsync_char_open(struct inode *inode, struct file *file) + { ++ struct ntsync_device *dev; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ xa_init_flags(&dev->objects, XA_FLAGS_ALLOC); ++ ++ file->private_data = dev; + return nonseekable_open(inode, file); + } + + static int ntsync_char_release(struct inode *inode, struct file *file) + { ++ struct ntsync_device *dev = file->private_data; ++ struct ntsync_obj *obj; ++ unsigned long id; ++ ++ xa_for_each(&dev->objects, id, obj) ++ put_obj(obj); ++ ++ xa_destroy(&dev->objects); ++ ++ kfree(dev); ++ ++ return 0; ++} ++ ++static void init_obj(struct ntsync_obj *obj) ++{ ++ kref_init(&obj->refcount); ++} ++ ++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; ++ __u32 id; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ if (args.count > args.max) ++ return -EINVAL; ++ ++ sem = kzalloc(sizeof(*sem), GFP_KERNEL); ++ if (!sem) ++ return -ENOMEM; ++ ++ init_obj(sem); ++ sem->type = NTSYNC_TYPE_SEM; ++ sem->u.sem.count = args.count; ++ sem->u.sem.max = args.max; ++ ++ ret = xa_alloc(&dev->objects, &id, sem, xa_limit_32b, GFP_KERNEL); ++ if (ret < 0) { ++ kfree(sem); ++ return ret; ++ } ++ ++ return put_user(id, &user_args->sem); ++} ++ ++static int ntsync_delete(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_obj *obj; ++ __u32 id; ++ ++ if (get_user(id, (__u32 __user *)argp)) ++ return -EFAULT; ++ ++ obj = xa_erase(&dev->objects, id); ++ if (!obj) ++ return -EINVAL; ++ ++ put_obj(obj); + return 0; + } + + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { ++ struct ntsync_device *dev = file->private_data; ++ void __user *argp = (void __user *)parm; ++ + switch (cmd) { ++ case NTSYNC_IOC_CREATE_SEM: ++ return ntsync_create_sem(dev, argp); ++ case NTSYNC_IOC_DELETE: ++ return ntsync_delete(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +new file mode 100644 +index 000000000000..d97afc138dcc +--- /dev/null ++++ b/include/uapi/linux/ntsync.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Kernel support for NT synchronization primitive emulation ++ * ++ * Copyright (C) 2021-2022 Elizabeth Figura ++ */ ++ ++#ifndef __LINUX_NTSYNC_H ++#define __LINUX_NTSYNC_H ++ ++#include ++ ++struct ntsync_sem_args { ++ __u32 sem; ++ __u32 count; ++ __u32 max; ++}; ++ ++#define NTSYNC_IOC_BASE 0xf7 ++ ++#define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ ++ struct ntsync_sem_args) ++#define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) ++ ++#endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch b/linux-clang/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch new file mode 100644 index 0000000..f5f30ce --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch @@ -0,0 +1,147 @@ +This corresponds to the NT syscall NtReleaseSemaphore(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 76 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 2 + + 2 files changed, 78 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 3287b94be351..d1c91c2a4f1a 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -21,9 +21,11 @@ enum ntsync_type { + struct ntsync_obj { + struct rcu_head rhead; + struct kref refcount; ++ spinlock_t lock; + + enum ntsync_type type; + ++ /* The following fields are protected by the object lock. */ + union { + struct { + __u32 count; +@@ -36,6 +38,19 @@ struct ntsync_device { + struct xarray objects; + }; + ++static struct ntsync_obj *get_obj(struct ntsync_device *dev, __u32 id) ++{ ++ struct ntsync_obj *obj; ++ ++ rcu_read_lock(); ++ obj = xa_load(&dev->objects, id); ++ if (obj && !kref_get_unless_zero(&obj->refcount)) ++ obj = NULL; ++ rcu_read_unlock(); ++ ++ return obj; ++} ++ + static void destroy_obj(struct kref *ref) + { + struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount); +@@ -48,6 +63,18 @@ static void put_obj(struct ntsync_obj *obj) + kref_put(&obj->refcount, destroy_obj); + } + ++static struct ntsync_obj *get_obj_typed(struct ntsync_device *dev, __u32 id, ++ enum ntsync_type type) ++{ ++ struct ntsync_obj *obj = get_obj(dev, id); ++ ++ if (obj && obj->type != type) { ++ put_obj(obj); ++ return NULL; ++ } ++ return obj; ++} ++ + static int ntsync_char_open(struct inode *inode, struct file *file) + { + struct ntsync_device *dev; +@@ -81,6 +108,7 @@ static int ntsync_char_release(struct inode *inode, struct file *file) + static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); ++ spin_lock_init(&obj->lock); + } + + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) +@@ -131,6 +159,52 @@ static int ntsync_delete(struct ntsync_device *dev, void __user *argp) + return 0; + } + ++/* ++ * Actually change the semaphore state, returning -EOVERFLOW if it is made ++ * invalid. ++ */ ++static int put_sem_state(struct ntsync_obj *sem, __u32 count) ++{ ++ lockdep_assert_held(&sem->lock); ++ ++ if (sem->u.sem.count + count < sem->u.sem.count || ++ sem->u.sem.count + count > sem->u.sem.max) ++ return -EOVERFLOW; ++ ++ sem->u.sem.count += count; ++ return 0; ++} ++ ++static int ntsync_put_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; ++ __u32 prev_count; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ sem = get_obj_typed(dev, args.sem, NTSYNC_TYPE_SEM); ++ if (!sem) ++ return -EINVAL; ++ ++ spin_lock(&sem->lock); ++ ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ ++ spin_unlock(&sem->lock); ++ ++ put_obj(sem); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -142,6 +216,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_PUT_SEM: ++ return ntsync_put_sem(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index d97afc138dcc..8c610d65f8ef 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -21,5 +21,7 @@ struct ntsync_sem_args { + #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ + struct ntsync_sem_args) + #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) ++#define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \ ++ struct ntsync_sem_args) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch b/linux-clang/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch new file mode 100644 index 0000000..8aa8f46 --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch @@ -0,0 +1,318 @@ +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. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 229 ++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 13 ++ + 2 files changed, 242 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index d1c91c2a4f1a..2e8d3c2d51a4 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -23,6 +23,8 @@ struct ntsync_obj { + struct kref refcount; + spinlock_t lock; + ++ struct list_head any_waiters; ++ + enum ntsync_type type; + + /* The following fields are protected by the object lock. */ +@@ -34,6 +36,28 @@ struct ntsync_obj { + } u; + }; + ++struct ntsync_q_entry { ++ struct list_head node; ++ struct ntsync_q *q; ++ struct ntsync_obj *obj; ++ __u32 index; ++}; ++ ++struct ntsync_q { ++ struct task_struct *task; ++ __u32 owner; ++ ++ /* ++ * Protected via atomic_cmpxchg(). Only the thread that wins the ++ * compare-and-swap may actually change object states and wake this ++ * task. ++ */ ++ atomic_t signaled; ++ ++ __u32 count; ++ struct ntsync_q_entry entries[]; ++}; ++ + struct ntsync_device { + struct xarray objects; + }; +@@ -109,6 +133,26 @@ static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); + spin_lock_init(&obj->lock); ++ INIT_LIST_HEAD(&obj->any_waiters); ++} ++ ++static void try_wake_any_sem(struct ntsync_obj *sem) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&sem->lock); ++ ++ list_for_each_entry(entry, &sem->any_waiters, node) { ++ struct ntsync_q *q = entry->q; ++ ++ if (!sem->u.sem.count) ++ break; ++ ++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ sem->u.sem.count--; ++ wake_up_process(q->task); ++ } ++ } + } + + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) +@@ -194,6 +238,8 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + + prev_count = sem->u.sem.count; + ret = put_sem_state(sem, args.count); ++ if (!ret) ++ try_wake_any_sem(sem); + + spin_unlock(&sem->lock); + +@@ -205,6 +251,187 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) ++{ ++ int ret = 0; ++ ++ do { ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (atomic_read(&q->signaled) != -1) { ++ ret = 0; ++ break; ++ } ++ ret = schedule_hrtimeout(timeout, HRTIMER_MODE_ABS); ++ } while (ret < 0); ++ __set_current_state(TASK_RUNNING); ++ ++ return ret; ++} ++ ++/* ++ * Allocate and initialize the ntsync_q structure, but do not queue us yet. ++ * Also, calculate the relative timeout. ++ */ ++static int setup_wait(struct ntsync_device *dev, ++ const struct ntsync_wait_args *args, ++ ktime_t *ret_timeout, struct ntsync_q **ret_q) ++{ ++ const __u32 count = args->count; ++ struct ntsync_q *q; ++ ktime_t timeout = 0; ++ __u32 *ids; ++ __u32 i, j; ++ ++ if (!args->owner || args->pad) ++ return -EINVAL; ++ ++ if (args->count > NTSYNC_MAX_WAIT_COUNT) ++ return -EINVAL; ++ ++ if (args->timeout) { ++ struct timespec64 to; ++ ++ if (get_timespec64(&to, u64_to_user_ptr(args->timeout))) ++ return -EFAULT; ++ if (!timespec64_valid(&to)) ++ return -EINVAL; ++ ++ timeout = timespec64_to_ns(&to); ++ } ++ ++ ids = kmalloc_array(count, sizeof(*ids), GFP_KERNEL); ++ if (!ids) ++ return -ENOMEM; ++ if (copy_from_user(ids, u64_to_user_ptr(args->objs), ++ array_size(count, sizeof(*ids)))) { ++ kfree(ids); ++ return -EFAULT; ++ } ++ ++ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL); ++ if (!q) { ++ kfree(ids); ++ return -ENOMEM; ++ } ++ q->task = current; ++ q->owner = args->owner; ++ atomic_set(&q->signaled, -1); ++ q->count = count; ++ ++ for (i = 0; i < count; i++) { ++ struct ntsync_q_entry *entry = &q->entries[i]; ++ struct ntsync_obj *obj = get_obj(dev, ids[i]); ++ ++ if (!obj) ++ goto err; ++ ++ entry->obj = obj; ++ entry->q = q; ++ entry->index = i; ++ } ++ ++ kfree(ids); ++ ++ *ret_q = q; ++ *ret_timeout = timeout; ++ return 0; ++ ++err: ++ for (j = 0; j < i; j++) ++ put_obj(q->entries[j].obj); ++ kfree(ids); ++ kfree(q); ++ return -EINVAL; ++} ++ ++static void try_wake_any_obj(struct 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; ++ ktime_t timeout; ++ int signaled; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ ret = setup_wait(dev, &args, &timeout, &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.timeout ? &timeout : NULL); ++ ++ /* 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 long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -218,6 +445,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_delete(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); ++ case NTSYNC_IOC_WAIT_ANY: ++ return ntsync_wait_any(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 8c610d65f8ef..10f07da7864e 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -16,6 +16,17 @@ struct ntsync_sem_args { + __u32 max; + }; + ++struct ntsync_wait_args { ++ __u64 timeout; ++ __u64 objs; ++ __u32 count; ++ __u32 owner; ++ __u32 index; ++ __u32 pad; ++}; ++ ++#define NTSYNC_MAX_WAIT_COUNT 64 ++ + #define NTSYNC_IOC_BASE 0xf7 + + #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ +@@ -23,5 +34,7 @@ struct ntsync_sem_args { + #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) + #define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \ + struct ntsync_sem_args) ++#define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \ ++ struct ntsync_wait_args) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch b/linux-clang/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch new file mode 100644 index 0000000..c63ef03 --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch @@ -0,0 +1,357 @@ +This corresponds to part of the functionality of the NT syscall +NtWaitForMultipleObjects(). Specifically, it implements the behaviour where +the third argument (wait_any) is FALSE, and it does not yet handle alertable +waits. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 241 ++++++++++++++++++++++++++++++++++-- + include/uapi/linux/ntsync.h | 2 + + 2 files changed, 235 insertions(+), 8 deletions(-) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 2e8d3c2d51a4..2685363fae9e 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -23,7 +23,34 @@ struct ntsync_obj { + struct kref refcount; + spinlock_t lock; + ++ /* ++ * any_waiters is protected by the object lock, but all_waiters is ++ * protected by the device wait_all_lock. ++ */ + struct list_head any_waiters; ++ struct list_head all_waiters; ++ ++ /* ++ * Hint describing how many tasks are queued on this object in a ++ * wait-all operation. ++ * ++ * Any time we do a wake, we may need to wake "all" waiters as well as ++ * "any" waiters. In order to atomically wake "all" waiters, we must ++ * lock all of the objects, and that means grabbing the wait_all_lock ++ * below (and, due to lock ordering rules, before locking this object). ++ * However, wait-all is a rare operation, and grabbing the wait-all ++ * lock for every wake would create unnecessary contention. Therefore we ++ * first check whether all_hint is zero, and, if it is, we skip trying ++ * to wake "all" waiters. ++ * ++ * This hint isn't protected by any lock. It might change during the ++ * course of a wake, but there's no meaningful race there; it's only a ++ * hint. ++ * ++ * Since wait requests must originate from user-space threads, we're ++ * limited here by PID_MAX_LIMIT, so there's no risk of saturation. ++ */ ++ atomic_t all_hint; + + enum ntsync_type type; + +@@ -54,11 +81,25 @@ struct ntsync_q { + */ + atomic_t signaled; + ++ bool all; + __u32 count; + struct ntsync_q_entry entries[]; + }; + + struct ntsync_device { ++ /* ++ * Wait-all operations must atomically grab all objects, and be totally ++ * ordered with respect to each other and wait-any operations. If one ++ * thread is trying to acquire several objects, another thread cannot ++ * touch the object at the same time. ++ * ++ * We achieve this by grabbing multiple object locks at the same time. ++ * However, this creates a lock ordering problem. To solve that problem, ++ * wait_all_lock is taken first whenever multiple objects must be locked ++ * at the same time. ++ */ ++ spinlock_t wait_all_lock; ++ + struct xarray objects; + }; + +@@ -107,6 +148,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file) + if (!dev) + return -ENOMEM; + ++ spin_lock_init(&dev->wait_all_lock); ++ + xa_init_flags(&dev->objects, XA_FLAGS_ALLOC); + + file->private_data = dev; +@@ -132,8 +175,81 @@ static int ntsync_char_release(struct inode *inode, struct file *file) + static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); ++ atomic_set(&obj->all_hint, 0); + spin_lock_init(&obj->lock); + INIT_LIST_HEAD(&obj->any_waiters); ++ INIT_LIST_HEAD(&obj->all_waiters); ++} ++ ++static bool is_signaled(struct ntsync_obj *obj, __u32 owner) ++{ ++ lockdep_assert_held(&obj->lock); ++ ++ switch (obj->type) { ++ case NTSYNC_TYPE_SEM: ++ return !!obj->u.sem.count; ++ } ++ ++ WARN(1, "bad object type %#x\n", obj->type); ++ return false; ++} ++ ++/* ++ * "locked_obj" is an optional pointer to an object which is already locked and ++ * should not be locked again. This is necessary so that changing an object's ++ * state and waking it can be a single atomic operation. ++ */ ++static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, ++ struct ntsync_obj *locked_obj) ++{ ++ __u32 count = q->count; ++ bool can_wake = true; ++ __u32 i; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ if (locked_obj) ++ lockdep_assert_held(&locked_obj->lock); ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_lock_nest_lock(&q->entries[i].obj->lock, &dev->wait_all_lock); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (!is_signaled(q->entries[i].obj, q->owner)) { ++ can_wake = false; ++ break; ++ } ++ } ++ ++ if (can_wake && atomic_cmpxchg(&q->signaled, -1, 0) == -1) { ++ for (i = 0; i < count; i++) { ++ struct ntsync_obj *obj = q->entries[i].obj; ++ ++ switch (obj->type) { ++ case NTSYNC_TYPE_SEM: ++ obj->u.sem.count--; ++ break; ++ } ++ } ++ wake_up_process(q->task); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_unlock(&q->entries[i].obj->lock); ++ } ++} ++ ++static void try_wake_all_obj(struct ntsync_device *dev, struct ntsync_obj *obj) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ lockdep_assert_held(&obj->lock); ++ ++ list_for_each_entry(entry, &obj->all_waiters, node) ++ try_wake_all(dev, entry->q, obj); + } + + static void try_wake_any_sem(struct ntsync_obj *sem) +@@ -234,14 +350,29 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + if (!sem) + return -EINVAL; + +- spin_lock(&sem->lock); ++ if (atomic_read(&sem->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock); + +- prev_count = sem->u.sem.count; +- ret = put_sem_state(sem, args.count); +- if (!ret) +- try_wake_any_sem(sem); ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ if (!ret) { ++ try_wake_all_obj(dev, sem); ++ try_wake_any_sem(sem); ++ } + +- spin_unlock(&sem->lock); ++ spin_unlock(&sem->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&sem->lock); ++ ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ if (!ret) ++ try_wake_any_sem(sem); ++ ++ spin_unlock(&sem->lock); ++ } + + put_obj(sem); + +@@ -278,7 +409,7 @@ static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + * Also, calculate the relative timeout. + */ + static int setup_wait(struct ntsync_device *dev, +- const struct ntsync_wait_args *args, ++ const struct ntsync_wait_args *args, bool all, + ktime_t *ret_timeout, struct ntsync_q **ret_q) + { + const __u32 count = args->count; +@@ -321,6 +452,7 @@ static int setup_wait(struct ntsync_device *dev, + q->task = current; + q->owner = args->owner; + atomic_set(&q->signaled, -1); ++ q->all = all; + q->count = count; + + for (i = 0; i < count; i++) { +@@ -330,6 +462,16 @@ static int setup_wait(struct ntsync_device *dev, + if (!obj) + goto err; + ++ if (all) { ++ /* Check that the objects are all distinct. */ ++ for (j = 0; j < i; j++) { ++ if (obj == q->entries[j].obj) { ++ put_obj(obj); ++ goto err; ++ } ++ } ++ } ++ + entry->obj = obj; + entry->q = q; + entry->index = i; +@@ -370,7 +512,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + +- ret = setup_wait(dev, &args, &timeout, &q); ++ ret = setup_wait(dev, &args, false, &timeout, &q); + if (ret < 0) + return ret; + +@@ -432,6 +574,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_wait_args args; ++ struct ntsync_q *q; ++ ktime_t timeout; ++ int signaled; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ ret = setup_wait(dev, &args, true, &timeout, &q); ++ if (ret < 0) ++ return ret; ++ ++ /* queue ourselves */ ++ ++ spin_lock(&dev->wait_all_lock); ++ ++ for (i = 0; i < args.count; i++) { ++ struct 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 it here. ++ */ ++ list_add_tail(&entry->node, &obj->all_waiters); ++ } ++ ++ /* check if we are already signaled */ ++ ++ try_wake_all(dev, q, NULL); ++ ++ spin_unlock(&dev->wait_all_lock); ++ ++ /* sleep */ ++ ++ ret = ntsync_schedule(q, args.timeout ? &timeout : NULL); ++ ++ /* and finally, unqueue */ ++ ++ spin_lock(&dev->wait_all_lock); ++ ++ for (i = 0; i < args.count; i++) { ++ struct ntsync_q_entry *entry = &q->entries[i]; ++ struct ntsync_obj *obj = entry->obj; ++ ++ /* ++ * obj->all_waiters is protected by dev->wait_all_lock rather ++ * than obj->lock, so there is no need to acquire it here. ++ */ ++ list_del(&entry->node); ++ ++ atomic_dec(&obj->all_hint); ++ ++ put_obj(obj); ++ } ++ ++ spin_unlock(&dev->wait_all_lock); ++ ++ signaled = atomic_read(&q->signaled); ++ if (signaled != -1) { ++ struct ntsync_wait_args __user *user_args = argp; ++ ++ /* even if we caught a signal, we need to communicate success */ ++ ret = 0; ++ ++ if (put_user(signaled, &user_args->index)) ++ ret = -EFAULT; ++ } else if (!ret) { ++ ret = -ETIMEDOUT; ++ } ++ ++ kfree(q); ++ return ret; ++} ++ + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -445,6 +668,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_delete(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); ++ case NTSYNC_IOC_WAIT_ALL: ++ return ntsync_wait_all(dev, argp); + case NTSYNC_IOC_WAIT_ANY: + return ntsync_wait_any(dev, argp); + default: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 10f07da7864e..a5bed5a39b21 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -36,5 +36,7 @@ struct ntsync_wait_args { + struct ntsync_sem_args) + #define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \ + struct ntsync_wait_args) ++#define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \ ++ struct ntsync_wait_args) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch b/linux-clang/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch new file mode 100644 index 0000000..5ed169a --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch @@ -0,0 +1,167 @@ +This corresponds to the NT syscall NtCreateMutant(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 8 +++++ + 2 files changed, 80 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 2685363fae9e..d48f2ef41341 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -16,6 +16,7 @@ + + enum ntsync_type { + NTSYNC_TYPE_SEM, ++ NTSYNC_TYPE_MUTEX, + }; + + struct ntsync_obj { +@@ -60,6 +61,10 @@ struct ntsync_obj { + __u32 count; + __u32 max; + } sem; ++ struct { ++ __u32 count; ++ __u32 owner; ++ } mutex; + } u; + }; + +@@ -188,6 +193,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner) + switch (obj->type) { + case NTSYNC_TYPE_SEM: + return !!obj->u.sem.count; ++ case NTSYNC_TYPE_MUTEX: ++ if (obj->u.mutex.owner && obj->u.mutex.owner != owner) ++ return false; ++ return obj->u.mutex.count < UINT_MAX; + } + + WARN(1, "bad object type %#x\n", obj->type); +@@ -230,6 +239,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, + case NTSYNC_TYPE_SEM: + obj->u.sem.count--; + break; ++ case NTSYNC_TYPE_MUTEX: ++ obj->u.mutex.count++; ++ obj->u.mutex.owner = q->owner; ++ break; + } + } + wake_up_process(q->task); +@@ -271,6 +284,28 @@ static void try_wake_any_sem(struct ntsync_obj *sem) + } + } + ++static void try_wake_any_mutex(struct ntsync_obj *mutex) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&mutex->lock); ++ ++ list_for_each_entry(entry, &mutex->any_waiters, node) { ++ struct ntsync_q *q = entry->q; ++ ++ if (mutex->u.mutex.count == UINT_MAX) ++ break; ++ if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner) ++ continue; ++ ++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ mutex->u.mutex.count++; ++ mutex->u.mutex.owner = q->owner; ++ wake_up_process(q->task); ++ } ++ } ++} ++ + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) + { + struct ntsync_sem_args __user *user_args = argp; +@@ -303,6 +338,38 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) + return put_user(id, &user_args->sem); + } + ++static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_mutex_args __user *user_args = argp; ++ struct ntsync_mutex_args args; ++ struct ntsync_obj *mutex; ++ __u32 id; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ if (!args.owner != !args.count) ++ return -EINVAL; ++ ++ mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); ++ if (!mutex) ++ return -ENOMEM; ++ ++ init_obj(mutex); ++ mutex->type = NTSYNC_TYPE_MUTEX; ++ mutex->u.mutex.count = args.count; ++ mutex->u.mutex.owner = args.owner; ++ ++ ret = xa_alloc(&dev->objects, &id, mutex, xa_limit_32b, GFP_KERNEL); ++ if (ret < 0) { ++ kfree(mutex); ++ return ret; ++ } ++ ++ return put_user(id, &user_args->mutex); ++} ++ + static int ntsync_delete(struct ntsync_device *dev, void __user *argp) + { + struct ntsync_obj *obj; +@@ -497,6 +564,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj) + case NTSYNC_TYPE_SEM: + try_wake_any_sem(obj); + break; ++ case NTSYNC_TYPE_MUTEX: ++ try_wake_any_mutex(obj); ++ break; + } + } + +@@ -662,6 +732,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + void __user *argp = (void __user *)parm; + + switch (cmd) { ++ case NTSYNC_IOC_CREATE_MUTEX: ++ return ntsync_create_mutex(dev, argp); + case NTSYNC_IOC_CREATE_SEM: + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index a5bed5a39b21..26d1b3d4847f 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -16,6 +16,12 @@ struct ntsync_sem_args { + __u32 max; + }; + ++struct ntsync_mutex_args { ++ __u32 mutex; ++ __u32 owner; ++ __u32 count; ++}; ++ + struct ntsync_wait_args { + __u64 timeout; + __u64 objs; +@@ -38,5 +44,7 @@ struct ntsync_wait_args { + struct ntsync_wait_args) + #define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \ + struct ntsync_wait_args) ++#define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \ ++ struct ntsync_mutex_args) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch b/linux-clang/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch new file mode 100644 index 0000000..5ad77ae --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch @@ -0,0 +1,107 @@ +This corresponds to the NT syscall NtReleaseMutant(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 67 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 2 ++ + 2 files changed, 69 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index d48f2ef41341..28f43768d1c3 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -449,6 +449,71 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++/* ++ * Actually change the mutex state, returning -EPERM if not the owner. ++ */ ++static int put_mutex_state(struct ntsync_obj *mutex, ++ const struct ntsync_mutex_args *args) ++{ ++ lockdep_assert_held(&mutex->lock); ++ ++ if (mutex->u.mutex.owner != args->owner) ++ return -EPERM; ++ ++ if (!--mutex->u.mutex.count) ++ mutex->u.mutex.owner = 0; ++ return 0; ++} ++ ++static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_mutex_args __user *user_args = argp; ++ struct ntsync_mutex_args args; ++ struct ntsync_obj *mutex; ++ __u32 prev_count; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ if (!args.owner) ++ return -EINVAL; ++ ++ mutex = get_obj_typed(dev, args.mutex, NTSYNC_TYPE_MUTEX); ++ if (!mutex) ++ return -EINVAL; ++ ++ if (atomic_read(&mutex->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) { ++ try_wake_all_obj(dev, mutex); ++ try_wake_any_mutex(mutex); ++ } ++ ++ spin_unlock(&mutex->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&mutex->lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) ++ try_wake_any_mutex(mutex); ++ ++ spin_unlock(&mutex->lock); ++ } ++ ++ put_obj(mutex); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + { + int ret = 0; +@@ -738,6 +803,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_PUT_MUTEX: ++ return ntsync_put_mutex(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); + case NTSYNC_IOC_WAIT_ALL: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 26d1b3d4847f..2e44e7e77776 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -46,5 +46,7 @@ struct ntsync_wait_args { + struct ntsync_wait_args) + #define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \ + struct ntsync_mutex_args) ++#define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \ ++ struct ntsync_mutex_args) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch b/linux-clang/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch new file mode 100644 index 0000000..eedfed0 --- /dev/null +++ b/linux-clang/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch @@ -0,0 +1,170 @@ +This does not correspond to any NT syscall, but rather should be called by the +user-space NT emulator when a thread dies. It is responsible for marking any +mutexes owned by that thread as abandoned. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 80 ++++++++++++++++++++++++++++++++++++- + include/uapi/linux/ntsync.h | 1 + + 2 files changed, 79 insertions(+), 2 deletions(-) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 28f43768d1c3..1173c750c106 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -64,6 +64,7 @@ struct ntsync_obj { + struct { + __u32 count; + __u32 owner; ++ bool ownerdead; + } mutex; + } u; + }; +@@ -87,6 +88,7 @@ struct ntsync_q { + atomic_t signaled; + + bool all; ++ bool ownerdead; + __u32 count; + struct ntsync_q_entry entries[]; + }; +@@ -240,6 +242,9 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, + obj->u.sem.count--; + break; + case NTSYNC_TYPE_MUTEX: ++ if (obj->u.mutex.ownerdead) ++ q->ownerdead = true; ++ obj->u.mutex.ownerdead = false; + obj->u.mutex.count++; + obj->u.mutex.owner = q->owner; + break; +@@ -299,6 +304,9 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex) + continue; + + if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ if (mutex->u.mutex.ownerdead) ++ q->ownerdead = true; ++ mutex->u.mutex.ownerdead = false; + mutex->u.mutex.count++; + mutex->u.mutex.owner = q->owner; + wake_up_process(q->task); +@@ -514,6 +522,71 @@ static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++/* ++ * Actually change the mutex state to mark its owner as dead. ++ */ ++static void put_mutex_ownerdead_state(struct ntsync_obj *mutex) ++{ ++ lockdep_assert_held(&mutex->lock); ++ ++ mutex->u.mutex.ownerdead = true; ++ mutex->u.mutex.owner = 0; ++ mutex->u.mutex.count = 0; ++} ++ ++static int ntsync_kill_owner(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_obj *obj; ++ unsigned long id; ++ __u32 owner; ++ ++ if (get_user(owner, (__u32 __user *)argp)) ++ return -EFAULT; ++ if (!owner) ++ return -EINVAL; ++ ++ rcu_read_lock(); ++ ++ xa_for_each(&dev->objects, id, obj) { ++ if (!kref_get_unless_zero(&obj->refcount)) ++ continue; ++ ++ if (obj->type != NTSYNC_TYPE_MUTEX) { ++ put_obj(obj); ++ continue; ++ } ++ ++ if (atomic_read(&obj->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock); ++ ++ if (obj->u.mutex.owner == owner) { ++ put_mutex_ownerdead_state(obj); ++ try_wake_all_obj(dev, obj); ++ try_wake_any_mutex(obj); ++ } ++ ++ spin_unlock(&obj->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&obj->lock); ++ ++ if (obj->u.mutex.owner == owner) { ++ put_mutex_ownerdead_state(obj); ++ try_wake_any_mutex(obj); ++ } ++ ++ spin_unlock(&obj->lock); ++ } ++ ++ put_obj(obj); ++ } ++ ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ + static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + { + int ret = 0; +@@ -585,6 +658,7 @@ static int setup_wait(struct ntsync_device *dev, + q->owner = args->owner; + atomic_set(&q->signaled, -1); + q->all = all; ++ q->ownerdead = false; + q->count = count; + + for (i = 0; i < count; i++) { +@@ -697,7 +771,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ +- ret = 0; ++ ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; +@@ -778,7 +852,7 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp) + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ +- ret = 0; ++ ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; +@@ -803,6 +877,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_KILL_OWNER: ++ return ntsync_kill_owner(dev, argp); + case NTSYNC_IOC_PUT_MUTEX: + return ntsync_put_mutex(dev, argp); + case NTSYNC_IOC_PUT_SEM: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 2e44e7e77776..fec9a3993322 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -48,5 +48,6 @@ struct ntsync_wait_args { + struct ntsync_mutex_args) + #define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \ + struct ntsync_mutex_args) ++#define NTSYNC_IOC_KILL_OWNER _IOW (NTSYNC_IOC_BASE, 7, __u32) + + #endif +-- +2.43.0 diff --git a/linux-clang/linux6.7-zen/template b/linux-clang/linux6.7-zen/template index 66a83ab..fe25e61 100644 --- a/linux-clang/linux6.7-zen/template +++ b/linux-clang/linux6.7-zen/template @@ -1,6 +1,6 @@ pkgname=linux6.7-zen version=6.7.1 -revision=1 +revision=2 zen=1 wrksrc="linux-${version}-zen${zen}" short_desc="Linux kernel and modules with Zen patches (${version%.*} series) compiled with Clang" diff --git "a/linux-gcc/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" "b/linux-gcc/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" new file mode 100644 index 0000000..580a595 --- /dev/null +++ "b/linux-gcc/linux6.7-zen/patches/0000-[PATCH_RFC_1_9]_ntsync:_Introduce_the_ntsync_driver_and_character\n_device..patch" @@ -0,0 +1,106 @@ +ntsync uses a misc device as the simplest and least intrusive uAPI interface. + +Each file description on the device represents an isolated NT instance, intended +to correspond to a single NT virtual machine. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/Kconfig | 9 ++++++++ + drivers/misc/Makefile | 1 + + drivers/misc/ntsync.c | 53 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 63 insertions(+) + create mode 100644 drivers/misc/ntsync.c + +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 4fb291f0bf7c..bdd8a71bd853 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -504,6 +504,15 @@ config OPEN_DICE + measured boot flow. Userspace can use CDIs for remote attestation + and sealing. + ++config NTSYNC ++ tristate "NT synchronization primitive emulation" ++ help ++ This module provides kernel support for emulation of Windows NT ++ synchronization primitives. It is not a hardware driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ntsync. ++ + If unsure, say N. + + config VCPU_STALL_DETECTOR +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index ea6ea5bbbc9c..153a3f4837e8 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/ + obj-$(CONFIG_UACCE) += uacce/ + obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o + obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o ++obj-$(CONFIG_NTSYNC) += ntsync.o + obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o + obj-$(CONFIG_OPEN_DICE) += open-dice.o + obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +new file mode 100644 +index 000000000000..9424c6210e51 +--- /dev/null ++++ b/drivers/misc/ntsync.c +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * ntsync.c - Kernel driver for NT synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Elizabeth Figura ++ */ ++ ++#include ++#include ++#include ++ ++#define NTSYNC_NAME "ntsync" ++ ++static int ntsync_char_open(struct inode *inode, struct file *file) ++{ ++ return nonseekable_open(inode, file); ++} ++ ++static int ntsync_char_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static long ntsync_char_ioctl(struct file *file, unsigned int cmd, ++ unsigned long parm) ++{ ++ switch (cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++static const struct file_operations ntsync_fops = { ++ .owner = THIS_MODULE, ++ .open = ntsync_char_open, ++ .release = ntsync_char_release, ++ .unlocked_ioctl = ntsync_char_ioctl, ++ .compat_ioctl = ntsync_char_ioctl, ++ .llseek = no_llseek, ++}; ++ ++static struct miscdevice ntsync_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = NTSYNC_NAME, ++ .fops = &ntsync_fops, ++}; ++ ++module_misc_device(ntsync_misc); ++ ++MODULE_AUTHOR("Elizabeth Figura"); ++MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("devname:" NTSYNC_NAME); +-- +2.43.0 diff --git "a/linux-gcc/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" "b/linux-gcc/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" new file mode 100644 index 0000000..0e03db0 --- /dev/null +++ "b/linux-gcc/linux6.7-zen/patches/0001-[PATCH_RFC_2_9]_ntsync:_Reserve_a_minor_device_number_and_ioctl\n_range..patch" @@ -0,0 +1,68 @@ +Signed-off-by: Elizabeth Figura +--- + Documentation/admin-guide/devices.txt | 3 ++- + Documentation/userspace-api/ioctl/ioctl-number.rst | 2 ++ + drivers/misc/ntsync.c | 3 ++- + include/linux/miscdevice.h | 1 + + 4 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt +index 94c98be1329a..041404397ee5 100644 +--- a/Documentation/admin-guide/devices.txt ++++ b/Documentation/admin-guide/devices.txt +@@ -376,8 +376,9 @@ + 240 = /dev/userio Serio driver testing device + 241 = /dev/vhost-vsock Host kernel driver for virtio vsock + 242 = /dev/rfkill Turning off radio transmissions (rfkill) ++ 243 = /dev/ntsync NT synchronization primitive device + +- 243-254 Reserved for local use ++ 244-254 Reserved for local use + 255 Reserved for MISC_DYNAMIC_MINOR + + 11 char Raw keyboard device (Linux/SPARC only) +diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst +index 457e16f06e04..a1326a5bc2e0 100644 +--- a/Documentation/userspace-api/ioctl/ioctl-number.rst ++++ b/Documentation/userspace-api/ioctl/ioctl-number.rst +@@ -378,6 +378,8 @@ Code Seq# Include File Comments + + 0xF6 all LTTng Linux Trace Toolkit Next Generation + ++0xF7 00-1F uapi/linux/ntsync.h NT synchronization primitives ++ + 0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver + + 0xFD all linux/dm-ioctl.h +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 9424c6210e51..84b498e2b2d5 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -40,7 +40,7 @@ static const struct file_operations ntsync_fops = { + }; + + static struct miscdevice ntsync_misc = { +- .minor = MISC_DYNAMIC_MINOR, ++ .minor = NTSYNC_MINOR, + .name = NTSYNC_NAME, + .fops = &ntsync_fops, + }; +@@ -51,3 +51,4 @@ MODULE_AUTHOR("Elizabeth Figura"); + MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS("devname:" NTSYNC_NAME); ++MODULE_ALIAS_MISCDEV(NTSYNC_MINOR); +diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h +index c0fea6ca5076..fe5d9366fdf7 100644 +--- a/include/linux/miscdevice.h ++++ b/include/linux/miscdevice.h +@@ -71,6 +71,7 @@ + #define USERIO_MINOR 240 + #define VHOST_VSOCK_MINOR 241 + #define RFKILL_MINOR 242 ++#define NTSYNC_MINOR 243 + #define MISC_DYNAMIC_MINOR 255 + + struct device; +-- +2.43.0 diff --git "a/linux-gcc/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" "b/linux-gcc/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" new file mode 100644 index 0000000..c233e43 --- /dev/null +++ "b/linux-gcc/linux6.7-zen/patches/0002-[PATCH_RFC_3_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_SEM_and\n_NTSYNC_IOC_DELETE..patch" @@ -0,0 +1,191 @@ +These correspond to the NT syscalls NtCreateSemaphore() and NtClose(). +Unlike those functions, however, these ioctls do not handle object names, or +lookup of existing objects, or handle reference counting, but simply create the +underlying primitive. The user space emulator is expected to implement those +functions if they are required. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 117 ++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 25 ++++++++ + 2 files changed, 142 insertions(+) + create mode 100644 include/uapi/linux/ntsync.h + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 84b498e2b2d5..3287b94be351 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -8,23 +8,140 @@ + #include + #include + #include ++#include ++#include ++#include + + #define NTSYNC_NAME "ntsync" + ++enum ntsync_type { ++ NTSYNC_TYPE_SEM, ++}; ++ ++struct ntsync_obj { ++ struct rcu_head rhead; ++ struct kref refcount; ++ ++ enum ntsync_type type; ++ ++ union { ++ struct { ++ __u32 count; ++ __u32 max; ++ } sem; ++ } u; ++}; ++ ++struct ntsync_device { ++ struct xarray objects; ++}; ++ ++static void destroy_obj(struct kref *ref) ++{ ++ struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount); ++ ++ kfree_rcu(obj, rhead); ++} ++ ++static void put_obj(struct ntsync_obj *obj) ++{ ++ kref_put(&obj->refcount, destroy_obj); ++} ++ + static int ntsync_char_open(struct inode *inode, struct file *file) + { ++ struct ntsync_device *dev; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ xa_init_flags(&dev->objects, XA_FLAGS_ALLOC); ++ ++ file->private_data = dev; + return nonseekable_open(inode, file); + } + + static int ntsync_char_release(struct inode *inode, struct file *file) + { ++ struct ntsync_device *dev = file->private_data; ++ struct ntsync_obj *obj; ++ unsigned long id; ++ ++ xa_for_each(&dev->objects, id, obj) ++ put_obj(obj); ++ ++ xa_destroy(&dev->objects); ++ ++ kfree(dev); ++ ++ return 0; ++} ++ ++static void init_obj(struct ntsync_obj *obj) ++{ ++ kref_init(&obj->refcount); ++} ++ ++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; ++ __u32 id; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ if (args.count > args.max) ++ return -EINVAL; ++ ++ sem = kzalloc(sizeof(*sem), GFP_KERNEL); ++ if (!sem) ++ return -ENOMEM; ++ ++ init_obj(sem); ++ sem->type = NTSYNC_TYPE_SEM; ++ sem->u.sem.count = args.count; ++ sem->u.sem.max = args.max; ++ ++ ret = xa_alloc(&dev->objects, &id, sem, xa_limit_32b, GFP_KERNEL); ++ if (ret < 0) { ++ kfree(sem); ++ return ret; ++ } ++ ++ return put_user(id, &user_args->sem); ++} ++ ++static int ntsync_delete(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_obj *obj; ++ __u32 id; ++ ++ if (get_user(id, (__u32 __user *)argp)) ++ return -EFAULT; ++ ++ obj = xa_erase(&dev->objects, id); ++ if (!obj) ++ return -EINVAL; ++ ++ put_obj(obj); + return 0; + } + + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { ++ struct ntsync_device *dev = file->private_data; ++ void __user *argp = (void __user *)parm; ++ + switch (cmd) { ++ case NTSYNC_IOC_CREATE_SEM: ++ return ntsync_create_sem(dev, argp); ++ case NTSYNC_IOC_DELETE: ++ return ntsync_delete(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +new file mode 100644 +index 000000000000..d97afc138dcc +--- /dev/null ++++ b/include/uapi/linux/ntsync.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Kernel support for NT synchronization primitive emulation ++ * ++ * Copyright (C) 2021-2022 Elizabeth Figura ++ */ ++ ++#ifndef __LINUX_NTSYNC_H ++#define __LINUX_NTSYNC_H ++ ++#include ++ ++struct ntsync_sem_args { ++ __u32 sem; ++ __u32 count; ++ __u32 max; ++}; ++ ++#define NTSYNC_IOC_BASE 0xf7 ++ ++#define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ ++ struct ntsync_sem_args) ++#define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) ++ ++#endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch b/linux-gcc/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch new file mode 100644 index 0000000..f5f30ce --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0003-[PATCH_RFC_4_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_SEM..patch @@ -0,0 +1,147 @@ +This corresponds to the NT syscall NtReleaseSemaphore(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 76 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 2 + + 2 files changed, 78 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 3287b94be351..d1c91c2a4f1a 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -21,9 +21,11 @@ enum ntsync_type { + struct ntsync_obj { + struct rcu_head rhead; + struct kref refcount; ++ spinlock_t lock; + + enum ntsync_type type; + ++ /* The following fields are protected by the object lock. */ + union { + struct { + __u32 count; +@@ -36,6 +38,19 @@ struct ntsync_device { + struct xarray objects; + }; + ++static struct ntsync_obj *get_obj(struct ntsync_device *dev, __u32 id) ++{ ++ struct ntsync_obj *obj; ++ ++ rcu_read_lock(); ++ obj = xa_load(&dev->objects, id); ++ if (obj && !kref_get_unless_zero(&obj->refcount)) ++ obj = NULL; ++ rcu_read_unlock(); ++ ++ return obj; ++} ++ + static void destroy_obj(struct kref *ref) + { + struct ntsync_obj *obj = container_of(ref, struct ntsync_obj, refcount); +@@ -48,6 +63,18 @@ static void put_obj(struct ntsync_obj *obj) + kref_put(&obj->refcount, destroy_obj); + } + ++static struct ntsync_obj *get_obj_typed(struct ntsync_device *dev, __u32 id, ++ enum ntsync_type type) ++{ ++ struct ntsync_obj *obj = get_obj(dev, id); ++ ++ if (obj && obj->type != type) { ++ put_obj(obj); ++ return NULL; ++ } ++ return obj; ++} ++ + static int ntsync_char_open(struct inode *inode, struct file *file) + { + struct ntsync_device *dev; +@@ -81,6 +108,7 @@ static int ntsync_char_release(struct inode *inode, struct file *file) + static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); ++ spin_lock_init(&obj->lock); + } + + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) +@@ -131,6 +159,52 @@ static int ntsync_delete(struct ntsync_device *dev, void __user *argp) + return 0; + } + ++/* ++ * Actually change the semaphore state, returning -EOVERFLOW if it is made ++ * invalid. ++ */ ++static int put_sem_state(struct ntsync_obj *sem, __u32 count) ++{ ++ lockdep_assert_held(&sem->lock); ++ ++ if (sem->u.sem.count + count < sem->u.sem.count || ++ sem->u.sem.count + count > sem->u.sem.max) ++ return -EOVERFLOW; ++ ++ sem->u.sem.count += count; ++ return 0; ++} ++ ++static int ntsync_put_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; ++ __u32 prev_count; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ sem = get_obj_typed(dev, args.sem, NTSYNC_TYPE_SEM); ++ if (!sem) ++ return -EINVAL; ++ ++ spin_lock(&sem->lock); ++ ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ ++ spin_unlock(&sem->lock); ++ ++ put_obj(sem); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -142,6 +216,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_PUT_SEM: ++ return ntsync_put_sem(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index d97afc138dcc..8c610d65f8ef 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -21,5 +21,7 @@ struct ntsync_sem_args { + #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ + struct ntsync_sem_args) + #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) ++#define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \ ++ struct ntsync_sem_args) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch b/linux-gcc/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch new file mode 100644 index 0000000..8aa8f46 --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0004-[PATCH_RFC_5_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ANY..patch @@ -0,0 +1,318 @@ +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. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 229 ++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 13 ++ + 2 files changed, 242 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index d1c91c2a4f1a..2e8d3c2d51a4 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -23,6 +23,8 @@ struct ntsync_obj { + struct kref refcount; + spinlock_t lock; + ++ struct list_head any_waiters; ++ + enum ntsync_type type; + + /* The following fields are protected by the object lock. */ +@@ -34,6 +36,28 @@ struct ntsync_obj { + } u; + }; + ++struct ntsync_q_entry { ++ struct list_head node; ++ struct ntsync_q *q; ++ struct ntsync_obj *obj; ++ __u32 index; ++}; ++ ++struct ntsync_q { ++ struct task_struct *task; ++ __u32 owner; ++ ++ /* ++ * Protected via atomic_cmpxchg(). Only the thread that wins the ++ * compare-and-swap may actually change object states and wake this ++ * task. ++ */ ++ atomic_t signaled; ++ ++ __u32 count; ++ struct ntsync_q_entry entries[]; ++}; ++ + struct ntsync_device { + struct xarray objects; + }; +@@ -109,6 +133,26 @@ static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); + spin_lock_init(&obj->lock); ++ INIT_LIST_HEAD(&obj->any_waiters); ++} ++ ++static void try_wake_any_sem(struct ntsync_obj *sem) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&sem->lock); ++ ++ list_for_each_entry(entry, &sem->any_waiters, node) { ++ struct ntsync_q *q = entry->q; ++ ++ if (!sem->u.sem.count) ++ break; ++ ++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ sem->u.sem.count--; ++ wake_up_process(q->task); ++ } ++ } + } + + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) +@@ -194,6 +238,8 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + + prev_count = sem->u.sem.count; + ret = put_sem_state(sem, args.count); ++ if (!ret) ++ try_wake_any_sem(sem); + + spin_unlock(&sem->lock); + +@@ -205,6 +251,187 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) ++{ ++ int ret = 0; ++ ++ do { ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (atomic_read(&q->signaled) != -1) { ++ ret = 0; ++ break; ++ } ++ ret = schedule_hrtimeout(timeout, HRTIMER_MODE_ABS); ++ } while (ret < 0); ++ __set_current_state(TASK_RUNNING); ++ ++ return ret; ++} ++ ++/* ++ * Allocate and initialize the ntsync_q structure, but do not queue us yet. ++ * Also, calculate the relative timeout. ++ */ ++static int setup_wait(struct ntsync_device *dev, ++ const struct ntsync_wait_args *args, ++ ktime_t *ret_timeout, struct ntsync_q **ret_q) ++{ ++ const __u32 count = args->count; ++ struct ntsync_q *q; ++ ktime_t timeout = 0; ++ __u32 *ids; ++ __u32 i, j; ++ ++ if (!args->owner || args->pad) ++ return -EINVAL; ++ ++ if (args->count > NTSYNC_MAX_WAIT_COUNT) ++ return -EINVAL; ++ ++ if (args->timeout) { ++ struct timespec64 to; ++ ++ if (get_timespec64(&to, u64_to_user_ptr(args->timeout))) ++ return -EFAULT; ++ if (!timespec64_valid(&to)) ++ return -EINVAL; ++ ++ timeout = timespec64_to_ns(&to); ++ } ++ ++ ids = kmalloc_array(count, sizeof(*ids), GFP_KERNEL); ++ if (!ids) ++ return -ENOMEM; ++ if (copy_from_user(ids, u64_to_user_ptr(args->objs), ++ array_size(count, sizeof(*ids)))) { ++ kfree(ids); ++ return -EFAULT; ++ } ++ ++ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL); ++ if (!q) { ++ kfree(ids); ++ return -ENOMEM; ++ } ++ q->task = current; ++ q->owner = args->owner; ++ atomic_set(&q->signaled, -1); ++ q->count = count; ++ ++ for (i = 0; i < count; i++) { ++ struct ntsync_q_entry *entry = &q->entries[i]; ++ struct ntsync_obj *obj = get_obj(dev, ids[i]); ++ ++ if (!obj) ++ goto err; ++ ++ entry->obj = obj; ++ entry->q = q; ++ entry->index = i; ++ } ++ ++ kfree(ids); ++ ++ *ret_q = q; ++ *ret_timeout = timeout; ++ return 0; ++ ++err: ++ for (j = 0; j < i; j++) ++ put_obj(q->entries[j].obj); ++ kfree(ids); ++ kfree(q); ++ return -EINVAL; ++} ++ ++static void try_wake_any_obj(struct 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; ++ ktime_t timeout; ++ int signaled; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ ret = setup_wait(dev, &args, &timeout, &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.timeout ? &timeout : NULL); ++ ++ /* 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 long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -218,6 +445,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_delete(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); ++ case NTSYNC_IOC_WAIT_ANY: ++ return ntsync_wait_any(dev, argp); + default: + return -ENOIOCTLCMD; + } +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 8c610d65f8ef..10f07da7864e 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -16,6 +16,17 @@ struct ntsync_sem_args { + __u32 max; + }; + ++struct ntsync_wait_args { ++ __u64 timeout; ++ __u64 objs; ++ __u32 count; ++ __u32 owner; ++ __u32 index; ++ __u32 pad; ++}; ++ ++#define NTSYNC_MAX_WAIT_COUNT 64 ++ + #define NTSYNC_IOC_BASE 0xf7 + + #define NTSYNC_IOC_CREATE_SEM _IOWR(NTSYNC_IOC_BASE, 0, \ +@@ -23,5 +34,7 @@ struct ntsync_sem_args { + #define NTSYNC_IOC_DELETE _IOW (NTSYNC_IOC_BASE, 1, __u32) + #define NTSYNC_IOC_PUT_SEM _IOWR(NTSYNC_IOC_BASE, 2, \ + struct ntsync_sem_args) ++#define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \ ++ struct ntsync_wait_args) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch b/linux-gcc/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch new file mode 100644 index 0000000..c63ef03 --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0005-[PATCH_RFC_6_9]_ntsync:_Introduce_NTSYNC_IOC_WAIT_ALL..patch @@ -0,0 +1,357 @@ +This corresponds to part of the functionality of the NT syscall +NtWaitForMultipleObjects(). Specifically, it implements the behaviour where +the third argument (wait_any) is FALSE, and it does not yet handle alertable +waits. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 241 ++++++++++++++++++++++++++++++++++-- + include/uapi/linux/ntsync.h | 2 + + 2 files changed, 235 insertions(+), 8 deletions(-) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 2e8d3c2d51a4..2685363fae9e 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -23,7 +23,34 @@ struct ntsync_obj { + struct kref refcount; + spinlock_t lock; + ++ /* ++ * any_waiters is protected by the object lock, but all_waiters is ++ * protected by the device wait_all_lock. ++ */ + struct list_head any_waiters; ++ struct list_head all_waiters; ++ ++ /* ++ * Hint describing how many tasks are queued on this object in a ++ * wait-all operation. ++ * ++ * Any time we do a wake, we may need to wake "all" waiters as well as ++ * "any" waiters. In order to atomically wake "all" waiters, we must ++ * lock all of the objects, and that means grabbing the wait_all_lock ++ * below (and, due to lock ordering rules, before locking this object). ++ * However, wait-all is a rare operation, and grabbing the wait-all ++ * lock for every wake would create unnecessary contention. Therefore we ++ * first check whether all_hint is zero, and, if it is, we skip trying ++ * to wake "all" waiters. ++ * ++ * This hint isn't protected by any lock. It might change during the ++ * course of a wake, but there's no meaningful race there; it's only a ++ * hint. ++ * ++ * Since wait requests must originate from user-space threads, we're ++ * limited here by PID_MAX_LIMIT, so there's no risk of saturation. ++ */ ++ atomic_t all_hint; + + enum ntsync_type type; + +@@ -54,11 +81,25 @@ struct ntsync_q { + */ + atomic_t signaled; + ++ bool all; + __u32 count; + struct ntsync_q_entry entries[]; + }; + + struct ntsync_device { ++ /* ++ * Wait-all operations must atomically grab all objects, and be totally ++ * ordered with respect to each other and wait-any operations. If one ++ * thread is trying to acquire several objects, another thread cannot ++ * touch the object at the same time. ++ * ++ * We achieve this by grabbing multiple object locks at the same time. ++ * However, this creates a lock ordering problem. To solve that problem, ++ * wait_all_lock is taken first whenever multiple objects must be locked ++ * at the same time. ++ */ ++ spinlock_t wait_all_lock; ++ + struct xarray objects; + }; + +@@ -107,6 +148,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file) + if (!dev) + return -ENOMEM; + ++ spin_lock_init(&dev->wait_all_lock); ++ + xa_init_flags(&dev->objects, XA_FLAGS_ALLOC); + + file->private_data = dev; +@@ -132,8 +175,81 @@ static int ntsync_char_release(struct inode *inode, struct file *file) + static void init_obj(struct ntsync_obj *obj) + { + kref_init(&obj->refcount); ++ atomic_set(&obj->all_hint, 0); + spin_lock_init(&obj->lock); + INIT_LIST_HEAD(&obj->any_waiters); ++ INIT_LIST_HEAD(&obj->all_waiters); ++} ++ ++static bool is_signaled(struct ntsync_obj *obj, __u32 owner) ++{ ++ lockdep_assert_held(&obj->lock); ++ ++ switch (obj->type) { ++ case NTSYNC_TYPE_SEM: ++ return !!obj->u.sem.count; ++ } ++ ++ WARN(1, "bad object type %#x\n", obj->type); ++ return false; ++} ++ ++/* ++ * "locked_obj" is an optional pointer to an object which is already locked and ++ * should not be locked again. This is necessary so that changing an object's ++ * state and waking it can be a single atomic operation. ++ */ ++static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, ++ struct ntsync_obj *locked_obj) ++{ ++ __u32 count = q->count; ++ bool can_wake = true; ++ __u32 i; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ if (locked_obj) ++ lockdep_assert_held(&locked_obj->lock); ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_lock_nest_lock(&q->entries[i].obj->lock, &dev->wait_all_lock); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (!is_signaled(q->entries[i].obj, q->owner)) { ++ can_wake = false; ++ break; ++ } ++ } ++ ++ if (can_wake && atomic_cmpxchg(&q->signaled, -1, 0) == -1) { ++ for (i = 0; i < count; i++) { ++ struct ntsync_obj *obj = q->entries[i].obj; ++ ++ switch (obj->type) { ++ case NTSYNC_TYPE_SEM: ++ obj->u.sem.count--; ++ break; ++ } ++ } ++ wake_up_process(q->task); ++ } ++ ++ for (i = 0; i < count; i++) { ++ if (q->entries[i].obj != locked_obj) ++ spin_unlock(&q->entries[i].obj->lock); ++ } ++} ++ ++static void try_wake_all_obj(struct ntsync_device *dev, struct ntsync_obj *obj) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&dev->wait_all_lock); ++ lockdep_assert_held(&obj->lock); ++ ++ list_for_each_entry(entry, &obj->all_waiters, node) ++ try_wake_all(dev, entry->q, obj); + } + + static void try_wake_any_sem(struct ntsync_obj *sem) +@@ -234,14 +350,29 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + if (!sem) + return -EINVAL; + +- spin_lock(&sem->lock); ++ if (atomic_read(&sem->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock); + +- prev_count = sem->u.sem.count; +- ret = put_sem_state(sem, args.count); +- if (!ret) +- try_wake_any_sem(sem); ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ if (!ret) { ++ try_wake_all_obj(dev, sem); ++ try_wake_any_sem(sem); ++ } + +- spin_unlock(&sem->lock); ++ spin_unlock(&sem->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&sem->lock); ++ ++ prev_count = sem->u.sem.count; ++ ret = put_sem_state(sem, args.count); ++ if (!ret) ++ try_wake_any_sem(sem); ++ ++ spin_unlock(&sem->lock); ++ } + + put_obj(sem); + +@@ -278,7 +409,7 @@ static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + * Also, calculate the relative timeout. + */ + static int setup_wait(struct ntsync_device *dev, +- const struct ntsync_wait_args *args, ++ const struct ntsync_wait_args *args, bool all, + ktime_t *ret_timeout, struct ntsync_q **ret_q) + { + const __u32 count = args->count; +@@ -321,6 +452,7 @@ static int setup_wait(struct ntsync_device *dev, + q->task = current; + q->owner = args->owner; + atomic_set(&q->signaled, -1); ++ q->all = all; + q->count = count; + + for (i = 0; i < count; i++) { +@@ -330,6 +462,16 @@ static int setup_wait(struct ntsync_device *dev, + if (!obj) + goto err; + ++ if (all) { ++ /* Check that the objects are all distinct. */ ++ for (j = 0; j < i; j++) { ++ if (obj == q->entries[j].obj) { ++ put_obj(obj); ++ goto err; ++ } ++ } ++ } ++ + entry->obj = obj; + entry->q = q; + entry->index = i; +@@ -370,7 +512,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + if (copy_from_user(&args, argp, sizeof(args))) + return -EFAULT; + +- ret = setup_wait(dev, &args, &timeout, &q); ++ ret = setup_wait(dev, &args, false, &timeout, &q); + if (ret < 0) + return ret; + +@@ -432,6 +574,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_wait_args args; ++ struct ntsync_q *q; ++ ktime_t timeout; ++ int signaled; ++ __u32 i; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ ret = setup_wait(dev, &args, true, &timeout, &q); ++ if (ret < 0) ++ return ret; ++ ++ /* queue ourselves */ ++ ++ spin_lock(&dev->wait_all_lock); ++ ++ for (i = 0; i < args.count; i++) { ++ struct 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 it here. ++ */ ++ list_add_tail(&entry->node, &obj->all_waiters); ++ } ++ ++ /* check if we are already signaled */ ++ ++ try_wake_all(dev, q, NULL); ++ ++ spin_unlock(&dev->wait_all_lock); ++ ++ /* sleep */ ++ ++ ret = ntsync_schedule(q, args.timeout ? &timeout : NULL); ++ ++ /* and finally, unqueue */ ++ ++ spin_lock(&dev->wait_all_lock); ++ ++ for (i = 0; i < args.count; i++) { ++ struct ntsync_q_entry *entry = &q->entries[i]; ++ struct ntsync_obj *obj = entry->obj; ++ ++ /* ++ * obj->all_waiters is protected by dev->wait_all_lock rather ++ * than obj->lock, so there is no need to acquire it here. ++ */ ++ list_del(&entry->node); ++ ++ atomic_dec(&obj->all_hint); ++ ++ put_obj(obj); ++ } ++ ++ spin_unlock(&dev->wait_all_lock); ++ ++ signaled = atomic_read(&q->signaled); ++ if (signaled != -1) { ++ struct ntsync_wait_args __user *user_args = argp; ++ ++ /* even if we caught a signal, we need to communicate success */ ++ ret = 0; ++ ++ if (put_user(signaled, &user_args->index)) ++ ret = -EFAULT; ++ } else if (!ret) { ++ ret = -ETIMEDOUT; ++ } ++ ++ kfree(q); ++ return ret; ++} ++ + static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + unsigned long parm) + { +@@ -445,6 +668,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_delete(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); ++ case NTSYNC_IOC_WAIT_ALL: ++ return ntsync_wait_all(dev, argp); + case NTSYNC_IOC_WAIT_ANY: + return ntsync_wait_any(dev, argp); + default: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 10f07da7864e..a5bed5a39b21 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -36,5 +36,7 @@ struct ntsync_wait_args { + struct ntsync_sem_args) + #define NTSYNC_IOC_WAIT_ANY _IOWR(NTSYNC_IOC_BASE, 3, \ + struct ntsync_wait_args) ++#define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \ ++ struct ntsync_wait_args) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch b/linux-gcc/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch new file mode 100644 index 0000000..5ed169a --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0006-[PATCH_RFC_7_9]_ntsync:_Introduce_NTSYNC_IOC_CREATE_MUTEX..patch @@ -0,0 +1,167 @@ +This corresponds to the NT syscall NtCreateMutant(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 8 +++++ + 2 files changed, 80 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 2685363fae9e..d48f2ef41341 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -16,6 +16,7 @@ + + enum ntsync_type { + NTSYNC_TYPE_SEM, ++ NTSYNC_TYPE_MUTEX, + }; + + struct ntsync_obj { +@@ -60,6 +61,10 @@ struct ntsync_obj { + __u32 count; + __u32 max; + } sem; ++ struct { ++ __u32 count; ++ __u32 owner; ++ } mutex; + } u; + }; + +@@ -188,6 +193,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner) + switch (obj->type) { + case NTSYNC_TYPE_SEM: + return !!obj->u.sem.count; ++ case NTSYNC_TYPE_MUTEX: ++ if (obj->u.mutex.owner && obj->u.mutex.owner != owner) ++ return false; ++ return obj->u.mutex.count < UINT_MAX; + } + + WARN(1, "bad object type %#x\n", obj->type); +@@ -230,6 +239,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, + case NTSYNC_TYPE_SEM: + obj->u.sem.count--; + break; ++ case NTSYNC_TYPE_MUTEX: ++ obj->u.mutex.count++; ++ obj->u.mutex.owner = q->owner; ++ break; + } + } + wake_up_process(q->task); +@@ -271,6 +284,28 @@ static void try_wake_any_sem(struct ntsync_obj *sem) + } + } + ++static void try_wake_any_mutex(struct ntsync_obj *mutex) ++{ ++ struct ntsync_q_entry *entry; ++ ++ lockdep_assert_held(&mutex->lock); ++ ++ list_for_each_entry(entry, &mutex->any_waiters, node) { ++ struct ntsync_q *q = entry->q; ++ ++ if (mutex->u.mutex.count == UINT_MAX) ++ break; ++ if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner) ++ continue; ++ ++ if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ mutex->u.mutex.count++; ++ mutex->u.mutex.owner = q->owner; ++ wake_up_process(q->task); ++ } ++ } ++} ++ + static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) + { + struct ntsync_sem_args __user *user_args = argp; +@@ -303,6 +338,38 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) + return put_user(id, &user_args->sem); + } + ++static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_mutex_args __user *user_args = argp; ++ struct ntsync_mutex_args args; ++ struct ntsync_obj *mutex; ++ __u32 id; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ ++ if (!args.owner != !args.count) ++ return -EINVAL; ++ ++ mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); ++ if (!mutex) ++ return -ENOMEM; ++ ++ init_obj(mutex); ++ mutex->type = NTSYNC_TYPE_MUTEX; ++ mutex->u.mutex.count = args.count; ++ mutex->u.mutex.owner = args.owner; ++ ++ ret = xa_alloc(&dev->objects, &id, mutex, xa_limit_32b, GFP_KERNEL); ++ if (ret < 0) { ++ kfree(mutex); ++ return ret; ++ } ++ ++ return put_user(id, &user_args->mutex); ++} ++ + static int ntsync_delete(struct ntsync_device *dev, void __user *argp) + { + struct ntsync_obj *obj; +@@ -497,6 +564,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj) + case NTSYNC_TYPE_SEM: + try_wake_any_sem(obj); + break; ++ case NTSYNC_TYPE_MUTEX: ++ try_wake_any_mutex(obj); ++ break; + } + } + +@@ -662,6 +732,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + void __user *argp = (void __user *)parm; + + switch (cmd) { ++ case NTSYNC_IOC_CREATE_MUTEX: ++ return ntsync_create_mutex(dev, argp); + case NTSYNC_IOC_CREATE_SEM: + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index a5bed5a39b21..26d1b3d4847f 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -16,6 +16,12 @@ struct ntsync_sem_args { + __u32 max; + }; + ++struct ntsync_mutex_args { ++ __u32 mutex; ++ __u32 owner; ++ __u32 count; ++}; ++ + struct ntsync_wait_args { + __u64 timeout; + __u64 objs; +@@ -38,5 +44,7 @@ struct ntsync_wait_args { + struct ntsync_wait_args) + #define NTSYNC_IOC_WAIT_ALL _IOWR(NTSYNC_IOC_BASE, 4, \ + struct ntsync_wait_args) ++#define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \ ++ struct ntsync_mutex_args) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch b/linux-gcc/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch new file mode 100644 index 0000000..5ad77ae --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0007-[PATCH_RFC_8_9]_ntsync:_Introduce_NTSYNC_IOC_PUT_MUTEX..patch @@ -0,0 +1,107 @@ +This corresponds to the NT syscall NtReleaseMutant(). + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 67 +++++++++++++++++++++++++++++++++++++ + include/uapi/linux/ntsync.h | 2 ++ + 2 files changed, 69 insertions(+) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index d48f2ef41341..28f43768d1c3 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -449,6 +449,71 @@ static int ntsync_put_sem(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++/* ++ * Actually change the mutex state, returning -EPERM if not the owner. ++ */ ++static int put_mutex_state(struct ntsync_obj *mutex, ++ const struct ntsync_mutex_args *args) ++{ ++ lockdep_assert_held(&mutex->lock); ++ ++ if (mutex->u.mutex.owner != args->owner) ++ return -EPERM; ++ ++ if (!--mutex->u.mutex.count) ++ mutex->u.mutex.owner = 0; ++ return 0; ++} ++ ++static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_mutex_args __user *user_args = argp; ++ struct ntsync_mutex_args args; ++ struct ntsync_obj *mutex; ++ __u32 prev_count; ++ int ret; ++ ++ if (copy_from_user(&args, argp, sizeof(args))) ++ return -EFAULT; ++ if (!args.owner) ++ return -EINVAL; ++ ++ mutex = get_obj_typed(dev, args.mutex, NTSYNC_TYPE_MUTEX); ++ if (!mutex) ++ return -EINVAL; ++ ++ if (atomic_read(&mutex->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&mutex->lock, &dev->wait_all_lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) { ++ try_wake_all_obj(dev, mutex); ++ try_wake_any_mutex(mutex); ++ } ++ ++ spin_unlock(&mutex->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&mutex->lock); ++ ++ prev_count = mutex->u.mutex.count; ++ ret = put_mutex_state(mutex, &args); ++ if (!ret) ++ try_wake_any_mutex(mutex); ++ ++ spin_unlock(&mutex->lock); ++ } ++ ++ put_obj(mutex); ++ ++ if (!ret && put_user(prev_count, &user_args->count)) ++ ret = -EFAULT; ++ ++ return ret; ++} ++ + static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + { + int ret = 0; +@@ -738,6 +803,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_PUT_MUTEX: ++ return ntsync_put_mutex(dev, argp); + case NTSYNC_IOC_PUT_SEM: + return ntsync_put_sem(dev, argp); + case NTSYNC_IOC_WAIT_ALL: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 26d1b3d4847f..2e44e7e77776 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -46,5 +46,7 @@ struct ntsync_wait_args { + struct ntsync_wait_args) + #define NTSYNC_IOC_CREATE_MUTEX _IOWR(NTSYNC_IOC_BASE, 5, \ + struct ntsync_mutex_args) ++#define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \ ++ struct ntsync_mutex_args) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch b/linux-gcc/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch new file mode 100644 index 0000000..eedfed0 --- /dev/null +++ b/linux-gcc/linux6.7-zen/patches/0008-[PATCH_RFC_9_9]_ntsync:_Introduce_NTSYNC_IOC_KILL_OWNER..patch @@ -0,0 +1,170 @@ +This does not correspond to any NT syscall, but rather should be called by the +user-space NT emulator when a thread dies. It is responsible for marking any +mutexes owned by that thread as abandoned. + +Signed-off-by: Elizabeth Figura +--- + drivers/misc/ntsync.c | 80 ++++++++++++++++++++++++++++++++++++- + include/uapi/linux/ntsync.h | 1 + + 2 files changed, 79 insertions(+), 2 deletions(-) + +diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c +index 28f43768d1c3..1173c750c106 100644 +--- a/drivers/misc/ntsync.c ++++ b/drivers/misc/ntsync.c +@@ -64,6 +64,7 @@ struct ntsync_obj { + struct { + __u32 count; + __u32 owner; ++ bool ownerdead; + } mutex; + } u; + }; +@@ -87,6 +88,7 @@ struct ntsync_q { + atomic_t signaled; + + bool all; ++ bool ownerdead; + __u32 count; + struct ntsync_q_entry entries[]; + }; +@@ -240,6 +242,9 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q, + obj->u.sem.count--; + break; + case NTSYNC_TYPE_MUTEX: ++ if (obj->u.mutex.ownerdead) ++ q->ownerdead = true; ++ obj->u.mutex.ownerdead = false; + obj->u.mutex.count++; + obj->u.mutex.owner = q->owner; + break; +@@ -299,6 +304,9 @@ static void try_wake_any_mutex(struct ntsync_obj *mutex) + continue; + + if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { ++ if (mutex->u.mutex.ownerdead) ++ q->ownerdead = true; ++ mutex->u.mutex.ownerdead = false; + mutex->u.mutex.count++; + mutex->u.mutex.owner = q->owner; + wake_up_process(q->task); +@@ -514,6 +522,71 @@ static int ntsync_put_mutex(struct ntsync_device *dev, void __user *argp) + return ret; + } + ++/* ++ * Actually change the mutex state to mark its owner as dead. ++ */ ++static void put_mutex_ownerdead_state(struct ntsync_obj *mutex) ++{ ++ lockdep_assert_held(&mutex->lock); ++ ++ mutex->u.mutex.ownerdead = true; ++ mutex->u.mutex.owner = 0; ++ mutex->u.mutex.count = 0; ++} ++ ++static int ntsync_kill_owner(struct ntsync_device *dev, void __user *argp) ++{ ++ struct ntsync_obj *obj; ++ unsigned long id; ++ __u32 owner; ++ ++ if (get_user(owner, (__u32 __user *)argp)) ++ return -EFAULT; ++ if (!owner) ++ return -EINVAL; ++ ++ rcu_read_lock(); ++ ++ xa_for_each(&dev->objects, id, obj) { ++ if (!kref_get_unless_zero(&obj->refcount)) ++ continue; ++ ++ if (obj->type != NTSYNC_TYPE_MUTEX) { ++ put_obj(obj); ++ continue; ++ } ++ ++ if (atomic_read(&obj->all_hint) > 0) { ++ spin_lock(&dev->wait_all_lock); ++ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock); ++ ++ if (obj->u.mutex.owner == owner) { ++ put_mutex_ownerdead_state(obj); ++ try_wake_all_obj(dev, obj); ++ try_wake_any_mutex(obj); ++ } ++ ++ spin_unlock(&obj->lock); ++ spin_unlock(&dev->wait_all_lock); ++ } else { ++ spin_lock(&obj->lock); ++ ++ if (obj->u.mutex.owner == owner) { ++ put_mutex_ownerdead_state(obj); ++ try_wake_any_mutex(obj); ++ } ++ ++ spin_unlock(&obj->lock); ++ } ++ ++ put_obj(obj); ++ } ++ ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ + static int ntsync_schedule(const struct ntsync_q *q, ktime_t *timeout) + { + int ret = 0; +@@ -585,6 +658,7 @@ static int setup_wait(struct ntsync_device *dev, + q->owner = args->owner; + atomic_set(&q->signaled, -1); + q->all = all; ++ q->ownerdead = false; + q->count = count; + + for (i = 0; i < count; i++) { +@@ -697,7 +771,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp) + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ +- ret = 0; ++ ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; +@@ -778,7 +852,7 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp) + struct ntsync_wait_args __user *user_args = argp; + + /* even if we caught a signal, we need to communicate success */ +- ret = 0; ++ ret = q->ownerdead ? -EOWNERDEAD : 0; + + if (put_user(signaled, &user_args->index)) + ret = -EFAULT; +@@ -803,6 +877,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd, + return ntsync_create_sem(dev, argp); + case NTSYNC_IOC_DELETE: + return ntsync_delete(dev, argp); ++ case NTSYNC_IOC_KILL_OWNER: ++ return ntsync_kill_owner(dev, argp); + case NTSYNC_IOC_PUT_MUTEX: + return ntsync_put_mutex(dev, argp); + case NTSYNC_IOC_PUT_SEM: +diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h +index 2e44e7e77776..fec9a3993322 100644 +--- a/include/uapi/linux/ntsync.h ++++ b/include/uapi/linux/ntsync.h +@@ -48,5 +48,6 @@ struct ntsync_wait_args { + struct ntsync_mutex_args) + #define NTSYNC_IOC_PUT_MUTEX _IOWR(NTSYNC_IOC_BASE, 6, \ + struct ntsync_mutex_args) ++#define NTSYNC_IOC_KILL_OWNER _IOW (NTSYNC_IOC_BASE, 7, __u32) + + #endif +-- +2.43.0 diff --git a/linux-gcc/linux6.7-zen/template b/linux-gcc/linux6.7-zen/template index eaebd8f..d47d01a 100644 --- a/linux-gcc/linux6.7-zen/template +++ b/linux-gcc/linux6.7-zen/template @@ -1,6 +1,6 @@ pkgname=linux6.7-zen version=6.7.1 -revision=1 +revision=2 zen=1 python_version=3 short_desc="Linux kernel and modules with Zen patches (${version%.*} series)"