148 lines
3.8 KiB
Diff
148 lines
3.8 KiB
Diff
|
This corresponds to the NT syscall NtReleaseSemaphore().
|
||
|
|
||
|
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
|
||
|
---
|
||
|
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
|