Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/*******************************************************************************
2 * This file contains the iSCSI Login Thread and Thread Queue functions.
3 *
4 * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
5 *
6 * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
7 *
8 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 ******************************************************************************/
20
21#include <linux/kthread.h>
22#include <linux/list.h>
23#include <linux/bitmap.h>
24
25#include "iscsi_target_core.h"
26#include "iscsi_target_tq.h"
27#include "iscsi_target.h"
28
29static LIST_HEAD(active_ts_list);
30static LIST_HEAD(inactive_ts_list);
31static DEFINE_SPINLOCK(active_ts_lock);
32static DEFINE_SPINLOCK(inactive_ts_lock);
33static DEFINE_SPINLOCK(ts_bitmap_lock);
34
35static void iscsi_add_ts_to_active_list(struct iscsi_thread_set *ts)
36{
37 spin_lock(&active_ts_lock);
38 list_add_tail(&ts->ts_list, &active_ts_list);
39 iscsit_global->active_ts++;
40 spin_unlock(&active_ts_lock);
41}
42
43static void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts)
44{
45 spin_lock(&inactive_ts_lock);
46 list_add_tail(&ts->ts_list, &inactive_ts_list);
47 iscsit_global->inactive_ts++;
48 spin_unlock(&inactive_ts_lock);
49}
50
51static void iscsi_del_ts_from_active_list(struct iscsi_thread_set *ts)
52{
53 spin_lock(&active_ts_lock);
54 list_del(&ts->ts_list);
55 iscsit_global->active_ts--;
56 spin_unlock(&active_ts_lock);
57}
58
59static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void)
60{
61 struct iscsi_thread_set *ts;
62
63 spin_lock(&inactive_ts_lock);
64 if (list_empty(&inactive_ts_list)) {
65 spin_unlock(&inactive_ts_lock);
66 return NULL;
67 }
68
69 ts = list_first_entry(&inactive_ts_list, struct iscsi_thread_set, ts_list);
70
71 list_del(&ts->ts_list);
72 iscsit_global->inactive_ts--;
73 spin_unlock(&inactive_ts_lock);
74
75 return ts;
76}
77
78int iscsi_allocate_thread_sets(u32 thread_pair_count)
79{
80 int allocated_thread_pair_count = 0, i, thread_id;
81 struct iscsi_thread_set *ts = NULL;
82
83 for (i = 0; i < thread_pair_count; i++) {
84 ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL);
85 if (!ts) {
86 pr_err("Unable to allocate memory for"
87 " thread set.\n");
88 return allocated_thread_pair_count;
89 }
90 /*
91 * Locate the next available regision in the thread_set_bitmap
92 */
93 spin_lock(&ts_bitmap_lock);
94 thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap,
95 iscsit_global->ts_bitmap_count, get_order(1));
96 spin_unlock(&ts_bitmap_lock);
97 if (thread_id < 0) {
98 pr_err("bitmap_find_free_region() failed for"
99 " thread_set_bitmap\n");
100 kfree(ts);
101 return allocated_thread_pair_count;
102 }
103
104 ts->thread_id = thread_id;
105 ts->status = ISCSI_THREAD_SET_FREE;
106 INIT_LIST_HEAD(&ts->ts_list);
107 spin_lock_init(&ts->ts_state_lock);
108 init_completion(&ts->rx_post_start_comp);
109 init_completion(&ts->tx_post_start_comp);
110 init_completion(&ts->rx_restart_comp);
111 init_completion(&ts->tx_restart_comp);
112 init_completion(&ts->rx_start_comp);
113 init_completion(&ts->tx_start_comp);
114
115 ts->create_threads = 1;
116 ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s",
117 ISCSI_TX_THREAD_NAME);
118 if (IS_ERR(ts->tx_thread)) {
119 dump_stack();
120 pr_err("Unable to start iscsi_target_tx_thread\n");
121 break;
122 }
123
124 ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s",
125 ISCSI_RX_THREAD_NAME);
126 if (IS_ERR(ts->rx_thread)) {
127 kthread_stop(ts->tx_thread);
128 pr_err("Unable to start iscsi_target_rx_thread\n");
129 break;
130 }
131 ts->create_threads = 0;
132
133 iscsi_add_ts_to_inactive_list(ts);
134 allocated_thread_pair_count++;
135 }
136
137 pr_debug("Spawned %d thread set(s) (%d total threads).\n",
138 allocated_thread_pair_count, allocated_thread_pair_count * 2);
139 return allocated_thread_pair_count;
140}
141
142void iscsi_deallocate_thread_sets(void)
143{
144 u32 released_count = 0;
145 struct iscsi_thread_set *ts = NULL;
146
147 while ((ts = iscsi_get_ts_from_inactive_list())) {
148
149 spin_lock_bh(&ts->ts_state_lock);
150 ts->status = ISCSI_THREAD_SET_DIE;
151 spin_unlock_bh(&ts->ts_state_lock);
152
153 if (ts->rx_thread) {
154 send_sig(SIGINT, ts->rx_thread, 1);
155 kthread_stop(ts->rx_thread);
156 }
157 if (ts->tx_thread) {
158 send_sig(SIGINT, ts->tx_thread, 1);
159 kthread_stop(ts->tx_thread);
160 }
161 /*
162 * Release this thread_id in the thread_set_bitmap
163 */
164 spin_lock(&ts_bitmap_lock);
165 bitmap_release_region(iscsit_global->ts_bitmap,
166 ts->thread_id, get_order(1));
167 spin_unlock(&ts_bitmap_lock);
168
169 released_count++;
170 kfree(ts);
171 }
172
173 if (released_count)
174 pr_debug("Stopped %d thread set(s) (%d total threads)."
175 "\n", released_count, released_count * 2);
176}
177
178static void iscsi_deallocate_extra_thread_sets(void)
179{
180 u32 orig_count, released_count = 0;
181 struct iscsi_thread_set *ts = NULL;
182
183 orig_count = TARGET_THREAD_SET_COUNT;
184
185 while ((iscsit_global->inactive_ts + 1) > orig_count) {
186 ts = iscsi_get_ts_from_inactive_list();
187 if (!ts)
188 break;
189
190 spin_lock_bh(&ts->ts_state_lock);
191 ts->status = ISCSI_THREAD_SET_DIE;
192 spin_unlock_bh(&ts->ts_state_lock);
193
194 if (ts->rx_thread) {
195 send_sig(SIGINT, ts->rx_thread, 1);
196 kthread_stop(ts->rx_thread);
197 }
198 if (ts->tx_thread) {
199 send_sig(SIGINT, ts->tx_thread, 1);
200 kthread_stop(ts->tx_thread);
201 }
202 /*
203 * Release this thread_id in the thread_set_bitmap
204 */
205 spin_lock(&ts_bitmap_lock);
206 bitmap_release_region(iscsit_global->ts_bitmap,
207 ts->thread_id, get_order(1));
208 spin_unlock(&ts_bitmap_lock);
209
210 released_count++;
211 kfree(ts);
212 }
213
214 if (released_count) {
215 pr_debug("Stopped %d thread set(s) (%d total threads)."
216 "\n", released_count, released_count * 2);
217 }
218}
219
220void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts)
221{
222 iscsi_add_ts_to_active_list(ts);
223
224 spin_lock_bh(&ts->ts_state_lock);
225 conn->thread_set = ts;
226 ts->conn = conn;
227 spin_unlock_bh(&ts->ts_state_lock);
228 /*
229 * Start up the RX thread and wait on rx_post_start_comp. The RX
230 * Thread will then do the same for the TX Thread in
231 * iscsi_rx_thread_pre_handler().
232 */
233 complete(&ts->rx_start_comp);
234 wait_for_completion(&ts->rx_post_start_comp);
235}
236
237struct iscsi_thread_set *iscsi_get_thread_set(void)
238{
239 int allocate_ts = 0;
240 struct completion comp;
241 struct iscsi_thread_set *ts = NULL;
242 /*
243 * If no inactive thread set is available on the first call to
244 * iscsi_get_ts_from_inactive_list(), sleep for a second and
245 * try again. If still none are available after two attempts,
246 * allocate a set ourselves.
247 */
248get_set:
249 ts = iscsi_get_ts_from_inactive_list();
250 if (!ts) {
251 if (allocate_ts == 2)
252 iscsi_allocate_thread_sets(1);
253
254 init_completion(&comp);
255 wait_for_completion_timeout(&comp, 1 * HZ);
256
257 allocate_ts++;
258 goto get_set;
259 }
260
261 ts->delay_inactive = 1;
262 ts->signal_sent = 0;
263 ts->thread_count = 2;
264 init_completion(&ts->rx_restart_comp);
265 init_completion(&ts->tx_restart_comp);
266
267 return ts;
268}
269
270void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear)
271{
272 struct iscsi_thread_set *ts = NULL;
273
274 if (!conn->thread_set) {
275 pr_err("struct iscsi_conn->thread_set is NULL\n");
276 return;
277 }
278 ts = conn->thread_set;
279
280 spin_lock_bh(&ts->ts_state_lock);
281 ts->thread_clear &= ~thread_clear;
282
283 if ((thread_clear & ISCSI_CLEAR_RX_THREAD) &&
284 (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD))
285 complete(&ts->rx_restart_comp);
286 else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) &&
287 (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD))
288 complete(&ts->tx_restart_comp);
289 spin_unlock_bh(&ts->ts_state_lock);
290}
291
292void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent)
293{
294 struct iscsi_thread_set *ts = NULL;
295
296 if (!conn->thread_set) {
297 pr_err("struct iscsi_conn->thread_set is NULL\n");
298 return;
299 }
300 ts = conn->thread_set;
301
302 spin_lock_bh(&ts->ts_state_lock);
303 ts->signal_sent |= signal_sent;
304 spin_unlock_bh(&ts->ts_state_lock);
305}
306
307int iscsi_release_thread_set(struct iscsi_conn *conn)
308{
309 int thread_called = 0;
310 struct iscsi_thread_set *ts = NULL;
311
312 if (!conn || !conn->thread_set) {
313 pr_err("connection or thread set pointer is NULL\n");
314 BUG();
315 }
316 ts = conn->thread_set;
317
318 spin_lock_bh(&ts->ts_state_lock);
319 ts->status = ISCSI_THREAD_SET_RESET;
320
321 if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME,
322 strlen(ISCSI_RX_THREAD_NAME)))
323 thread_called = ISCSI_RX_THREAD;
324 else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME,
325 strlen(ISCSI_TX_THREAD_NAME)))
326 thread_called = ISCSI_TX_THREAD;
327
328 if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) &&
329 (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) {
330
331 if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) {
332 send_sig(SIGINT, ts->rx_thread, 1);
333 ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
334 }
335 ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD;
336 spin_unlock_bh(&ts->ts_state_lock);
337 wait_for_completion(&ts->rx_restart_comp);
338 spin_lock_bh(&ts->ts_state_lock);
339 ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD;
340 }
341 if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) &&
342 (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) {
343
344 if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) {
345 send_sig(SIGINT, ts->tx_thread, 1);
346 ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
347 }
348 ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD;
349 spin_unlock_bh(&ts->ts_state_lock);
350 wait_for_completion(&ts->tx_restart_comp);
351 spin_lock_bh(&ts->ts_state_lock);
352 ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD;
353 }
354
355 ts->conn = NULL;
356 ts->status = ISCSI_THREAD_SET_FREE;
357 spin_unlock_bh(&ts->ts_state_lock);
358
359 return 0;
360}
361
362int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn)
363{
364 struct iscsi_thread_set *ts;
365
366 if (!conn->thread_set)
367 return -1;
368 ts = conn->thread_set;
369
370 spin_lock_bh(&ts->ts_state_lock);
371 if (ts->status != ISCSI_THREAD_SET_ACTIVE) {
372 spin_unlock_bh(&ts->ts_state_lock);
373 return -1;
374 }
375
376 if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) {
377 send_sig(SIGINT, ts->tx_thread, 1);
378 ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD;
379 }
380 if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) {
381 send_sig(SIGINT, ts->rx_thread, 1);
382 ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD;
383 }
384 spin_unlock_bh(&ts->ts_state_lock);
385
386 return 0;
387}
388
389static void iscsi_check_to_add_additional_sets(void)
390{
391 int thread_sets_add;
392
393 spin_lock(&inactive_ts_lock);
394 thread_sets_add = iscsit_global->inactive_ts;
395 spin_unlock(&inactive_ts_lock);
396 if (thread_sets_add == 1)
397 iscsi_allocate_thread_sets(1);
398}
399
400static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts)
401{
402 spin_lock_bh(&ts->ts_state_lock);
403 if ((ts->status == ISCSI_THREAD_SET_DIE) || signal_pending(current)) {
404 spin_unlock_bh(&ts->ts_state_lock);
405 return -1;
406 }
407 spin_unlock_bh(&ts->ts_state_lock);
408
409 return 0;
410}
411
412struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts)
413{
414 int ret;
415
416 spin_lock_bh(&ts->ts_state_lock);
417 if (ts->create_threads) {
418 spin_unlock_bh(&ts->ts_state_lock);
419 goto sleep;
420 }
421
422 flush_signals(current);
423
424 if (ts->delay_inactive && (--ts->thread_count == 0)) {
425 spin_unlock_bh(&ts->ts_state_lock);
426 iscsi_del_ts_from_active_list(ts);
427
428 if (!iscsit_global->in_shutdown)
429 iscsi_deallocate_extra_thread_sets();
430
431 iscsi_add_ts_to_inactive_list(ts);
432 spin_lock_bh(&ts->ts_state_lock);
433 }
434
435 if ((ts->status == ISCSI_THREAD_SET_RESET) &&
436 (ts->thread_clear & ISCSI_CLEAR_RX_THREAD))
437 complete(&ts->rx_restart_comp);
438
439 ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD;
440 spin_unlock_bh(&ts->ts_state_lock);
441sleep:
442 ret = wait_for_completion_interruptible(&ts->rx_start_comp);
443 if (ret != 0)
444 return NULL;
445
446 if (iscsi_signal_thread_pre_handler(ts) < 0)
447 return NULL;
448
449 if (!ts->conn) {
450 pr_err("struct iscsi_thread_set->conn is NULL for"
451 " thread_id: %d, going back to sleep\n", ts->thread_id);
452 goto sleep;
453 }
454 iscsi_check_to_add_additional_sets();
455 /*
456 * The RX Thread starts up the TX Thread and sleeps.
457 */
458 ts->thread_clear |= ISCSI_CLEAR_RX_THREAD;
459 complete(&ts->tx_start_comp);
460 wait_for_completion(&ts->tx_post_start_comp);
461
462 return ts->conn;
463}
464
465struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts)
466{
467 int ret;
468
469 spin_lock_bh(&ts->ts_state_lock);
470 if (ts->create_threads) {
471 spin_unlock_bh(&ts->ts_state_lock);
472 goto sleep;
473 }
474
475 flush_signals(current);
476
477 if (ts->delay_inactive && (--ts->thread_count == 0)) {
478 spin_unlock_bh(&ts->ts_state_lock);
479 iscsi_del_ts_from_active_list(ts);
480
481 if (!iscsit_global->in_shutdown)
482 iscsi_deallocate_extra_thread_sets();
483
484 iscsi_add_ts_to_inactive_list(ts);
485 spin_lock_bh(&ts->ts_state_lock);
486 }
487 if ((ts->status == ISCSI_THREAD_SET_RESET) &&
488 (ts->thread_clear & ISCSI_CLEAR_TX_THREAD))
489 complete(&ts->tx_restart_comp);
490
491 ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD;
492 spin_unlock_bh(&ts->ts_state_lock);
493sleep:
494 ret = wait_for_completion_interruptible(&ts->tx_start_comp);
495 if (ret != 0)
496 return NULL;
497
498 if (iscsi_signal_thread_pre_handler(ts) < 0)
499 return NULL;
500
501 if (!ts->conn) {
502 pr_err("struct iscsi_thread_set->conn is NULL for "
503 " thread_id: %d, going back to sleep\n",
504 ts->thread_id);
505 goto sleep;
506 }
507
508 iscsi_check_to_add_additional_sets();
509 /*
510 * From the TX thread, up the tx_post_start_comp that the RX Thread is
511 * sleeping on in iscsi_rx_thread_pre_handler(), then up the
512 * rx_post_start_comp that iscsi_activate_thread_set() is sleeping on.
513 */
514 ts->thread_clear |= ISCSI_CLEAR_TX_THREAD;
515 complete(&ts->tx_post_start_comp);
516 complete(&ts->rx_post_start_comp);
517
518 spin_lock_bh(&ts->ts_state_lock);
519 ts->status = ISCSI_THREAD_SET_ACTIVE;
520 spin_unlock_bh(&ts->ts_state_lock);
521
522 return ts->conn;
523}
524
525int iscsi_thread_set_init(void)
526{
527 int size;
528
529 iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS;
530
531 size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long);
532 iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL);
533 if (!iscsit_global->ts_bitmap) {
534 pr_err("Unable to allocate iscsit_global->ts_bitmap\n");
535 return -ENOMEM;
536 }
537
538 return 0;
539}
540
541void iscsi_thread_set_free(void)
542{
543 kfree(iscsit_global->ts_bitmap);
544}