Skip to content

Commit 22c43c8

Browse files
Michal Nazarewiczgregkh
authored andcommitted
wait_event_interruptible_locked() interface
New wait_event_interruptible{,_exclusive}_locked{,_irq} macros added. They work just like versions without _locked* suffix but require the wait queue's lock to be held. Also __wake_up_locked() is now exported as to pair it with the above macros. The use case of this new facility is when one uses wait queue's lock to protect a data structure. This may be advantageous if the structure needs to be protected by a spinlock anyway. In particular, with additional spinlock the following code has to be used to wait for a condition: spin_lock(&data.lock); ... for (ret = 0; !ret && !(condition); ) { spin_unlock(&data.lock); ret = wait_event_interruptible(data.wqh, (condition)); spin_lock(&data.lock); } ... spin_unlock(&data.lock); This looks bizarre plus wait_event_interruptible() locks the wait queue's lock anyway so there is a unlock+lock sequence where it could be avoided. To avoid those problems and benefit from wait queue's lock, a code similar to the following should be used: /* Waiting */ spin_lock(&data.wqh.lock); ... ret = wait_event_interruptible_locked(data.wqh, (condition)); ... spin_unlock(&data.wqh.lock); /* Waiting exclusively */ spin_lock(&data.whq.lock); ... ret = wait_event_interruptible_exclusive_locked(data.whq, (condition)); ... spin_unlock(&data.whq.lock); /* Waking up */ spin_lock(&data.wqh.lock); ... wake_up_locked(&data.wqh); ... spin_unlock(&data.wqh.lock); When spin_lock_irq() is used matching versions of macros need to be used (*_locked_irq()). Signed-off-by: Michal Nazarewicz <m.nazarewicz@samsung.com> Cc: Kyungmin Park <kyungmin.park@samsung.com> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Takashi Iwai <tiwai@suse.de> Cc: David Howells <dhowells@redhat.com> Cc: Andreas Herrmann <andreas.herrmann3@amd.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Mike Galbraith <efault@gmx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1 parent 24337c1 commit 22c43c8

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

include/linux/wait.h

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,155 @@ do { \
362362
__ret; \
363363
})
364364

