Merge branch '6.2/scsi-queue' into 6.2/scsi-fixes
[platform/kernel/linux-starfive.git] / drivers / scsi / scsi_transport_iscsi.c
index 13cfd3e..b9b9730 100644 (file)
@@ -1677,6 +1677,13 @@ static const char *iscsi_session_state_name(int state)
        return name;
 }
 
+static char *iscsi_session_target_state_name[] = {
+       [ISCSI_SESSION_TARGET_UNBOUND]   = "UNBOUND",
+       [ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED",
+       [ISCSI_SESSION_TARGET_SCANNED]   = "SCANNED",
+       [ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING",
+};
+
 int iscsi_session_chkready(struct iscsi_cls_session *session)
 {
        int err;
@@ -1786,9 +1793,13 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
                if ((scan_data->channel == SCAN_WILD_CARD ||
                     scan_data->channel == 0) &&
                    (scan_data->id == SCAN_WILD_CARD ||
-                    scan_data->id == id))
+                    scan_data->id == id)) {
                        scsi_scan_target(&session->dev, 0, id,
                                         scan_data->lun, scan_data->rescan);
+                       spin_lock_irqsave(&session->lock, flags);
+                       session->target_state = ISCSI_SESSION_TARGET_SCANNED;
+                       spin_unlock_irqrestore(&session->lock, flags);
+               }
        }
 
 user_scan_exit:
@@ -1961,31 +1972,41 @@ static void __iscsi_unbind_session(struct work_struct *work)
        struct iscsi_cls_host *ihost = shost->shost_data;
        unsigned long flags;
        unsigned int target_id;
+       bool remove_target = true;
 
        ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
 
        /* Prevent new scans and make sure scanning is not in progress */
        mutex_lock(&ihost->mutex);
        spin_lock_irqsave(&session->lock, flags);
-       if (session->target_id == ISCSI_MAX_TARGET) {
+       if (session->target_state == ISCSI_SESSION_TARGET_ALLOCATED) {
+               remove_target = false;
+       } else if (session->target_state != ISCSI_SESSION_TARGET_SCANNED) {
                spin_unlock_irqrestore(&session->lock, flags);
                mutex_unlock(&ihost->mutex);
-               goto unbind_session_exit;
+               ISCSI_DBG_TRANS_SESSION(session,
+                       "Skipping target unbinding: Session is unbound/unbinding.\n");
+               return;
        }
 
+       session->target_state = ISCSI_SESSION_TARGET_UNBINDING;
        target_id = session->target_id;
        session->target_id = ISCSI_MAX_TARGET;
        spin_unlock_irqrestore(&session->lock, flags);
        mutex_unlock(&ihost->mutex);
 
-       scsi_remove_target(&session->dev);
+       if (remove_target)
+               scsi_remove_target(&session->dev);
 
        if (session->ida_used)
                ida_free(&iscsi_sess_ida, target_id);
 
-unbind_session_exit:
        iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
        ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
+
+       spin_lock_irqsave(&session->lock, flags);
+       session->target_state = ISCSI_SESSION_TARGET_UNBOUND;
+       spin_unlock_irqrestore(&session->lock, flags);
 }
 
 static void __iscsi_destroy_session(struct work_struct *work)
@@ -2062,6 +2083,9 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
                session->ida_used = true;
        } else
                session->target_id = target_id;
+       spin_lock_irqsave(&session->lock, flags);
+       session->target_state = ISCSI_SESSION_TARGET_ALLOCATED;
+       spin_unlock_irqrestore(&session->lock, flags);
 
        dev_set_name(&session->dev, "session%u", session->sid);
        err = device_add(&session->dev);
@@ -4370,6 +4394,19 @@ iscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0);
 iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0);
 
 static ssize_t
+show_priv_session_target_state(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+
+       return sysfs_emit(buf, "%s\n",
+                       iscsi_session_target_state_name[session->target_state]);
+}
+
+static ISCSI_CLASS_ATTR(priv_sess, target_state, S_IRUGO,
+                       show_priv_session_target_state, NULL);
+
+static ssize_t
 show_priv_session_state(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
@@ -4471,6 +4508,7 @@ static struct attribute *iscsi_session_attrs[] = {
        &dev_attr_sess_boot_target.attr,
        &dev_attr_priv_sess_recovery_tmo.attr,
        &dev_attr_priv_sess_state.attr,
+       &dev_attr_priv_sess_target_state.attr,
        &dev_attr_priv_sess_creator.attr,
        &dev_attr_sess_chap_out_idx.attr,
        &dev_attr_sess_chap_in_idx.attr,
@@ -4584,6 +4622,8 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
                return S_IRUGO | S_IWUSR;
        else if (attr == &dev_attr_priv_sess_state.attr)
                return S_IRUGO;
+       else if (attr == &dev_attr_priv_sess_target_state.attr)
+               return S_IRUGO;
        else if (attr == &dev_attr_priv_sess_creator.attr)
                return S_IRUGO;
        else if (attr == &dev_attr_priv_sess_target_id.attr)