Merge branch '6.7/scsi-staging' into 6.7/scsi-fixes
[linux-block.git] / drivers / scsi / sd.c
index 3ef3b00397fe2c0eaeacd316ebeb309b9bc4e9aa..fa00dd503cbf618b066eac2001d60a3dc59fd1ed 100644 (file)
@@ -202,18 +202,93 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
 }
 
 static ssize_t
-manage_start_stop_show(struct device *dev, struct device_attribute *attr,
-                      char *buf)
+manage_start_stop_show(struct device *dev,
+                      struct device_attribute *attr, char *buf)
 {
        struct scsi_disk *sdkp = to_scsi_disk(dev);
        struct scsi_device *sdp = sdkp->device;
 
-       return sprintf(buf, "%u\n", sdp->manage_start_stop);
+       return sysfs_emit(buf, "%u\n",
+                         sdp->manage_system_start_stop &&
+                         sdp->manage_runtime_start_stop &&
+                         sdp->manage_shutdown);
 }
+static DEVICE_ATTR_RO(manage_start_stop);
 
 static ssize_t
-manage_start_stop_store(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t count)
+manage_system_start_stop_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+       struct scsi_device *sdp = sdkp->device;
+
+       return sysfs_emit(buf, "%u\n", sdp->manage_system_start_stop);
+}
+
+static ssize_t
+manage_system_start_stop_store(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+       struct scsi_device *sdp = sdkp->device;
+       bool v;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       if (kstrtobool(buf, &v))
+               return -EINVAL;
+
+       sdp->manage_system_start_stop = v;
+
+       return count;
+}
+static DEVICE_ATTR_RW(manage_system_start_stop);
+
+static ssize_t
+manage_runtime_start_stop_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+       struct scsi_device *sdp = sdkp->device;
+
+       return sysfs_emit(buf, "%u\n", sdp->manage_runtime_start_stop);
+}
+
+static ssize_t
+manage_runtime_start_stop_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+       struct scsi_device *sdp = sdkp->device;
+       bool v;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       if (kstrtobool(buf, &v))
+               return -EINVAL;
+
+       sdp->manage_runtime_start_stop = v;
+
+       return count;
+}
+static DEVICE_ATTR_RW(manage_runtime_start_stop);
+
+static ssize_t manage_shutdown_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct scsi_disk *sdkp = to_scsi_disk(dev);
+       struct scsi_device *sdp = sdkp->device;
+
+       return sysfs_emit(buf, "%u\n", sdp->manage_shutdown);
+}
+
+static ssize_t manage_shutdown_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
 {
        struct scsi_disk *sdkp = to_scsi_disk(dev);
        struct scsi_device *sdp = sdkp->device;
@@ -225,11 +300,11 @@ manage_start_stop_store(struct device *dev, struct device_attribute *attr,
        if (kstrtobool(buf, &v))
                return -EINVAL;
 
-       sdp->manage_start_stop = v;
+       sdp->manage_shutdown = v;
 
        return count;
 }
-static DEVICE_ATTR_RW(manage_start_stop);
+static DEVICE_ATTR_RW(manage_shutdown);
 
 static ssize_t
 allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -561,6 +636,9 @@ static struct attribute *sd_disk_attrs[] = {
        &dev_attr_FUA.attr,
        &dev_attr_allow_restart.attr,
        &dev_attr_manage_start_stop.attr,
+       &dev_attr_manage_system_start_stop.attr,
+       &dev_attr_manage_runtime_start_stop.attr,
+       &dev_attr_manage_shutdown.attr,
        &dev_attr_protection_type.attr,
        &dev_attr_protection_mode.attr,
        &dev_attr_app_tag_own.attr,
@@ -3705,7 +3783,8 @@ static int sd_remove(struct device *dev)
 
        device_del(&sdkp->disk_dev);
        del_gendisk(sdkp->disk);
-       sd_shutdown(dev);
+       if (!sdkp->suspended)
+               sd_shutdown(dev);
 
        put_disk(sdkp->disk);
        return 0;
@@ -3782,13 +3861,22 @@ static void sd_shutdown(struct device *dev)
                sd_sync_cache(sdkp);
        }
 
-       if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
+       if ((system_state != SYSTEM_RESTART &&
+            sdkp->device->manage_system_start_stop) ||
+           (system_state == SYSTEM_POWER_OFF &&
+            sdkp->device->manage_shutdown)) {
                sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
                sd_start_stop_device(sdkp, 0);
        }
 }
 
-static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
+static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime)
+{
+       return (sdev->manage_system_start_stop && !runtime) ||
+               (sdev->manage_runtime_start_stop && runtime);
+}
+
+static int sd_suspend_common(struct device *dev, bool runtime)
 {
        struct scsi_disk *sdkp = dev_get_drvdata(dev);
        int ret = 0;
@@ -3808,15 +3896,18 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
                        return ret;
        }
 
-       if (sdkp->device->manage_start_stop) {
+       if (sd_do_start_stop(sdkp->device, runtime)) {
                if (!sdkp->device->silence_suspend)
                        sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
                /* an error is not worth aborting a system sleep */
                ret = sd_start_stop_device(sdkp, 0);
-               if (ignore_stop_errors)
+               if (!runtime)
                        ret = 0;
        }
 
+       if (!ret)
+               sdkp->suspended = true;
+
        return ret;
 }
 
@@ -3825,32 +3916,34 @@ static int sd_suspend_system(struct device *dev)
        if (pm_runtime_suspended(dev))
                return 0;
 
-       return sd_suspend_common(dev, true);
+       return sd_suspend_common(dev, false);
 }
 
 static int sd_suspend_runtime(struct device *dev)
 {
-       return sd_suspend_common(dev, false);
+       return sd_suspend_common(dev, true);
 }
 
-static int sd_resume(struct device *dev)
+static int sd_resume(struct device *dev, bool runtime)
 {
        struct scsi_disk *sdkp = dev_get_drvdata(dev);
-       int ret = 0;
+       int ret;
 
        if (!sdkp)      /* E.g.: runtime resume at the start of sd_probe() */
                return 0;
 
-       if (!sdkp->device->manage_start_stop)
+       if (!sd_do_start_stop(sdkp->device, runtime)) {
+               sdkp->suspended = false;
                return 0;
-
-       if (!sdkp->device->no_start_on_resume) {
-               sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
-               ret = sd_start_stop_device(sdkp, 1);
        }
 
-       if (!ret)
+       sd_printk(KERN_NOTICE, sdkp, "Starting disk\n");
+       ret = sd_start_stop_device(sdkp, 1);
+       if (!ret) {
                opal_unlock_from_suspend(sdkp->opal_dev);
+               sdkp->suspended = false;
+       }
+
        return ret;
 }
 
@@ -3859,7 +3952,7 @@ static int sd_resume_system(struct device *dev)
        if (pm_runtime_suspended(dev))
                return 0;
 
-       return sd_resume(dev);
+       return sd_resume(dev, false);
 }
 
 static int sd_resume_runtime(struct device *dev)
@@ -3886,7 +3979,7 @@ static int sd_resume_runtime(struct device *dev)
                                  "Failed to clear sense data\n");
        }
 
-       return sd_resume(dev);
+       return sd_resume(dev, true);
 }
 
 static const struct dev_pm_ops sd_pm_ops = {