diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-09-05 17:54:04 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2013-09-09 17:26:41 -0400 |
commit | bb048357dad6d604520c91586334c9c230366a14 (patch) | |
tree | 091e848fd2604b1cfaf1bfba80f07c3535f7ae4c /drivers/target | |
parent | d381a8010a052813a88e20e089be4a58aad8b40a (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.h | 2 | ||||
-rw-r--r-- | drivers/target/iscsi/iscsi_target_nego.c | 101 |
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 | ||
416 | static void iscsi_target_sk_state_change(struct sock *); | ||
417 | |||
416 | static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) | 418 | static 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 | ||
433 | static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn) | 440 | static 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 | ||
453 | static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); | 464 | static 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 | ||
587 | static 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 | |||
609 | static 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 | |||
576 | static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login) | 660 | static 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 | } |