Re: [GIT PULL] sched.h split-up

From: Linus Torvalds
Date: Tue Mar 07 2017 - 18:42:41 EST


On Fri, Mar 3, 2017 at 12:13 PM, Linus Torvalds
<torvalds@xxxxxxxxxxxxxxxxxxxx> wrote:
>
> The fact that you can include <linux/wait.h>, and then cannot use the
> wait event functions because you're missing "signal_pending()" is
> complete garbage. This needs to be fixed.

Here's a (totally untested) patch that tries to do exactly that.

It moves the stuff inside the wait-loop into two helper functions -
one for "interrupts off" and one for "interrupts on" case - and does
the meat of the whole __wait_event_interruptible_locked() out of line.

NOTE! This is all the slow case, when we will schedule and mess around
with spinlocks. The fast case will have been taken care of by the
macros that then use the whole "__wait_event_interruptible_locked()"
machinery.

So moving it out of line not only makes the build simpler (no need for
<linux/sched/signal.h> for the "signal_pending()" thing), but seems to
be the right thing to do from a code size standpoint too.

But as mentioned - this is untested. It seems to build, and it looks
"ObviouslyCorrect(tm)", but I didn't actually try to boot it.

Comments?

Linus
include/linux/wait.h | 31 ++++++++++---------------------
kernel/sched/wait.c | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 21 deletions(-)

diff --git a/include/linux/wait.h b/include/linux/wait.h
index aacb1282d19a..db076ca7f11d 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -620,30 +620,19 @@ do { \
__ret; \
})

+extern int do_wait_intr(wait_queue_head_t *, wait_queue_t *);
+extern int do_wait_intr_irq(wait_queue_head_t *, wait_queue_t *);

-#define __wait_event_interruptible_locked(wq, condition, exclusive, irq) \
+#define __wait_event_interruptible_locked(wq, condition, exclusive, fn) \
({ \
- int __ret = 0; \
+ int __ret; \
DEFINE_WAIT(__wait); \
if (exclusive) \
__wait.flags |= WQ_FLAG_EXCLUSIVE; \
do { \
- if (likely(list_empty(&__wait.task_list))) \
- __add_wait_queue_tail(&(wq), &__wait); \
- set_current_state(TASK_INTERRUPTIBLE); \
- if (signal_pending(current)) { \
- __ret = -ERESTARTSYS; \
+ __ret = fn(&(wq), &__wait); \
+ if (__ret) \
break; \
- } \
- if (irq) \
- spin_unlock_irq(&(wq).lock); \
- else \
- spin_unlock(&(wq).lock); \
- schedule(); \
- if (irq) \
- spin_lock_irq(&(wq).lock); \
- else \
- spin_lock(&(wq).lock); \
} while (!(condition)); \
__remove_wait_queue(&(wq), &__wait); \
__set_current_state(TASK_RUNNING); \
@@ -676,7 +665,7 @@ do { \
*/
#define wait_event_interruptible_locked(wq, condition) \
((condition) \
- ? 0 : __wait_event_interruptible_locked(wq, condition, 0, 0))
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 0, do_wait_intr))

/**
* wait_event_interruptible_locked_irq - sleep until a condition gets true
@@ -703,7 +692,7 @@ do { \
*/
#define wait_event_interruptible_locked_irq(wq, condition) \
((condition) \
- ? 0 : __wait_event_interruptible_locked(wq, condition, 0, 1))
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 0, do_wait_intr_irq))

/**
* wait_event_interruptible_exclusive_locked - sleep exclusively until a condition gets true
@@ -734,7 +723,7 @@ do { \
*/
#define wait_event_interruptible_exclusive_locked(wq, condition) \
((condition) \
- ? 0 : __wait_event_interruptible_locked(wq, condition, 1, 0))
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 1, do_wait_intr))

/**
* wait_event_interruptible_exclusive_locked_irq - sleep until a condition gets true
@@ -765,7 +754,7 @@ do { \
*/
#define wait_event_interruptible_exclusive_locked_irq(wq, condition) \
((condition) \
- ? 0 : __wait_event_interruptible_locked(wq, condition, 1, 1))
+ ? 0 : __wait_event_interruptible_locked(wq, condition, 1, do_wait_intr_irq))


#define __wait_event_killable(wq, condition) \
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 4d2ea6f25568..b8c84c6dee64 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -242,6 +242,45 @@ long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)
}
EXPORT_SYMBOL(prepare_to_wait_event);

+/*
+ * Note! These two wait functions are entered with the
+ * wait-queue lock held (and interrupts off in the _irq
+ * case), so there is no race with testing the wakeup
+ * condition in the caller before they add the wait
+ * entry to the wake queue.
+ */
+int do_wait_intr(wait_queue_head_t *wq, wait_queue_t *wait)
+{
+ if (likely(list_empty(&wait->task_list)))
+ __add_wait_queue_tail(wq, wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_unlock(&wq->lock);
+ schedule();
+ spin_lock(&wq->lock);
+ return 0;
+}
+EXPORT_SYMBOL(do_wait_intr);
+
+int do_wait_intr_irq(wait_queue_head_t *wq, wait_queue_t *wait)
+{
+ if (likely(list_empty(&wait->task_list)))
+ __add_wait_queue_tail(wq, wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_unlock_irq(&wq->lock);
+ schedule();
+ spin_lock_irq(&wq->lock);
+ return 0;
+}
+EXPORT_SYMBOL(do_wait_intr_irq);
+
/**
* finish_wait - clean up after waiting in a queue
* @q: waitqueue waited on