All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/3] Fix possible hangs and race conditions during login
@ 2023-04-17 17:17 Maurizio Lombardi
  2023-04-17 17:17 ` [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code Maurizio Lombardi
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Maurizio Lombardi @ 2023-04-17 17:17 UTC (permalink / raw)
  To: martin.petersen; +Cc: michael.christie, target-devel

PATCH 1 fixes a possible hang when during a login operation the initiator
stops sending data to the target and the login_work doesn't get scheduled.

PATCH 3 fixes a bug in iscsi_target_locate_portal(), in the error
code path the login semaphore may end up being released even if
no one called down() against it, this may allow
multiple threads to access the semaphore-protected code.

v2: the login timeout of 15 seconds now is relative to the entire
    login operation and not per-PDU.

Maurizio Lombardi (3):
  target: iscsi: fix hang in the iSCSI login code
  target: iscsi: remove unused transport_timer
  target: iscsi: prevent login threads from racing between each other

 drivers/target/iscsi/iscsi_target.c       |  2 -
 drivers/target/iscsi/iscsi_target_login.c | 63 ++------------------
 drivers/target/iscsi/iscsi_target_nego.c  | 72 +++++++++++++----------
 drivers/target/iscsi/iscsi_target_util.c  | 51 ++++++++++++++++
 drivers/target/iscsi/iscsi_target_util.h  |  4 ++
 include/target/iscsi/iscsi_target_core.h  |  7 ++-
 6 files changed, 104 insertions(+), 95 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code
  2023-04-17 17:17 [PATCH V2 0/3] Fix possible hangs and race conditions during login Maurizio Lombardi
@ 2023-04-17 17:17 ` Maurizio Lombardi
  2023-04-26 15:35   ` Mike Christie
  2023-04-17 17:18 ` [PATCH V2 2/3] target: iscsi: remove unused transport_timer Maurizio Lombardi
  2023-04-17 17:18 ` [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other Maurizio Lombardi
  2 siblings, 1 reply; 8+ messages in thread
From: Maurizio Lombardi @ 2023-04-17 17:17 UTC (permalink / raw)
  To: martin.petersen; +Cc: michael.christie, target-devel

If the initiator suddenly stops sending data during a login while
keeping the TCP connection open, the login_work won't be scheduled
and will never release the login semaphore;
concurrent login operations will therefore get stuck and fail.

The bug is due to the inability of the login timeout code to properly
handle this particular case.

Fix the problem by replacing the old per-NP login timer
with a new per-connection timer.

The timer is started when an initiator connects to the target;
if it expires, it sends a SIGINT signal to the thread
pointed at by the conn->login_kworker pointer.

conn->login_kworker is set by calling the iscsit_set_login_timer_kworker()
helper, initially it will point to the np thread;
When the login operation's control is in the process
of being passed from the NP-thread to login_work, the conn->login_worker
pointer is set to NULL.
Finally, login_kworker will be changed to point to the worker thread
executing the login_work job.

If conn->login_kworker is NULL when the timer expires, it means that
the login operation hasn't been completed yet but login_work isn't running,
in this case the timer will mark the login process as failed
and will schedule login_work so the latter will be
forced to free the resources it holds.

Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
---
 drivers/target/iscsi/iscsi_target.c       |  2 -
 drivers/target/iscsi/iscsi_target_login.c | 63 ++-------------------
 drivers/target/iscsi/iscsi_target_nego.c  | 68 ++++++++++++-----------
 drivers/target/iscsi/iscsi_target_util.c  | 51 +++++++++++++++++
 drivers/target/iscsi/iscsi_target_util.h  |  4 ++
 include/target/iscsi/iscsi_target_core.h  |  6 +-
 6 files changed, 101 insertions(+), 93 deletions(-)

diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index baf4da7bb3b4..d3bd005cdc65 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -363,8 +363,6 @@ struct iscsi_np *iscsit_add_np(
 	init_completion(&np->np_restart_comp);
 	INIT_LIST_HEAD(&np->np_list);
 
-	timer_setup(&np->np_login_timer, iscsi_handle_login_thread_timeout, 0);
-
 	ret = iscsi_target_setup_login_socket(np, sockaddr);
 	if (ret != 0) {
 		kfree(np);
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 27e448c2d066..3e49573289bb 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -811,59 +811,6 @@ void iscsi_post_login_handler(
 	iscsit_dec_conn_usage_count(conn);
 }
 
-void iscsi_handle_login_thread_timeout(struct timer_list *t)
-{
-	struct iscsi_np *np = from_timer(np, t, np_login_timer);
-
-	spin_lock_bh(&np->np_thread_lock);
-	pr_err("iSCSI Login timeout on Network Portal %pISpc\n",
-			&np->np_sockaddr);
-
-	if (np->np_login_timer_flags & ISCSI_TF_STOP) {
-		spin_unlock_bh(&np->np_thread_lock);
-		return;
-	}
-
-	if (np->np_thread)
-		send_sig(SIGINT, np->np_thread, 1);
-
-	np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
-	spin_unlock_bh(&np->np_thread_lock);
-}
-
-static void iscsi_start_login_thread_timer(struct iscsi_np *np)
-{
-	/*
-	 * This used the TA_LOGIN_TIMEOUT constant because at this
-	 * point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout
-	 */
-	spin_lock_bh(&np->np_thread_lock);
-	np->np_login_timer_flags &= ~ISCSI_TF_STOP;
-	np->np_login_timer_flags |= ISCSI_TF_RUNNING;
-	mod_timer(&np->np_login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
-
-	pr_debug("Added timeout timer to iSCSI login request for"
-			" %u seconds.\n", TA_LOGIN_TIMEOUT);
-	spin_unlock_bh(&np->np_thread_lock);
-}
-
-static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
-{
-	spin_lock_bh(&np->np_thread_lock);
-	if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) {
-		spin_unlock_bh(&np->np_thread_lock);
-		return;
-	}
-	np->np_login_timer_flags |= ISCSI_TF_STOP;
-	spin_unlock_bh(&np->np_thread_lock);
-
-	del_timer_sync(&np->np_login_timer);
-
-	spin_lock_bh(&np->np_thread_lock);
-	np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
-	spin_unlock_bh(&np->np_thread_lock);
-}
-
 int iscsit_setup_np(
 	struct iscsi_np *np,
 	struct sockaddr_storage *sockaddr)
@@ -1123,10 +1070,13 @@ static struct iscsit_conn *iscsit_alloc_conn(struct iscsi_np *np)
 	spin_lock_init(&conn->nopin_timer_lock);
 	spin_lock_init(&conn->response_queue_lock);
 	spin_lock_init(&conn->state_lock);
+	spin_lock_init(&conn->login_worker_lock);
+	spin_lock_init(&conn->login_timer_lock);
 
 	timer_setup(&conn->nopin_response_timer,
 		    iscsit_handle_nopin_response_timeout, 0);
 	timer_setup(&conn->nopin_timer, iscsit_handle_nopin_timeout, 0);
+	timer_setup(&conn->login_timer, iscsit_login_timeout, 0);
 
 	if (iscsit_conn_set_transport(conn, np->np_transport) < 0)
 		goto free_conn;
@@ -1297,7 +1247,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 		goto new_sess_out;
 	}
 
-	iscsi_start_login_thread_timer(np);
+	iscsit_start_login_timer(conn, current);
 
 	pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
 	conn->conn_state = TARG_CONN_STATE_XPT_UP;
@@ -1410,8 +1360,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 	if (ret < 0)
 		goto new_sess_out;
 
-	iscsi_stop_login_thread_timer(np);
-
 	if (ret == 1) {
 		tpg_np = conn->tpg_np;
 
@@ -1427,7 +1375,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 new_sess_out:
 	new_sess = true;
 old_sess_out:
-	iscsi_stop_login_thread_timer(np);
+	iscsit_stop_login_timer(conn);
 	tpg_np = conn->tpg_np;
 	iscsi_target_login_sess_out(conn, zero_tsih, new_sess);
 	new_sess = false;
@@ -1441,7 +1389,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 	return 1;
 
 exit:
-	iscsi_stop_login_thread_timer(np);
 	spin_lock_bh(&np->np_thread_lock);
 	np->np_thread_state = ISCSI_NP_THREAD_EXIT;
 	spin_unlock_bh(&np->np_thread_lock);
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 24040c118e49..4164977c170e 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -535,25 +535,6 @@ static void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login
 	iscsi_target_login_sess_out(conn, zero_tsih, true);
 }
 
-struct conn_timeout {
-	struct timer_list timer;
-	struct iscsit_conn *conn;
-};
-
-static void iscsi_target_login_timeout(struct timer_list *t)
-{
-	struct conn_timeout *timeout = from_timer(timeout, t, timer);
-	struct iscsit_conn *conn = timeout->conn;
-
-	pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
-
-	if (conn->login_kworker) {
-		pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
-			 conn->login_kworker->comm, conn->login_kworker->pid);
-		send_sig(SIGINT, conn->login_kworker, 1);
-	}
-}
-
 static void iscsi_target_do_login_rx(struct work_struct *work)
 {
 	struct iscsit_conn *conn = container_of(work,
@@ -562,12 +543,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
 	struct iscsi_np *np = login->np;
 	struct iscsi_portal_group *tpg = conn->tpg;
 	struct iscsi_tpg_np *tpg_np = conn->tpg_np;
-	struct conn_timeout timeout;
 	int rc, zero_tsih = login->zero_tsih;
 	bool state;
 
 	pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
 			conn, current->comm, current->pid);
+
+	spin_lock(&conn->login_worker_lock);
+	set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
+	spin_unlock(&conn->login_worker_lock);
 	/*
 	 * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
 	 * before initial PDU processing in iscsi_target_start_negotiation()
@@ -597,19 +581,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
 		goto err;
 	}
 
-	conn->login_kworker = current;
 	allow_signal(SIGINT);
-
-	timeout.conn = conn;
-	timer_setup_on_stack(&timeout.timer, iscsi_target_login_timeout, 0);
-	mod_timer(&timeout.timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
-	pr_debug("Starting login timer for %s/%d\n", current->comm, current->pid);
+	rc = iscsit_set_login_timer_kworker(conn, current);
+	if (rc < 0) {
+		/* The login timer has already expired */
+		pr_debug("iscsi_target_do_login_rx, login failed\n");
+		goto err;
+	}
 
 	rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
-	del_timer_sync(&timeout.timer);
-	destroy_timer_on_stack(&timeout.timer);
 	flush_signals(current);
-	conn->login_kworker = NULL;
 
 	if (rc < 0)
 		goto err;
@@ -646,7 +627,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
 		if (iscsi_target_sk_check_and_clear(conn,
 						    LOGIN_FLAGS_WRITE_ACTIVE))
 			goto err;
+
+		/* Set the login timer thread pointer to NULL to prevent the
+		 * login process from getting stuck if the initiator
+		 * stops sending data.
+		 */
+		rc = iscsit_set_login_timer_kworker(conn, NULL);
+		if (rc < 0)
+			goto err;
 	} else if (rc == 1) {
+		iscsit_stop_login_timer(conn);
 		cancel_delayed_work(&conn->login_work);
 		iscsi_target_nego_release(conn);
 		iscsi_post_login_handler(np, conn, zero_tsih);
@@ -656,6 +646,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
 
 err:
 	iscsi_target_restore_sock_callbacks(conn);
+	iscsit_stop_login_timer(conn);
 	cancel_delayed_work(&conn->login_work);
 	iscsi_target_login_drop(conn, login);
 	iscsit_deaccess_np(np, tpg, tpg_np);
@@ -1368,14 +1359,29 @@ int iscsi_target_start_negotiation(
 	 * and perform connection cleanup now.
 	 */
 	ret = iscsi_target_do_login(conn, login);
-	if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
-		ret = -1;
+	if (!ret) {
+		spin_lock(&conn->login_worker_lock);
+
+		if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
+			ret = -1;
+		else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
+			if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
+				/* The timeout has expired already.
+				 * Schedule login_work to perform the cleanup.
+				 */
+				schedule_delayed_work(&conn->login_work, 0);
+			}
+		}
+
+		spin_unlock(&conn->login_worker_lock);
+	}
 
 	if (ret < 0) {
 		iscsi_target_restore_sock_callbacks(conn);
 		iscsi_remove_failed_auth_entry(conn);
 	}
 	if (ret != 0) {
+		iscsit_stop_login_timer(conn);
 		cancel_delayed_work_sync(&conn->login_work);
 		iscsi_target_nego_release(conn);
 	}
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 26dc8ed3045b..b14835fcb033 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -1040,6 +1040,57 @@ void iscsit_stop_nopin_timer(struct iscsit_conn *conn)
 	spin_unlock_bh(&conn->nopin_timer_lock);
 }
 
+void iscsit_login_timeout(struct timer_list *t)
+{
+	struct iscsit_conn *conn = from_timer(conn, t, login_timer);
+	struct iscsi_login *login = conn->login;
+
+	pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
+
+	spin_lock_bh(&conn->login_timer_lock);
+	login->login_failed = 1;
+
+	if (conn->login_kworker) {
+		pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
+			 conn->login_kworker->comm, conn->login_kworker->pid);
+		send_sig(SIGINT, conn->login_kworker, 1);
+	} else {
+		schedule_delayed_work(&conn->login_work, 0);
+	}
+	spin_unlock_bh(&conn->login_timer_lock);
+}
+
+void iscsit_start_login_timer(struct iscsit_conn *conn, struct task_struct *kthr)
+{
+	pr_debug("Login timer started\n");
+
+	conn->login_kworker = kthr;
+	mod_timer(&conn->login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
+}
+
+int iscsit_set_login_timer_kworker(struct iscsit_conn *conn, struct task_struct *kthr)
+{
+	struct iscsi_login *login = conn->login;
+	int ret = 0;
+
+	spin_lock_bh(&conn->login_timer_lock);
+	if (login->login_failed) {
+		/* The timer has already expired */
+		ret = -1;
+	} else {
+		conn->login_kworker = kthr;
+	}
+	spin_unlock_bh(&conn->login_timer_lock);
+
+	return ret;
+}
+
+void iscsit_stop_login_timer(struct iscsit_conn *conn)
+{
+	pr_debug("Login timer stopped\n");
+	timer_delete_sync(&conn->login_timer);
+}
+
 int iscsit_send_tx_data(
 	struct iscsit_cmd *cmd,
 	struct iscsit_conn *conn,
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h
index 33ea799a0850..24b8e577575a 100644
--- a/drivers/target/iscsi/iscsi_target_util.h
+++ b/drivers/target/iscsi/iscsi_target_util.h
@@ -56,6 +56,10 @@ extern void iscsit_handle_nopin_timeout(struct timer_list *t);
 extern void __iscsit_start_nopin_timer(struct iscsit_conn *);
 extern void iscsit_start_nopin_timer(struct iscsit_conn *);
 extern void iscsit_stop_nopin_timer(struct iscsit_conn *);
+extern void iscsit_login_timeout(struct timer_list *t);
+extern void iscsit_start_login_timer(struct iscsit_conn *, struct task_struct *kthr);
+extern void iscsit_stop_login_timer(struct iscsit_conn *);
+extern int iscsit_set_login_timer_kworker(struct iscsit_conn *, struct task_struct *kthr);
 extern int iscsit_send_tx_data(struct iscsit_cmd *, struct iscsit_conn *, int);
 extern int iscsit_fe_sendpage_sg(struct iscsit_cmd *, struct iscsit_conn *);
 extern int iscsit_tx_login_rsp(struct iscsit_conn *, u8, u8);
diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 94d06ddfd80a..082aaad8ef1b 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -562,12 +562,14 @@ struct iscsit_conn {
 #define LOGIN_FLAGS_READ_ACTIVE		2
 #define LOGIN_FLAGS_WRITE_ACTIVE	3
 #define LOGIN_FLAGS_CLOSED		4
+#define LOGIN_FLAGS_WORKER_RUNNING	5
 	unsigned long		login_flags;
 	struct delayed_work	login_work;
 	struct iscsi_login	*login;
 	struct timer_list	nopin_timer;
 	struct timer_list	nopin_response_timer;
 	struct timer_list	transport_timer;
+	struct timer_list	login_timer;
 	struct task_struct	*login_kworker;
 	/* Spinlock used for add/deleting cmd's from conn_cmd_list */
 	spinlock_t		cmd_lock;
@@ -576,6 +578,8 @@ struct iscsit_conn {
 	spinlock_t		nopin_timer_lock;
 	spinlock_t		response_queue_lock;
 	spinlock_t		state_lock;
+	spinlock_t		login_timer_lock;
+	spinlock_t		login_worker_lock;
 	/* libcrypto RX and TX contexts for crc32c */
 	struct ahash_request	*conn_rx_hash;
 	struct ahash_request	*conn_tx_hash;
@@ -791,7 +795,6 @@ struct iscsi_np {
 	enum np_thread_state_table np_thread_state;
 	bool                    enabled;
 	atomic_t		np_reset_count;
-	enum iscsi_timer_flags_table np_login_timer_flags;
 	u32			np_exports;
 	enum np_flags_table	np_flags;
 	spinlock_t		np_thread_lock;
@@ -799,7 +802,6 @@ struct iscsi_np {
 	struct socket		*np_socket;
 	struct sockaddr_storage np_sockaddr;
 	struct task_struct	*np_thread;
-	struct timer_list	np_login_timer;
 	void			*np_context;
 	struct iscsit_transport *np_transport;
 	struct list_head	np_list;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V2 2/3] target: iscsi: remove unused transport_timer
  2023-04-17 17:17 [PATCH V2 0/3] Fix possible hangs and race conditions during login Maurizio Lombardi
  2023-04-17 17:17 ` [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code Maurizio Lombardi
@ 2023-04-17 17:18 ` Maurizio Lombardi
  2023-04-26 15:43   ` Mike Christie
  2023-04-17 17:18 ` [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other Maurizio Lombardi
  2 siblings, 1 reply; 8+ messages in thread
From: Maurizio Lombardi @ 2023-04-17 17:18 UTC (permalink / raw)
  To: martin.petersen; +Cc: michael.christie, target-devel

Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
---
 include/target/iscsi/iscsi_target_core.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 082aaad8ef1b..b550056a38fd 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -568,7 +568,6 @@ struct iscsit_conn {
 	struct iscsi_login	*login;
 	struct timer_list	nopin_timer;
 	struct timer_list	nopin_response_timer;
-	struct timer_list	transport_timer;
 	struct timer_list	login_timer;
 	struct task_struct	*login_kworker;
 	/* Spinlock used for add/deleting cmd's from conn_cmd_list */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other
  2023-04-17 17:17 [PATCH V2 0/3] Fix possible hangs and race conditions during login Maurizio Lombardi
  2023-04-17 17:17 ` [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code Maurizio Lombardi
  2023-04-17 17:18 ` [PATCH V2 2/3] target: iscsi: remove unused transport_timer Maurizio Lombardi
@ 2023-04-17 17:18 ` Maurizio Lombardi
  2023-04-26 15:44   ` Mike Christie
  2 siblings, 1 reply; 8+ messages in thread
From: Maurizio Lombardi @ 2023-04-17 17:18 UTC (permalink / raw)
  To: martin.petersen; +Cc: michael.christie, target-devel

The tpg->np_login_sem is a semaphore that is used to serialize the login
process when multiple login threads run concurrently against the same
target portal group.

The iscsi_target_locate_portal() function finds the tpg,
calls iscsit_access_np() against the np_login_sem semaphore
and saves the tpg pointer in conn->tpg;

If iscsi_target_locate_portal() fails, the caller will check for the
conn->tpg pointer and, if it's not NULL, then it will assume
that iscsi_target_locate_portal() called iscsit_access_np() on the
semaphore.

Make sure that conn->tpg gets initialized only if iscsit_access_np()
was successful, otherwise iscsit_deaccess_np() may end up
being called against a semaphore we never took, allowing more than one
thread to access the same tpg.

Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
---
 drivers/target/iscsi/iscsi_target_nego.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 4164977c170e..9b0c1196fe8e 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -1121,6 +1121,7 @@ int iscsi_target_locate_portal(
 	iscsi_target_set_sock_callbacks(conn);
 
 	login->np = np;
+	conn->tpg = NULL;
 
 	login_req = (struct iscsi_login_req *) login->req;
 	payload_length = ntoh24(login_req->dlength);
@@ -1188,7 +1189,6 @@ int iscsi_target_locate_portal(
 	 */
 	sessiontype = strncmp(s_buf, DISCOVERY, 9);
 	if (!sessiontype) {
-		conn->tpg = iscsit_global->discovery_tpg;
 		if (!login->leading_connection)
 			goto get_target;
 
@@ -1205,9 +1205,11 @@ int iscsi_target_locate_portal(
 		 * Serialize access across the discovery struct iscsi_portal_group to
 		 * process login attempt.
 		 */
+		conn->tpg = iscsit_global->discovery_tpg;
 		if (iscsit_access_np(np, conn->tpg) < 0) {
 			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
 				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+			conn->tpg = NULL;
 			ret = -1;
 			goto out;
 		}
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code
  2023-04-17 17:17 ` [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code Maurizio Lombardi
@ 2023-04-26 15:35   ` Mike Christie
  2023-04-26 15:39     ` Mike Christie
  0 siblings, 1 reply; 8+ messages in thread
From: Mike Christie @ 2023-04-26 15:35 UTC (permalink / raw)
  To: Maurizio Lombardi, martin.petersen; +Cc: target-devel

On 4/17/23 12:17 PM, Maurizio Lombardi wrote:
>  static void iscsi_target_do_login_rx(struct work_struct *work)
>  {
>  	struct iscsit_conn *conn = container_of(work,
> @@ -562,12 +543,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
>  	struct iscsi_np *np = login->np;
>  	struct iscsi_portal_group *tpg = conn->tpg;
>  	struct iscsi_tpg_np *tpg_np = conn->tpg_np;
> -	struct conn_timeout timeout;
>  	int rc, zero_tsih = login->zero_tsih;
>  	bool state;
>  
>  	pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
>  			conn, current->comm, current->pid);
> +
> +	spin_lock(&conn->login_worker_lock);
> +	set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
> +	spin_unlock(&conn->login_worker_lock);


I think the locking bh use is reversed. You are in thread context here so you need to
do spin_lock/unlock_bh to prevent a softirq on the same cpu from interrupting us and 
running your timeout function which would try to grab the login_worker_lock.

iscsit_login_timeout is run from softirq context so you wouldn't need to do the _bh
calls from it.


>  	/*
>  	 * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
>  	 * before initial PDU processing in iscsi_target_start_negotiation()
> @@ -597,19 +581,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
>  		goto err;
>  	}
>  
> -	conn->login_kworker = current;
>  	allow_signal(SIGINT);
> -
> -	timeout.conn = conn;
> -	timer_setup_on_stack(&timeout.timer, iscsi_target_login_timeout, 0);
> -	mod_timer(&timeout.timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
> -	pr_debug("Starting login timer for %s/%d\n", current->comm, current->pid);
> +	rc = iscsit_set_login_timer_kworker(conn, current);
> +	if (rc < 0) {
> +		/* The login timer has already expired */
> +		pr_debug("iscsi_target_do_login_rx, login failed\n");
> +		goto err;
> +	}
>  
>  	rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
> -	del_timer_sync(&timeout.timer);
> -	destroy_timer_on_stack(&timeout.timer);
>  	flush_signals(current);
> -	conn->login_kworker = NULL;
>  
>  	if (rc < 0)
>  		goto err;
> @@ -646,7 +627,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
>  		if (iscsi_target_sk_check_and_clear(conn,
>  						    LOGIN_FLAGS_WRITE_ACTIVE))
>  			goto err;
> +
> +		/* Set the login timer thread pointer to NULL to prevent the
> +		 * login process from getting stuck if the initiator
> +		 * stops sending data.
> +		 */

Use the more common comment style we have in the target code (there was some other
cases below):

/*
 * Set the...



> +		rc = iscsit_set_login_timer_kworker(conn, NULL);
> +		if (rc < 0)
> +			goto err;
>  	} else if (rc == 1) {
> +		iscsit_stop_login_timer(conn);
>  		cancel_delayed_work(&conn->login_work);
>  		iscsi_target_nego_release(conn);
>  		iscsi_post_login_handler(np, conn, zero_tsih);
> @@ -656,6 +646,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
>  
>  err:
>  	iscsi_target_restore_sock_callbacks(conn);
> +	iscsit_stop_login_timer(conn);
>  	cancel_delayed_work(&conn->login_work);
>  	iscsi_target_login_drop(conn, login);
>  	iscsit_deaccess_np(np, tpg, tpg_np);
> @@ -1368,14 +1359,29 @@ int iscsi_target_start_negotiation(
>  	 * and perform connection cleanup now.
>  	 */
>  	ret = iscsi_target_do_login(conn, login);
> -	if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
> -		ret = -1;
> +	if (!ret) {
> +		spin_lock(&conn->login_worker_lock);

Same locking comment.

> +
> +		if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
> +			ret = -1;
> +		else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
> +			if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {

I think this will deadlock because iscsit_set_login_timer_kworker tries to take the
login_worker_lock we took above.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code
  2023-04-26 15:35   ` Mike Christie
@ 2023-04-26 15:39     ` Mike Christie
  0 siblings, 0 replies; 8+ messages in thread
From: Mike Christie @ 2023-04-26 15:39 UTC (permalink / raw)
  To: Maurizio Lombardi, martin.petersen; +Cc: target-devel

On 4/26/23 10:35 AM, Mike Christie wrote:
> On 4/17/23 12:17 PM, Maurizio Lombardi wrote:
>>  static void iscsi_target_do_login_rx(struct work_struct *work)
>>  {
>>  	struct iscsit_conn *conn = container_of(work,
>> @@ -562,12 +543,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
>>  	struct iscsi_np *np = login->np;
>>  	struct iscsi_portal_group *tpg = conn->tpg;
>>  	struct iscsi_tpg_np *tpg_np = conn->tpg_np;
>> -	struct conn_timeout timeout;
>>  	int rc, zero_tsih = login->zero_tsih;
>>  	bool state;
>>  
>>  	pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
>>  			conn, current->comm, current->pid);
>> +
>> +	spin_lock(&conn->login_worker_lock);
>> +	set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
>> +	spin_unlock(&conn->login_worker_lock);
> 
> 
> I think the locking bh use is reversed.

Ignore the locking comment like here and below. I see there are 2 locks.


>> +
>> +		if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
>> +			ret = -1;
>> +		else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
>> +			if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
> 
> I think this will deadlock because iscsit_set_login_timer_kworker tries to take the
> login_worker_lock we took above.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH V2 2/3] target: iscsi: remove unused transport_timer
  2023-04-17 17:18 ` [PATCH V2 2/3] target: iscsi: remove unused transport_timer Maurizio Lombardi
@ 2023-04-26 15:43   ` Mike Christie
  0 siblings, 0 replies; 8+ messages in thread
From: Mike Christie @ 2023-04-26 15:43 UTC (permalink / raw)
  To: Maurizio Lombardi, martin.petersen; +Cc: target-devel

On 4/17/23 12:18 PM, Maurizio Lombardi wrote:
> Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>
> ---
>  include/target/iscsi/iscsi_target_core.h | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
> index 082aaad8ef1b..b550056a38fd 100644
> --- a/include/target/iscsi/iscsi_target_core.h
> +++ b/include/target/iscsi/iscsi_target_core.h
> @@ -568,7 +568,6 @@ struct iscsit_conn {
>  	struct iscsi_login	*login;
>  	struct timer_list	nopin_timer;
>  	struct timer_list	nopin_response_timer;
> -	struct timer_list	transport_timer;
>  	struct timer_list	login_timer;
>  	struct task_struct	*login_kworker;
>  	/* Spinlock used for add/deleting cmd's from conn_cmd_list */

Reviewed-by: Mike Christie <michael.christie@oracle.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other
  2023-04-17 17:18 ` [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other Maurizio Lombardi
@ 2023-04-26 15:44   ` Mike Christie
  0 siblings, 0 replies; 8+ messages in thread
From: Mike Christie @ 2023-04-26 15:44 UTC (permalink / raw)
  To: Maurizio Lombardi, martin.petersen; +Cc: target-devel

On 4/17/23 12:18 PM, Maurizio Lombardi wrote:
> The tpg->np_login_sem is a semaphore that is used to serialize the login
> process when multiple login threads run concurrently against the same
> target portal group.
> 
> The iscsi_target_locate_portal() function finds the tpg,
> calls iscsit_access_np() against the np_login_sem semaphore
> and saves the tpg pointer in conn->tpg;
> 
> If iscsi_target_locate_portal() fails, the caller will check for the
> conn->tpg pointer and, if it's not NULL, then it will assume
> that iscsi_target_locate_portal() called iscsit_access_np() on the
> semaphore.
> 
> Make sure that conn->tpg gets initialized only if iscsit_access_np()
> was successful, otherwise iscsit_deaccess_np() may end up
> being called against a semaphore we never took, allowing more than one
> thread to access the same tpg.
> 
> Signed-off-by: Maurizio Lombardi <mlombard@redhat.com>

Reviewed-by: Mike Christie <michael.christie@oracle.com>


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-04-26 15:44 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-17 17:17 [PATCH V2 0/3] Fix possible hangs and race conditions during login Maurizio Lombardi
2023-04-17 17:17 ` [PATCH V2 1/3] target: iscsi: fix hang in the iSCSI login code Maurizio Lombardi
2023-04-26 15:35   ` Mike Christie
2023-04-26 15:39     ` Mike Christie
2023-04-17 17:18 ` [PATCH V2 2/3] target: iscsi: remove unused transport_timer Maurizio Lombardi
2023-04-26 15:43   ` Mike Christie
2023-04-17 17:18 ` [PATCH V2 3/3] target: iscsi: prevent login threads from racing between each other Maurizio Lombardi
2023-04-26 15:44   ` Mike Christie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.