aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2013-08-15 16:40:17 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2013-09-09 17:13:31 -0400
commitd381a8010a052813a88e20e089be4a58aad8b40a (patch)
tree89118ae7b5c1bf6fe1be03d4ca6a952fa562d68e
parenta91eb7d9dc8e00de9618633dcb62643fd5eee498 (diff)
iscsi-target: Add login negotiation multi-plexing support
This patch adds support for login negotiation multi-plexing in iscsi-target code. This involves handling the first login request PDU + payload and login response PDU + payload within __iscsi_target_login_thread() process context, and then changing struct sock->sk_data_ready() so that all subsequent exchanges are handled by workqueue process context, to allow other incoming login requests to be received in parallel by __iscsi_target_login_thread(). Upon login negotiation completion (or failure), ->sk_data_ready() is replaced with the original kernel sockets handler saved in iscsi_conn->orig_data_ready. v3 changes: - Convert iscsi_target_sk_data_ready() lock access to write[lock,unlock]_bh() - Only clear LOGIN_FLAGS_READ_ACTIVE when iscsi_target_do_login() returns zero - Add LOGIN_FLAGS_READY + LOGIN_FLAGS_CLOSED bit checks to iscsi_target_sk_data_ready() - Make INIT_DELAYED_WORK() + iscsi_target_set_sock_callbacks() setup happen earlier by moving from iscsi_target_start_negotiation() into iscsi_target_locate_portal() - Set LOGIN_FLAGS_READY bit in iscsi_target_start_negotiation() after iscsi_target_do_login() returns zero. v2 changes: - Add login_timer in iscsi_target_do_login_rx() to avoid possible endless sleep with MSG_WAITALL for traditional iscsi-target in certain network configurations. - Convert lprintk() -> pr_debug() - Remove forward declarations of iscsi_target_set_sock_callbacks(), iscsi_target_restore_sock_callbacks() and iscsi_target_sk_data_ready() - Make iscsi_target_set_sock_callbacks + iscsi_target_restore_sock_callbacks() static (Fengguang) - Make iscsi_target_do_login_rx() safe for iser-target w/o conn->sock Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h1
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c234
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c7
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.h2
4 files changed, 234 insertions, 10 deletions
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index 46228465d190..43228dc786c9 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -564,6 +564,7 @@ struct iscsi_conn {
564 struct timer_list nopin_timer; 564 struct timer_list nopin_timer;
565 struct timer_list nopin_response_timer; 565 struct timer_list nopin_response_timer;
566 struct timer_list transport_timer; 566 struct timer_list transport_timer;
567 struct task_struct *login_kworker;
567 /* Spinlock used for add/deleting cmd's from conn_cmd_list */ 568 /* Spinlock used for add/deleting cmd's from conn_cmd_list */
568 spinlock_t cmd_lock; 569 spinlock_t cmd_lock;
569 spinlock_t conn_usage_lock; 570 spinlock_t conn_usage_lock;
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index c4675b4ceb49..9defee0f0aeb 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -377,14 +377,207 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
377 return 0; 377 return 0;
378} 378}
379 379
380static void iscsi_target_sk_data_ready(struct sock *sk, int count)
381{
382 struct iscsi_conn *conn = sk->sk_user_data;
383 bool rc;
384
385 pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn);
386
387 write_lock_bh(&sk->sk_callback_lock);
388 if (!sk->sk_user_data) {
389 write_unlock_bh(&sk->sk_callback_lock);
390 return;
391 }
392 if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
393 write_unlock_bh(&sk->sk_callback_lock);
394 pr_debug("Got LOGIN_FLAGS_READY=0, conn: %p >>>>\n", conn);
395 return;
396 }
397 if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
398 write_unlock_bh(&sk->sk_callback_lock);
399 pr_debug("Got LOGIN_FLAGS_CLOSED=1, conn: %p >>>>\n", conn);
400 return;
401 }
402 if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
403 write_unlock_bh(&sk->sk_callback_lock);
404 pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p >>>>\n", conn);
405 return;
406 }
407
408 rc = schedule_delayed_work(&conn->login_work, 0);
409 if (rc == false) {
410 pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work"
411 " got false\n");
412 }
413 write_unlock_bh(&sk->sk_callback_lock);
414}
415
416static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
417{
418 struct sock *sk;
419
420 if (!conn->sock)
421 return;
422
423 sk = conn->sock->sk;
424 pr_debug("Entering iscsi_target_set_sock_callbacks: conn: %p\n", conn);
425
426 write_lock_bh(&sk->sk_callback_lock);
427 sk->sk_user_data = conn;
428 conn->orig_data_ready = sk->sk_data_ready;
429 sk->sk_data_ready = iscsi_target_sk_data_ready;
430 write_unlock_bh(&sk->sk_callback_lock);
431}
432
433static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
434{
435 struct sock *sk;
436
437 if (!conn->sock)
438 return;
439
440 sk = conn->sock->sk;
441 pr_debug("Entering iscsi_target_restore_sock_callbacks: conn: %p\n", conn);
442
443 write_lock_bh(&sk->sk_callback_lock);
444 if (!sk->sk_user_data) {
445 write_unlock_bh(&sk->sk_callback_lock);
446 return;
447 }
448 sk->sk_user_data = NULL;
449 sk->sk_data_ready = conn->orig_data_ready;
450 write_unlock_bh(&sk->sk_callback_lock);
451}
452
453static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
454
455static bool iscsi_target_sk_state_check(struct sock *sk)
456{
457 if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
458 pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE,"
459 "returning FALSE\n");
460 return false;
461 }
462 return true;
463}
464
465static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
466{
467 struct iscsi_np *np = login->np;
468 bool zero_tsih = login->zero_tsih;
469
470 iscsi_remove_failed_auth_entry(conn);
471 iscsi_target_nego_release(conn);
472 iscsi_target_login_sess_out(conn, np, zero_tsih, true);
473}
474
475static void iscsi_target_login_timeout(unsigned long data)
476{
477 struct iscsi_conn *conn = (struct iscsi_conn *)data;
478
479 pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
480
481 if (conn->login_kworker) {
482 pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
483 conn->login_kworker->comm, conn->login_kworker->pid);
484 send_sig(SIGINT, conn->login_kworker, 1);
485 }
486}
487
488static void iscsi_target_do_login_rx(struct work_struct *work)
489{
490 struct iscsi_conn *conn = container_of(work,
491 struct iscsi_conn, login_work.work);
492 struct iscsi_login *login = conn->login;
493 struct iscsi_np *np = login->np;
494 struct iscsi_portal_group *tpg = conn->tpg;
495 struct iscsi_tpg_np *tpg_np = conn->tpg_np;
496 struct timer_list login_timer;
497 int rc, zero_tsih = login->zero_tsih;
498 bool state;
499
500 pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
501 conn, current->comm, current->pid);
502
503 spin_lock(&tpg->tpg_state_lock);
504 state = (tpg->tpg_state == TPG_STATE_ACTIVE);
505 spin_unlock(&tpg->tpg_state_lock);
506
507 if (state == false) {
508 pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
509 iscsi_target_restore_sock_callbacks(conn);
510 iscsi_target_login_drop(conn, login);
511 iscsit_deaccess_np(np, tpg, tpg_np);
512 return;
513 }
514
515 if (conn->sock) {
516 struct sock *sk = conn->sock->sk;
517
518 read_lock_bh(&sk->sk_callback_lock);
519 state = iscsi_target_sk_state_check(sk);
520 read_unlock_bh(&sk->sk_callback_lock);
521
522 if (state == false) {
523 pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
524 iscsi_target_restore_sock_callbacks(conn);
525 iscsi_target_login_drop(conn, login);
526 iscsit_deaccess_np(np, tpg, tpg_np);
527 return;
528 }
529 }
530
531 conn->login_kworker = current;
532 allow_signal(SIGINT);
533
534 init_timer(&login_timer);
535 login_timer.expires = (get_jiffies_64() + TA_LOGIN_TIMEOUT * HZ);
536 login_timer.data = (unsigned long)conn;
537 login_timer.function = iscsi_target_login_timeout;
538 add_timer(&login_timer);
539 pr_debug("Starting login_timer for %s/%d\n", current->comm, current->pid);
540
541 rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
542 del_timer_sync(&login_timer);
543 flush_signals(current);
544 conn->login_kworker = NULL;
545
546 if (rc < 0) {
547 iscsi_target_restore_sock_callbacks(conn);
548 iscsi_target_login_drop(conn, login);
549 iscsit_deaccess_np(np, tpg, tpg_np);
550 return;
551 }
552
553 pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
554 conn, current->comm, current->pid);
555
556 rc = iscsi_target_do_login(conn, login);
557 if (rc < 0) {
558 iscsi_target_restore_sock_callbacks(conn);
559 iscsi_target_login_drop(conn, login);
560 iscsit_deaccess_np(np, tpg, tpg_np);
561 } else if (!rc) {
562 if (conn->sock) {
563 struct sock *sk = conn->sock->sk;
564
565 write_lock_bh(&sk->sk_callback_lock);
566 clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
567 write_unlock_bh(&sk->sk_callback_lock);
568 }
569 } else if (rc == 1) {
570 iscsi_target_nego_release(conn);
571 iscsi_post_login_handler(np, conn, zero_tsih);
572 iscsit_deaccess_np(np, tpg, tpg_np);
573 }
574}
575
380static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login) 576static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
381{ 577{
382 if (iscsi_target_do_tx_login_io(conn, login) < 0) 578 if (iscsi_target_do_tx_login_io(conn, login) < 0)
383 return -1; 579 return -1;
384 580
385 if (conn->conn_transport->iscsit_get_login_rx(conn, login) < 0)
386 return -1;
387
388 return 0; 581 return 0;
389} 582}
390 583
@@ -643,10 +836,11 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
643 if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { 836 if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
644 login->tsih = conn->sess->tsih; 837 login->tsih = conn->sess->tsih;
645 login->login_complete = 1; 838 login->login_complete = 1;
839 iscsi_target_restore_sock_callbacks(conn);
646 if (iscsi_target_do_tx_login_io(conn, 840 if (iscsi_target_do_tx_login_io(conn,
647 login) < 0) 841 login) < 0)
648 return -1; 842 return -1;
649 return 0; 843 return 1;
650 } 844 }
651 break; 845 break;
652 default: 846 default:
@@ -663,6 +857,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
663 login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT; 857 login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT;
664 login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; 858 login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
665 } 859 }
860 break;
666 } 861 }
667 862
668 return 0; 863 return 0;
@@ -695,10 +890,16 @@ int iscsi_target_locate_portal(
695 char *tmpbuf, *start = NULL, *end = NULL, *key, *value; 890 char *tmpbuf, *start = NULL, *end = NULL, *key, *value;
696 struct iscsi_session *sess = conn->sess; 891 struct iscsi_session *sess = conn->sess;
697 struct iscsi_tiqn *tiqn; 892 struct iscsi_tiqn *tiqn;
893 struct iscsi_tpg_np *tpg_np = NULL;
698 struct iscsi_login_req *login_req; 894 struct iscsi_login_req *login_req;
699 u32 payload_length; 895 u32 payload_length;
700 int sessiontype = 0, ret = 0; 896 int sessiontype = 0, ret = 0;
701 897
898 INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx);
899 iscsi_target_set_sock_callbacks(conn);
900
901 login->np = np;
902
702 login_req = (struct iscsi_login_req *) login->req; 903 login_req = (struct iscsi_login_req *) login->req;
703 payload_length = ntoh24(login_req->dlength); 904 payload_length = ntoh24(login_req->dlength);
704 905
@@ -822,7 +1023,7 @@ get_target:
822 /* 1023 /*
823 * Locate Target Portal Group from Storage Node. 1024 * Locate Target Portal Group from Storage Node.
824 */ 1025 */
825 conn->tpg = iscsit_get_tpg_from_np(tiqn, np); 1026 conn->tpg = iscsit_get_tpg_from_np(tiqn, np, &tpg_np);
826 if (!conn->tpg) { 1027 if (!conn->tpg) {
827 pr_err("Unable to locate Target Portal Group" 1028 pr_err("Unable to locate Target Portal Group"
828 " on %s\n", tiqn->tiqn); 1029 " on %s\n", tiqn->tiqn);
@@ -832,12 +1033,16 @@ get_target:
832 ret = -1; 1033 ret = -1;
833 goto out; 1034 goto out;
834 } 1035 }
1036 conn->tpg_np = tpg_np;
835 pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt); 1037 pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
836 /* 1038 /*
837 * Setup crc32c modules from libcrypto 1039 * Setup crc32c modules from libcrypto
838 */ 1040 */
839 if (iscsi_login_setup_crypto(conn) < 0) { 1041 if (iscsi_login_setup_crypto(conn) < 0) {
840 pr_err("iscsi_login_setup_crypto() failed\n"); 1042 pr_err("iscsi_login_setup_crypto() failed\n");
1043 kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
1044 iscsit_put_tiqn_for_login(tiqn);
1045 conn->tpg = NULL;
841 ret = -1; 1046 ret = -1;
842 goto out; 1047 goto out;
843 } 1048 }
@@ -846,11 +1051,12 @@ get_target:
846 * process login attempt. 1051 * process login attempt.
847 */ 1052 */
848 if (iscsit_access_np(np, conn->tpg) < 0) { 1053 if (iscsit_access_np(np, conn->tpg) < 0) {
1054 kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
849 iscsit_put_tiqn_for_login(tiqn); 1055 iscsit_put_tiqn_for_login(tiqn);
850 iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 1056 iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
851 ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); 1057 ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
852 ret = -1;
853 conn->tpg = NULL; 1058 conn->tpg = NULL;
1059 ret = -1;
854 goto out; 1060 goto out;
855 } 1061 }
856 1062
@@ -897,10 +1103,22 @@ int iscsi_target_start_negotiation(
897 int ret; 1103 int ret;
898 1104
899 ret = iscsi_target_do_login(conn, login); 1105 ret = iscsi_target_do_login(conn, login);
900 if (ret != 0) 1106 if (!ret) {
1107 if (conn->sock) {
1108 struct sock *sk = conn->sock->sk;
1109
1110 write_lock_bh(&sk->sk_callback_lock);
1111 set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
1112 write_unlock_bh(&sk->sk_callback_lock);
1113 }
1114 } else if (ret < 0) {
1115 cancel_delayed_work_sync(&conn->login_work);
1116 iscsi_target_restore_sock_callbacks(conn);
901 iscsi_remove_failed_auth_entry(conn); 1117 iscsi_remove_failed_auth_entry(conn);
1118 }
1119 if (ret != 0)
1120 iscsi_target_nego_release(conn);
902 1121
903 iscsi_target_nego_release(conn);
904 return ret; 1122 return ret;
905} 1123}
906 1124
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 4bf8913992a5..e070ce2de60a 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -129,7 +129,8 @@ void iscsit_release_discovery_tpg(void)
129 129
130struct iscsi_portal_group *iscsit_get_tpg_from_np( 130struct iscsi_portal_group *iscsit_get_tpg_from_np(
131 struct iscsi_tiqn *tiqn, 131 struct iscsi_tiqn *tiqn,
132 struct iscsi_np *np) 132 struct iscsi_np *np,
133 struct iscsi_tpg_np **tpg_np_out)
133{ 134{
134 struct iscsi_portal_group *tpg = NULL; 135 struct iscsi_portal_group *tpg = NULL;
135 struct iscsi_tpg_np *tpg_np; 136 struct iscsi_tpg_np *tpg_np;
@@ -147,6 +148,8 @@ struct iscsi_portal_group *iscsit_get_tpg_from_np(
147 spin_lock(&tpg->tpg_np_lock); 148 spin_lock(&tpg->tpg_np_lock);
148 list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { 149 list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
149 if (tpg_np->tpg_np == np) { 150 if (tpg_np->tpg_np == np) {
151 *tpg_np_out = tpg_np;
152 kref_get(&tpg_np->tpg_np_kref);
150 spin_unlock(&tpg->tpg_np_lock); 153 spin_unlock(&tpg->tpg_np_lock);
151 spin_unlock(&tiqn->tiqn_tpg_lock); 154 spin_unlock(&tiqn->tiqn_tpg_lock);
152 return tpg; 155 return tpg;
@@ -494,6 +497,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
494 INIT_LIST_HEAD(&tpg_np->tpg_np_child_list); 497 INIT_LIST_HEAD(&tpg_np->tpg_np_child_list);
495 INIT_LIST_HEAD(&tpg_np->tpg_np_parent_list); 498 INIT_LIST_HEAD(&tpg_np->tpg_np_parent_list);
496 spin_lock_init(&tpg_np->tpg_np_parent_lock); 499 spin_lock_init(&tpg_np->tpg_np_parent_lock);
500 init_completion(&tpg_np->tpg_np_comp);
501 kref_init(&tpg_np->tpg_np_kref);
497 tpg_np->tpg_np = np; 502 tpg_np->tpg_np = np;
498 tpg_np->tpg = tpg; 503 tpg_np->tpg = tpg;
499 504
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h
index 60ef4a9ad91b..b77693e2c209 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.h
+++ b/drivers/target/iscsi/iscsi_target_tpg.h
@@ -5,7 +5,7 @@ extern struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *,
5extern int iscsit_load_discovery_tpg(void); 5extern int iscsit_load_discovery_tpg(void);
6extern void iscsit_release_discovery_tpg(void); 6extern void iscsit_release_discovery_tpg(void);
7extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *, 7extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *,
8 struct iscsi_np *); 8 struct iscsi_np *, struct iscsi_tpg_np **);
9extern int iscsit_get_tpg(struct iscsi_portal_group *); 9extern int iscsit_get_tpg(struct iscsi_portal_group *);
10extern void iscsit_put_tpg(struct iscsi_portal_group *); 10extern void iscsit_put_tpg(struct iscsi_portal_group *);
11extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *, bool); 11extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *, bool);