linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
@ 2008-02-18 23:03 Dmitry Adamushko
  2008-02-19  6:44 ` Nick Piggin
  2008-02-19 13:00 ` Andy Whitcroft
  0 siblings, 2 replies; 8+ messages in thread
From: Dmitry Adamushko @ 2008-02-18 23:03 UTC (permalink / raw)
  To: linux-kernel
  Cc: Ingo Molnar, Andrew Morton, Peter Zijlstra, Rusty Russel,
	dmitry.adamushko


Hi,


[ description ]

Subject: kthread: add a memory barrier to kthread_stop()

'kthread' threads do a check in the following order:
- set_current_state(TASK_INTERRUPTIBLE);
- kthread_should_stop();

and set_current_state() implies an smp_mb().

on another side (kthread_stop), wake_up_process() does not seem to
guarantee a full mb.

And 'kthread_stop_info.k' must be visible before wake_up_process()
checks for/modifies a state of the 'kthread' task.

(the patch is at the end of the message)


[ more detailed description ]

the current code might well be safe in case a to-be-stopped 'kthread'
task is _not_ running on another CPU at the moment when kthread_stop()
is called (in this case, 'rq->lock' will act as a kind of synch.
point/barrier).

Another case is as follows:

CPU#0:

...
while (kthread_should_stop()) {

       if (condition)
             schedule();

       /* ... do something useful ... */   <--- EIP

       set_current_state(TASK_INTERRUPTIBLE);
}

so a 'kthread' task is about to call
set_current_state(TASK_INTERRUPTIBLE) ...


(in the mean time)

CPU#1:

kthread_stop()

-> kthread_stop_info.k = k                 (*)
-> wake_up_process()

wake_up_process() looks like:

(try_to_wake_up)

IRQ_OFF
LOCK

old_state = p->state;
if (!(old_state & state))                  (**)
         goto out;

...

UNLOCK
IRQ_ON


let's suppose (*) and (**) are reordered
(according to Documentation/memory-barriers.txt, neither IRQ_OFF nor
LOCK may prevent it from happening).

- the state is TASK_RUNNING, so we are about to return.

- CPU#1 is about to execute (*) (it's guaranteed to be done before
spin_unlock(&rq->lock) at the end of try_to_wake_up())


(in the mean time)

CPU#0:

- set_current_state(TASK_INTERRUPTIBLE);
- kthread_should_stop();

here, kthread_stop_info.k is not yet visible

- schedule()

... 

we missed a 'kthread_stop' event.

hum?


TIA,

---

From: Dmitry Adamushko <dmitry.adamushko@gmail.com>
Subject: kthread: add a memory barrier to kthread_stop()

'kthread' threads do a check in the following order:
- set_current_state(TASK_INTERRUPTIBLE);
- kthread_should_stop();

and set_current_state() implies an smp_mb().

on another side (kthread_stop), wake_up_process() is not guaranteed to
act as a full mb.

'kthread_stop_info.k' must be visible before wake_up_process() checks
for/modifies a state of the 'kthread' task.


Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>


diff --git a/kernel/kthread.c b/kernel/kthread.c
index 0ac8878..5167110 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
 
 	/* Now set kthread_should_stop() to true, and wake it up. */
 	kthread_stop_info.k = k;
