scsi: iscsi: Hold task ref during TMF timeout handling
authorMike Christie <michael.christie@oracle.com>
Tue, 25 May 2021 18:18:10 +0000 (13:18 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 2 Jun 2021 05:28:22 +0000 (01:28 -0400)
For aborts, qedi needs to cleanup the FW then send the TMF from a worker
thread. While it's doing these the cmd could complete normally and the TMF
could time out. libiscsi would then complete the iscsi_task which will call
into the driver to cleanup the driver level resources while it still might
be accessing them for the cleanup/abort.

This has iscsi_eh_abort keep the iscsi_task ref if the TMF times out, so
qedi does not have to worry about if the task is being freed while in use
and does not need to get its own ref.

Link: https://lore.kernel.org/r/20210525181821.7617-18-michael.christie@oracle.com
Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/libiscsi.c
include/scsi/libiscsi.h

index 8222db4f8fef61363d8f19fc7a7cb0d0549a5b60..e57d6355e7c7abfd89def515bec16ac13cfe3b77 100644 (file)
@@ -573,6 +573,11 @@ static bool cleanup_queued_task(struct iscsi_task *task)
                        __iscsi_put_task(task);
        }
 
+       if (conn->session->running_aborted_task == task) {
+               conn->session->running_aborted_task = NULL;
+               __iscsi_put_task(task);
+       }
+
        if (conn->task == task) {
                conn->task = NULL;
                __iscsi_put_task(task);
@@ -2334,6 +2339,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                iscsi_start_tx(conn);
                goto success_unlocked;
        case TMF_TIMEDOUT:
+               session->running_aborted_task = task;
                spin_unlock_bh(&session->frwd_lock);
                iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST);
                goto failed_unlocked;
@@ -2367,7 +2373,14 @@ failed:
 failed_unlocked:
        ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc,
                     task ? task->itt : 0);
-       iscsi_put_task(task);
+       /*
+        * The driver might be accessing the task so hold the ref. The conn
+        * stop cleanup will drop the ref after ep_disconnect so we know the
+        * driver's no longer touching the task.
+        */
+       if (!session->running_aborted_task)
+               iscsi_put_task(task);
+
        iscsi_put_conn(conn->cls_conn);
        mutex_unlock(&session->eh_mutex);
        return FAILED;
index 9d7908265afea1c1399b0dff95ced7d72c2631a9..4ee233e5a6ffa5c64cc2ae8af0ae58bb030a3b00 100644 (file)
@@ -276,6 +276,7 @@ struct iscsi_session {
        struct iscsi_tm         tmhdr;
        struct timer_list       tmf_timer;
        int                     tmf_state;      /* see TMF_INITIAL, etc.*/
+       struct iscsi_task       *running_aborted_task;
 
        /* iSCSI session-wide sequencing */
        uint32_t                cmdsn;