[PATCH-v2 3/4] iscsi-target: Add login negotiation multi-plexing support

2013-08-18 Thread Nicholas A. Bellinger
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

2013-08-18 Thread Nicholas A. Bellinger
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