@@ -1053,10 +1053,11 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
/*
* Ask the sd driver to issue START STOP UNIT on runtime suspend
- * and resume only. For system level suspend/resume, devices
- * power state is handled directly by libata EH.
+ * and resume and shutdown only. For system level suspend/resume,
+ * devices power state is handled directly by libata EH.
*/
sdev->manage_runtime_start_stop = true;
+ sdev->manage_shutdown = true;
}
/*
@@ -1521,6 +1521,7 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
if (sbp2_param_exclusive_login) {
sdev->manage_system_start_stop = true;
sdev->manage_runtime_start_stop = true;
+ sdev->manage_shutdown = true;
}
if (sdev->type == TYPE_ROM)
@@ -209,7 +209,8 @@ manage_start_stop_show(struct device *dev,
return sysfs_emit(buf, "%u\n",
sdp->manage_system_start_stop &&
- sdp->manage_runtime_start_stop);
+ sdp->manage_runtime_start_stop &&
+ sdp->manage_shutdown);
}
static DEVICE_ATTR_RO(manage_start_stop);
@@ -275,6 +276,35 @@ manage_runtime_start_stop_store(struct device *dev,
}
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;
+ bool v;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (kstrtobool(buf, &v))
+ return -EINVAL;
+
+ sdp->manage_shutdown = v;
+
+ return count;
+}
+static DEVICE_ATTR_RW(manage_shutdown);
+
static ssize_t
allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -607,6 +637,7 @@ static struct attribute *sd_disk_attrs[] = {
&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,
@@ -3819,8 +3850,10 @@ static void sd_shutdown(struct device *dev)
sd_sync_cache(sdkp, NULL);
}
- if (system_state != SYSTEM_RESTART &&
- sdkp->device->manage_system_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);
}
@@ -162,8 +162,24 @@ struct scsi_device {
* core. */
unsigned int eh_timeout; /* Error handling timeout */
- bool manage_system_start_stop; /* Let HLD (sd) manage system start/stop */
- bool manage_runtime_start_stop; /* Let HLD (sd) manage runtime start/stop */
+ /*
+ * If true, let the high-level device driver (sd) manage the device
+ * power state for system suspend/resume (suspend to RAM and
+ * hibernation) operations.
+ */
+ bool manage_system_start_stop;
+
+ /*
+ * If true, let the high-level device driver (sd) manage the device
+ * power state for runtime device suspand and resume operations.
+ */
+ bool manage_runtime_start_stop;
+
+ /*
+ * If true, let the high-level device driver (sd) manage the device
+ * power state for system shutdown (power off) operations.
+ */
+ bool manage_shutdown;
unsigned removable:1;
unsigned changed:1; /* Data invalid due to media change */