summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <koverstreet@google.com>2012-12-20 22:19:24 -0800
committerKent Overstreet <koverstreet@google.com>2012-12-20 23:17:41 -0800
commitb12fda42e43f6b6b925df397fb91d100fe4bc5aa (patch)
treea7b4e67e7d73575929be70241df12d9515f95595
parent468327c90a746c26fcb26584561bd4f6b37abeea (diff)
more wait_event() consolidationwait_events
-rw-r--r--include/linux/timer.h5
-rw-r--r--include/linux/wait.h111
-rw-r--r--kernel/timer.c11
3 files changed, 76 insertions, 51 deletions
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 8c5a197e1587..b02432251f78 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -33,6 +33,11 @@ struct timer_list {
#endif
};
+struct timer_sleeper {
+ struct timer_list timer;
+ struct task_struct *task;
+};
+
extern struct tvec_base boot_tvec_bases;
#ifdef CONFIG_LOCKDEP
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 7f6937836910..3f5a85494866 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -173,9 +173,10 @@ wait_queue_head_t *bit_waitqueue(void *, int);
#define wake_up_interruptible_sync_poll(x, m) \
__wake_up_sync_key((x), TASK_INTERRUPTIBLE, 1, (void *) (m))
-#define ___wait_event_timeout(wq, condition, state, exclusive, __ret) \
-do { \
+#define ___wait_event(wq, condition, state, exclusive, timer) \
+({ \
__label__ finish; \
+ int __ret; \
DEFINE_WAIT(__wait); \
if (exclusive) \
__wait.flags |= WQ_FLAG_EXCLUSIVE; \
@@ -191,29 +192,28 @@ do { \
__ret = -ERESTARTSYS; \
break; \
} \
- __ret = schedule_timeout(__ret); \
- if (!__ret) \
+ if (!(timer)) { \
+ __ret = -ETIME; \
break; \
+ } \
+ schedule(); \
} \
if (exclusive) { \
abort_exclusive_wait(&wq, &__wait, state, NULL); \
} else { \
-finish: \
- finish_wait(&wq, &__wait); \
+finish: finish_wait(&wq, &__wait); \
} \
-} while (0)
+ __ret; \
+})
-#define __wait_event_timeout(wq, condition, state, exclusive, timeout) \
+#define __wait_event(wq, condition, state, exclusive) \
({ \
- long __ret = timeout; \
+ int __ret = 0; \
if (!(condition)) \
- ___wait_event_timeout(wq, condition, state, exclusive, __ret);\
+ ___wait_event(wq, condition, state, false, true); \
__ret; \
})
-#define __wait_event(wq, condition, state) \
- __wait_event_timeout(wq, condition, state, false, MAX_SCHEDULE_TIMEOUT)
-
/**
* wait_event - sleep until a condition gets true
* @wq: the waitqueue to wait on
@@ -227,7 +227,7 @@ finish: \
* change the result of the wait condition.
*/
#define wait_event(wq, condition) \
- __wait_event(wq, condition, TASK_UNINTERRUPTIBLE)
+ __wait_event(wq, condition, TASK_UNINTERRUPTIBLE, false)
/**
* wait_event_interruptible - sleep until a condition gets true
@@ -245,7 +245,50 @@ finish: \
* signal and 0 if @condition evaluated to true.
*/
#define wait_event_interruptible(wq, condition) \
- __wait_event(wq, condition, TASK_INTERRUPTIBLE)
+ __wait_event(wq, condition, TASK_INTERRUPTIBLE, false)
+
+/**
+ * wait_event_killable - 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_KILLABLE) until the
+ * @condition evaluates to true or a signal is received.
+ * 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 will return -ERESTARTSYS if it was interrupted by a
+ * signal and 0 if @condition evaluated to true.
+ */
+#define wait_event_killable(wq, condition) \
+ __wait_event(wq, condition, TASK_KILLABLE, false)
+
+#define ___wait_event_timeout(wq, condition, state, exclusive, timeout) \
+({ \
+ long __ret; \
+ struct timer_sleeper __t; \
+ \
+ setup_timer_on_stack(&__t.timer, timer_wakeup, (unsigned long) &__t);\
+ __t.tank = current; \
+ if (timeout != MAX_SCHEDULE_TIMEOUT) \
+ __mod_timer(&__t, jiffies + timeout, false, TIMER_NOT_PINNED);\
+ \
+ __ret = ___wait_event(wq, condition, state, false, __t); \
+ \
+ del_singleshot_timer_sync(&__t); \
+ destroy_timer_on_stack(&__t); \
+ __ret ? __ret : max_t(long, 0L, __t.expire - jiffies); \
+})
+
+#define __wait_event_timeout(wq, condition, state, exclusive, timeout) \
+({ \
+ long __ret = timeout; \
+ if (!(condition)) \
+ __ret = ___wait_event_timeout(wq, condition, state, exclusive, __ret);\
+ __ret; \
+})
/**
* wait_event_timeout - sleep until a condition gets true or a timeout elapses
@@ -288,8 +331,7 @@ finish: \
#define ___wait_event_hrtimeout(wq, condition, state, timeout) \
({ \
- int __ret = 0; \
- DEFINE_WAIT(__wait); \
+ int __ret; \
struct hrtimer_sleeper __t; \
\
hrtimer_init_on_stack(&__t.timer, CLOCK_MONOTONIC, \
@@ -300,25 +342,10 @@ finish: \
current->timer_slack_ns, \
HRTIMER_MODE_REL); \
\
- for (;;) { \
- prepare_to_wait(&wq, &__wait, state); \
- if (condition) \
- break; \
- if (state == TASK_INTERRUPTIBLE && \
- signal_pending(current)) { \
- __ret = -ERESTARTSYS; \
- break; \
- } \
- if (!__t.task) { \
- __ret = -ETIME; \
- break; \
- } \
- schedule(); \
- } \
+ __ret = ___wait_event(wq, condition, state, false, __t.task); \
\
hrtimer_cancel(&__t.timer); \
destroy_hrtimer_on_stack(&__t.timer); \
- finish_wait(&wq, &__wait); \
__ret; \
})
@@ -488,24 +515,6 @@ finish: \
? 0 : __wait_event_interruptible_locked(wq, condition, 1, 1))
-/**
- * wait_event_killable - 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_KILLABLE) until the
- * @condition evaluates to true or a signal is received.
- * 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 will return -ERESTARTSYS if it was interrupted by a
- * signal and 0 if @condition evaluated to true.
- */
-#define wait_event_killable(wq, condition) \
- __wait_event(wq, condition, TASK_KILLABLE)
-
/*
* These are the old interfaces to sleep waiting for an event.
* They are racy. DO NOT use them, use the wait_event* interfaces above.
diff --git a/kernel/timer.c b/kernel/timer.c
index b016c04ed33a..c6ae5f1707a2 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -649,6 +649,17 @@ void init_timer_key(struct timer_list *timer, unsigned int flags,
}
EXPORT_SYMBOL(init_timer_key);
+static void timer_wakeup(unsigned long *data)
+{
+ struct timer_sleeper *t = (void *) data;
+ struct task_struct *task = t->task;
+
+ t->task = NULL;
+ if (task)
+ wake_up_process(task);
+}
+EXPORT_SYMBOL_GPL(timer_wakeup);
+
static inline void detach_timer(struct timer_list *timer, bool clear_pending)
{
struct list_head *entry = &timer->entry;