Commit bb048357 authored by Nicholas Bellinger's avatar Nicholas Bellinger

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: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent d381a801
...@@ -555,11 +555,13 @@ struct iscsi_conn { ...@@ -555,11 +555,13 @@ struct iscsi_conn {
/* socket used by this connection */ /* socket used by this connection */
struct socket *sock; struct socket *sock;
void (*orig_data_ready)(struct sock *, int); void (*orig_data_ready)(struct sock *, int);
void (*orig_state_change)(struct sock *);
#define LOGIN_FLAGS_READ_ACTIVE 1 #define LOGIN_FLAGS_READ_ACTIVE 1
#define LOGIN_FLAGS_CLOSED 2 #define LOGIN_FLAGS_CLOSED 2
#define LOGIN_FLAGS_READY 4 #define LOGIN_FLAGS_READY 4
unsigned long login_flags; unsigned long login_flags;
struct delayed_work login_work; struct delayed_work login_work;
struct delayed_work login_cleanup_work;
struct iscsi_login *login; struct iscsi_login *login;
struct timer_list nopin_timer; struct timer_list nopin_timer;
struct timer_list nopin_response_timer; struct timer_list nopin_response_timer;
......
...@@ -413,6 +413,8 @@ static void iscsi_target_sk_data_ready(struct sock *sk, int count) ...@@ -413,6 +413,8 @@ static void iscsi_target_sk_data_ready(struct sock *sk, int count)
write_unlock_bh(&sk->sk_callback_lock); write_unlock_bh(&sk->sk_callback_lock);
} }
static void iscsi_target_sk_state_change(struct sock *);
static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
{ {
struct sock *sk; struct sock *sk;
...@@ -426,8 +428,13 @@ static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) ...@@ -426,8 +428,13 @@ static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
write_lock_bh(&sk->sk_callback_lock); write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = conn; sk->sk_user_data = conn;
conn->orig_data_ready = sk->sk_data_ready; conn->orig_data_ready = sk->sk_data_ready;
conn->orig_state_change = sk->sk_state_change;
sk->sk_data_ready = iscsi_target_sk_data_ready; sk->sk_data_ready = iscsi_target_sk_data_ready;
sk->sk_state_change = iscsi_target_sk_state_change;
write_unlock_bh(&sk->sk_callback_lock); write_unlock_bh(&sk->sk_callback_lock);
sk->sk_sndtimeo = TA_LOGIN_TIMEOUT * HZ;
sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ;
} }
static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn) 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,7 +454,11 @@ static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
} }
sk->sk_user_data = NULL; sk->sk_user_data = NULL;
sk->sk_data_ready = conn->orig_data_ready; sk->sk_data_ready = conn->orig_data_ready;
sk->sk_state_change = conn->orig_state_change;
write_unlock_bh(&sk->sk_callback_lock); write_unlock_bh(&sk->sk_callback_lock);
sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
} }
static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); 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,6 +584,79 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
} }
} }
static void iscsi_target_do_cleanup(struct work_struct *work)
{
struct iscsi_conn *conn = container_of(work,
struct iscsi_conn, login_cleanup_work.work);
struct sock *sk = conn->sock->sk;
struct iscsi_login *login = conn->login;
struct iscsi_np *np = login->np;
struct iscsi_portal_group *tpg = conn->tpg;
struct iscsi_tpg_np *tpg_np = conn->tpg_np;
pr_debug("Entering iscsi_target_do_cleanup\n");
cancel_delayed_work_sync(&conn->login_work);
conn->orig_state_change(sk);
iscsi_target_restore_sock_callbacks(conn);
iscsi_target_login_drop(conn, login);
iscsit_deaccess_np(np, tpg, tpg_np);
pr_debug("iscsi_target_do_cleanup done()\n");
}
static void iscsi_target_sk_state_change(struct sock *sk)
{
struct iscsi_conn *conn;
void (*orig_state_change)(struct sock *);
bool state;
pr_debug("Entering iscsi_target_sk_state_change\n");
write_lock_bh(&sk->sk_callback_lock);
conn = sk->sk_user_data;
if (!conn) {
write_unlock_bh(&sk->sk_callback_lock);
return;
}
orig_state_change = conn->orig_state_change;
if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_READY=0 sk_state_change conn: %p\n",
conn);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
return;
}
if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
" conn: %p\n", conn);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
return;
}
if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
conn);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
return;
}
state = iscsi_target_sk_state_check(sk);
write_unlock_bh(&sk->sk_callback_lock);
pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
if (!state) {
pr_debug("iscsi_target_sk_state_change got failed state\n");
schedule_delayed_work(&conn->login_cleanup_work, 0);
return;
}
orig_state_change(sk);
}
static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login) static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
{ {
if (iscsi_target_do_tx_login_io(conn, login) < 0) 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,6 +944,21 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
break; break;
} }
if (conn->sock) {
struct sock *sk = conn->sock->sk;
bool state;
read_lock_bh(&sk->sk_callback_lock);
state = iscsi_target_sk_state_check(sk);
read_unlock_bh(&sk->sk_callback_lock);
if (!state) {
pr_debug("iscsi_target_do_login() failed state for"
" conn: %p\n", conn);
return -1;
}
}
return 0; return 0;
} }
...@@ -896,6 +995,7 @@ int iscsi_target_locate_portal( ...@@ -896,6 +995,7 @@ int iscsi_target_locate_portal(
int sessiontype = 0, ret = 0; int sessiontype = 0, ret = 0;
INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx); INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx);
INIT_DELAYED_WORK(&conn->login_cleanup_work, iscsi_target_do_cleanup);
iscsi_target_set_sock_callbacks(conn); iscsi_target_set_sock_callbacks(conn);
login->np = np; login->np = np;
...@@ -1113,6 +1213,7 @@ int iscsi_target_start_negotiation( ...@@ -1113,6 +1213,7 @@ int iscsi_target_start_negotiation(
} }
} else if (ret < 0) { } else if (ret < 0) {
cancel_delayed_work_sync(&conn->login_work); cancel_delayed_work_sync(&conn->login_work);
cancel_delayed_work_sync(&conn->login_cleanup_work);
iscsi_target_restore_sock_callbacks(conn); iscsi_target_restore_sock_callbacks(conn);
iscsi_remove_failed_auth_entry(conn); iscsi_remove_failed_auth_entry(conn);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment