diff mbox series

[15/15] libiscsi: convert ping_task to refcount handler

Message ID 1606858196-5421-16-git-send-email-michael.christie@oracle.com
State New
Headers show
Series libiscsi: lock clean ups | expand

Commit Message

Mike Christie Dec. 1, 2020, 9:29 p.m. UTC
The conn_send_pdu API is evil in that it returns a pointer to a
iscsi_task, but that task might have been freed already. This
would happen with the ping_task code. To fix up the API so the
caller can access the task if it needs to like in the ping_task
case, this has conn_send_pdu grab a ref to the task for the
caller. We then move the ping_task clearing to when all the
refcounts are dropped, so we know the caller and a completion
do not race.

Signed-off-by: Mike Christie <michael.christie@oracle.com>
---
 drivers/scsi/libiscsi.c | 39 +++++++++++++++++++++------------------
 include/scsi/libiscsi.h |  3 ---
 2 files changed, 21 insertions(+), 21 deletions(-)

Comments

Mike Christie Dec. 2, 2020, 1:27 a.m. UTC | #1
On 12/1/20 3:29 PM, Mike Christie wrote:
> The conn_send_pdu API is evil in that it returns a pointer to a

> iscsi_task, but that task might have been freed already. This

> would happen with the ping_task code. To fix up the API so the

> caller can access the task if it needs to like in the ping_task

> case, this has conn_send_pdu grab a ref to the task for the

> caller. We then move the ping_task clearing to when all the

> refcounts are dropped, so we know the caller and a completion

> do not race.

> 


Ignore this patch. It's wrong, because it doesn't handle the check for 
if the nop is from userspace or kernel.
diff mbox series

Patch

diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index e020fba..79cec93 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -546,13 +546,16 @@  static void iscsi_free_task(struct iscsi_task *task)
 		return;
 
 	iscsi_free_itt(session, task);
-
+ 
 	if (session->win_opened && !work_pending(&conn->xmitwork)) {
 		session->win_opened = false;
 		iscsi_conn_queue_work(conn);
 	}
 
 	if (!sc) {
+		if (READ_ONCE(conn->ping_task) == task)
+			WRITE_ONCE(conn->ping_task, NULL);
+
 		spin_lock_bh(&session->mgmt_lock);
 		kfifo_in(&session->mgmt_pool.queue, (void*)&task, sizeof(void*));
 		spin_unlock_bh(&session->mgmt_lock);
@@ -603,9 +606,6 @@  static void iscsi_complete_task(struct iscsi_task *task, int state)
 	WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
 	task->state = state;
 
-	if (READ_ONCE(conn->ping_task) == task)
-		WRITE_ONCE(conn->ping_task, NULL);
-
 	/* release get from queueing */
 	iscsi_put_task(task);
 }
@@ -819,6 +819,8 @@  static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
 	task->sc = NULL;
 	INIT_LIST_HEAD(&task->running);
 	task->state = ISCSI_TASK_PENDING;
+	/* Take an extra ref so the caller can access the task */
+	iscsi_get_task(task);
 
 	if (data_size) {
 		memcpy(task->data, data, data_size);
@@ -846,9 +848,6 @@  static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
 						   task->conn->session->age);
 	}
 
-	if (unlikely(READ_ONCE(conn->ping_task) == INVALID_SCSI_TASK))
-		WRITE_ONCE(conn->ping_task, task);
-
 	if (!ihost->workq) {
 		if (iscsi_prep_mgmt_task(conn, task))
 			goto free_task;
@@ -863,6 +862,8 @@  static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
 	return task;
 
 free_task:
+	/* drop extra count taken for caller and count from allocation */
+	iscsi_put_task(task);
 	iscsi_put_task(task);
 	return NULL;
 }
@@ -872,11 +873,15 @@  int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct iscsi_session *session = conn->session;
+	struct iscsi_task *task;
 	int err = 0;
 
 	spin_lock_bh(&session->frwd_lock);
-	if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+	task = __iscsi_conn_send_pdu(conn, hdr, data, data_size);
+	if (!task)
 		err = -EPERM;
+	else
+		iscsi_put_task(task);
 	spin_unlock_bh(&session->frwd_lock);
 	return err;
 }
@@ -1047,12 +1052,6 @@  static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
         struct iscsi_nopout hdr;
 	struct iscsi_task *task;
 
-	if (!rhdr) {
-		if (READ_ONCE(conn->ping_task))
-			return -EINVAL;
-		WRITE_ONCE(conn->ping_task, INVALID_SCSI_TASK);
-	}
-
 	memset(&hdr, 0, sizeof(struct iscsi_nopout));
 	hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
 	hdr.flags = ISCSI_FLAG_CMD_FINAL;
@@ -1061,20 +1060,23 @@  static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
 		hdr.lun = rhdr->lun;
 		hdr.ttt = rhdr->ttt;
 		hdr.itt = RESERVED_ITT;
-	} else
+	} else {
+		if (READ_ONCE(conn->ping_task))
+			return -EINVAL;
+
 		hdr.ttt = RESERVED_ITT;
+	}
 
 	task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
 	if (!task) {
-		if (!rhdr)
-			WRITE_ONCE(conn->ping_task, NULL);
 		iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
 		return -EIO;
 	} else if (!rhdr) {
 		/* only track our nops */
 		conn->last_ping = jiffies;
+		WRITE_ONCE(conn->ping_task, task);
 	}
-
+	iscsi_put_task(task);
 	return 0;
 }
 
@@ -1907,6 +1909,7 @@  static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
 		spin_lock_bh(&session->frwd_lock);
 		return -EPERM;
 	}
+	iscsi_put_task(task);
 	conn->tmfcmd_pdus_cnt++;
 	conn->tmf_timer.expires = timeout * HZ + jiffies;
 	add_timer(&conn->tmf_timer);
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index d0a6834..a17c551 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -133,9 +133,6 @@  struct iscsi_task {
 	void			*dd_data;	/* driver/transport data */
 };
 
-/* invalid scsi_task pointer */
-#define	INVALID_SCSI_TASK	(struct iscsi_task *)-1l
-
 static inline int iscsi_task_has_unsol_data(struct iscsi_task *task)
 {
 	return task->unsol_r2t.data_length > task->unsol_r2t.sent;