[PATCH-v2 3/4] iscsi-target: Add login negotiation multi-plexing support
From: Nicholas Bellinger 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. 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 --- drivers/target/iscsi/iscsi_target_core.h |1 + drivers/target/iscsi/iscsi_target_nego.c | 209 - drivers/target/iscsi/iscsi_target_tpg.c |7 +- drivers/target/iscsi/iscsi_target_tpg.h |2 +- 4 files changed, 209 insertions(+), 10 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 711a028..8a4c32d 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -562,6 +562,7 @@ struct iscsi_conn { struct timer_list nopin_timer; struct timer_list nopin_response_timer; struct timer_list transport_timer; + struct task_struct *login_kworker; /* Spinlock used for add/deleting cmd's from conn_cmd_list */ spinlock_t cmd_lock; spinlock_t conn_usage_lock; diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index c4675b4..daebe32 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -377,14 +377,191 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log return 0; } +static void iscsi_target_sk_data_ready(struct sock *sk, int count) +{ + struct iscsi_conn *conn = sk->sk_user_data; + bool rc; + + pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn); + + read_lock_bh(>sk_callback_lock); + if (!sk->sk_user_data) { + read_unlock_bh(>sk_callback_lock); + return; + } + + if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, >login_flags)) { + read_unlock_bh(>sk_callback_lock); + pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p \n", conn); + return; + } + + rc = schedule_delayed_work(>login_work, 0); + if (rc == false) { + pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work" +" got false\n"); + } + read_unlock_bh(>sk_callback_lock); +} + +static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) +{ + struct sock *sk; + + if (!conn->sock) + return; + + sk = conn->sock->sk; + pr_debug("Entering iscsi_target_set_sock_callbacks: conn: %p\n", conn); + + write_lock_bh(>sk_callback_lock); + sk->sk_user_data = conn; + conn->orig_data_ready = sk->sk_data_ready; + sk->sk_data_ready = iscsi_target_sk_data_ready; + write_unlock_bh(>sk_callback_lock); +} + +static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn) +{ + struct sock *sk; + + if (!conn->sock) + return; + + sk = conn->sock->sk; + pr_debug("Entering iscsi_target_restore_sock_callbacks: conn: %p\n", conn); + + write_lock_bh(>sk_callback_lock); + if (!sk->sk_user_data) { + write_unlock_bh(>sk_callback_lock); + return; + } + sk->sk_user_data = NULL; + sk->sk_data_ready = conn->orig_data_ready; + write_unlock_bh(>sk_callback_lock); +} + +static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); + +static bool iscsi_target_sk_state_check(struct sock *sk) +{ + if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) { + pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE," + "returning FALSE\n"); + return false; + } + return true; +} + +static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login) +{ + struct iscsi_np *np = login->np; + bool zero_tsih = login->zero_tsih;
[PATCH-v2 3/4] iscsi-target: Add login negotiation multi-plexing support
From: Nicholas Bellinger n...@linux-iscsi.org 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. 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 n...@linux-iscsi.org --- drivers/target/iscsi/iscsi_target_core.h |1 + drivers/target/iscsi/iscsi_target_nego.c | 209 - drivers/target/iscsi/iscsi_target_tpg.c |7 +- drivers/target/iscsi/iscsi_target_tpg.h |2 +- 4 files changed, 209 insertions(+), 10 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 711a028..8a4c32d 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -562,6 +562,7 @@ struct iscsi_conn { struct timer_list nopin_timer; struct timer_list nopin_response_timer; struct timer_list transport_timer; + struct task_struct *login_kworker; /* Spinlock used for add/deleting cmd's from conn_cmd_list */ spinlock_t cmd_lock; spinlock_t conn_usage_lock; diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index c4675b4..daebe32 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -377,14 +377,191 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log return 0; } +static void iscsi_target_sk_data_ready(struct sock *sk, int count) +{ + struct iscsi_conn *conn = sk-sk_user_data; + bool rc; + + pr_debug(Entering iscsi_target_sk_data_ready: conn: %p\n, conn); + + read_lock_bh(sk-sk_callback_lock); + if (!sk-sk_user_data) { + read_unlock_bh(sk-sk_callback_lock); + return; + } + + if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, conn-login_flags)) { + read_unlock_bh(sk-sk_callback_lock); + pr_debug(Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p \n, conn); + return; + } + + rc = schedule_delayed_work(conn-login_work, 0); + if (rc == false) { + pr_debug(iscsi_target_sk_data_ready, schedule_delayed_work + got false\n); + } + read_unlock_bh(sk-sk_callback_lock); +} + +static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn) +{ + struct sock *sk; + + if (!conn-sock) + return; + + sk = conn-sock-sk; + pr_debug(Entering iscsi_target_set_sock_callbacks: conn: %p\n, conn); + + write_lock_bh(sk-sk_callback_lock); + sk-sk_user_data = conn; + conn-orig_data_ready = sk-sk_data_ready; + sk-sk_data_ready = iscsi_target_sk_data_ready; + write_unlock_bh(sk-sk_callback_lock); +} + +static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn) +{ + struct sock *sk; + + if (!conn-sock) + return; + + sk = conn-sock-sk; + pr_debug(Entering iscsi_target_restore_sock_callbacks: conn: %p\n, conn); + + write_lock_bh(sk-sk_callback_lock); + if (!sk-sk_user_data) { + write_unlock_bh(sk-sk_callback_lock); + return; + } + sk-sk_user_data = NULL; + sk-sk_data_ready = conn-orig_data_ready; + write_unlock_bh(sk-sk_callback_lock); +} + +static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *); + +static bool iscsi_target_sk_state_check(struct sock *sk) +{ + if (sk-sk_state == TCP_CLOSE_WAIT || sk-sk_state == TCP_CLOSE) { + pr_debug(iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE, + returning FALSE\n); + return false; + } + return true; +} + +static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login) +{ + struct iscsi_np *np = login-np; + bool