diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-09-06 23:46:50 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-09-10 19:48:54 -0400 |
commit | d5705c4ab5035be618a34c95505727acac2e70ec (patch) | |
tree | 512bd3f2a432db2e1ae147a46950927ce2cb0502 /drivers/target | |
parent | 0105c257be8bfb7370631e442eff3213adbfdcb7 (diff) |
iscsi-target: Fix race with thread_pre_handler flush_signals + ISCSI_THREAD_SET_DIE
This patch addresses an long standing race in iscsi_[rx,tx]_thread_pre_handler()
use of flush_signals(), and between iscsi_deallocate_extra_thread_sets() setting
ISCSI_THREAD_SET_DIE before calling kthread_stop().
It addresses the issue by both holding ts_state_lock before calling send_sig()
in iscsi_deallocate_extra_thread_sets(), as well as only calling flush_signals()
when ts->status != ISCSI_THREAD_SET_DIE within iscsi_[rx,tx]_thread_pre_handler()
code.
v2 changes:
- Add explicit complete(&ts->[rx,tx]_start_comp); before kthread_stop() in
iscsi_deallocate_extra_thread_sets()
- Drop left-over send_sig() calls in iscsi_deallocate_extra_thread_sets()
- Add kthread_should_stop() check in iscsi_signal_thread_pre_handler()
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/iscsi/iscsi_target_tq.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c index 81289520f96b..1a5bbec03f3e 100644 --- a/drivers/target/iscsi/iscsi_target_tq.c +++ b/drivers/target/iscsi/iscsi_target_tq.c | |||
@@ -189,16 +189,20 @@ static void iscsi_deallocate_extra_thread_sets(void) | |||
189 | 189 | ||
190 | spin_lock_bh(&ts->ts_state_lock); | 190 | spin_lock_bh(&ts->ts_state_lock); |
191 | ts->status = ISCSI_THREAD_SET_DIE; | 191 | ts->status = ISCSI_THREAD_SET_DIE; |
192 | spin_unlock_bh(&ts->ts_state_lock); | ||
193 | 192 | ||
194 | if (ts->rx_thread) { | 193 | if (ts->rx_thread) { |
195 | send_sig(SIGINT, ts->rx_thread, 1); | 194 | complete(&ts->rx_start_comp); |
195 | spin_unlock_bh(&ts->ts_state_lock); | ||
196 | kthread_stop(ts->rx_thread); | 196 | kthread_stop(ts->rx_thread); |
197 | spin_lock_bh(&ts->ts_state_lock); | ||
197 | } | 198 | } |
198 | if (ts->tx_thread) { | 199 | if (ts->tx_thread) { |
199 | send_sig(SIGINT, ts->tx_thread, 1); | 200 | complete(&ts->tx_start_comp); |
201 | spin_unlock_bh(&ts->ts_state_lock); | ||
200 | kthread_stop(ts->tx_thread); | 202 | kthread_stop(ts->tx_thread); |
203 | spin_lock_bh(&ts->ts_state_lock); | ||
201 | } | 204 | } |
205 | spin_unlock_bh(&ts->ts_state_lock); | ||
202 | /* | 206 | /* |
203 | * Release this thread_id in the thread_set_bitmap | 207 | * Release this thread_id in the thread_set_bitmap |
204 | */ | 208 | */ |
@@ -400,7 +404,8 @@ static void iscsi_check_to_add_additional_sets(void) | |||
400 | static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts) | 404 | static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts) |
401 | { | 405 | { |
402 | spin_lock_bh(&ts->ts_state_lock); | 406 | spin_lock_bh(&ts->ts_state_lock); |
403 | if ((ts->status == ISCSI_THREAD_SET_DIE) || signal_pending(current)) { | 407 | if (ts->status == ISCSI_THREAD_SET_DIE || kthread_should_stop() || |
408 | signal_pending(current)) { | ||
404 | spin_unlock_bh(&ts->ts_state_lock); | 409 | spin_unlock_bh(&ts->ts_state_lock); |
405 | return -1; | 410 | return -1; |
406 | } | 411 | } |
@@ -419,7 +424,8 @@ struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts) | |||
419 | goto sleep; | 424 | goto sleep; |
420 | } | 425 | } |
421 | 426 | ||
422 | flush_signals(current); | 427 | if (ts->status != ISCSI_THREAD_SET_DIE) |
428 | flush_signals(current); | ||
423 | 429 | ||
424 | if (ts->delay_inactive && (--ts->thread_count == 0)) { | 430 | if (ts->delay_inactive && (--ts->thread_count == 0)) { |
425 | spin_unlock_bh(&ts->ts_state_lock); | 431 | spin_unlock_bh(&ts->ts_state_lock); |
@@ -472,7 +478,8 @@ struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts) | |||
472 | goto sleep; | 478 | goto sleep; |
473 | } | 479 | } |
474 | 480 | ||
475 | flush_signals(current); | 481 | if (ts->status != ISCSI_THREAD_SET_DIE) |
482 | flush_signals(current); | ||
476 | 483 | ||
477 | if (ts->delay_inactive && (--ts->thread_count == 0)) { | 484 | if (ts->delay_inactive && (--ts->thread_count == 0)) { |
478 | spin_unlock_bh(&ts->ts_state_lock); | 485 | spin_unlock_bh(&ts->ts_state_lock); |