Skip to content

Commit

Permalink
winesync: Introduce WINESYNC_IOC_PUT_SEM
Browse files Browse the repository at this point in the history
  • Loading branch information
Zebediah Figura authored and xanmod committed May 30, 2022
1 parent a30ebf2 commit d98d162
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
76 changes: 76 additions & 0 deletions drivers/misc/winesync.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ enum winesync_type {
struct winesync_obj {
struct rcu_head rhead;
struct kref refcount;
spinlock_t lock;

enum winesync_type type;

/* The following fields are protected by the object lock. */
union {
struct {
__u32 count;
Expand All @@ -36,6 +38,19 @@ struct winesync_device {
struct xarray objects;
};

static struct winesync_obj *get_obj(struct winesync_device *dev, __u32 id)
{
struct winesync_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 winesync_obj *obj = container_of(ref, struct winesync_obj, refcount);
Expand All @@ -48,6 +63,18 @@ static void put_obj(struct winesync_obj *obj)
kref_put(&obj->refcount, destroy_obj);
}

static struct winesync_obj *get_obj_typed(struct winesync_device *dev, __u32 id,
enum winesync_type type)
{
struct winesync_obj *obj = get_obj(dev, id);

if (obj && obj->type != type) {
put_obj(obj);
return NULL;
}
return obj;
}

static int winesync_char_open(struct inode *inode, struct file *file)
{
struct winesync_device *dev;
Expand Down Expand Up @@ -81,6 +108,7 @@ static int winesync_char_release(struct inode *inode, struct file *file)
static void init_obj(struct winesync_obj *obj)
{
kref_init(&obj->refcount);
spin_lock_init(&obj->lock);
}

static int winesync_create_sem(struct winesync_device *dev, void __user *argp)
Expand Down Expand Up @@ -131,6 +159,52 @@ static int winesync_delete(struct winesync_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 winesync_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 winesync_put_sem(struct winesync_device *dev, void __user *argp)
{
struct winesync_sem_args __user *user_args = argp;
struct winesync_sem_args args;
struct winesync_obj *sem;
__u32 prev_count;
int ret;

if (copy_from_user(&args, argp, sizeof(args)))
return -EFAULT;

sem = get_obj_typed(dev, args.sem, WINESYNC_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 winesync_char_ioctl(struct file *file, unsigned int cmd,
unsigned long parm)
{
Expand All @@ -142,6 +216,8 @@ static long winesync_char_ioctl(struct file *file, unsigned int cmd,
return winesync_create_sem(dev, argp);
case WINESYNC_IOC_DELETE:
return winesync_delete(dev, argp);
case WINESYNC_IOC_PUT_SEM:
return winesync_put_sem(dev, argp);
default:
return -ENOSYS;
}
Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/winesync.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ struct winesync_sem_args {
#define WINESYNC_IOC_CREATE_SEM _IOWR(WINESYNC_IOC_BASE, 0, \
struct winesync_sem_args)
#define WINESYNC_IOC_DELETE _IOW (WINESYNC_IOC_BASE, 1, __u32)
#define WINESYNC_IOC_PUT_SEM _IOWR(WINESYNC_IOC_BASE, 2, \
struct winesync_sem_args)

#endif

0 comments on commit d98d162

Please sign in to comment.