aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2013-09-05 17:54:04 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2013-09-09 17:26:41 -0400
commitbb048357dad6d604520c91586334c9c230366a14 (patch)
tree091e848fd2604b1cfaf1bfba80f07c3535f7ae4c /drivers/target
parentd381a8010a052813a88e20e089be4a58aad8b40a (diff)
iscsi-target: Add sk->sk_state_change to cleanup after TCP failure
This patch adds a sock->sk_state_change() -> iscsi_target_sk_state_change() callback in order to handle transient TCP failures during the login process, where sock->sk_data_ready() -> iscsi_target_sk_data_ready() may not be called to release connection resources, and relinquish tpg->np_login_lock via iscsit_deaccess_np() It performs the sk->sk_state check using iscsi_target_sk_state_check() to look for TCP_CLOSE_WAIT + TCP_CLOSE, and invokes schedule_delayed_work() -> iscsi_target_do_cleanup() to perform the remaining cleanup from process context. It adds an explicit sk_state_check to iscsi_target_do_login() in order to determine a state failure when iscsi_target_sk_state_change() may not be able to proceed before LOGIN_FLAGS_READY=1 is set. Also use sk->sk_sndtimeo -> sk->sk_rcvtimeo settings during login to iscsi_target_set_sock_callbacks(), and revert back post login to use MAX_SCHEDULE_TIMEOUT in iscsi_target_restore_sock_callbacks(). Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c101
2 files changed, 103 insertions, 0 deletions
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index 43228dc786c9..089a0e915da8 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -555,11 +555,13 @@ struct iscsi_conn {
555 /* socket used by this connection */ 555 /* socket used by this connection */
556 struct socket *sock; 556 struct socket *sock;
557 void (*orig_data_ready)(struct sock *, int); 557 void (*orig_data_ready)(struct sock *, int);
558 void (*orig_state_change)(struct sock *);
558#define LOGIN_FLAGS_READ_ACTIVE 1 559#define LOGIN_FLAGS_READ_ACTIVE 1
559#define LOGIN_FLAGS_CLOSED 2 560#define LOGIN_FLAGS_CLOSED 2
560#define LOGIN_FLAGS_READY 4 561#define LOGIN_FLAGS_READY 4
561 unsigned long login_flags; 562 unsigned long login_flags;
562 struct delayed_work login_work; 563 struct delayed_work login_work;
564 struct delayed_work login_cleanup_work;
563 struct iscsi_login *login; 565 struct iscsi_login *login;
564 struct timer_list nopin_timer; 566 struct timer_list nopin_timer;
565 struct timer_list nopin_response_timer; 567 struct timer_list nopin_response_timer;
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 9defee0f0aeb..9d0c910e41f9 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -413,6 +413,8 @@ static void iscsi_target_sk_data_ready(struct sock *sk, int count)
413 write_unlock_bh(&sk->sk_callback_lock); 413 write_unlock_bh(&sk->sk_callback_lock);
414} 414}
415 415
416static void iscsi_target_sk_state_change(struct sock *);
417
416static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) 418static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
417{ 419{
418 struct sock *sk; 420 struct sock *sk;
@@ -426,8 +428,13 @@ static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
426 write_lock_bh(&sk->sk_callback_lock); 428 write_lock_bh(&sk->sk_callback_lock);
427 sk->sk_user_data = conn; 429 sk->sk_user_data = conn;
428 conn->orig_data_ready = sk->sk_data_ready; 430 conn->orig_data_ready = sk->sk_data_ready;
431 conn->orig_state_change = sk->sk_state_change;
429 sk->sk_data_ready = iscsi_target_sk_data_ready; 432 sk->sk_data_ready = iscsi_target_sk_data_ready;
433 sk->sk_state_change = iscsi_target_sk_state_change;
430 write_unlock_bh(&sk->sk_callback_lock); 434 write_unlock_bh(&sk->sk_callback_lock);
435
436 sk->sk_sndtimeo = TA_LOGIN_TIMEOUT * HZ;
437 sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ;
431} 438}
432 439
433static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn) 440static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
@@ -447,7 +454,11 @@ static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
447 } 454 }
448 sk->sk_user_data = NULL; 455 sk->sk_user_data = NULL;
449 sk->sk_data_ready = conn->orig_data_ready; 456 sk->sk_data_ready = conn->orig_data_ready;
457 sk->sk_state_change = conn->orig_state_change;
450 write_unlock_bh(&sk->sk_callback_lock); 458 write_unlock_bh(&sk->sk_callback_lock);
459
460 sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
461 sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
451} 462}
452 463
453static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); 464static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
@@ -573,6 +584,79 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
573 } 584 }
574} 585}
575 586
587static void iscsi_target_do_cleanup(struct work_struct *work)
588{
589 struct iscsi_conn *conn = container_of(work,
590 struct iscsi_conn, login_cleanup_work.work);
591 struct sock *sk = conn->sock->sk;
592 struct iscsi_login *login = conn->login;
593 struct iscsi_np *np = login->np;
594 struct iscsi_portal_group *tpg = conn->tpg;
595 struct iscsi_tpg_np *tpg_np = conn->tpg_np;
596
597 pr_debug("Entering iscsi_target_do_cleanup\n");
598
599 cancel_delayed_work_sync(&conn->login_work);
600 conn->orig_state_change(sk);
601
602 iscsi_target_restore_sock_callbacks(conn);
603 iscsi_target_login_drop(conn, login);
604 iscsit_deaccess_np(np, tpg, tpg_np);
605
606 pr_debug("iscsi_target_do_cleanup done()\n");
607}
608
609static void iscsi_target_sk_state_change(struct sock *sk)
610{
611 struct iscsi_conn *conn;
612 void (*orig_state_change)(struct sock *);
613 bool state;
614
615 pr_debug("Entering iscsi_target_sk_state_change\n");
616
617 write_lock_bh(&sk->sk_callback_lock);
618 conn = sk->sk_user_data;
619 if (!conn) {
620 write_unlock_bh(&sk->sk_callback_lock);
621 return;
622 }
623 orig_state_change = conn->orig_state_change;
624
625 if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
626 pr_debug("Got LOGIN_FLAGS_READY=0 sk_state_change conn: %p\n",
627 conn);
628 write_unlock_bh(&sk->sk_callback_lock);
629 orig_state_change(sk);
630 return;
631 }
632 if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
633 pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
634 " conn: %p\n", conn);
635 write_unlock_bh(&sk->sk_callback_lock);
636 orig_state_change(sk);
637 return;
638 }
639 if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
640 pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
641 conn);
642 write_unlock_bh(&sk->sk_callback_lock);
643 orig_state_change(sk);
644 return;
645 }
646
647 state = iscsi_target_sk_state_check(sk);
648 write_unlock_bh(&sk->sk_callback_lock);
649
650 pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
651
652 if (!state) {
653 pr_debug("iscsi_target_sk_state_change got failed state\n");
654 schedule_delayed_work(&conn->login_cleanup_work, 0);
655 return;
656 }
657 orig_state_change(sk);
658}
659
576static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login) 660static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
577{ 661{
578 if (iscsi_target_do_tx_login_io(conn, login) < 0) 662 if (iscsi_target_do_tx_login_io(conn, login) < 0)
@@ -860,6 +944,21 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
860 break; 944 break;
861 } 945 }
862 946
947 if (conn->sock) {
948 struct sock *sk = conn->sock->sk;
949 bool state;
950
951 read_lock_bh(&sk->sk_callback_lock);
952 state = iscsi_target_sk_state_check(sk);
953 read_unlock_bh(&sk->sk_callback_lock);
954
955 if (!state) {
956 pr_debug("iscsi_target_do_login() failed state for"
957 " conn: %p\n", conn);
958 return -1;
959 }
960 }
961
863 return 0; 962 return 0;
864} 963}
865 964
@@ -896,6 +995,7 @@ int iscsi_target_locate_portal(
896 int sessiontype = 0, ret = 0; 995 int sessiontype = 0, ret = 0;
897 996
898 INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx); 997 INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx);
998 INIT_DELAYED_WORK(&conn->login_cleanup_work, iscsi_target_do_cleanup);
899 iscsi_target_set_sock_callbacks(conn); 999 iscsi_target_set_sock_callbacks(conn);
900 1000
901 login->np = np; 1001 login->np = np;
@@ -1113,6 +1213,7 @@ int iscsi_target_start_negotiation(
1113 } 1213 }
1114 } else if (ret < 0) { 1214 } else if (ret < 0) {
1115 cancel_delayed_work_sync(&conn->login_work); 1215 cancel_delayed_work_sync(&conn->login_work);
1216 cancel_delayed_work_sync(&conn->login_cleanup_work);
1116 iscsi_target_restore_sock_callbacks(conn); 1217 iscsi_target_restore_sock_callbacks(conn);
1117 iscsi_remove_failed_auth_entry(conn); 1218 iscsi_remove_failed_auth_entry(conn);
1118 } 1219 }