Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1455994 > unrolled thread
| Started by | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| First post | 2016-08-03 21:50 +0200 |
| Last post | 2016-08-10 23:30 +0200 |
| Articles | 11 — 3 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Peter Zijlstra <peterz@infradead.org> - 2016-08-03 21:50 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-03 23:40 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Peter Zijlstra <peterz@infradead.org> - 2016-08-04 16:20 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Peter Zijlstra <peterz@infradead.org> - 2016-08-08 12:30 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Bart Van Assche <bvanassche@acm.org> - 2016-08-08 16:40 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-08 18:30 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-09 19:20 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-10 21:00 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-10 21:10 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Peter Zijlstra <peterz@infradead.org> - 2016-08-10 21:20 +0200
Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs Oleg Nesterov <oleg@redhat.com> - 2016-08-10 23:30 +0200
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-08-03 21:50 +0200 |
| Subject | Re: [PATCH] sched: Avoid that __wait_on_bit_lock() hangs |
| Message-ID | <s2akV-6WY-1@gated-at.bofh.it> |
On Wed, Aug 03, 2016 at 09:35:03AM -0700, Bart Van Assche wrote:
> If try_to_wakeup() reads the task state before abort_exclusive_wait()
> sets the task state and if autoremove_wake_function() is called after
> abort_exclusive_wait() has removed a task from a wait list then the
> cascading mechanism for exclusive wakeups in abort_exclusive_wait()
> won't be triggered. Avoid this by serializing the task state change
> in abort_exclusive_wait() and try_to_wakeup().
I'm dense.. what!?
CPU0 CPU1 CPU2
__lock_page_killable()
__wait_on_bit_lock()
bit_wait_io()
schedule()
__wake_up_bit()
__wake_up(.nr_exclusive=1)
spin_lock(&q->lock)
__wake_up_common()
autoremove_wake_func()
try_to_wake_up(p, TASK_NORMAL)
list_del_init(&wait->task_list)
spin_unlock(&q->lock)
complete_signal(p)
signal_wake_up(p, 1)
sigaddset(&p->pending.signal, SIGKILL)
try_to_wake_up(p, TASK_WAKEKILL)
if (signal_pending_state(TASK_KILLABLE))
return -EINTR;
abort_exclusive_wait()
__set_current_state(RUNNING)
spin_lock(q->lock)
if (!list_empty()) /* empty */
else if (waitqueue_active()) /* pending ? */
__wake_up_locked_key(q, mode, key)
spin_unlock(q->lock)
That seems to do the right thing, so clearly I misunderstand. Please
clarify.
> +++ b/kernel/sched/wait.c
> @@ -277,10 +277,17 @@ void abort_exclusive_wait(wait_queue_head_t *q, wait_queue_t *wait,
> unsigned int mode, void *key)
> {
> unsigned long flags;
> + long wake_up;
> +
> + /* Serialize against try_to_wake_up() */
> + raw_spin_lock_irqsave(¤t->pi_lock, flags);
> + wake_up = current->state & (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE);
> + if (wake_up)
> + __set_current_state(TASK_RUNNING);
> + raw_spin_unlock_irqrestore(¤t->pi_lock, flags);
>
> - __set_current_state(TASK_RUNNING);
> spin_lock_irqsave(&q->lock, flags);
> - if (!list_empty(&wait->task_list))
> + if (wake_up)
> list_del_init(&wait->task_list);
> else if (waitqueue_active(q))
> __wake_up_locked_key(q, mode, key);
That just feels wrong,.. very wrong.
[toc] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-03 23:40 +0200 |
| Message-ID | <s2c3n-85c-11@gated-at.bofh.it> |
| In reply to | #1455994 |
Hi Bart, I too can't understand the problem. Perhaps you missed the fact that abort_exclusive_wait() does everything under wait_queue_head_t->lock ? On 08/03, Bart Van Assche wrote: > > try_to_wake_up() locks task_struct.pi_lock but abort_exclusive_wait() not. > My assumption is that the following sequence of events leads to the lockup > that I had mentioned in the description of my patch: > * try_to_wake_up() is called for the task that will execute > abort_exclusive_wait(). > * After try_to_wake_up() has checked task_struct.state and before > autoremove_wake_function() has tried to remove the task from the wait > queue, abort_exclusive_wait() is executed for the same task. But we do not care if we race with another try_to_wake_up(), or even with another exclusive wake_up_nr(wq)/whatever unless wq is the same. And if this wq is the same, then wake_up_nr() will do try_to_wake_up/autoremove either before or after abort_exclusive_wait(), wake_up_nr() takes the same wq->lock. And this means that abort_exclusive_wait() can't be called "After try_to_wake_up()" and "before autoremove_wake_function()". Oleg.
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-08-04 16:20 +0200 |
| Message-ID | <s2rF8-1SU-31@gated-at.bofh.it> |
| In reply to | #1456036 |
On Wed, Aug 03, 2016 at 02:51:23PM -0700, Bart Van Assche wrote: > So I started testing the patch below that should fix the same hang but > without triggering any wait list corruption. > > diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c > index f15d6b6..4e3f651 100644 > --- a/kernel/sched/wait.c > +++ b/kernel/sched/wait.c > @@ -282,7 +282,7 @@ void abort_exclusive_wait(wait_queue_head_t *q, > wait_queue_t *wait, > spin_lock_irqsave(&q->lock, flags); > if (!list_empty(&wait->task_list)) > list_del_init(&wait->task_list); > - else if (waitqueue_active(q)) > + if (waitqueue_active(q)) > __wake_up_locked_key(q, mode, key); > spin_unlock_irqrestore(&q->lock, flags); > } So the problem with this patch is that it will violate the nr_exclusive semantics in that it can result in too many wakeups -- which is a much less severe (typically harmless) issue. We now always wake up the next waiter, even if there wasn't an actual wakeup we raced against. And if we then also get a wakeup, we can end up with 2 woken tasks (instead of the nr_exclusive=1). Now, since wait loops must all deal with spurious wakeups, this ends up as harmless overhead. But I'd still like to understand where we loose the wakeup. What are you doing to reproduce this issue?
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-08-08 12:30 +0200 |
| Message-ID | <s3PYK-7f-35@gated-at.bofh.it> |
| In reply to | #1456431 |
On Fri, Aug 05, 2016 at 10:41:33AM -0700, Bart Van Assche wrote: > On 08/04/2016 07:09 AM, Peter Zijlstra wrote: > >But I'd still like to understand where we loose the wakeup. > > My assumption is that __wake_up_common() and signal delivery happen > concurrently, that __wake_up_common() wakes up bit_wait_io() and that signal > delivery happens after bit_wait_io() has been woken up but before it tests > the signal pending state. That would be the exact scenario I drew a picture of, no? I'm still failing to see the hole there. Please draw a picture like that and illustrate the hole.
[toc] | [prev] | [next] | [standalone]
| From | Bart Van Assche <bvanassche@acm.org> |
|---|---|
| Date | 2016-08-08 16:40 +0200 |
| Message-ID | <s3TSF-2yD-17@gated-at.bofh.it> |
| In reply to | #1457689 |
On 08/08/16 03:22, Peter Zijlstra wrote:
> That would be the exact scenario I drew a picture of, no? I'm still
> failing to see the hole there.
>
> Please draw a picture like that and illustrate the hole.
Hi Peter,
This is the sequence of which I think that it leads to the missed wakeup:
Task 1 Task 2 Task 3 Task 4
lock_page()
...
lock_page_killable()
__lock_page_killable()
__wait_on_bit_lock()
bit_wait_io()
io_schedule()
...
lock_page()
__lock_page()
__wait_on_bit_lock()
bit_wait_io()
io_schedule()
...
(signal delivery to task 2)
try_to_wake_up(task2, ..., ...)
(try_to_wake_up() returns 1)
unlock_page()
wake_up_page()
__wake_up_bit()
__wake_up(wq, TASK_NORMAL, 1, &key)
__wake_up_common(wq, mode=TASK_NORMAL, nr_exclusive=1, 0, key)
wake_bit_function()
autoremove_wake_function()
default_wake_function()
try_to_wake_up() <- skips task 2 because task 3 already changed
the task state of task 2
(autoremove_wake_function() does not do
list_del_init(&wait->task_list))
bit_wait_io() returns -EINTR
abort_exclusive_wait() is called by __wait_on_bit_lock()
In the above sequence task 1 does not remove task 2 from the waitqueue
because task 3 had already woken up task 2. The result is that when task 2
calls abort_exclusive_wait() that task 2 is still on the waitqueue. With the
current implementation of abort_exclusive_wait() in the above scenario task
4 is not woken up although it should be woken up. Hence the patch that removes
the "else" keyword from abort_exclusive_wait().
Bart.
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-08 18:30 +0200 |
| Message-ID | <s3VB8-3GF-25@gated-at.bofh.it> |
| In reply to | #1457839 |
On 08/08, Bart Van Assche wrote: > > This is the sequence of which I think that it leads to the missed wakeup: > > Task 1 Task 2 Task 3 Task 4 > > lock_page() > ... > lock_page_killable() > __lock_page_killable() > __wait_on_bit_lock() > bit_wait_io() > io_schedule() > ... > lock_page() > __lock_page() > __wait_on_bit_lock() > bit_wait_io() > io_schedule() > ... > > > (signal delivery to task 2) > try_to_wake_up(task2, ..., ...) > (try_to_wake_up() returns 1) > > unlock_page() > wake_up_page() > __wake_up_bit() > __wake_up(wq, TASK_NORMAL, 1, &key) > __wake_up_common(wq, mode=TASK_NORMAL, nr_exclusive=1, 0, key) > wake_bit_function() > autoremove_wake_function() > default_wake_function() > try_to_wake_up() <- skips task 2 because task 3 already changed > the task state of task 2 > (autoremove_wake_function() does not do > list_del_init(&wait->task_list)) Yes. But since it skips task2, __wake_up_common() doesn't decrement nr_exclusive, doesn't stop. It continues the list_for_each_entry_safe() loop, and finds the sleeping task4, and wakes it up, > bit_wait_io() returns -EINTR > abort_exclusive_wait() is called by __wait_on_bit_lock() > > > In the above sequence task 1 does not remove task 2 from the waitqueue > because task 3 had already woken up task 2. The result is that when task 2 > calls abort_exclusive_wait() that task 2 is still on the waitqueue. Yes, but this is fine, > With the > current implementation of abort_exclusive_wait() in the above scenario task > 4 is not woken up although it should be woken up. See above, it must be already woken by __wake_up_common(). So far _I think_ that the bug is somewhere else... Say, someone clears PG_locked without wake_up(). Then SIGKILL sent to the task sleeping in sys_read() "adds" the necessary wakeup... Do you use external modules during the testing? Oleg.
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-09 19:20 +0200 |
| Message-ID | <s4iR4-24N-31@gated-at.bofh.it> |
| In reply to | #1457896 |
On 08/08, Bart Van Assche wrote: > > No external modules were loaded when I triggered the lockup Heh. Could you test the patch below? Oleg. --- x/kernel/sched/wait.c +++ x/kernel/sched/wait.c @@ -283,7 +283,7 @@ void abort_exclusive_wait(wait_queue_hea if (!list_empty(&wait->task_list)) list_del_init(&wait->task_list); else if (waitqueue_active(q)) - __wake_up_locked_key(q, mode, key); + __wake_up_locked_key(q, TASK_NORMAL, key); spin_unlock_irqrestore(&q->lock, flags); } EXPORT_SYMBOL(abort_exclusive_wait);
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-10 21:00 +0200 |
| Message-ID | <s4GTo-n4-29@gated-at.bofh.it> |
| In reply to | #1459008 |
On 08/10, Bart Van Assche wrote:
>
> On 08/10/2016 03:46 AM, Oleg Nesterov wrote:
> > OK. Could you try another debugging patch below?
> >
> > Oleg.
> > ---
> >
> > diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
> > index e5a3244..9d5f892 100644
> > --- a/include/linux/page-flags.h
> > +++ b/include/linux/page-flags.h
> > @@ -711,6 +711,15 @@ static inline int page_has_private(struct page *page)
> > return !!(page->flags & PAGE_FLAGS_PRIVATE);
> > }
> >
> > +void unlock_page(struct page *page);
> > +static inline void __ClearPageLocked_x(struct page *page)
> > +{
> > + if (PageLocked(compound_head(page)))
> > + unlock_page(page);
> > +}
> > +
> > +#define __ClearPageLocked(page) __ClearPageLocked_x(page)
> > +
> > #undef PF_ANY
> > #undef PF_HEAD
> > #undef PF_NO_TAIL
>
> Hi Oleg,
>
> Are you sure that all __ClearPageLocked() users pass the compound head
> to that macro?
Hmm. it obviously should... which kernel version do you use for testing?
From include/linux/page-flags.h
__PAGEFLAG(Locked, locked, PF_NO_TAIL)
and
#define PF_NO_TAIL(page, enforce) ({ \
VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page); \
compound_head(page);})
and this matches compound_head() in lock/unlock_page().
> --- a/include/linux/page-flags.h
> +++ b/include/linux/page-flags.h
> @@ -711,6 +711,17 @@ static inline int page_has_private(struct page *page)
> return !!(page->flags & PAGE_FLAGS_PRIVATE);
> }
>
> +void unlock_page(struct page *page);
> +static inline void __ClearPageLocked_x(struct page *page)
> +{
> + if (PageLocked(compound_head(page)))
> + unlock_page(page);
> + else
> + __ClearPageLocked(page);
> +}
No, no. If you use an old kernel (which doesn't call compound_head() in
lock_page()), then just remove compound_head() from __ClearPageLocked_x()
above:
static inline void __ClearPageLocked_x(struct page *page)
{
if (PageLocked(page))
unlock_page(page);
}
even if this shouldn't make any difference afaics, note the
VM_BUG_ON_PGFLAGS() above.
Oleg.
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-10 21:10 +0200 |
| Message-ID | <s4GTo-n4-33@gated-at.bofh.it> |
| In reply to | #1459008 |
On 08/09, Bart Van Assche wrote:
>
> On 08/09/2016 10:15 AM, Oleg Nesterov wrote:
> >
> > --- x/kernel/sched/wait.c
> > +++ x/kernel/sched/wait.c
> > @@ -283,7 +283,7 @@ void abort_exclusive_wait(wait_queue_hea
> > if (!list_empty(&wait->task_list))
> > list_del_init(&wait->task_list);
> > else if (waitqueue_active(q))
> > - __wake_up_locked_key(q, mode, key);
> > + __wake_up_locked_key(q, TASK_NORMAL, key);
> > spin_unlock_irqrestore(&q->lock, flags);
> > }
> > EXPORT_SYMBOL(abort_exclusive_wait);
>
> Hello Oleg,
>
> That patch looks interesting to me.
And I'll redo/resend it, __wake_up_locked_key(mode) is simply wrong I think.
But it can't affect lock_page() because TASK_KILLABLE includes TASK_UNINTERRUPTIBLE
and we do not have lock_page_interruptible().
> Unfortunately even with that patch
> applied I still see lockups.
Thanks. I hoped this change can fix some another exclusive wait...
OK. Could you try another debugging patch below?
Oleg.
---
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index e5a3244..9d5f892 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -711,6 +711,15 @@ static inline int page_has_private(struct page *page)
return !!(page->flags & PAGE_FLAGS_PRIVATE);
}
+void unlock_page(struct page *page);
+static inline void __ClearPageLocked_x(struct page *page)
+{
+ if (PageLocked(compound_head(page)))
+ unlock_page(page);
+}
+
+#define __ClearPageLocked(page) __ClearPageLocked_x(page)
+
#undef PF_ANY
#undef PF_HEAD
#undef PF_NO_TAIL
[toc] | [prev] | [next] | [standalone]
| From | Peter Zijlstra <peterz@infradead.org> |
|---|---|
| Date | 2016-08-10 21:20 +0200 |
| Message-ID | <s4HcK-NR-17@gated-at.bofh.it> |
| In reply to | #1457896 |
On Wed, Aug 10, 2016 at 12:57:25PM +0200, Oleg Nesterov wrote: > This condition is fine, and the trace is clear. This means that lock_page_killable() > was interrupted and wake_bit_function() was not called. We do not need another wakeup > in this case but somehow it helps. Again, I think because the necessary wakeup was > already lost/missed. I suspect the same. Removing that else generates 'spurious' wakeups, which can unstick the situation, hiding the real source of the problem.
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2016-08-10 23:30 +0200 |
| Message-ID | <s4HcK-NR-21@gated-at.bofh.it> |
| In reply to | #1457896 |
On 08/09, Bart Van Assche wrote: > > Hello Oleg, > > Something that puzzles me is that removing the "else" keyword from > abort_exclusive_wait() is sufficient to avoid the hang. Yes, we need to understand this. > If there would > be code that clears PG_locked without calling wake_up() this hang > probably would also be triggered by workloads that do not wake up > lock_page_killable() with a signal. Yes, and I already have another debugging patch to test this... it simply turns lock_page_killable() into lock_page(). But lets check __ClearPageLocked() first (the patch I sent a minute ago). > BTW, the > WARN_ONCE(!list_empty(&wait->task_list) && waitqueue_active(q), "mode = > %#x\n", mode) statement that I added in abort_exclusive_wait() just > produced the following call stack: This condition is fine, and the trace is clear. This means that lock_page_killable() was interrupted and wake_bit_function() was not called. We do not need another wakeup in this case but somehow it helps. Again, I think because the necessary wakeup was already lost/missed. Oleg.
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web