/* * Simple waitqueues without fancy flags and callbacks * * (C) 2011 Thomas Gleixner * * Based on kernel/wait.c * * For licencing details see kernel-base/COPYING */ #include #include #include #include /* Adds w to head->list. Must be called with head->lock locked. */ static inline void __swait_enqueue(struct swait_head *head, struct swaiter *w) { list_add(&w->node, &head->list); /* We can't let the condition leak before the setting of head */ smp_mb(); } /* Removes w from head->list. Must be called with head->lock locked. */ static inline void __swait_dequeue(struct swaiter *w) { list_del_init(&w->node); } void __init_swait_head(struct swait_head *head, struct lock_class_key *key) { raw_spin_lock_init(&head->lock); lockdep_set_class(&head->lock, key); INIT_LIST_HEAD(&head->list); } EXPORT_SYMBOL(__init_swait_head); void swait_prepare_locked(struct swait_head *head, struct swaiter *w) { w->task = current; if (list_empty(&w->node)) __swait_enqueue(head, w); } void swait_prepare(struct swait_head *head, struct swaiter *w, int state) { unsigned long flags; raw_spin_lock_irqsave(&head->lock, flags); swait_prepare_locked(head, w); __set_current_state(state); raw_spin_unlock_irqrestore(&head->lock, flags); } EXPORT_SYMBOL(swait_prepare); void swait_finish_locked(struct swait_head *head, struct swaiter *w) { __set_current_state(TASK_RUNNING); if (w->task) __swait_dequeue(w); } void swait_finish(struct swait_head *head, struct swaiter *w) { unsigned long flags; __set_current_state(TASK_RUNNING); if (w->task) { raw_spin_lock_irqsave(&head->lock, flags); __swait_dequeue(w); raw_spin_unlock_irqrestore(&head->lock, flags); } } EXPORT_SYMBOL(swait_finish); unsigned int __swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num) { struct swaiter *curr, *next; int woken = 0; list_for_each_entry_safe(curr, next, &head->list, node) { if (wake_up_state(curr->task, state)) { __swait_dequeue(curr); /* * The waiting task can free the waiter as * soon as curr->task = NULL is written, * without taking any locks. A memory barrier * is required here to prevent the following * store to curr->task from getting ahead of * the dequeue operation. */ smp_wmb(); curr->task = NULL; if (++woken == num) break; } } return woken; } unsigned int __swait_wake(struct swait_head *head, unsigned int state, unsigned int num) { unsigned long flags; int woken; if (!swaitqueue_active(head)) return 0; raw_spin_lock_irqsave(&head->lock, flags); woken = __swait_wake_locked(head, state, num); raw_spin_unlock_irqrestore(&head->lock, flags); return woken; } EXPORT_SYMBOL(__swait_wake);