On Thu, 2013-12-12 at 08:18 +0100, Hannes Reinecke wrote:
> On 12/12/2013 12:44 AM, Nicholas A. Bellinger wrote:
<SNIP>
> > I'm not sure this extra logic is necessary. How about just clearing
> > np->np_thread in iscsit_del_np instead..?
> >
> > Care to verify on your side with the following patch..?
> >
> > --nab
> >
> > diff --git a/drivers/target/iscsi/iscsi_target.c
> > b/drivers/target/iscsi/iscsi_target.c
> > index 02182ab..0086719 100644
> > --- a/drivers/target/iscsi/iscsi_target.c
> > +++ b/drivers/target/iscsi/iscsi_target.c
> > @@ -465,6 +465,7 @@ int iscsit_del_np(struct iscsi_np *np)
> > */
> > send_sig(SIGINT, np->np_thread, 1);
> > kthread_stop(np->np_thread);
> > + np->np_thread = NULL;
> > }
> >
> > np->np_transport->iscsit_free_np(np);
> > diff --git a/drivers/target/iscsi/iscsi_target_login.c
> > b/drivers/target/iscsi/iscsi_target_login.c
> > index 4eb93b2..6ab43b6 100644
> > --- a/drivers/target/iscsi/iscsi_target_login.c
> > +++ b/drivers/target/iscsi/iscsi_target_login.c
> > @@ -1415,7 +1415,6 @@ exit:
> > iscsi_stop_login_thread_timer(np);
> > spin_lock_bh(&np->np_thread_lock);
> > np->np_thread_state = ISCSI_NP_THREAD_EXIT;
> > - np->np_thread = NULL;
> > spin_unlock_bh(&np->np_thread_lock);
> >
> > return 0;
> >
> >
> The problem here is that 'kthread_stop()' is supposed to be called
> with a _valid_ task structure.
>
> There is this race window:
>
> np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN;
> spin_unlock_bh(&np->np_thread_lock);
> here ->
> if (np->np_thread) {
> /*
>
> If the login thread exits before we evaluate 'np->np_thread'
> the pointer is stale and kthread_stop will be called with
> an invalid task structure.
>
> So at the very least we need to check the thread_state before
> evaluating 'np->np_thread' (which will evaluate to 'true' anyway if
> we were to follow up with your patch).
> But in doing so we would need to protect is by the thread_lock
> to synchronize the state.
> And we'll end up with quite the same patch as I've send originally.
>
> In fact, it was an invalid call to kthread_stop() which triggered
> the whole patch in the first place :-)
>
Mmmm, point taken..
> I would love to be proven wrong, as I'm not keen on the 'schedule()'
> in there. But I fail to see another way out here, short of
> converting the entire kthread into a workqueue item ...
Thinking about this a bit more, I think the pre-kthread API
np_thead_state check to exit at the out: label in
__iscsi_target_login_thread() is the culprit..
How about following to only exit when iscsit_del_np() -> kthread_stop()
has been called, and kthread_should_stop() is true..?
--nab
diff --git a/drivers/target/iscsi/iscsi_target.c
b/drivers/target/iscsi/iscsi_target.c
index 02182ab..0086719 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -465,6 +465,7 @@ int iscsit_del_np(struct iscsi_np *np)
*/
send_sig(SIGINT, np->np_thread, 1);
kthread_stop(np->np_thread);
+ np->np_thread = NULL;
}
np->np_transport->iscsit_free_np(np);
diff --git a/drivers/target/iscsi/iscsi_target_login.c
b/drivers/target/iscsi/iscsi_target_login.c
index 4eb93b2..e29279e 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -1403,11 +1403,6 @@ old_sess_out:
out:
stop = kthread_should_stop();
- if (!stop && signal_pending(current)) {
- spin_lock_bh(&np->np_thread_lock);
- stop = (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN);
- spin_unlock_bh(&np->np_thread_lock);
- }
/* Wait for another socket.. */
if (!stop)
return 1;
@@ -1415,7 +1410,6 @@ exit:
iscsi_stop_login_thread_timer(np);
spin_lock_bh(&np->np_thread_lock);
np->np_thread_state = ISCSI_NP_THREAD_EXIT;
- np->np_thread = NULL;
spin_unlock_bh(&np->np_thread_lock);
return 0;
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html