| From 1206ffb590b55b8568714a4eb348ddd271150134 Mon Sep 17 00:00:00 2001 |
| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Mon, 12 Dec 2011 12:29:04 +0100 |
| Subject: [PATCH 140/366] wait-simple: Simple waitqueue implementation |
| |
| wait_queue is a swiss army knife and in most of the cases the |
| complexity is not needed. For RT waitqueues are a constant source of |
| trouble as we can't convert the head lock to a raw spinlock due to |
| fancy and long lasting callbacks. |
| |
| Provide a slim version, which allows RT to replace wait queues. This |
| should go mainline as well, as it lowers memory consumption and |
| runtime overhead. |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| |
| smp_mb() added by Steven Rostedt to fix a race condition with swait |
| wakeups vs adding items to the list. |
| --- |
| include/linux/wait-simple.h | 207 ++++++++++++++++++++++++++++++++++++++++++++ |
| kernel/sched/Makefile | 2 +- |
| kernel/sched/wait-simple.c | 115 ++++++++++++++++++++++++ |
| 3 files changed, 323 insertions(+), 1 deletion(-) |
| create mode 100644 include/linux/wait-simple.h |
| create mode 100644 kernel/sched/wait-simple.c |
| |
| diff --git a/include/linux/wait-simple.h b/include/linux/wait-simple.h |
| new file mode 100644 |
| index 0000000..f86bca2 |
| --- /dev/null |
| +++ b/include/linux/wait-simple.h |
| @@ -0,0 +1,207 @@ |
| +#ifndef _LINUX_WAIT_SIMPLE_H |
| +#define _LINUX_WAIT_SIMPLE_H |
| + |
| +#include <linux/spinlock.h> |
| +#include <linux/list.h> |
| + |
| +#include <asm/current.h> |
| + |
| +struct swaiter { |
| + struct task_struct *task; |
| + struct list_head node; |
| +}; |
| + |
| +#define DEFINE_SWAITER(name) \ |
| + struct swaiter name = { \ |
| + .task = current, \ |
| + .node = LIST_HEAD_INIT((name).node), \ |
| + } |
| + |
| +struct swait_head { |
| + raw_spinlock_t lock; |
| + struct list_head list; |
| +}; |
| + |
| +#define SWAIT_HEAD_INITIALIZER(name) { \ |
| + .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ |
| + .list = LIST_HEAD_INIT((name).list), \ |
| + } |
| + |
| +#define DEFINE_SWAIT_HEAD(name) \ |
| + struct swait_head name = SWAIT_HEAD_INITIALIZER(name) |
| + |
| +extern void __init_swait_head(struct swait_head *h, struct lock_class_key *key); |
| + |
| +#define init_swait_head(swh) \ |
| + do { \ |
| + static struct lock_class_key __key; \ |
| + \ |
| + __init_swait_head((swh), &__key); \ |
| + } while (0) |
| + |
| +/* |
| + * Waiter functions |
| + */ |
| +extern void swait_prepare_locked(struct swait_head *head, struct swaiter *w); |
| +extern void swait_prepare(struct swait_head *head, struct swaiter *w, int state); |
| +extern void swait_finish_locked(struct swait_head *head, struct swaiter *w); |
| +extern void swait_finish(struct swait_head *head, struct swaiter *w); |
| + |
| +/* Check whether a head has waiters enqueued */ |
| +static inline bool swaitqueue_active(struct swait_head *h) |
| +{ |
| + /* Make sure the condition is visible before checking list_empty() */ |
| + smp_mb(); |
| + return !list_empty(&h->list); |
| +} |
| + |
| +/* |
| + * Wakeup functions |
| + */ |
| +extern unsigned int __swait_wake(struct swait_head *head, unsigned int state, unsigned int num); |
| +extern unsigned int __swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num); |
| + |
| +#define swait_wake(head) __swait_wake(head, TASK_NORMAL, 1) |
| +#define swait_wake_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 1) |
| +#define swait_wake_all(head) __swait_wake(head, TASK_NORMAL, 0) |
| +#define swait_wake_all_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 0) |
| + |
| +/* |
| + * Event API |
| + */ |
| +#define __swait_event(wq, condition) \ |
| +do { \ |
| + DEFINE_SWAITER(__wait); \ |
| + \ |
| + for (;;) { \ |
| + swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ |
| + if (condition) \ |
| + break; \ |
| + schedule(); \ |
| + } \ |
| + swait_finish(&wq, &__wait); \ |
| +} while (0) |
| + |
| +/** |
| + * swait_event - sleep until a condition gets true |
| + * @wq: the waitqueue to wait on |
| + * @condition: a C expression for the event to wait for |
| + * |
| + * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the |
| + * @condition evaluates to true. The @condition is checked each time |
| + * the waitqueue @wq is woken up. |
| + * |
| + * wake_up() has to be called after changing any variable that could |
| + * change the result of the wait condition. |
| + */ |
| +#define swait_event(wq, condition) \ |
| +do { \ |
| + if (condition) \ |
| + break; \ |
| + __swait_event(wq, condition); \ |
| +} while (0) |
| + |
| +#define __swait_event_interruptible(wq, condition, ret) \ |
| +do { \ |
| + DEFINE_SWAITER(__wait); \ |
| + \ |
| + for (;;) { \ |
| + swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ |
| + if (condition) \ |
| + break; \ |
| + if (signal_pending(current)) { \ |
| + ret = -ERESTARTSYS; \ |
| + break; \ |
| + } \ |
| + schedule(); \ |
| + } \ |
| + swait_finish(&wq, &__wait); \ |
| +} while (0) |
| + |
| +#define __swait_event_interruptible_timeout(wq, condition, ret) \ |
| +do { \ |
| + DEFINE_SWAITER(__wait); \ |
| + \ |
| + for (;;) { \ |
| + swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ |
| + if (condition) \ |
| + break; \ |
| + if (signal_pending(current)) { \ |
| + ret = -ERESTARTSYS; \ |
| + break; \ |
| + } \ |
| + ret = schedule_timeout(ret); \ |
| + if (!ret) \ |
| + break; \ |
| + } \ |
| + swait_finish(&wq, &__wait); \ |
| +} while (0) |
| + |
| +/** |
| + * swait_event_interruptible - sleep until a condition gets true |
| + * @wq: the waitqueue to wait on |
| + * @condition: a C expression for the event to wait for |
| + * |
| + * The process is put to sleep (TASK_INTERRUPTIBLE) until the |
| + * @condition evaluates to true. The @condition is checked each time |
| + * the waitqueue @wq is woken up. |
| + * |
| + * wake_up() has to be called after changing any variable that could |
| + * change the result of the wait condition. |
| + */ |
| +#define swait_event_interruptible(wq, condition) \ |
| +({ \ |
| + int __ret = 0; \ |
| + if (!(condition)) \ |
| + __swait_event_interruptible(wq, condition, __ret); \ |
| + __ret; \ |
| +}) |
| + |
| +#define swait_event_interruptible_timeout(wq, condition, timeout) \ |
| +({ \ |
| + int __ret = timeout; \ |
| + if (!(condition)) \ |
| + __swait_event_interruptible_timeout(wq, condition, __ret); \ |
| + __ret; \ |
| +}) |
| + |
| +#define __swait_event_timeout(wq, condition, ret) \ |
| +do { \ |
| + DEFINE_SWAITER(__wait); \ |
| + \ |
| + for (;;) { \ |
| + swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ |
| + if (condition) \ |
| + break; \ |
| + ret = schedule_timeout(ret); \ |
| + if (!ret) \ |
| + break; \ |
| + } \ |
| + swait_finish(&wq, &__wait); \ |
| +} while (0) |
| + |
| +/** |
| + * swait_event_timeout - sleep until a condition gets true or a timeout elapses |
| + * @wq: the waitqueue to wait on |
| + * @condition: a C expression for the event to wait for |
| + * @timeout: timeout, in jiffies |
| + * |
| + * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the |
| + * @condition evaluates to true. The @condition is checked each time |
| + * the waitqueue @wq is woken up. |
| + * |
| + * wake_up() has to be called after changing any variable that could |
| + * change the result of the wait condition. |
| + * |
| + * The function returns 0 if the @timeout elapsed, and the remaining |
| + * jiffies if the condition evaluated to true before the timeout elapsed. |
| + */ |
| +#define swait_event_timeout(wq, condition, timeout) \ |
| +({ \ |
| + long __ret = timeout; \ |
| + if (!(condition)) \ |
| + __swait_event_timeout(wq, condition, __ret); \ |
| + __ret; \ |
| +}) |
| + |
| +#endif |
| diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile |
| index 43fb319c..26505c1 100644 |
| --- a/kernel/sched/Makefile |
| +++ b/kernel/sched/Makefile |
| @@ -15,7 +15,7 @@ endif |
| |
| obj-y += core.o loadavg.o clock.o cputime.o |
| obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o |
| -obj-y += wait.o completion.o idle.o |
| +obj-y += wait.o wait-simple.o completion.o idle.o |
| obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o |
| obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o |
| obj-$(CONFIG_SCHEDSTATS) += stats.o |
| diff --git a/kernel/sched/wait-simple.c b/kernel/sched/wait-simple.c |
| new file mode 100644 |
| index 0000000..7dfa86d |
| --- /dev/null |
| +++ b/kernel/sched/wait-simple.c |
| @@ -0,0 +1,115 @@ |
| +/* |
| + * Simple waitqueues without fancy flags and callbacks |
| + * |
| + * (C) 2011 Thomas Gleixner <tglx@linutronix.de> |
| + * |
| + * Based on kernel/wait.c |
| + * |
| + * For licencing details see kernel-base/COPYING |
| + */ |
| +#include <linux/init.h> |
| +#include <linux/export.h> |
| +#include <linux/sched.h> |
| +#include <linux/wait-simple.h> |
| + |
| +/* 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); |
| -- |
| 1.9.1 |
| |