xref: /linux-6.15/drivers/misc/ntsync.c (revision 92527e47)
125b9cadbSElizabeth Figura // SPDX-License-Identifier: GPL-2.0-only
225b9cadbSElizabeth Figura /*
325b9cadbSElizabeth Figura  * ntsync.c - Kernel driver for NT synchronization primitives
425b9cadbSElizabeth Figura  *
525b9cadbSElizabeth Figura  * Copyright (C) 2024 Elizabeth Figura <[email protected]>
625b9cadbSElizabeth Figura  */
725b9cadbSElizabeth Figura 
8b46271ecSElizabeth Figura #include <linux/anon_inodes.h>
9b4a7b5feSElizabeth Figura #include <linux/atomic.h>
10b46271ecSElizabeth Figura #include <linux/file.h>
1125b9cadbSElizabeth Figura #include <linux/fs.h>
12b4a7b5feSElizabeth Figura #include <linux/hrtimer.h>
13b4a7b5feSElizabeth Figura #include <linux/ktime.h>
1425b9cadbSElizabeth Figura #include <linux/miscdevice.h>
1525b9cadbSElizabeth Figura #include <linux/module.h>
16cdbb9978SElizabeth Figura #include <linux/mutex.h>
17dc806bd4SElizabeth Figura #include <linux/overflow.h>
18b4a7b5feSElizabeth Figura #include <linux/sched.h>
19b4a7b5feSElizabeth Figura #include <linux/sched/signal.h>
20b46271ecSElizabeth Figura #include <linux/slab.h>
21dc806bd4SElizabeth Figura #include <linux/spinlock.h>
22b46271ecSElizabeth Figura #include <uapi/linux/ntsync.h>
2325b9cadbSElizabeth Figura 
2425b9cadbSElizabeth Figura #define NTSYNC_NAME	"ntsync"
2525b9cadbSElizabeth Figura 
26b46271ecSElizabeth Figura enum ntsync_type {
27b46271ecSElizabeth Figura 	NTSYNC_TYPE_SEM,
285bc2479aSElizabeth Figura 	NTSYNC_TYPE_MUTEX,
294c7404b9SElizabeth Figura 	NTSYNC_TYPE_EVENT,
30b46271ecSElizabeth Figura };
31b46271ecSElizabeth Figura 
32b46271ecSElizabeth Figura /*
33b46271ecSElizabeth Figura  * Individual synchronization primitives are represented by
34b46271ecSElizabeth Figura  * struct ntsync_obj, and each primitive is backed by a file.
35b46271ecSElizabeth Figura  *
36b46271ecSElizabeth Figura  * The whole namespace is represented by a struct ntsync_device also
37b46271ecSElizabeth Figura  * backed by a file.
38b46271ecSElizabeth Figura  *
39b46271ecSElizabeth Figura  * Both rely on struct file for reference counting. Individual
40b46271ecSElizabeth Figura  * ntsync_obj objects take a reference to the device when created.
41b4a7b5feSElizabeth Figura  * Wait operations take a reference to each object being waited on for
42b4a7b5feSElizabeth Figura  * the duration of the wait.
43b46271ecSElizabeth Figura  */
44b46271ecSElizabeth Figura 
45b46271ecSElizabeth Figura struct ntsync_obj {
46dc806bd4SElizabeth Figura 	spinlock_t lock;
47cdbb9978SElizabeth Figura 	int dev_locked;
48dc806bd4SElizabeth Figura 
49b46271ecSElizabeth Figura 	enum ntsync_type type;
50b46271ecSElizabeth Figura 
51dc806bd4SElizabeth Figura 	struct file *file;
52dc806bd4SElizabeth Figura 	struct ntsync_device *dev;
53dc806bd4SElizabeth Figura 
54dc806bd4SElizabeth Figura 	/* The following fields are protected by the object lock. */
55b46271ecSElizabeth Figura 	union {
56b46271ecSElizabeth Figura 		struct {
57b46271ecSElizabeth Figura 			__u32 count;
58b46271ecSElizabeth Figura 			__u32 max;
59b46271ecSElizabeth Figura 		} sem;
605bc2479aSElizabeth Figura 		struct {
615bc2479aSElizabeth Figura 			__u32 count;
625bc2479aSElizabeth Figura 			pid_t owner;
63ecc2ee36SElizabeth Figura 			bool ownerdead;
645bc2479aSElizabeth Figura 		} mutex;
654c7404b9SElizabeth Figura 		struct {
664c7404b9SElizabeth Figura 			bool manual;
674c7404b9SElizabeth Figura 			bool signaled;
684c7404b9SElizabeth Figura 		} event;
69b46271ecSElizabeth Figura 	} u;
70b4a7b5feSElizabeth Figura 
71cdbb9978SElizabeth Figura 	/*
72cdbb9978SElizabeth Figura 	 * any_waiters is protected by the object lock, but all_waiters is
73cdbb9978SElizabeth Figura 	 * protected by the device wait_all_lock.
74cdbb9978SElizabeth Figura 	 */
75b4a7b5feSElizabeth Figura 	struct list_head any_waiters;
76cdbb9978SElizabeth Figura 	struct list_head all_waiters;
77cdbb9978SElizabeth Figura 
78cdbb9978SElizabeth Figura 	/*
79cdbb9978SElizabeth Figura 	 * Hint describing how many tasks are queued on this object in a
80cdbb9978SElizabeth Figura 	 * wait-all operation.
81cdbb9978SElizabeth Figura 	 *
82cdbb9978SElizabeth Figura 	 * Any time we do a wake, we may need to wake "all" waiters as well as
83cdbb9978SElizabeth Figura 	 * "any" waiters. In order to atomically wake "all" waiters, we must
84cdbb9978SElizabeth Figura 	 * lock all of the objects, and that means grabbing the wait_all_lock
85cdbb9978SElizabeth Figura 	 * below (and, due to lock ordering rules, before locking this object).
86cdbb9978SElizabeth Figura 	 * However, wait-all is a rare operation, and grabbing the wait-all
87cdbb9978SElizabeth Figura 	 * lock for every wake would create unnecessary contention.
88cdbb9978SElizabeth Figura 	 * Therefore we first check whether all_hint is zero, and, if it is,
89cdbb9978SElizabeth Figura 	 * we skip trying to wake "all" waiters.
90cdbb9978SElizabeth Figura 	 *
91cdbb9978SElizabeth Figura 	 * Since wait requests must originate from user-space threads, we're
92cdbb9978SElizabeth Figura 	 * limited here by PID_MAX_LIMIT, so there's no risk of overflow.
93cdbb9978SElizabeth Figura 	 */
94cdbb9978SElizabeth Figura 	atomic_t all_hint;
95b4a7b5feSElizabeth Figura };
96b4a7b5feSElizabeth Figura 
97b4a7b5feSElizabeth Figura struct ntsync_q_entry {
98b4a7b5feSElizabeth Figura 	struct list_head node;
99b4a7b5feSElizabeth Figura 	struct ntsync_q *q;
100b4a7b5feSElizabeth Figura 	struct ntsync_obj *obj;
101b4a7b5feSElizabeth Figura 	__u32 index;
102b4a7b5feSElizabeth Figura };
103b4a7b5feSElizabeth Figura 
104b4a7b5feSElizabeth Figura struct ntsync_q {
105b4a7b5feSElizabeth Figura 	struct task_struct *task;
1065bc2479aSElizabeth Figura 	__u32 owner;
107b4a7b5feSElizabeth Figura 
108b4a7b5feSElizabeth Figura 	/*
109b4a7b5feSElizabeth Figura 	 * Protected via atomic_try_cmpxchg(). Only the thread that wins the
110b4a7b5feSElizabeth Figura 	 * compare-and-swap may actually change object states and wake this
111b4a7b5feSElizabeth Figura 	 * task.
112b4a7b5feSElizabeth Figura 	 */
113b4a7b5feSElizabeth Figura 	atomic_t signaled;
114b4a7b5feSElizabeth Figura 
115cdbb9978SElizabeth Figura 	bool all;
116ecc2ee36SElizabeth Figura 	bool ownerdead;
117b4a7b5feSElizabeth Figura 	__u32 count;
118b4a7b5feSElizabeth Figura 	struct ntsync_q_entry entries[];
119b46271ecSElizabeth Figura };
120b46271ecSElizabeth Figura 
121b46271ecSElizabeth Figura struct ntsync_device {
122cdbb9978SElizabeth Figura 	/*
123cdbb9978SElizabeth Figura 	 * Wait-all operations must atomically grab all objects, and be totally
124cdbb9978SElizabeth Figura 	 * ordered with respect to each other and wait-any operations.
125cdbb9978SElizabeth Figura 	 * If one thread is trying to acquire several objects, another thread
126cdbb9978SElizabeth Figura 	 * cannot touch the object at the same time.
127cdbb9978SElizabeth Figura 	 *
128cdbb9978SElizabeth Figura 	 * This device-wide lock is used to serialize wait-for-all
129cdbb9978SElizabeth Figura 	 * operations, and operations on an object that is involved in a
130cdbb9978SElizabeth Figura 	 * wait-for-all.
131cdbb9978SElizabeth Figura 	 */
132cdbb9978SElizabeth Figura 	struct mutex wait_all_lock;
133cdbb9978SElizabeth Figura 
134b46271ecSElizabeth Figura 	struct file *file;
135b46271ecSElizabeth Figura };
136b46271ecSElizabeth Figura 
137cdbb9978SElizabeth Figura /*
138cdbb9978SElizabeth Figura  * Single objects are locked using obj->lock.
139cdbb9978SElizabeth Figura  *
140cdbb9978SElizabeth Figura  * Multiple objects are 'locked' while holding dev->wait_all_lock.
141cdbb9978SElizabeth Figura  * In this case however, individual objects are not locked by holding
142cdbb9978SElizabeth Figura  * obj->lock, but by setting obj->dev_locked.
143cdbb9978SElizabeth Figura  *
144cdbb9978SElizabeth Figura  * This means that in order to lock a single object, the sequence is slightly
145cdbb9978SElizabeth Figura  * more complicated than usual. Specifically it needs to check obj->dev_locked
146cdbb9978SElizabeth Figura  * after acquiring obj->lock, if set, it needs to drop the lock and acquire
147cdbb9978SElizabeth Figura  * dev->wait_all_lock in order to serialize against the multi-object operation.
148cdbb9978SElizabeth Figura  */
149cdbb9978SElizabeth Figura 
dev_lock_obj(struct ntsync_device * dev,struct ntsync_obj * obj)150cdbb9978SElizabeth Figura static void dev_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
151cdbb9978SElizabeth Figura {
152cdbb9978SElizabeth Figura 	lockdep_assert_held(&dev->wait_all_lock);
153cdbb9978SElizabeth Figura 	lockdep_assert(obj->dev == dev);
154cdbb9978SElizabeth Figura 	spin_lock(&obj->lock);
155cdbb9978SElizabeth Figura 	/*
156cdbb9978SElizabeth Figura 	 * By setting obj->dev_locked inside obj->lock, it is ensured that
157cdbb9978SElizabeth Figura 	 * anyone holding obj->lock must see the value.
158cdbb9978SElizabeth Figura 	 */
159cdbb9978SElizabeth Figura 	obj->dev_locked = 1;
160cdbb9978SElizabeth Figura 	spin_unlock(&obj->lock);
161cdbb9978SElizabeth Figura }
162cdbb9978SElizabeth Figura 
dev_unlock_obj(struct ntsync_device * dev,struct ntsync_obj * obj)163cdbb9978SElizabeth Figura static void dev_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
164cdbb9978SElizabeth Figura {
165cdbb9978SElizabeth Figura 	lockdep_assert_held(&dev->wait_all_lock);
166cdbb9978SElizabeth Figura 	lockdep_assert(obj->dev == dev);
167cdbb9978SElizabeth Figura 	spin_lock(&obj->lock);
168cdbb9978SElizabeth Figura 	obj->dev_locked = 0;
169cdbb9978SElizabeth Figura 	spin_unlock(&obj->lock);
170cdbb9978SElizabeth Figura }
171cdbb9978SElizabeth Figura 
obj_lock(struct ntsync_obj * obj)172cdbb9978SElizabeth Figura static void obj_lock(struct ntsync_obj *obj)
173cdbb9978SElizabeth Figura {
174cdbb9978SElizabeth Figura 	struct ntsync_device *dev = obj->dev;
175cdbb9978SElizabeth Figura 
176cdbb9978SElizabeth Figura 	for (;;) {
177cdbb9978SElizabeth Figura 		spin_lock(&obj->lock);
178cdbb9978SElizabeth Figura 		if (likely(!obj->dev_locked))
179cdbb9978SElizabeth Figura 			break;
180cdbb9978SElizabeth Figura 
181cdbb9978SElizabeth Figura 		spin_unlock(&obj->lock);
182cdbb9978SElizabeth Figura 		mutex_lock(&dev->wait_all_lock);
183cdbb9978SElizabeth Figura 		spin_lock(&obj->lock);
184cdbb9978SElizabeth Figura 		/*
185cdbb9978SElizabeth Figura 		 * obj->dev_locked should be set and released under the same
186cdbb9978SElizabeth Figura 		 * wait_all_lock section, since we now own this lock, it should
187cdbb9978SElizabeth Figura 		 * be clear.
188cdbb9978SElizabeth Figura 		 */
189cdbb9978SElizabeth Figura 		lockdep_assert(!obj->dev_locked);
190cdbb9978SElizabeth Figura 		spin_unlock(&obj->lock);
191cdbb9978SElizabeth Figura 		mutex_unlock(&dev->wait_all_lock);
192cdbb9978SElizabeth Figura 	}
193cdbb9978SElizabeth Figura }
194cdbb9978SElizabeth Figura 
obj_unlock(struct ntsync_obj * obj)195cdbb9978SElizabeth Figura static void obj_unlock(struct ntsync_obj *obj)
196cdbb9978SElizabeth Figura {
197cdbb9978SElizabeth Figura 	spin_unlock(&obj->lock);
198cdbb9978SElizabeth Figura }
199cdbb9978SElizabeth Figura 
ntsync_lock_obj(struct ntsync_device * dev,struct ntsync_obj * obj)200cdbb9978SElizabeth Figura static bool ntsync_lock_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
201cdbb9978SElizabeth Figura {
202cdbb9978SElizabeth Figura 	bool all;
203cdbb9978SElizabeth Figura 
204cdbb9978SElizabeth Figura 	obj_lock(obj);
205cdbb9978SElizabeth Figura 	all = atomic_read(&obj->all_hint);
206cdbb9978SElizabeth Figura 	if (unlikely(all)) {
207cdbb9978SElizabeth Figura 		obj_unlock(obj);
208cdbb9978SElizabeth Figura 		mutex_lock(&dev->wait_all_lock);
209cdbb9978SElizabeth Figura 		dev_lock_obj(dev, obj);
210cdbb9978SElizabeth Figura 	}
211cdbb9978SElizabeth Figura 
212cdbb9978SElizabeth Figura 	return all;
213cdbb9978SElizabeth Figura }
214cdbb9978SElizabeth Figura 
ntsync_unlock_obj(struct ntsync_device * dev,struct ntsync_obj * obj,bool all)215cdbb9978SElizabeth Figura static void ntsync_unlock_obj(struct ntsync_device *dev, struct ntsync_obj *obj, bool all)
216cdbb9978SElizabeth Figura {
217cdbb9978SElizabeth Figura 	if (all) {
218cdbb9978SElizabeth Figura 		dev_unlock_obj(dev, obj);
219cdbb9978SElizabeth Figura 		mutex_unlock(&dev->wait_all_lock);
220cdbb9978SElizabeth Figura 	} else {
221cdbb9978SElizabeth Figura 		obj_unlock(obj);
222cdbb9978SElizabeth Figura 	}
223cdbb9978SElizabeth Figura }
224cdbb9978SElizabeth Figura 
225cdbb9978SElizabeth Figura #define ntsync_assert_held(obj) \
226cdbb9978SElizabeth Figura 	lockdep_assert((lockdep_is_held(&(obj)->lock) != LOCK_STATE_NOT_HELD) || \
227cdbb9978SElizabeth Figura 		       ((lockdep_is_held(&(obj)->dev->wait_all_lock) != LOCK_STATE_NOT_HELD) && \
228cdbb9978SElizabeth Figura 			(obj)->dev_locked))
229cdbb9978SElizabeth Figura 
is_signaled(struct ntsync_obj * obj,__u32 owner)2305bc2479aSElizabeth Figura static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
231cdbb9978SElizabeth Figura {
232cdbb9978SElizabeth Figura 	ntsync_assert_held(obj);
233cdbb9978SElizabeth Figura 
234cdbb9978SElizabeth Figura 	switch (obj->type) {
235cdbb9978SElizabeth Figura 	case NTSYNC_TYPE_SEM:
236cdbb9978SElizabeth Figura 		return !!obj->u.sem.count;
2375bc2479aSElizabeth Figura 	case NTSYNC_TYPE_MUTEX:
2385bc2479aSElizabeth Figura 		if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
2395bc2479aSElizabeth Figura 			return false;
2405bc2479aSElizabeth Figura 		return obj->u.mutex.count < UINT_MAX;
2414c7404b9SElizabeth Figura 	case NTSYNC_TYPE_EVENT:
2424c7404b9SElizabeth Figura 		return obj->u.event.signaled;
243cdbb9978SElizabeth Figura 	}
244cdbb9978SElizabeth Figura 
245cdbb9978SElizabeth Figura 	WARN(1, "bad object type %#x\n", obj->type);
246cdbb9978SElizabeth Figura 	return false;
247cdbb9978SElizabeth Figura }
248cdbb9978SElizabeth Figura 
249cdbb9978SElizabeth Figura /*
250cdbb9978SElizabeth Figura  * "locked_obj" is an optional pointer to an object which is already locked and
251cdbb9978SElizabeth Figura  * should not be locked again. This is necessary so that changing an object's
252cdbb9978SElizabeth Figura  * state and waking it can be a single atomic operation.
253cdbb9978SElizabeth Figura  */
try_wake_all(struct ntsync_device * dev,struct ntsync_q * q,struct ntsync_obj * locked_obj)254cdbb9978SElizabeth Figura static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
255cdbb9978SElizabeth Figura 			 struct ntsync_obj *locked_obj)
256cdbb9978SElizabeth Figura {
257cdbb9978SElizabeth Figura 	__u32 count = q->count;
258cdbb9978SElizabeth Figura 	bool can_wake = true;
259cdbb9978SElizabeth Figura 	int signaled = -1;
260cdbb9978SElizabeth Figura 	__u32 i;
261cdbb9978SElizabeth Figura 
262cdbb9978SElizabeth Figura 	lockdep_assert_held(&dev->wait_all_lock);
263cdbb9978SElizabeth Figura 	if (locked_obj)
264cdbb9978SElizabeth Figura 		lockdep_assert(locked_obj->dev_locked);
265cdbb9978SElizabeth Figura 
266cdbb9978SElizabeth Figura 	for (i = 0; i < count; i++) {
267cdbb9978SElizabeth Figura 		if (q->entries[i].obj != locked_obj)
268cdbb9978SElizabeth Figura 			dev_lock_obj(dev, q->entries[i].obj);
269cdbb9978SElizabeth Figura 	}
270cdbb9978SElizabeth Figura 
271cdbb9978SElizabeth Figura 	for (i = 0; i < count; i++) {
2725bc2479aSElizabeth Figura 		if (!is_signaled(q->entries[i].obj, q->owner)) {
273cdbb9978SElizabeth Figura 			can_wake = false;
274cdbb9978SElizabeth Figura 			break;
275cdbb9978SElizabeth Figura 		}
276cdbb9978SElizabeth Figura 	}
277cdbb9978SElizabeth Figura 
278cdbb9978SElizabeth Figura 	if (can_wake && atomic_try_cmpxchg(&q->signaled, &signaled, 0)) {
279cdbb9978SElizabeth Figura 		for (i = 0; i < count; i++) {
280cdbb9978SElizabeth Figura 			struct ntsync_obj *obj = q->entries[i].obj;
281cdbb9978SElizabeth Figura 
282cdbb9978SElizabeth Figura 			switch (obj->type) {
283cdbb9978SElizabeth Figura 			case NTSYNC_TYPE_SEM:
284cdbb9978SElizabeth Figura 				obj->u.sem.count--;
285cdbb9978SElizabeth Figura 				break;
2865bc2479aSElizabeth Figura 			case NTSYNC_TYPE_MUTEX:
287ecc2ee36SElizabeth Figura 				if (obj->u.mutex.ownerdead)
288ecc2ee36SElizabeth Figura 					q->ownerdead = true;
289ecc2ee36SElizabeth Figura 				obj->u.mutex.ownerdead = false;
2905bc2479aSElizabeth Figura 				obj->u.mutex.count++;
2915bc2479aSElizabeth Figura 				obj->u.mutex.owner = q->owner;
2925bc2479aSElizabeth Figura 				break;
2934c7404b9SElizabeth Figura 			case NTSYNC_TYPE_EVENT:
2944c7404b9SElizabeth Figura 				if (!obj->u.event.manual)
2954c7404b9SElizabeth Figura 					obj->u.event.signaled = false;
2964c7404b9SElizabeth Figura 				break;
297cdbb9978SElizabeth Figura 			}
298cdbb9978SElizabeth Figura 		}
299cdbb9978SElizabeth Figura 		wake_up_process(q->task);
300cdbb9978SElizabeth Figura 	}
301cdbb9978SElizabeth Figura 
302cdbb9978SElizabeth Figura 	for (i = 0; i < count; i++) {
303cdbb9978SElizabeth Figura 		if (q->entries[i].obj != locked_obj)
304cdbb9978SElizabeth Figura 			dev_unlock_obj(dev, q->entries[i].obj);
305cdbb9978SElizabeth Figura 	}
306cdbb9978SElizabeth Figura }
307cdbb9978SElizabeth Figura 
try_wake_all_obj(struct ntsync_device * dev,struct ntsync_obj * obj)308cdbb9978SElizabeth Figura static void try_wake_all_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
309cdbb9978SElizabeth Figura {
310cdbb9978SElizabeth Figura 	struct ntsync_q_entry *entry;
311cdbb9978SElizabeth Figura 
312cdbb9978SElizabeth Figura 	lockdep_assert_held(&dev->wait_all_lock);
313cdbb9978SElizabeth Figura 	lockdep_assert(obj->dev_locked);
314cdbb9978SElizabeth Figura 
315cdbb9978SElizabeth Figura 	list_for_each_entry(entry, &obj->all_waiters, node)
316cdbb9978SElizabeth Figura 		try_wake_all(dev, entry->q, obj);
317cdbb9978SElizabeth Figura }
318cdbb9978SElizabeth Figura 
try_wake_any_sem(struct ntsync_obj * sem)319b4a7b5feSElizabeth Figura static void try_wake_any_sem(struct ntsync_obj *sem)
320b4a7b5feSElizabeth Figura {
321b4a7b5feSElizabeth Figura 	struct ntsync_q_entry *entry;
322b4a7b5feSElizabeth Figura 
323cdbb9978SElizabeth Figura 	ntsync_assert_held(sem);
324cdbb9978SElizabeth Figura 	lockdep_assert(sem->type == NTSYNC_TYPE_SEM);
325b4a7b5feSElizabeth Figura 
326b4a7b5feSElizabeth Figura 	list_for_each_entry(entry, &sem->any_waiters, node) {
327b4a7b5feSElizabeth Figura 		struct ntsync_q *q = entry->q;
328b4a7b5feSElizabeth Figura 		int signaled = -1;
329b4a7b5feSElizabeth Figura 
330b4a7b5feSElizabeth Figura 		if (!sem->u.sem.count)
331b4a7b5feSElizabeth Figura 			break;
332b4a7b5feSElizabeth Figura 
333b4a7b5feSElizabeth Figura 		if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
334b4a7b5feSElizabeth Figura 			sem->u.sem.count--;
335b4a7b5feSElizabeth Figura 			wake_up_process(q->task);
336b4a7b5feSElizabeth Figura 		}
337b4a7b5feSElizabeth Figura 	}
338b4a7b5feSElizabeth Figura }
339b4a7b5feSElizabeth Figura 
try_wake_any_mutex(struct ntsync_obj * mutex)3405bc2479aSElizabeth Figura static void try_wake_any_mutex(struct ntsync_obj *mutex)
3415bc2479aSElizabeth Figura {
3425bc2479aSElizabeth Figura 	struct ntsync_q_entry *entry;
3435bc2479aSElizabeth Figura 
3445bc2479aSElizabeth Figura 	ntsync_assert_held(mutex);
3455bc2479aSElizabeth Figura 	lockdep_assert(mutex->type == NTSYNC_TYPE_MUTEX);
3465bc2479aSElizabeth Figura 
3475bc2479aSElizabeth Figura 	list_for_each_entry(entry, &mutex->any_waiters, node) {
3485bc2479aSElizabeth Figura 		struct ntsync_q *q = entry->q;
3495bc2479aSElizabeth Figura 		int signaled = -1;
3505bc2479aSElizabeth Figura 
3515bc2479aSElizabeth Figura 		if (mutex->u.mutex.count == UINT_MAX)
3525bc2479aSElizabeth Figura 			break;
3535bc2479aSElizabeth Figura 		if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner)
3545bc2479aSElizabeth Figura 			continue;
3555bc2479aSElizabeth Figura 
3565bc2479aSElizabeth Figura 		if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
357ecc2ee36SElizabeth Figura 			if (mutex->u.mutex.ownerdead)
358ecc2ee36SElizabeth Figura 				q->ownerdead = true;
359ecc2ee36SElizabeth Figura 			mutex->u.mutex.ownerdead = false;
3605bc2479aSElizabeth Figura 			mutex->u.mutex.count++;
3615bc2479aSElizabeth Figura 			mutex->u.mutex.owner = q->owner;
3625bc2479aSElizabeth Figura 			wake_up_process(q->task);
3635bc2479aSElizabeth Figura 		}
3645bc2479aSElizabeth Figura 	}
3655bc2479aSElizabeth Figura }
3665bc2479aSElizabeth Figura 
try_wake_any_event(struct ntsync_obj * event)3674c7404b9SElizabeth Figura static void try_wake_any_event(struct ntsync_obj *event)
3684c7404b9SElizabeth Figura {
3694c7404b9SElizabeth Figura 	struct ntsync_q_entry *entry;
3704c7404b9SElizabeth Figura 
3714c7404b9SElizabeth Figura 	ntsync_assert_held(event);
3724c7404b9SElizabeth Figura 	lockdep_assert(event->type == NTSYNC_TYPE_EVENT);
3734c7404b9SElizabeth Figura 
3744c7404b9SElizabeth Figura 	list_for_each_entry(entry, &event->any_waiters, node) {
3754c7404b9SElizabeth Figura 		struct ntsync_q *q = entry->q;
3764c7404b9SElizabeth Figura 		int signaled = -1;
3774c7404b9SElizabeth Figura 
3784c7404b9SElizabeth Figura 		if (!event->u.event.signaled)
3794c7404b9SElizabeth Figura 			break;
3804c7404b9SElizabeth Figura 
3814c7404b9SElizabeth Figura 		if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
3824c7404b9SElizabeth Figura 			if (!event->u.event.manual)
3834c7404b9SElizabeth Figura 				event->u.event.signaled = false;
3844c7404b9SElizabeth Figura 			wake_up_process(q->task);
3854c7404b9SElizabeth Figura 		}
3864c7404b9SElizabeth Figura 	}
3874c7404b9SElizabeth Figura }
3884c7404b9SElizabeth Figura 
389dc806bd4SElizabeth Figura /*
390dc806bd4SElizabeth Figura  * Actually change the semaphore state, returning -EOVERFLOW if it is made
391dc806bd4SElizabeth Figura  * invalid.
392dc806bd4SElizabeth Figura  */
release_sem_state(struct ntsync_obj * sem,__u32 count)3935ec43d6bSElizabeth Figura static int release_sem_state(struct ntsync_obj *sem, __u32 count)
394dc806bd4SElizabeth Figura {
395dc806bd4SElizabeth Figura 	__u32 sum;
396dc806bd4SElizabeth Figura 
397cdbb9978SElizabeth Figura 	ntsync_assert_held(sem);
398dc806bd4SElizabeth Figura 
399dc806bd4SElizabeth Figura 	if (check_add_overflow(sem->u.sem.count, count, &sum) ||
400dc806bd4SElizabeth Figura 	    sum > sem->u.sem.max)
401dc806bd4SElizabeth Figura 		return -EOVERFLOW;
402dc806bd4SElizabeth Figura 
403dc806bd4SElizabeth Figura 	sem->u.sem.count = sum;
404dc806bd4SElizabeth Figura 	return 0;
405dc806bd4SElizabeth Figura }
406dc806bd4SElizabeth Figura 
ntsync_sem_release(struct ntsync_obj * sem,void __user * argp)4075ec43d6bSElizabeth Figura static int ntsync_sem_release(struct ntsync_obj *sem, void __user *argp)
408dc806bd4SElizabeth Figura {
409cdbb9978SElizabeth Figura 	struct ntsync_device *dev = sem->dev;
410dc806bd4SElizabeth Figura 	__u32 __user *user_args = argp;
411dc806bd4SElizabeth Figura 	__u32 prev_count;
412dc806bd4SElizabeth Figura 	__u32 args;
413cdbb9978SElizabeth Figura 	bool all;
414dc806bd4SElizabeth Figura 	int ret;
415dc806bd4SElizabeth Figura 
416dc806bd4SElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
417dc806bd4SElizabeth Figura 		return -EFAULT;
418dc806bd4SElizabeth Figura 
419dc806bd4SElizabeth Figura 	if (sem->type != NTSYNC_TYPE_SEM)
420dc806bd4SElizabeth Figura 		return -EINVAL;
421dc806bd4SElizabeth Figura 
422cdbb9978SElizabeth Figura 	all = ntsync_lock_obj(dev, sem);
423dc806bd4SElizabeth Figura 
424dc806bd4SElizabeth Figura 	prev_count = sem->u.sem.count;
425cdbb9978SElizabeth Figura 	ret = release_sem_state(sem, args);
426cdbb9978SElizabeth Figura 	if (!ret) {
427cdbb9978SElizabeth Figura 		if (all)
428cdbb9978SElizabeth Figura 			try_wake_all_obj(dev, sem);
429b4a7b5feSElizabeth Figura 		try_wake_any_sem(sem);
430cdbb9978SElizabeth Figura 	}
431dc806bd4SElizabeth Figura 
432cdbb9978SElizabeth Figura 	ntsync_unlock_obj(dev, sem, all);
433dc806bd4SElizabeth Figura 
434dc806bd4SElizabeth Figura 	if (!ret && put_user(prev_count, user_args))
435dc806bd4SElizabeth Figura 		ret = -EFAULT;
436dc806bd4SElizabeth Figura 
437dc806bd4SElizabeth Figura 	return ret;
438dc806bd4SElizabeth Figura }
439dc806bd4SElizabeth Figura 
44031ca7bb8SElizabeth Figura /*
44131ca7bb8SElizabeth Figura  * Actually change the mutex state, returning -EPERM if not the owner.
44231ca7bb8SElizabeth Figura  */
unlock_mutex_state(struct ntsync_obj * mutex,const struct ntsync_mutex_args * args)44331ca7bb8SElizabeth Figura static int unlock_mutex_state(struct ntsync_obj *mutex,
44431ca7bb8SElizabeth Figura 			      const struct ntsync_mutex_args *args)
44531ca7bb8SElizabeth Figura {
44631ca7bb8SElizabeth Figura 	ntsync_assert_held(mutex);
44731ca7bb8SElizabeth Figura 
44831ca7bb8SElizabeth Figura 	if (mutex->u.mutex.owner != args->owner)
44931ca7bb8SElizabeth Figura 		return -EPERM;
45031ca7bb8SElizabeth Figura 
45131ca7bb8SElizabeth Figura 	if (!--mutex->u.mutex.count)
45231ca7bb8SElizabeth Figura 		mutex->u.mutex.owner = 0;
45331ca7bb8SElizabeth Figura 	return 0;
45431ca7bb8SElizabeth Figura }
45531ca7bb8SElizabeth Figura 
ntsync_mutex_unlock(struct ntsync_obj * mutex,void __user * argp)45631ca7bb8SElizabeth Figura static int ntsync_mutex_unlock(struct ntsync_obj *mutex, void __user *argp)
45731ca7bb8SElizabeth Figura {
45831ca7bb8SElizabeth Figura 	struct ntsync_mutex_args __user *user_args = argp;
45931ca7bb8SElizabeth Figura 	struct ntsync_device *dev = mutex->dev;
46031ca7bb8SElizabeth Figura 	struct ntsync_mutex_args args;
46131ca7bb8SElizabeth Figura 	__u32 prev_count;
46231ca7bb8SElizabeth Figura 	bool all;
46331ca7bb8SElizabeth Figura 	int ret;
46431ca7bb8SElizabeth Figura 
46531ca7bb8SElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
46631ca7bb8SElizabeth Figura 		return -EFAULT;
46731ca7bb8SElizabeth Figura 	if (!args.owner)
46831ca7bb8SElizabeth Figura 		return -EINVAL;
46931ca7bb8SElizabeth Figura 
47031ca7bb8SElizabeth Figura 	if (mutex->type != NTSYNC_TYPE_MUTEX)
47131ca7bb8SElizabeth Figura 		return -EINVAL;
47231ca7bb8SElizabeth Figura 
47331ca7bb8SElizabeth Figura 	all = ntsync_lock_obj(dev, mutex);
47431ca7bb8SElizabeth Figura 
47531ca7bb8SElizabeth Figura 	prev_count = mutex->u.mutex.count;
47631ca7bb8SElizabeth Figura 	ret = unlock_mutex_state(mutex, &args);
47731ca7bb8SElizabeth Figura 	if (!ret) {
47831ca7bb8SElizabeth Figura 		if (all)
47931ca7bb8SElizabeth Figura 			try_wake_all_obj(dev, mutex);
48031ca7bb8SElizabeth Figura 		try_wake_any_mutex(mutex);
48131ca7bb8SElizabeth Figura 	}
48231ca7bb8SElizabeth Figura 
48331ca7bb8SElizabeth Figura 	ntsync_unlock_obj(dev, mutex, all);
48431ca7bb8SElizabeth Figura 
48531ca7bb8SElizabeth Figura 	if (!ret && put_user(prev_count, &user_args->count))
48631ca7bb8SElizabeth Figura 		ret = -EFAULT;
48731ca7bb8SElizabeth Figura 
48831ca7bb8SElizabeth Figura 	return ret;
48931ca7bb8SElizabeth Figura }
49031ca7bb8SElizabeth Figura 
491ecc2ee36SElizabeth Figura /*
492ecc2ee36SElizabeth Figura  * Actually change the mutex state to mark its owner as dead,
493ecc2ee36SElizabeth Figura  * returning -EPERM if not the owner.
494ecc2ee36SElizabeth Figura  */
kill_mutex_state(struct ntsync_obj * mutex,__u32 owner)495ecc2ee36SElizabeth Figura static int kill_mutex_state(struct ntsync_obj *mutex, __u32 owner)
496ecc2ee36SElizabeth Figura {
497ecc2ee36SElizabeth Figura 	ntsync_assert_held(mutex);
498ecc2ee36SElizabeth Figura 
499ecc2ee36SElizabeth Figura 	if (mutex->u.mutex.owner != owner)
500ecc2ee36SElizabeth Figura 		return -EPERM;
501ecc2ee36SElizabeth Figura 
502ecc2ee36SElizabeth Figura 	mutex->u.mutex.ownerdead = true;
503ecc2ee36SElizabeth Figura 	mutex->u.mutex.owner = 0;
504ecc2ee36SElizabeth Figura 	mutex->u.mutex.count = 0;
505ecc2ee36SElizabeth Figura 	return 0;
506ecc2ee36SElizabeth Figura }
507ecc2ee36SElizabeth Figura 
ntsync_mutex_kill(struct ntsync_obj * mutex,void __user * argp)508ecc2ee36SElizabeth Figura static int ntsync_mutex_kill(struct ntsync_obj *mutex, void __user *argp)
509ecc2ee36SElizabeth Figura {
510ecc2ee36SElizabeth Figura 	struct ntsync_device *dev = mutex->dev;
511ecc2ee36SElizabeth Figura 	__u32 owner;
512ecc2ee36SElizabeth Figura 	bool all;
513ecc2ee36SElizabeth Figura 	int ret;
514ecc2ee36SElizabeth Figura 
515ecc2ee36SElizabeth Figura 	if (get_user(owner, (__u32 __user *)argp))
516ecc2ee36SElizabeth Figura 		return -EFAULT;
517ecc2ee36SElizabeth Figura 	if (!owner)
518ecc2ee36SElizabeth Figura 		return -EINVAL;
519ecc2ee36SElizabeth Figura 
520ecc2ee36SElizabeth Figura 	if (mutex->type != NTSYNC_TYPE_MUTEX)
521ecc2ee36SElizabeth Figura 		return -EINVAL;
522ecc2ee36SElizabeth Figura 
523ecc2ee36SElizabeth Figura 	all = ntsync_lock_obj(dev, mutex);
524ecc2ee36SElizabeth Figura 
525ecc2ee36SElizabeth Figura 	ret = kill_mutex_state(mutex, owner);
526ecc2ee36SElizabeth Figura 	if (!ret) {
527ecc2ee36SElizabeth Figura 		if (all)
528ecc2ee36SElizabeth Figura 			try_wake_all_obj(dev, mutex);
529ecc2ee36SElizabeth Figura 		try_wake_any_mutex(mutex);
530ecc2ee36SElizabeth Figura 	}
531ecc2ee36SElizabeth Figura 
532ecc2ee36SElizabeth Figura 	ntsync_unlock_obj(dev, mutex, all);
533ecc2ee36SElizabeth Figura 
534ecc2ee36SElizabeth Figura 	return ret;
535ecc2ee36SElizabeth Figura }
536ecc2ee36SElizabeth Figura 
ntsync_event_set(struct ntsync_obj * event,void __user * argp,bool pulse)53712b29d30SElizabeth Figura static int ntsync_event_set(struct ntsync_obj *event, void __user *argp, bool pulse)
5382dcba6fcSElizabeth Figura {
5392dcba6fcSElizabeth Figura 	struct ntsync_device *dev = event->dev;
5402dcba6fcSElizabeth Figura 	__u32 prev_state;
5412dcba6fcSElizabeth Figura 	bool all;
5422dcba6fcSElizabeth Figura 
5432dcba6fcSElizabeth Figura 	if (event->type != NTSYNC_TYPE_EVENT)
5442dcba6fcSElizabeth Figura 		return -EINVAL;
5452dcba6fcSElizabeth Figura 
5462dcba6fcSElizabeth Figura 	all = ntsync_lock_obj(dev, event);
5472dcba6fcSElizabeth Figura 
5482dcba6fcSElizabeth Figura 	prev_state = event->u.event.signaled;
5492dcba6fcSElizabeth Figura 	event->u.event.signaled = true;
5502dcba6fcSElizabeth Figura 	if (all)
5512dcba6fcSElizabeth Figura 		try_wake_all_obj(dev, event);
5522dcba6fcSElizabeth Figura 	try_wake_any_event(event);
55312b29d30SElizabeth Figura 	if (pulse)
55412b29d30SElizabeth Figura 		event->u.event.signaled = false;
5552dcba6fcSElizabeth Figura 
5562dcba6fcSElizabeth Figura 	ntsync_unlock_obj(dev, event, all);
5572dcba6fcSElizabeth Figura 
5582dcba6fcSElizabeth Figura 	if (put_user(prev_state, (__u32 __user *)argp))
5592dcba6fcSElizabeth Figura 		return -EFAULT;
5602dcba6fcSElizabeth Figura 
5612dcba6fcSElizabeth Figura 	return 0;
5622dcba6fcSElizabeth Figura }
5632dcba6fcSElizabeth Figura 
ntsync_event_reset(struct ntsync_obj * event,void __user * argp)564bbb97975SElizabeth Figura static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
565bbb97975SElizabeth Figura {
566bbb97975SElizabeth Figura 	struct ntsync_device *dev = event->dev;
567bbb97975SElizabeth Figura 	__u32 prev_state;
568bbb97975SElizabeth Figura 	bool all;
569bbb97975SElizabeth Figura 
570bbb97975SElizabeth Figura 	if (event->type != NTSYNC_TYPE_EVENT)
571bbb97975SElizabeth Figura 		return -EINVAL;
572bbb97975SElizabeth Figura 
573bbb97975SElizabeth Figura 	all = ntsync_lock_obj(dev, event);
574bbb97975SElizabeth Figura 
575bbb97975SElizabeth Figura 	prev_state = event->u.event.signaled;
576bbb97975SElizabeth Figura 	event->u.event.signaled = false;
577bbb97975SElizabeth Figura 
578bbb97975SElizabeth Figura 	ntsync_unlock_obj(dev, event, all);
579bbb97975SElizabeth Figura 
580bbb97975SElizabeth Figura 	if (put_user(prev_state, (__u32 __user *)argp))
581bbb97975SElizabeth Figura 		return -EFAULT;
582bbb97975SElizabeth Figura 
583bbb97975SElizabeth Figura 	return 0;
584bbb97975SElizabeth Figura }
585bbb97975SElizabeth Figura 
ntsync_sem_read(struct ntsync_obj * sem,void __user * argp)586a948f417SElizabeth Figura static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
587a948f417SElizabeth Figura {
588a948f417SElizabeth Figura 	struct ntsync_sem_args __user *user_args = argp;
589a948f417SElizabeth Figura 	struct ntsync_device *dev = sem->dev;
590a948f417SElizabeth Figura 	struct ntsync_sem_args args;
591a948f417SElizabeth Figura 	bool all;
592a948f417SElizabeth Figura 
593a948f417SElizabeth Figura 	if (sem->type != NTSYNC_TYPE_SEM)
594a948f417SElizabeth Figura 		return -EINVAL;
595a948f417SElizabeth Figura 
596a948f417SElizabeth Figura 	all = ntsync_lock_obj(dev, sem);
597a948f417SElizabeth Figura 
598a948f417SElizabeth Figura 	args.count = sem->u.sem.count;
599a948f417SElizabeth Figura 	args.max = sem->u.sem.max;
600a948f417SElizabeth Figura 
601a948f417SElizabeth Figura 	ntsync_unlock_obj(dev, sem, all);
602a948f417SElizabeth Figura 
603a948f417SElizabeth Figura 	if (copy_to_user(user_args, &args, sizeof(args)))
604a948f417SElizabeth Figura 		return -EFAULT;
605a948f417SElizabeth Figura 	return 0;
606a948f417SElizabeth Figura }
607a948f417SElizabeth Figura 
ntsync_mutex_read(struct ntsync_obj * mutex,void __user * argp)6080b3c3144SElizabeth Figura static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
6090b3c3144SElizabeth Figura {
6100b3c3144SElizabeth Figura 	struct ntsync_mutex_args __user *user_args = argp;
6110b3c3144SElizabeth Figura 	struct ntsync_device *dev = mutex->dev;
6120b3c3144SElizabeth Figura 	struct ntsync_mutex_args args;
6130b3c3144SElizabeth Figura 	bool all;
6140b3c3144SElizabeth Figura 	int ret;
6150b3c3144SElizabeth Figura 
6160b3c3144SElizabeth Figura 	if (mutex->type != NTSYNC_TYPE_MUTEX)
6170b3c3144SElizabeth Figura 		return -EINVAL;
6180b3c3144SElizabeth Figura 
6190b3c3144SElizabeth Figura 	all = ntsync_lock_obj(dev, mutex);
6200b3c3144SElizabeth Figura 
6210b3c3144SElizabeth Figura 	args.count = mutex->u.mutex.count;
6220b3c3144SElizabeth Figura 	args.owner = mutex->u.mutex.owner;
6230b3c3144SElizabeth Figura 	ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
6240b3c3144SElizabeth Figura 
6250b3c3144SElizabeth Figura 	ntsync_unlock_obj(dev, mutex, all);
6260b3c3144SElizabeth Figura 
6270b3c3144SElizabeth Figura 	if (copy_to_user(user_args, &args, sizeof(args)))
6280b3c3144SElizabeth Figura 		return -EFAULT;
6290b3c3144SElizabeth Figura 	return ret;
6300b3c3144SElizabeth Figura }
6310b3c3144SElizabeth Figura 
ntsync_event_read(struct ntsync_obj * event,void __user * argp)632e864071aSElizabeth Figura static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
633e864071aSElizabeth Figura {
634e864071aSElizabeth Figura 	struct ntsync_event_args __user *user_args = argp;
635e864071aSElizabeth Figura 	struct ntsync_device *dev = event->dev;
636e864071aSElizabeth Figura 	struct ntsync_event_args args;
637e864071aSElizabeth Figura 	bool all;
638e864071aSElizabeth Figura 
639e864071aSElizabeth Figura 	if (event->type != NTSYNC_TYPE_EVENT)
640e864071aSElizabeth Figura 		return -EINVAL;
641e864071aSElizabeth Figura 
642e864071aSElizabeth Figura 	all = ntsync_lock_obj(dev, event);
643e864071aSElizabeth Figura 
644e864071aSElizabeth Figura 	args.manual = event->u.event.manual;
645e864071aSElizabeth Figura 	args.signaled = event->u.event.signaled;
646e864071aSElizabeth Figura 
647e864071aSElizabeth Figura 	ntsync_unlock_obj(dev, event, all);
648e864071aSElizabeth Figura 
649e864071aSElizabeth Figura 	if (copy_to_user(user_args, &args, sizeof(args)))
650e864071aSElizabeth Figura 		return -EFAULT;
651e864071aSElizabeth Figura 	return 0;
652e864071aSElizabeth Figura }
653e864071aSElizabeth Figura 
ntsync_free_obj(struct ntsync_obj * obj)6540e7d523bSAl Viro static void ntsync_free_obj(struct ntsync_obj *obj)
655b46271ecSElizabeth Figura {
656b46271ecSElizabeth Figura 	fput(obj->dev->file);
657b46271ecSElizabeth Figura 	kfree(obj);
6580e7d523bSAl Viro }
659b46271ecSElizabeth Figura 
ntsync_obj_release(struct inode * inode,struct file * file)6600e7d523bSAl Viro static int ntsync_obj_release(struct inode *inode, struct file *file)
6610e7d523bSAl Viro {
6620e7d523bSAl Viro 	ntsync_free_obj(file->private_data);
663b46271ecSElizabeth Figura 	return 0;
664b46271ecSElizabeth Figura }
665b46271ecSElizabeth Figura 
ntsync_obj_ioctl(struct file * file,unsigned int cmd,unsigned long parm)666dc806bd4SElizabeth Figura static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
667dc806bd4SElizabeth Figura 			     unsigned long parm)
668dc806bd4SElizabeth Figura {
669dc806bd4SElizabeth Figura 	struct ntsync_obj *obj = file->private_data;
670dc806bd4SElizabeth Figura 	void __user *argp = (void __user *)parm;
671dc806bd4SElizabeth Figura 
672dc806bd4SElizabeth Figura 	switch (cmd) {
6735ec43d6bSElizabeth Figura 	case NTSYNC_IOC_SEM_RELEASE:
6745ec43d6bSElizabeth Figura 		return ntsync_sem_release(obj, argp);
675a948f417SElizabeth Figura 	case NTSYNC_IOC_SEM_READ:
676a948f417SElizabeth Figura 		return ntsync_sem_read(obj, argp);
67731ca7bb8SElizabeth Figura 	case NTSYNC_IOC_MUTEX_UNLOCK:
67831ca7bb8SElizabeth Figura 		return ntsync_mutex_unlock(obj, argp);
679ecc2ee36SElizabeth Figura 	case NTSYNC_IOC_MUTEX_KILL:
680ecc2ee36SElizabeth Figura 		return ntsync_mutex_kill(obj, argp);
6810b3c3144SElizabeth Figura 	case NTSYNC_IOC_MUTEX_READ:
6820b3c3144SElizabeth Figura 		return ntsync_mutex_read(obj, argp);
6832dcba6fcSElizabeth Figura 	case NTSYNC_IOC_EVENT_SET:
68412b29d30SElizabeth Figura 		return ntsync_event_set(obj, argp, false);
685bbb97975SElizabeth Figura 	case NTSYNC_IOC_EVENT_RESET:
686bbb97975SElizabeth Figura 		return ntsync_event_reset(obj, argp);
68712b29d30SElizabeth Figura 	case NTSYNC_IOC_EVENT_PULSE:
68812b29d30SElizabeth Figura 		return ntsync_event_set(obj, argp, true);
689e864071aSElizabeth Figura 	case NTSYNC_IOC_EVENT_READ:
690e864071aSElizabeth Figura 		return ntsync_event_read(obj, argp);
691dc806bd4SElizabeth Figura 	default:
692dc806bd4SElizabeth Figura 		return -ENOIOCTLCMD;
693dc806bd4SElizabeth Figura 	}
694dc806bd4SElizabeth Figura }
695dc806bd4SElizabeth Figura 
696b46271ecSElizabeth Figura static const struct file_operations ntsync_obj_fops = {
697b46271ecSElizabeth Figura 	.owner		= THIS_MODULE,
698b46271ecSElizabeth Figura 	.release	= ntsync_obj_release,
699dc806bd4SElizabeth Figura 	.unlocked_ioctl	= ntsync_obj_ioctl,
700dc806bd4SElizabeth Figura 	.compat_ioctl	= compat_ptr_ioctl,
701b46271ecSElizabeth Figura };
702b46271ecSElizabeth Figura 
ntsync_alloc_obj(struct ntsync_device * dev,enum ntsync_type type)703b46271ecSElizabeth Figura static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
704b46271ecSElizabeth Figura 					   enum ntsync_type type)
705b46271ecSElizabeth Figura {
706b46271ecSElizabeth Figura 	struct ntsync_obj *obj;
707b46271ecSElizabeth Figura 
708b46271ecSElizabeth Figura 	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
709b46271ecSElizabeth Figura 	if (!obj)
710b46271ecSElizabeth Figura 		return NULL;
711b46271ecSElizabeth Figura 	obj->type = type;
712b46271ecSElizabeth Figura 	obj->dev = dev;
713b46271ecSElizabeth Figura 	get_file(dev->file);
714dc806bd4SElizabeth Figura 	spin_lock_init(&obj->lock);
715b4a7b5feSElizabeth Figura 	INIT_LIST_HEAD(&obj->any_waiters);
716cdbb9978SElizabeth Figura 	INIT_LIST_HEAD(&obj->all_waiters);
717cdbb9978SElizabeth Figura 	atomic_set(&obj->all_hint, 0);
718b46271ecSElizabeth Figura 
719b46271ecSElizabeth Figura 	return obj;
720b46271ecSElizabeth Figura }
721b46271ecSElizabeth Figura 
ntsync_obj_get_fd(struct ntsync_obj * obj)722b46271ecSElizabeth Figura static int ntsync_obj_get_fd(struct ntsync_obj *obj)
723b46271ecSElizabeth Figura {
724b46271ecSElizabeth Figura 	struct file *file;
725b46271ecSElizabeth Figura 	int fd;
726b46271ecSElizabeth Figura 
727b46271ecSElizabeth Figura 	fd = get_unused_fd_flags(O_CLOEXEC);
728b46271ecSElizabeth Figura 	if (fd < 0)
729b46271ecSElizabeth Figura 		return fd;
730b46271ecSElizabeth Figura 	file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
731b46271ecSElizabeth Figura 	if (IS_ERR(file)) {
732b46271ecSElizabeth Figura 		put_unused_fd(fd);
733b46271ecSElizabeth Figura 		return PTR_ERR(file);
734b46271ecSElizabeth Figura 	}
735b46271ecSElizabeth Figura 	obj->file = file;
736b46271ecSElizabeth Figura 	fd_install(fd, file);
737b46271ecSElizabeth Figura 
738b46271ecSElizabeth Figura 	return fd;
739b46271ecSElizabeth Figura }
740b46271ecSElizabeth Figura 
ntsync_create_sem(struct ntsync_device * dev,void __user * argp)741b46271ecSElizabeth Figura static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
742b46271ecSElizabeth Figura {
743b46271ecSElizabeth Figura 	struct ntsync_sem_args args;
744b46271ecSElizabeth Figura 	struct ntsync_obj *sem;
745b46271ecSElizabeth Figura 	int fd;
746b46271ecSElizabeth Figura 
747b46271ecSElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
748b46271ecSElizabeth Figura 		return -EFAULT;
749b46271ecSElizabeth Figura 
750b46271ecSElizabeth Figura 	if (args.count > args.max)
751b46271ecSElizabeth Figura 		return -EINVAL;
752b46271ecSElizabeth Figura 
753b46271ecSElizabeth Figura 	sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
754b46271ecSElizabeth Figura 	if (!sem)
755b46271ecSElizabeth Figura 		return -ENOMEM;
756b46271ecSElizabeth Figura 	sem->u.sem.count = args.count;
757b46271ecSElizabeth Figura 	sem->u.sem.max = args.max;
758b46271ecSElizabeth Figura 	fd = ntsync_obj_get_fd(sem);
759d75abf2fSElizabeth Figura 	if (fd < 0)
7600e7d523bSAl Viro 		ntsync_free_obj(sem);
761b46271ecSElizabeth Figura 
762d75abf2fSElizabeth Figura 	return fd;
763b46271ecSElizabeth Figura }
764b46271ecSElizabeth Figura 
ntsync_create_mutex(struct ntsync_device * dev,void __user * argp)7655bc2479aSElizabeth Figura static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
7665bc2479aSElizabeth Figura {
7675bc2479aSElizabeth Figura 	struct ntsync_mutex_args args;
7685bc2479aSElizabeth Figura 	struct ntsync_obj *mutex;
7695bc2479aSElizabeth Figura 	int fd;
7705bc2479aSElizabeth Figura 
7715bc2479aSElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
7725bc2479aSElizabeth Figura 		return -EFAULT;
7735bc2479aSElizabeth Figura 
7745bc2479aSElizabeth Figura 	if (!args.owner != !args.count)
7755bc2479aSElizabeth Figura 		return -EINVAL;
7765bc2479aSElizabeth Figura 
7775bc2479aSElizabeth Figura 	mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX);
7785bc2479aSElizabeth Figura 	if (!mutex)
7795bc2479aSElizabeth Figura 		return -ENOMEM;
7805bc2479aSElizabeth Figura 	mutex->u.mutex.count = args.count;
7815bc2479aSElizabeth Figura 	mutex->u.mutex.owner = args.owner;
7825bc2479aSElizabeth Figura 	fd = ntsync_obj_get_fd(mutex);
7835bc2479aSElizabeth Figura 	if (fd < 0)
784970b9757SElizabeth Figura 		ntsync_free_obj(mutex);
7855bc2479aSElizabeth Figura 
7865bc2479aSElizabeth Figura 	return fd;
7875bc2479aSElizabeth Figura }
7885bc2479aSElizabeth Figura 
ntsync_create_event(struct ntsync_device * dev,void __user * argp)7894c7404b9SElizabeth Figura static int ntsync_create_event(struct ntsync_device *dev, void __user *argp)
7904c7404b9SElizabeth Figura {
7914c7404b9SElizabeth Figura 	struct ntsync_event_args args;
7924c7404b9SElizabeth Figura 	struct ntsync_obj *event;
7934c7404b9SElizabeth Figura 	int fd;
7944c7404b9SElizabeth Figura 
7954c7404b9SElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
7964c7404b9SElizabeth Figura 		return -EFAULT;
7974c7404b9SElizabeth Figura 
7984c7404b9SElizabeth Figura 	event = ntsync_alloc_obj(dev, NTSYNC_TYPE_EVENT);
7994c7404b9SElizabeth Figura 	if (!event)
8004c7404b9SElizabeth Figura 		return -ENOMEM;
8014c7404b9SElizabeth Figura 	event->u.event.manual = args.manual;
8024c7404b9SElizabeth Figura 	event->u.event.signaled = args.signaled;
8034c7404b9SElizabeth Figura 	fd = ntsync_obj_get_fd(event);
8044c7404b9SElizabeth Figura 	if (fd < 0)
805970b9757SElizabeth Figura 		ntsync_free_obj(event);
8064c7404b9SElizabeth Figura 
8074c7404b9SElizabeth Figura 	return fd;
8084c7404b9SElizabeth Figura }
8094c7404b9SElizabeth Figura 
get_obj(struct ntsync_device * dev,int fd)810b4a7b5feSElizabeth Figura static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
811b4a7b5feSElizabeth Figura {
812b4a7b5feSElizabeth Figura 	struct file *file = fget(fd);
813b4a7b5feSElizabeth Figura 	struct ntsync_obj *obj;
814b4a7b5feSElizabeth Figura 
815b4a7b5feSElizabeth Figura 	if (!file)
816b4a7b5feSElizabeth Figura 		return NULL;
817b4a7b5feSElizabeth Figura 
818b4a7b5feSElizabeth Figura 	if (file->f_op != &ntsync_obj_fops) {
819b4a7b5feSElizabeth Figura 		fput(file);
820b4a7b5feSElizabeth Figura 		return NULL;
821b4a7b5feSElizabeth Figura 	}
822b4a7b5feSElizabeth Figura 
823b4a7b5feSElizabeth Figura 	obj = file->private_data;
824b4a7b5feSElizabeth Figura 	if (obj->dev != dev) {
825b4a7b5feSElizabeth Figura 		fput(file);
826b4a7b5feSElizabeth Figura 		return NULL;
827b4a7b5feSElizabeth Figura 	}
828b4a7b5feSElizabeth Figura 
829b4a7b5feSElizabeth Figura 	return obj;
830b4a7b5feSElizabeth Figura }
831b4a7b5feSElizabeth Figura 
put_obj(struct ntsync_obj * obj)832b4a7b5feSElizabeth Figura static void put_obj(struct ntsync_obj *obj)
833b4a7b5feSElizabeth Figura {
834b4a7b5feSElizabeth Figura 	fput(obj->file);
835b4a7b5feSElizabeth Figura }
836b4a7b5feSElizabeth Figura 
ntsync_schedule(const struct ntsync_q * q,const struct ntsync_wait_args * args)837b4a7b5feSElizabeth Figura static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
838b4a7b5feSElizabeth Figura {
839b4a7b5feSElizabeth Figura 	ktime_t timeout = ns_to_ktime(args->timeout);
840b4a7b5feSElizabeth Figura 	clockid_t clock = CLOCK_MONOTONIC;
841b4a7b5feSElizabeth Figura 	ktime_t *timeout_ptr;
842b4a7b5feSElizabeth Figura 	int ret = 0;
843b4a7b5feSElizabeth Figura 
844b4a7b5feSElizabeth Figura 	timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
845b4a7b5feSElizabeth Figura 
846b4a7b5feSElizabeth Figura 	if (args->flags & NTSYNC_WAIT_REALTIME)
847b4a7b5feSElizabeth Figura 		clock = CLOCK_REALTIME;
848b4a7b5feSElizabeth Figura 
849b4a7b5feSElizabeth Figura 	do {
850b4a7b5feSElizabeth Figura 		if (signal_pending(current)) {
851b4a7b5feSElizabeth Figura 			ret = -ERESTARTSYS;
852b4a7b5feSElizabeth Figura 			break;
853b4a7b5feSElizabeth Figura 		}
854b4a7b5feSElizabeth Figura 
855b4a7b5feSElizabeth Figura 		set_current_state(TASK_INTERRUPTIBLE);
856b4a7b5feSElizabeth Figura 		if (atomic_read(&q->signaled) != -1) {
857b4a7b5feSElizabeth Figura 			ret = 0;
858b4a7b5feSElizabeth Figura 			break;
859b4a7b5feSElizabeth Figura 		}
860b4a7b5feSElizabeth Figura 		ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
861b4a7b5feSElizabeth Figura 	} while (ret < 0);
862b4a7b5feSElizabeth Figura 	__set_current_state(TASK_RUNNING);
863b4a7b5feSElizabeth Figura 
864b4a7b5feSElizabeth Figura 	return ret;
865b4a7b5feSElizabeth Figura }
866b4a7b5feSElizabeth Figura 
867b4a7b5feSElizabeth Figura /*
868b4a7b5feSElizabeth Figura  * Allocate and initialize the ntsync_q structure, but do not queue us yet.
869b4a7b5feSElizabeth Figura  */
setup_wait(struct ntsync_device * dev,const struct ntsync_wait_args * args,bool all,struct ntsync_q ** ret_q)870b4a7b5feSElizabeth Figura static int setup_wait(struct ntsync_device *dev,
871cdbb9978SElizabeth Figura 		      const struct ntsync_wait_args *args, bool all,
872b4a7b5feSElizabeth Figura 		      struct ntsync_q **ret_q)
873b4a7b5feSElizabeth Figura {
874a138179aSElizabeth Figura 	int fds[NTSYNC_MAX_WAIT_COUNT + 1];
875b4a7b5feSElizabeth Figura 	const __u32 count = args->count;
876*92527e47SElizabeth Figura 	size_t size = array_size(count, sizeof(fds[0]));
877b4a7b5feSElizabeth Figura 	struct ntsync_q *q;
878a138179aSElizabeth Figura 	__u32 total_count;
879b4a7b5feSElizabeth Figura 	__u32 i, j;
880b4a7b5feSElizabeth Figura 
881a138179aSElizabeth Figura 	if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
882b4a7b5feSElizabeth Figura 		return -EINVAL;
883b4a7b5feSElizabeth Figura 
884*92527e47SElizabeth Figura 	if (size >= sizeof(fds))
885b4a7b5feSElizabeth Figura 		return -EINVAL;
886b4a7b5feSElizabeth Figura 
887a138179aSElizabeth Figura 	total_count = count;
888a138179aSElizabeth Figura 	if (args->alert)
889a138179aSElizabeth Figura 		total_count++;
890a138179aSElizabeth Figura 
891*92527e47SElizabeth Figura 	if (copy_from_user(fds, u64_to_user_ptr(args->objs), size))
892b4a7b5feSElizabeth Figura 		return -EFAULT;
893a138179aSElizabeth Figura 	if (args->alert)
894a138179aSElizabeth Figura 		fds[count] = args->alert;
895b4a7b5feSElizabeth Figura 
896a138179aSElizabeth Figura 	q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL);
897b4a7b5feSElizabeth Figura 	if (!q)
898b4a7b5feSElizabeth Figura 		return -ENOMEM;
899b4a7b5feSElizabeth Figura 	q->task = current;
9005bc2479aSElizabeth Figura 	q->owner = args->owner;
901b4a7b5feSElizabeth Figura 	atomic_set(&q->signaled, -1);
902cdbb9978SElizabeth Figura 	q->all = all;
903ecc2ee36SElizabeth Figura 	q->ownerdead = false;
904b4a7b5feSElizabeth Figura 	q->count = count;
905b4a7b5feSElizabeth Figura 
906a138179aSElizabeth Figura 	for (i = 0; i < total_count; i++) {
907b4a7b5feSElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[i];
908b4a7b5feSElizabeth Figura 		struct ntsync_obj *obj = get_obj(dev, fds[i]);
909b4a7b5feSElizabeth Figura 
910b4a7b5feSElizabeth Figura 		if (!obj)
911b4a7b5feSElizabeth Figura 			goto err;
912b4a7b5feSElizabeth Figura 
913cdbb9978SElizabeth Figura 		if (all) {
914cdbb9978SElizabeth Figura 			/* Check that the objects are all distinct. */
915cdbb9978SElizabeth Figura 			for (j = 0; j < i; j++) {
916cdbb9978SElizabeth Figura 				if (obj == q->entries[j].obj) {
917cdbb9978SElizabeth Figura 					put_obj(obj);
918cdbb9978SElizabeth Figura 					goto err;
919cdbb9978SElizabeth Figura 				}
920cdbb9978SElizabeth Figura 			}
921cdbb9978SElizabeth Figura 		}
922cdbb9978SElizabeth Figura 
923b4a7b5feSElizabeth Figura 		entry->obj = obj;
924b4a7b5feSElizabeth Figura 		entry->q = q;
925b4a7b5feSElizabeth Figura 		entry->index = i;
926b4a7b5feSElizabeth Figura 	}
927b4a7b5feSElizabeth Figura 
928b4a7b5feSElizabeth Figura 	*ret_q = q;
929b4a7b5feSElizabeth Figura 	return 0;
930b4a7b5feSElizabeth Figura 
931b4a7b5feSElizabeth Figura err:
932b4a7b5feSElizabeth Figura 	for (j = 0; j < i; j++)
933b4a7b5feSElizabeth Figura 		put_obj(q->entries[j].obj);
934b4a7b5feSElizabeth Figura 	kfree(q);
935b4a7b5feSElizabeth Figura 	return -EINVAL;
936b4a7b5feSElizabeth Figura }
937b4a7b5feSElizabeth Figura 
try_wake_any_obj(struct ntsync_obj * obj)938b4a7b5feSElizabeth Figura static void try_wake_any_obj(struct ntsync_obj *obj)
939b4a7b5feSElizabeth Figura {
940b4a7b5feSElizabeth Figura 	switch (obj->type) {
941b4a7b5feSElizabeth Figura 	case NTSYNC_TYPE_SEM:
942b4a7b5feSElizabeth Figura 		try_wake_any_sem(obj);
943b4a7b5feSElizabeth Figura 		break;
9445bc2479aSElizabeth Figura 	case NTSYNC_TYPE_MUTEX:
9455bc2479aSElizabeth Figura 		try_wake_any_mutex(obj);
9465bc2479aSElizabeth Figura 		break;
9474c7404b9SElizabeth Figura 	case NTSYNC_TYPE_EVENT:
9484c7404b9SElizabeth Figura 		try_wake_any_event(obj);
9494c7404b9SElizabeth Figura 		break;
950b4a7b5feSElizabeth Figura 	}
951b4a7b5feSElizabeth Figura }
952b4a7b5feSElizabeth Figura 
ntsync_wait_any(struct ntsync_device * dev,void __user * argp)953b4a7b5feSElizabeth Figura static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
954b4a7b5feSElizabeth Figura {
955b4a7b5feSElizabeth Figura 	struct ntsync_wait_args args;
956a138179aSElizabeth Figura 	__u32 i, total_count;
957b4a7b5feSElizabeth Figura 	struct ntsync_q *q;
958b4a7b5feSElizabeth Figura 	int signaled;
959cdbb9978SElizabeth Figura 	bool all;
960b4a7b5feSElizabeth Figura 	int ret;
961b4a7b5feSElizabeth Figura 
962b4a7b5feSElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
963b4a7b5feSElizabeth Figura 		return -EFAULT;
964b4a7b5feSElizabeth Figura 
965cdbb9978SElizabeth Figura 	ret = setup_wait(dev, &args, false, &q);
966b4a7b5feSElizabeth Figura 	if (ret < 0)
967b4a7b5feSElizabeth Figura 		return ret;
968b4a7b5feSElizabeth Figura 
969a138179aSElizabeth Figura 	total_count = args.count;
970a138179aSElizabeth Figura 	if (args.alert)
971a138179aSElizabeth Figura 		total_count++;
972a138179aSElizabeth Figura 
973b4a7b5feSElizabeth Figura 	/* queue ourselves */
974b4a7b5feSElizabeth Figura 
975a138179aSElizabeth Figura 	for (i = 0; i < total_count; i++) {
976b4a7b5feSElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[i];
977b4a7b5feSElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
978b4a7b5feSElizabeth Figura 
979cdbb9978SElizabeth Figura 		all = ntsync_lock_obj(dev, obj);
980b4a7b5feSElizabeth Figura 		list_add_tail(&entry->node, &obj->any_waiters);
981cdbb9978SElizabeth Figura 		ntsync_unlock_obj(dev, obj, all);
982b4a7b5feSElizabeth Figura 	}
983b4a7b5feSElizabeth Figura 
984a138179aSElizabeth Figura 	/*
985a138179aSElizabeth Figura 	 * Check if we are already signaled.
986a138179aSElizabeth Figura 	 *
987a138179aSElizabeth Figura 	 * Note that the API requires that normal objects are checked before
988a138179aSElizabeth Figura 	 * the alert event. Hence we queue the alert event last, and check
989a138179aSElizabeth Figura 	 * objects in order.
990a138179aSElizabeth Figura 	 */
991b4a7b5feSElizabeth Figura 
992a138179aSElizabeth Figura 	for (i = 0; i < total_count; i++) {
993b4a7b5feSElizabeth Figura 		struct ntsync_obj *obj = q->entries[i].obj;
994b4a7b5feSElizabeth Figura 
995b4a7b5feSElizabeth Figura 		if (atomic_read(&q->signaled) != -1)
996b4a7b5feSElizabeth Figura 			break;
997b4a7b5feSElizabeth Figura 
998cdbb9978SElizabeth Figura 		all = ntsync_lock_obj(dev, obj);
999b4a7b5feSElizabeth Figura 		try_wake_any_obj(obj);
1000cdbb9978SElizabeth Figura 		ntsync_unlock_obj(dev, obj, all);
1001b4a7b5feSElizabeth Figura 	}
1002b4a7b5feSElizabeth Figura 
1003b4a7b5feSElizabeth Figura 	/* sleep */
1004b4a7b5feSElizabeth Figura 
1005b4a7b5feSElizabeth Figura 	ret = ntsync_schedule(q, &args);
1006b4a7b5feSElizabeth Figura 
1007b4a7b5feSElizabeth Figura 	/* and finally, unqueue */
1008b4a7b5feSElizabeth Figura 
1009a138179aSElizabeth Figura 	for (i = 0; i < total_count; i++) {
1010b4a7b5feSElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[i];
1011b4a7b5feSElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
1012b4a7b5feSElizabeth Figura 
1013cdbb9978SElizabeth Figura 		all = ntsync_lock_obj(dev, obj);
1014b4a7b5feSElizabeth Figura 		list_del(&entry->node);
1015cdbb9978SElizabeth Figura 		ntsync_unlock_obj(dev, obj, all);
1016b4a7b5feSElizabeth Figura 
1017b4a7b5feSElizabeth Figura 		put_obj(obj);
1018b4a7b5feSElizabeth Figura 	}
1019b4a7b5feSElizabeth Figura 
1020b4a7b5feSElizabeth Figura 	signaled = atomic_read(&q->signaled);
1021b4a7b5feSElizabeth Figura 	if (signaled != -1) {
1022b4a7b5feSElizabeth Figura 		struct ntsync_wait_args __user *user_args = argp;
1023b4a7b5feSElizabeth Figura 
1024b4a7b5feSElizabeth Figura 		/* even if we caught a signal, we need to communicate success */
1025ecc2ee36SElizabeth Figura 		ret = q->ownerdead ? -EOWNERDEAD : 0;
1026b4a7b5feSElizabeth Figura 
1027b4a7b5feSElizabeth Figura 		if (put_user(signaled, &user_args->index))
1028b4a7b5feSElizabeth Figura 			ret = -EFAULT;
1029b4a7b5feSElizabeth Figura 	} else if (!ret) {
1030b4a7b5feSElizabeth Figura 		ret = -ETIMEDOUT;
1031b4a7b5feSElizabeth Figura 	}
1032b4a7b5feSElizabeth Figura 
1033b4a7b5feSElizabeth Figura 	kfree(q);
1034b4a7b5feSElizabeth Figura 	return ret;
1035b4a7b5feSElizabeth Figura }
1036b4a7b5feSElizabeth Figura 
ntsync_wait_all(struct ntsync_device * dev,void __user * argp)1037cdbb9978SElizabeth Figura static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
1038cdbb9978SElizabeth Figura {
1039cdbb9978SElizabeth Figura 	struct ntsync_wait_args args;
1040cdbb9978SElizabeth Figura 	struct ntsync_q *q;
1041cdbb9978SElizabeth Figura 	int signaled;
1042cdbb9978SElizabeth Figura 	__u32 i;
1043cdbb9978SElizabeth Figura 	int ret;
1044cdbb9978SElizabeth Figura 
1045cdbb9978SElizabeth Figura 	if (copy_from_user(&args, argp, sizeof(args)))
1046cdbb9978SElizabeth Figura 		return -EFAULT;
1047cdbb9978SElizabeth Figura 
1048cdbb9978SElizabeth Figura 	ret = setup_wait(dev, &args, true, &q);
1049cdbb9978SElizabeth Figura 	if (ret < 0)
1050cdbb9978SElizabeth Figura 		return ret;
1051cdbb9978SElizabeth Figura 
1052cdbb9978SElizabeth Figura 	/* queue ourselves */
1053cdbb9978SElizabeth Figura 
1054cdbb9978SElizabeth Figura 	mutex_lock(&dev->wait_all_lock);
1055cdbb9978SElizabeth Figura 
1056cdbb9978SElizabeth Figura 	for (i = 0; i < args.count; i++) {
1057cdbb9978SElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[i];
1058cdbb9978SElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
1059cdbb9978SElizabeth Figura 
1060cdbb9978SElizabeth Figura 		atomic_inc(&obj->all_hint);
1061cdbb9978SElizabeth Figura 
1062cdbb9978SElizabeth Figura 		/*
1063cdbb9978SElizabeth Figura 		 * obj->all_waiters is protected by dev->wait_all_lock rather
1064cdbb9978SElizabeth Figura 		 * than obj->lock, so there is no need to acquire obj->lock
1065cdbb9978SElizabeth Figura 		 * here.
1066cdbb9978SElizabeth Figura 		 */
1067cdbb9978SElizabeth Figura 		list_add_tail(&entry->node, &obj->all_waiters);
1068cdbb9978SElizabeth Figura 	}
1069a138179aSElizabeth Figura 	if (args.alert) {
1070a138179aSElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[args.count];
1071a138179aSElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
1072a138179aSElizabeth Figura 
1073a138179aSElizabeth Figura 		dev_lock_obj(dev, obj);
1074a138179aSElizabeth Figura 		list_add_tail(&entry->node, &obj->any_waiters);
1075a138179aSElizabeth Figura 		dev_unlock_obj(dev, obj);
1076a138179aSElizabeth Figura 	}
1077cdbb9978SElizabeth Figura 
1078cdbb9978SElizabeth Figura 	/* check if we are already signaled */
1079cdbb9978SElizabeth Figura 
1080cdbb9978SElizabeth Figura 	try_wake_all(dev, q, NULL);
1081cdbb9978SElizabeth Figura 
1082cdbb9978SElizabeth Figura 	mutex_unlock(&dev->wait_all_lock);
1083cdbb9978SElizabeth Figura 
1084a138179aSElizabeth Figura 	/*
1085a138179aSElizabeth Figura 	 * Check if the alert event is signaled, making sure to do so only
1086a138179aSElizabeth Figura 	 * after checking if the other objects are signaled.
1087a138179aSElizabeth Figura 	 */
1088a138179aSElizabeth Figura 
1089a138179aSElizabeth Figura 	if (args.alert) {
1090a138179aSElizabeth Figura 		struct ntsync_obj *obj = q->entries[args.count].obj;
1091a138179aSElizabeth Figura 
1092a138179aSElizabeth Figura 		if (atomic_read(&q->signaled) == -1) {
1093a138179aSElizabeth Figura 			bool all = ntsync_lock_obj(dev, obj);
1094a138179aSElizabeth Figura 			try_wake_any_obj(obj);
1095a138179aSElizabeth Figura 			ntsync_unlock_obj(dev, obj, all);
1096a138179aSElizabeth Figura 		}
1097a138179aSElizabeth Figura 	}
1098a138179aSElizabeth Figura 
1099cdbb9978SElizabeth Figura 	/* sleep */
1100cdbb9978SElizabeth Figura 
1101cdbb9978SElizabeth Figura 	ret = ntsync_schedule(q, &args);
1102cdbb9978SElizabeth Figura 
1103cdbb9978SElizabeth Figura 	/* and finally, unqueue */
1104cdbb9978SElizabeth Figura 
1105cdbb9978SElizabeth Figura 	mutex_lock(&dev->wait_all_lock);
1106cdbb9978SElizabeth Figura 
1107cdbb9978SElizabeth Figura 	for (i = 0; i < args.count; i++) {
1108cdbb9978SElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[i];
1109cdbb9978SElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
1110cdbb9978SElizabeth Figura 
1111cdbb9978SElizabeth Figura 		/*
1112cdbb9978SElizabeth Figura 		 * obj->all_waiters is protected by dev->wait_all_lock rather
1113cdbb9978SElizabeth Figura 		 * than obj->lock, so there is no need to acquire it here.
1114cdbb9978SElizabeth Figura 		 */
1115cdbb9978SElizabeth Figura 		list_del(&entry->node);
1116cdbb9978SElizabeth Figura 
1117cdbb9978SElizabeth Figura 		atomic_dec(&obj->all_hint);
1118cdbb9978SElizabeth Figura 
1119cdbb9978SElizabeth Figura 		put_obj(obj);
1120cdbb9978SElizabeth Figura 	}
1121cdbb9978SElizabeth Figura 
1122cdbb9978SElizabeth Figura 	mutex_unlock(&dev->wait_all_lock);
1123cdbb9978SElizabeth Figura 
1124a138179aSElizabeth Figura 	if (args.alert) {
1125a138179aSElizabeth Figura 		struct ntsync_q_entry *entry = &q->entries[args.count];
1126a138179aSElizabeth Figura 		struct ntsync_obj *obj = entry->obj;
1127a138179aSElizabeth Figura 		bool all;
1128a138179aSElizabeth Figura 
1129a138179aSElizabeth Figura 		all = ntsync_lock_obj(dev, obj);
1130a138179aSElizabeth Figura 		list_del(&entry->node);
1131a138179aSElizabeth Figura 		ntsync_unlock_obj(dev, obj, all);
1132a138179aSElizabeth Figura 
1133a138179aSElizabeth Figura 		put_obj(obj);
1134a138179aSElizabeth Figura 	}
1135a138179aSElizabeth Figura 
1136cdbb9978SElizabeth Figura 	signaled = atomic_read(&q->signaled);
1137cdbb9978SElizabeth Figura 	if (signaled != -1) {
1138cdbb9978SElizabeth Figura 		struct ntsync_wait_args __user *user_args = argp;
1139cdbb9978SElizabeth Figura 
1140cdbb9978SElizabeth Figura 		/* even if we caught a signal, we need to communicate success */
1141ecc2ee36SElizabeth Figura 		ret = q->ownerdead ? -EOWNERDEAD : 0;
1142cdbb9978SElizabeth Figura 
1143cdbb9978SElizabeth Figura 		if (put_user(signaled, &user_args->index))
1144cdbb9978SElizabeth Figura 			ret = -EFAULT;
1145cdbb9978SElizabeth Figura 	} else if (!ret) {
1146cdbb9978SElizabeth Figura 		ret = -ETIMEDOUT;
1147cdbb9978SElizabeth Figura 	}
1148cdbb9978SElizabeth Figura 
1149cdbb9978SElizabeth Figura 	kfree(q);
1150cdbb9978SElizabeth Figura 	return ret;
1151cdbb9978SElizabeth Figura }
1152cdbb9978SElizabeth Figura 
ntsync_char_open(struct inode * inode,struct file * file)115325b9cadbSElizabeth Figura static int ntsync_char_open(struct inode *inode, struct file *file)
115425b9cadbSElizabeth Figura {
1155b46271ecSElizabeth Figura 	struct ntsync_device *dev;
1156b46271ecSElizabeth Figura 
1157b46271ecSElizabeth Figura 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1158b46271ecSElizabeth Figura 	if (!dev)
1159b46271ecSElizabeth Figura 		return -ENOMEM;
1160b46271ecSElizabeth Figura 
1161cdbb9978SElizabeth Figura 	mutex_init(&dev->wait_all_lock);
1162cdbb9978SElizabeth Figura 
1163b46271ecSElizabeth Figura 	file->private_data = dev;
1164b46271ecSElizabeth Figura 	dev->file = file;
116525b9cadbSElizabeth Figura 	return nonseekable_open(inode, file);
116625b9cadbSElizabeth Figura }
116725b9cadbSElizabeth Figura 
ntsync_char_release(struct inode * inode,struct file * file)116825b9cadbSElizabeth Figura static int ntsync_char_release(struct inode *inode, struct file *file)
116925b9cadbSElizabeth Figura {
1170b46271ecSElizabeth Figura 	struct ntsync_device *dev = file->private_data;
1171b46271ecSElizabeth Figura 
1172b46271ecSElizabeth Figura 	kfree(dev);
1173b46271ecSElizabeth Figura 
117425b9cadbSElizabeth Figura 	return 0;
117525b9cadbSElizabeth Figura }
117625b9cadbSElizabeth Figura 
ntsync_char_ioctl(struct file * file,unsigned int cmd,unsigned long parm)117725b9cadbSElizabeth Figura static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
117825b9cadbSElizabeth Figura 			      unsigned long parm)
117925b9cadbSElizabeth Figura {
1180b46271ecSElizabeth Figura 	struct ntsync_device *dev = file->private_data;
1181b46271ecSElizabeth Figura 	void __user *argp = (void __user *)parm;
1182b46271ecSElizabeth Figura 
118325b9cadbSElizabeth Figura 	switch (cmd) {
11844c7404b9SElizabeth Figura 	case NTSYNC_IOC_CREATE_EVENT:
11854c7404b9SElizabeth Figura 		return ntsync_create_event(dev, argp);
11865bc2479aSElizabeth Figura 	case NTSYNC_IOC_CREATE_MUTEX:
11875bc2479aSElizabeth Figura 		return ntsync_create_mutex(dev, argp);
1188b46271ecSElizabeth Figura 	case NTSYNC_IOC_CREATE_SEM:
1189b46271ecSElizabeth Figura 		return ntsync_create_sem(dev, argp);
1190cdbb9978SElizabeth Figura 	case NTSYNC_IOC_WAIT_ALL:
1191cdbb9978SElizabeth Figura 		return ntsync_wait_all(dev, argp);
1192b4a7b5feSElizabeth Figura 	case NTSYNC_IOC_WAIT_ANY:
1193b4a7b5feSElizabeth Figura 		return ntsync_wait_any(dev, argp);
119425b9cadbSElizabeth Figura 	default:
119525b9cadbSElizabeth Figura 		return -ENOIOCTLCMD;
119625b9cadbSElizabeth Figura 	}
119725b9cadbSElizabeth Figura }
119825b9cadbSElizabeth Figura 
119925b9cadbSElizabeth Figura static const struct file_operations ntsync_fops = {
120025b9cadbSElizabeth Figura 	.owner		= THIS_MODULE,
120125b9cadbSElizabeth Figura 	.open		= ntsync_char_open,
120225b9cadbSElizabeth Figura 	.release	= ntsync_char_release,
120325b9cadbSElizabeth Figura 	.unlocked_ioctl	= ntsync_char_ioctl,
120425b9cadbSElizabeth Figura 	.compat_ioctl	= compat_ptr_ioctl,
120525b9cadbSElizabeth Figura };
120625b9cadbSElizabeth Figura 
120725b9cadbSElizabeth Figura static struct miscdevice ntsync_misc = {
120825b9cadbSElizabeth Figura 	.minor		= MISC_DYNAMIC_MINOR,
120925b9cadbSElizabeth Figura 	.name		= NTSYNC_NAME,
121025b9cadbSElizabeth Figura 	.fops		= &ntsync_fops,
1211fa2e5581SMike Lothian 	.mode		= 0666,
121225b9cadbSElizabeth Figura };
121325b9cadbSElizabeth Figura 
121425b9cadbSElizabeth Figura module_misc_device(ntsync_misc);
121525b9cadbSElizabeth Figura 
121625b9cadbSElizabeth Figura MODULE_AUTHOR("Elizabeth Figura <[email protected]>");
121725b9cadbSElizabeth Figura MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
121825b9cadbSElizabeth Figura MODULE_LICENSE("GPL");
1219