+
+	/* The previous store operation must not get ahead of the wakeup. */
+	smp_mb();
+
 	wake_up_process(k);
 	put_task_struct(k);
 



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-18 23:03 [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop() Dmitry Adamushko
@ 2008-02-19  6:44 ` Nick Piggin
  2008-02-19  9:24   ` Peter Zijlstra
  2008-02-19 13:00 ` Andy Whitcroft
  1 sibling, 1 reply; 8+ messages in thread
From: Nick Piggin @ 2008-02-19  6:44 UTC (permalink / raw)
  To: Dmitry Adamushko
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Peter Zijlstra, Rusty Russel

On Tuesday 19 February 2008 10:03, Dmitry Adamushko wrote:
> Hi,
>
>
> [ description ]
>
> Subject: kthread: add a memory barrier to kthread_stop()
>
> 'kthread' threads do a check in the following order:
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
>
> and set_current_state() implies an smp_mb().
>
> on another side (kthread_stop), wake_up_process() does not seem to
> guarantee a full mb.
>
> And 'kthread_stop_info.k' must be visible before wake_up_process()
> checks for/modifies a state of the 'kthread' task.
>
> (the patch is at the end of the message)
>
>
> [ more detailed description ]
>
> the current code might well be safe in case a to-be-stopped 'kthread'
> task is _not_ running on another CPU at the moment when kthread_stop()
> is called (in this case, 'rq->lock' will act as a kind of synch.
> point/barrier).
>
> Another case is as follows:
>
> CPU#0:
>
> ...
> while (kthread_should_stop()) {
>
>        if (condition)
>              schedule();
>
>        /* ... do something useful ... */   <--- EIP
>
>        set_current_state(TASK_INTERRUPTIBLE);
> }
>
> so a 'kthread' task is about to call
> set_current_state(TASK_INTERRUPTIBLE) ...
>
>
> (in the mean time)
>
> CPU#1:
>
> kthread_stop()
>
> -> kthread_stop_info.k = k                 (*)
> -> wake_up_process()
>
> wake_up_process() looks like:
>
> (try_to_wake_up)
>
> IRQ_OFF
> LOCK
>
> old_state = p->state;
> if (!(old_state & state))                  (**)
>          goto out;
>
> ...
>
> UNLOCK
> IRQ_ON
>
>
> let's suppose (*) and (**) are reordered
> (according to Documentation/memory-barriers.txt, neither IRQ_OFF nor
> LOCK may prevent it from happening).
>
> - the state is TASK_RUNNING, so we are about to return.
>
> - CPU#1 is about to execute (*) (it's guaranteed to be done before
> spin_unlock(&rq->lock) at the end of try_to_wake_up())
>
>
> (in the mean time)
>
> CPU#0:
>
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
>
> here, kthread_stop_info.k is not yet visible
>
> - schedule()
>
> ...
>
> we missed a 'kthread_stop' event.
>
> hum?

Looks like you are correct to me.


> TIA,
>
> ---
>
> From: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> Subject: kthread: add a memory barrier to kthread_stop()
>
> 'kthread' threads do a check in the following order:
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
>
> and set_current_state() implies an smp_mb().
>
> on another side (kthread_stop), wake_up_process() is not guaranteed to
> act as a full mb.
>
> 'kthread_stop_info.k' must be visible before wake_up_process() checks
> for/modifies a state of the 'kthread' task.
>
>
> Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
>
>
> diff --git a/kernel/kthread.c b/kernel/kthread.c
> index 0ac8878..5167110 100644
> --- a/kernel/kthread.c
> +++ b/kernel/kthread.c
> @@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
>
>  	/* Now set kthread_should_stop() to true, and wake it up. */
>  	kthread_stop_info.k = k;
> +
> +	/* The previous store operation must not get ahead of the wakeup. */
> +	smp_mb();
> +
>  	wake_up_process(k);
>  	put_task_struct(k);
>
>
>
> --


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-19  6:44 ` Nick Piggin
@ 2008-02-19  9:24   ` Peter Zijlstra
  2008-02-19  9:53     ` Dmitry Adamushko
  2008-02-19 13:41     ` Dmitry Adamushko
  0 siblings, 2 replies; 8+ messages in thread
From: Peter Zijlstra @ 2008-02-19  9:24 UTC (permalink / raw)
  To: Nick Piggin
  Cc: Dmitry Adamushko, linux-kernel, Ingo Molnar, Andrew Morton,
	Rusty Russel, Paul E. McKenney


On Tue, 2008-02-19 at 17:44 +1100, Nick Piggin wrote:
> On Tuesday 19 February 2008 10:03, Dmitry Adamushko wrote:
> > Hi,
> >
> >
> > [ description ]
> >
> > Subject: kthread: add a memory barrier to kthread_stop()
> >
> > 'kthread' threads do a check in the following order:
> > - set_current_state(TASK_INTERRUPTIBLE);
> > - kthread_should_stop();
> >
> > and set_current_state() implies an smp_mb().
> >
> > on another side (kthread_stop), wake_up_process() does not seem to
> > guarantee a full mb.
> >
> > And 'kthread_stop_info.k' must be visible before wake_up_process()
> > checks for/modifies a state of the 'kthread' task.
> >
> > (the patch is at the end of the message)
> >
> >
> > [ more detailed description ]
> >
> > the current code might well be safe in case a to-be-stopped 'kthread'
> > task is _not_ running on another CPU at the moment when kthread_stop()
> > is called (in this case, 'rq->lock' will act as a kind of synch.
> > point/barrier).
> >
> > Another case is as follows:
> >
> > CPU#0:
> >
> > ...
> > while (kthread_should_stop()) {
> >
> >        if (condition)
> >              schedule();
> >
> >        /* ... do something useful ... */   <--- EIP
> >
> >        set_current_state(TASK_INTERRUPTIBLE);
> > }
> >
> > so a 'kthread' task is about to call
> > set_current_state(TASK_INTERRUPTIBLE) ...
> >
> >
> > (in the mean time)
> >
> > CPU#1:
> >
> > kthread_stop()
> >
> > -> kthread_stop_info.k = k                 (*)
> > -> wake_up_process()
> >
> > wake_up_process() looks like:
> >
> > (try_to_wake_up)
> >
> > IRQ_OFF
> > LOCK
> >
> > old_state = p->state;
> > if (!(old_state & state))                  (**)
> >          goto out;
> >
> > ...
> >
> > UNLOCK
> > IRQ_ON
> >
> >
> > let's suppose (*) and (**) are reordered
> > (according to Documentation/memory-barriers.txt, neither IRQ_OFF nor
> > LOCK may prevent it from happening).
> >
> > - the state is TASK_RUNNING, so we are about to return.
> >
> > - CPU#1 is about to execute (*) (it's guaranteed to be done before
> > spin_unlock(&rq->lock) at the end of try_to_wake_up())
> >
> >
> > (in the mean time)
> >
> > CPU#0:
> >
> > - set_current_state(TASK_INTERRUPTIBLE);
> > - kthread_should_stop();
> >
> > here, kthread_stop_info.k is not yet visible
> >
> > - schedule()
> >
> > ...
> >
> > we missed a 'kthread_stop' event.
> >
> > hum?
> 
> Looks like you are correct to me.
> 
> 
> > TIA,
> >
> > ---
> >
> > From: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> > Subject: kthread: add a memory barrier to kthread_stop()
> >
> > 'kthread' threads do a check in the following order:
> > - set_current_state(TASK_INTERRUPTIBLE);
> > - kthread_should_stop();
> >
> > and set_current_state() implies an smp_mb().
> >
> > on another side (kthread_stop), wake_up_process() is not guaranteed to
> > act as a full mb.
> >
> > 'kthread_stop_info.k' must be visible before wake_up_process() checks
> > for/modifies a state of the 'kthread' task.
> >
> >
> > Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> >
> >
> > diff --git a/kernel/kthread.c b/kernel/kthread.c
> > index 0ac8878..5167110 100644
> > --- a/kernel/kthread.c
> > +++ b/kernel/kthread.c
> > @@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
> >
> >  	/* Now set kthread_should_stop() to true, and wake it up. */
> >  	kthread_stop_info.k = k;
> > +
> > +	/* The previous store operation must not get ahead of the wakeup. */
> > +	smp_mb();

Does this not also imply you need a matching barrier in
kthread_should_stop() ?

> >  	wake_up_process(k);
> >  	put_task_struct(k);
> >
> >
> >
> > --
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-19  9:24   ` Peter Zijlstra
@ 2008-02-19  9:53     ` Dmitry Adamushko
  2008-02-19 13:41     ` Dmitry Adamushko
  1 sibling, 0 replies; 8+ messages in thread
From: Dmitry Adamushko @ 2008-02-19  9:53 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Nick Piggin, linux-kernel, Ingo Molnar, Andrew Morton,
	Rusty Russel, Paul E. McKenney

On 19/02/2008, Peter Zijlstra <a.p.zijlstra@chello.nl> wrote:
>  [ ... ]
> > >
> > > From: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> > > Subject: kthread: add a memory barrier to kthread_stop()
> > >
> > > 'kthread' threads do a check in the following order:
> > > - set_current_state(TASK_INTERRUPTIBLE);
> > > - kthread_should_stop();
> > >
> > > and set_current_state() implies an smp_mb().
> > >
> > > on another side (kthread_stop), wake_up_process() is not guaranteed to
> > > act as a full mb.
> > >
> > > 'kthread_stop_info.k' must be visible before wake_up_process() checks
> > > for/modifies a state of the 'kthread' task.
> > >
> > >
> > > Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> > >
> > >
> > > diff --git a/kernel/kthread.c b/kernel/kthread.c
> > > index 0ac8878..5167110 100644
> > > --- a/kernel/kthread.c
> > > +++ b/kernel/kthread.c
> > > @@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
> > >
> > >     /* Now set kthread_should_stop() to true, and wake it up. */
> > >     kthread_stop_info.k = k;
> > > +
> > > +   /* The previous store operation must not get ahead of the wakeup. */
> > > +   smp_mb();
>
> Does this not also imply you need a matching barrier in
> kthread_should_stop() ?

Yes, but only when it's used in combination with something that alters
a state of the task.

So it's rather a question of the interface-design.

We currently impose a requirement on how a main loop of 'kthread'
threads (ok, so it seems to dictate a policy :-) has to be orginized.
Namely, the following sequence must be kept in order:

(1) set_current_task(TASK_INTERRUPTIBLE);
(2) kthread_should_stop()
...
- schedule()

and (1) already provides a mb which becomes a "matching barrier" on
the kthread_should_stop() side.


-- 
Best regards,
Dmitry Adamushko

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-18 23:03 [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop() Dmitry Adamushko
  2008-02-19  6:44 ` Nick Piggin
@ 2008-02-19 13:00 ` Andy Whitcroft
  2008-02-19 13:11   ` Dmitry Adamushko
  1 sibling, 1 reply; 8+ messages in thread
From: Andy Whitcroft @ 2008-02-19 13:00 UTC (permalink / raw)
  To: Dmitry Adamushko
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Peter Zijlstra, Rusty Russel

On Tue, Feb 19, 2008 at 12:03:37AM +0100, Dmitry Adamushko wrote:
> 
> Hi,
> 
> 
> [ description ]
> 
> Subject: kthread: add a memory barrier to kthread_stop()
> 
> 'kthread' threads do a check in the following order:
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
> 
> and set_current_state() implies an smp_mb().
> 
> on another side (kthread_stop), wake_up_process() does not seem to
> guarantee a full mb.
> 
> And 'kthread_stop_info.k' must be visible before wake_up_process()
> checks for/modifies a state of the 'kthread' task.
> 
> (the patch is at the end of the message)
> 
> 
> [ more detailed description ]
> 
> the current code might well be safe in case a to-be-stopped 'kthread'
> task is _not_ running on another CPU at the moment when kthread_stop()
> is called (in this case, 'rq->lock' will act as a kind of synch.
> point/barrier).
> 
> Another case is as follows:
> 
> CPU#0:
> 
> ...
> while (kthread_should_stop()) {
> 
>        if (condition)
>              schedule();
> 
>        /* ... do something useful ... */   <--- EIP
> 
>        set_current_state(TASK_INTERRUPTIBLE);
> }
> 
> so a 'kthread' task is about to call
> set_current_state(TASK_INTERRUPTIBLE) ...
> 
> 
> (in the mean time)
> 
> CPU#1:
> 
> kthread_stop()
> 
> -> kthread_stop_info.k = k                 (*)
> -> wake_up_process()
> 
> wake_up_process() looks like:
> 
> (try_to_wake_up)
> 
> IRQ_OFF
> LOCK
> 
> old_state = p->state;
> if (!(old_state & state))                  (**)
>          goto out;
> 
> ...
> 
> UNLOCK
> IRQ_ON
> 
> 
> let's suppose (*) and (**) are reordered
> (according to Documentation/memory-barriers.txt, neither IRQ_OFF nor
> LOCK may prevent it from happening).
> 
> - the state is TASK_RUNNING, so we are about to return.
> 
> - CPU#1 is about to execute (*) (it's guaranteed to be done before
> spin_unlock(&rq->lock) at the end of try_to_wake_up())
> 
> 
> (in the mean time)
> 
> CPU#0:
> 
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
> 
> here, kthread_stop_info.k is not yet visible
> 
> - schedule()
> 
> ... 
> 
> we missed a 'kthread_stop' event.
> 
> hum?
> 
> 
> TIA,
> 
> ---
> 
> From: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> Subject: kthread: add a memory barrier to kthread_stop()
> 
> 'kthread' threads do a check in the following order:
> - set_current_state(TASK_INTERRUPTIBLE);
> - kthread_should_stop();
> 
> and set_current_state() implies an smp_mb().
> 
> on another side (kthread_stop), wake_up_process() is not guaranteed to
> act as a full mb.
> 
> 'kthread_stop_info.k' must be visible before wake_up_process() checks
> for/modifies a state of the 'kthread' task.
> 
> 
> Signed-off-by: Dmitry Adamushko <dmitry.adamushko@gmail.com>
> 
> 
> diff --git a/kernel/kthread.c b/kernel/kthread.c
> index 0ac8878..5167110 100644
> --- a/kernel/kthread.c
> +++ b/kernel/kthread.c
> @@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
>  
>  	/* Now set kthread_should_stop() to true, and wake it up. */
>  	kthread_stop_info.k = k;
> +
> +	/* The previous store operation must not get ahead of the wakeup. */
> +	smp_mb();
> +
>  	wake_up_process(k);
>  	put_task_struct(k);

The rules as written do seem to support your theory.  The CPU has every
right to delay the .k = k as late as the UNLOCK operation.

On the read-side there is a full barrier implied by the
set_current_state(TASK_INTERRUPTIBLE), however this synchronises us with
the current global state, which may well not have the updated version
of .k.

That seems to imply that a write memory barrier would be sufficient to
cover this.

So three comments.  First, should this not be an smp_wmb.  Second, this
memory barrier is paired with the barrier in
set_current_state(TASK_INTERRUPTIBLE) and that probabally should be
documented as part of this patch.  Finally, I think the comment as is is
hard to understand I got the sense of it backwards on first reading;
perhaps something like this:

	/*
	 * Ensure kthread_stop_info.k is visible before wakeup, paired
	 * with barrier in set_current_state().
	 */

-apw

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-19 13:00 ` Andy Whitcroft
@ 2008-02-19 13:11   ` Dmitry Adamushko
  0 siblings, 0 replies; 8+ messages in thread
From: Dmitry Adamushko @ 2008-02-19 13:11 UTC (permalink / raw)
  To: Andy Whitcroft
  Cc: linux-kernel, Ingo Molnar, Andrew Morton, Peter Zijlstra, Rusty Russel

On 19/02/2008, Andy Whitcroft <apw@shadowen.org> wrote:
> [ ... ]
>
> > diff --git a/kernel/kthread.c b/kernel/kthread.c
> > index 0ac8878..5167110 100644
> > --- a/kernel/kthread.c
> > +++ b/kernel/kthread.c
> > @@ -211,6 +211,10 @@ int kthread_stop(struct task_struct *k)
> >
> >       /* Now set kthread_should_stop() to true, and wake it up. */
> >       kthread_stop_info.k = k;
> > +
> > +     /* The previous store operation must not get ahead of the wakeup. */
> > +     smp_mb();
> > +
> >       wake_up_process(k);
> >       put_task_struct(k);
>
> The rules as written do seem to support your theory.  The CPU has every
> right to delay the .k = k as late as the UNLOCK operation.
>
> On the read-side there is a full barrier implied by the
> set_current_state(TASK_INTERRUPTIBLE), however this synchronises us with
> the current global state, which may well not have the updated version
> of .k.

yes.

>
> That seems to imply that a write memory barrier would be sufficient to
> cover this.
>
> So three comments.  First, should this not be an smp_wmb.

No. We also need to be sure that ".k = k" is updated by the moment we
check for a state of the task in try_to_wake_up(), so that's write vs.
read ops.

The point is that a 'kthread' loop does :

(1) set TASK_INTERRUPTIBLE
(2) check for .k == k

and kthread_stop() must do it in the _reverse_ order:

(1) .k = k
(2) check for a task state and wakeup if necessary.

Only this way we ensure that a wakeup is not lost.

>  Second, this
> memory barrier is paired with the barrier in
> set_current_state(TASK_INTERRUPTIBLE) and that probabally should be
> documented as part of this patch.  Finally, I think the comment as is is
> hard to understand I got the sense of it backwards on first reading;
> perhaps something like this:
>
>         /*
>          * Ensure kthread_stop_info.k is visible before wakeup, paired
>          * with barrier in set_current_state().
>          */

Yes, I'll try to come up with a better description.


>
> -apw
>

-- 
Best regards,
Dmitry Adamushko

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-19  9:24   ` Peter Zijlstra
  2008-02-19  9:53     ` Dmitry Adamushko
@ 2008-02-19 13:41     ` Dmitry Adamushko
  2008-02-19 22:52       ` Dmitry Adamushko
  1 sibling, 1 reply; 8+ messages in thread
From: Dmitry Adamushko @ 2008-02-19 13:41 UTC (permalink / raw)
  To: linux-kernel
  Cc: Nick Piggin, Ingo Molnar, Andrew Morton, Rusty Russel,
	Paul E. McKenney, Andy Whitcroft, Peter Zijlstra

btw.,

(a bit more of 'nit-picking' :-)

to work properly, kthread_stop() should also impose one of the
following requirements on a 'kthread' loop:

- a loop can be interrupted _only_ as a result of
'kthread_should_stop() == true'
and no concurrent kthread_stop() calls are possible;

or

- if it can exit due to another reason, a user has to use additional
synchronization means to make sure than kthread_stop() is never
called/running after a main loop has finished (makes sense as 'struct
task_struct *' can be 'obsolete')

otherwise,

note, the comment in kthread() that says "It might have exited on its
own, w/o kthread_stop.  Check."

so let's suppose a 'kthread' is really "exiting on its own" and at the
same time, kthread_stop() is running on another CPU... as a result, we
may end up with kthread_stop() being blocked on
wait_for_completion(&kthread_stop_info.done) without anybody to call
complete().

Although, the requirements above don't seem to be so insane in this case.


static int kthread(void *_create)
{
...
         if (!kthread_should_stop())
                ret = threadfn(data);     <---- our main loop is
inside this function

         /* It might have exited on its own, w/o kthread_stop.  Check. */
         if (kthread_should_stop()) {
                 kthread_stop_info.err = ret;
                 complete(&kthread_stop_info.done);
         }
         return 0;
}

-- 
Best regards,
Dmitry Adamushko

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop()
  2008-02-19 13:41     ` Dmitry Adamushko
@ 2008-02-19 22:52       ` Dmitry Adamushko
  0 siblings, 0 replies; 8+ messages in thread
From: Dmitry Adamushko @ 2008-02-19 22:52 UTC (permalink / raw)
  To: linux-kernel
  Cc: Nick Piggin, Ingo Molnar, Andrew Morton, Rusty Russel,
	Paul E. McKenney, Andy Whitcroft, Peter Zijlstra, Linus Torvalds

humm... following the same logic, there is also a problem in kthread.c.

(1) the main loop of kthreadd() :

                set_current_state(TASK_INTERRUPTIBLE);
                if (list_empty(&kthread_create_list))
                        schedule();

and

(2) kthread_create() does:

spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
wake_up_process(kthreadd_task);
spin_unlock(&kthread_create_lock);

provided,

- (1) doesn't want to take the 'kthread_create_lock' in order to do a
check for list_empty(&kthread_create_list)
[ which can be possible if list_empty() results in a single word-size
aligned read op. -- which is guaranteed to be atomic on any arch, iirc
]

and

- (1) and (2) can run in parallel.

then it's crucial that a modification of the list (i.e.
list_add_tail()) is completed by the moment a state of the task
(kthreadd_task->state) is checked in try_to_wake_up(). i.e. they must
not be re-ordered.

which makes me think that try_to_wake_up() could be better off just
acting as a full mb.

otherwise, a possible fix would be:

this way we get a pair of UNLOCK/LOCK which is guaranteed to be a full mb
(the LOCK is in try_to_wake_up())

[ moreover, there seems to be no need to call wake_up_process() with
'kthread_create_lock' being held ]

--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -145,8 +145,8 @@ struct task_struct *kthread_create(int
(*threadfn)(void *data),

        spin_lock(&kthread_create_lock);
        list_add_tail(&create.list, &kthread_create_list);
-       wake_up_process(kthreadd_task);
        spin_unlock(&kthread_create_lock);
+       wake_up_process(kthreadd_task);

        wait_for_completion(&create.done);


-- 
Best regards,
Dmitry Adamushko

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-02-19 22:52 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-18 23:03 [PATCH, RFC] kthread: (possibly) a missing memory barrier in kthread_stop() Dmitry Adamushko
2008-02-19  6:44 ` Nick Piggin
2008-02-19  9:24   ` Peter Zijlstra
2008-02-19  9:53     ` Dmitry Adamushko
2008-02-19 13:41     ` Dmitry Adamushko
2008-02-19 22:52       ` Dmitry Adamushko
2008-02-19 13:00 ` Andy Whitcroft
2008-02-19 13:11   ` Dmitry Adamushko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).