365+
366+
#define __wait_event_interruptible_locked(wq, condition, exclusive, irq) \
367+
({ \
368+
int __ret = 0; \
369+
DEFINE_WAIT(__wait); \
370+
if (exclusive) \
371+
__wait.flags |= WQ_FLAG_EXCLUSIVE; \
372+
do { \
373+
if (likely(list_empty(&__wait.task_list))) \
374+
__add_wait_queue_tail(&(wq), &__wait); \
375+
set_current_state(TASK_INTERRUPTIBLE); \
376+
if (signal_pending(current)) { \
377+
__ret = -ERESTARTSYS; \
378+
break; \
379+
} \
380+
if (irq) \
381+
spin_unlock_irq(&(wq).lock); \
382+
else \
383+
spin_unlock(&(wq).lock); \
384+
schedule(); \
385+
if (irq) \
386+
spin_lock_irq(&(wq).lock); \
387+
else \
388+
spin_lock(&(wq).lock); \
389+
} while (!(condition)); \
390+
__remove_wait_queue(&(wq), &__wait); \
391+
__set_current_state(TASK_RUNNING); \
392+
__ret; \
393+
})
394+
395+
396+
/**
397+
* wait_event_interruptible_locked - sleep until a condition gets true
398+
* @wq: the waitqueue to wait on
399+
* @condition: a C expression for the event to wait for
400+
*
401+
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
402+
* @condition evaluates to true or a signal is received.
403+
* The @condition is checked each time the waitqueue @wq is woken up.
404+
*
405+
* It must be called with wq.lock being held. This spinlock is
406+
* unlocked while sleeping but @condition testing is done while lock
407+
* is held and when this macro exits the lock is held.
408+
*
409+
* The lock is locked/unlocked using spin_lock()/spin_unlock()
410+
* functions which must match the way they are locked/unlocked outside
411+
* of this macro.
412+
*
413+
* wake_up_locked() has to be called after changing any variable that could
414+
* change the result of the wait condition.
415+
*
416+
* The function will return -ERESTARTSYS if it was interrupted by a
417+
* signal and 0 if @condition evaluated to true.
418+
*/
419+
#define wait_event_interruptible_locked(wq, condition) \
420+
((condition) \
421+
? 0 : __wait_event_interruptible_locked(wq, condition, 0, 0))
422+
423+
/**
424+
* wait_event_interruptible_locked_irq - sleep until a condition gets true
425+
* @wq: the waitqueue to wait on
426+
* @condition: a C expression for the event to wait for
427+
*
428+
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
429+
* @condition evaluates to true or a signal is received.
430+
* The @condition is checked each time the waitqueue @wq is woken up.
431+
*
432+
* It must be called with wq.lock being held. This spinlock is
433+
* unlocked while sleeping but @condition testing is done while lock
434+
* is held and when this macro exits the lock is held.
435+
*
436+
* The lock is locked/unlocked using spin_lock_irq()/spin_unlock_irq()
437+
* functions which must match the way they are locked/unlocked outside
438+
* of this macro.
439+
*
440+
* wake_up_locked() has to be called after changing any variable that could
441+
* change the result of the wait condition.
442+
*
443+
* The function will return -ERESTARTSYS if it was interrupted by a
444+
* signal and 0 if @condition evaluated to true.
445+
*/
446+
#define wait_event_interruptible_locked_irq(wq, condition) \
447+
((condition) \
448+
? 0 : __wait_event_interruptible_locked(wq, condition, 0, 1))
449+
450+
/**
451+
* wait_event_interruptible_exclusive_locked - sleep exclusively until a condition gets true
452+
* @wq: the waitqueue to wait on
453+
* @condition: a C expression for the event to wait for
454+
*
455+
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
456+
* @condition evaluates to true or a signal is received.
457+
* The @condition is checked each time the waitqueue @wq is woken up.
458+
*
459+
* It must be called with wq.lock being held. This spinlock is
460+
* unlocked while sleeping but @condition testing is done while lock
461+
* is held and when this macro exits the lock is held.
462+
*
463+
* The lock is locked/unlocked using spin_lock()/spin_unlock()
464+
* functions which must match the way they are locked/unlocked outside
465+
* of this macro.
466+
*
467+
* The process is put on the wait queue with an WQ_FLAG_EXCLUSIVE flag
468+
* set thus when other process waits process on the list if this
469+
* process is awaken further processes are not considered.
470+
*
471+
* wake_up_locked() has to be called after changing any variable that could
472+
* change the result of the wait condition.
473+
*
474+
* The function will return -ERESTARTSYS if it was interrupted by a
475+
* signal and 0 if @condition evaluated to true.
476+
*/
477+
#define wait_event_interruptible_exclusive_locked(wq, condition) \
478+
((condition) \
479+
? 0 : __wait_event_interruptible_locked(wq, condition, 1, 0))
480+
481+
/**
482+
* wait_event_interruptible_exclusive_locked_irq - sleep until a condition gets true
483+
* @wq: the waitqueue to wait on
484+
* @condition: a C expression for the event to wait for
485+
*
486+
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
487+
* @condition evaluates to true or a signal is received.
488+
* The @condition is checked each time the waitqueue @wq is woken up.
489+
*
490+
* It must be called with wq.lock being held. This spinlock is
491+
* unlocked while sleeping but @condition testing is done while lock
492+
* is held and when this macro exits the lock is held.
493+
*
494+
* The lock is locked/unlocked using spin_lock_irq()/spin_unlock_irq()
495+
* functions which must match the way they are locked/unlocked outside
496+
* of this macro.
497+
*
498+
* The process is put on the wait queue with an WQ_FLAG_EXCLUSIVE flag
499+
* set thus when other process waits process on the list if this
500+
* process is awaken further processes are not considered.
501+
*
502+
* wake_up_locked() has to be called after changing any variable that could
503+
* change the result of the wait condition.
504+
*
505+
* The function will return -ERESTARTSYS if it was interrupted by a
506+
* signal and 0 if @condition evaluated to true.
507+
*/
508+
#define wait_event_interruptible_exclusive_locked_irq(wq, condition) \
509+
((condition) \
510+
? 0 : __wait_event_interruptible_locked(wq, condition, 1, 1))
511+
512+
513+
365514
#define __wait_event_killable(wq, condition, ret) \
366515
do { \
367516
DEFINE_WAIT(__wait); \

kernel/sched.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3950,6 +3950,7 @@ void __wake_up_locked(wait_queue_head_t *q, unsigned int mode)
39503950
{
39513951
__wake_up_common(q, mode, 1, 0, NULL);
39523952
}
3953+
EXPORT_SYMBOL_GPL(__wake_up_locked);
39533954

39543955
void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key)
39553956
{

0 commit comments

Comments
 (0)