OSDN Git Service

scsi: iscsi: Add helper to remove a session from the kernel
authorMike Christie <michael.christie@oracle.com>
Thu, 16 Jun 2022 22:27:36 +0000 (17:27 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 22 Jun 2022 01:14:54 +0000 (21:14 -0400)
During qedi shutdown we need to stop the iSCSI layer from sending new nops
as pings and from responding to target ones and make sure there is no
running connection cleanups. Commit d1f2ce77638d ("scsi: qedi: Fix host
removal with running sessions") converted the driver to use the libicsi
helper to drive session removal, so the above issues could be handled. The
problem is that during system shutdown iscsid will not be running so when
we try to remove the root session we will hang waiting for userspace to
reply.

Add a helper that will drive the destruction of sessions like these during
system shutdown.

Link: https://lore.kernel.org/r/20220616222738.5722-5-michael.christie@oracle.com
Tested-by: Nilesh Javali <njavali@marvell.com>
Reviewed-by: Nilesh Javali <njavali@marvell.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_transport_iscsi.c
include/scsi/scsi_transport_iscsi.h

index c9de0ac..8be99d0 100644 (file)
@@ -2334,6 +2334,55 @@ static void iscsi_cleanup_conn_work_fn(struct work_struct *work)
        ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n");
 }
 
+static int iscsi_iter_force_destroy_conn_fn(struct device *dev, void *data)
+{
+       struct iscsi_transport *transport;
+       struct iscsi_cls_conn *conn;
+
+       if (!iscsi_is_conn_dev(dev))
+               return 0;
+
+       conn = iscsi_dev_to_conn(dev);
+       transport = conn->transport;
+
+       if (READ_ONCE(conn->state) != ISCSI_CONN_DOWN)
+               iscsi_if_stop_conn(conn, STOP_CONN_TERM);
+
+       transport->destroy_conn(conn);
+       return 0;
+}
+
+/**
+ * iscsi_force_destroy_session - destroy a session from the kernel
+ * @session: session to destroy
+ *
+ * Force the destruction of a session from the kernel. This should only be
+ * used when userspace is no longer running during system shutdown.
+ */
+void iscsi_force_destroy_session(struct iscsi_cls_session *session)
+{
+       struct iscsi_transport *transport = session->transport;
+       unsigned long flags;
+
+       WARN_ON_ONCE(system_state == SYSTEM_RUNNING);
+
+       spin_lock_irqsave(&sesslock, flags);
+       if (list_empty(&session->sess_list)) {
+               spin_unlock_irqrestore(&sesslock, flags);
+               /*
+                * Conn/ep is already freed. Session is being torn down via
+                * async path. For shutdown we don't care about it so return.
+                */
+               return;
+       }
+       spin_unlock_irqrestore(&sesslock, flags);
+
+       device_for_each_child(&session->dev, NULL,
+                             iscsi_iter_force_destroy_conn_fn);
+       transport->destroy_session(session);
+}
+EXPORT_SYMBOL_GPL(iscsi_force_destroy_session);
+
 void iscsi_free_session(struct iscsi_cls_session *session)
 {
        ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
index 695396a..cab52b0 100644 (file)
@@ -442,6 +442,7 @@ extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
                                                struct iscsi_transport *t,
                                                int dd_size,
                                                unsigned int target_id);
+extern void iscsi_force_destroy_session(struct iscsi_cls_session *session);
 extern void iscsi_remove_session(struct iscsi_cls_session *session);
 extern void iscsi_free_session(struct iscsi_cls_session *session);
 extern struct iscsi_cls_conn *iscsi_alloc_conn(struct iscsi_cls_session *sess,