Skip to content

Commit 3cc2ffe

Browse files
committed
scsi: sd: Differentiate system and runtime start/stop management
The underlying device and driver of a SCSI disk may have different system and runtime power mode control requirements. This is because runtime power management affects only the SCSI disk, while system level power management affects all devices, including the controller for the SCSI disk. For instance, issuing a START STOP UNIT command when a SCSI disk is runtime suspended and resumed is fine: the command is translated to a STANDBY IMMEDIATE command to spin down the ATA disk and to a VERIFY command to wake it up. The SCSI disk runtime operations have no effect on the ata port device used to connect the ATA disk. However, for system suspend/resume operations, the ATA port used to connect the device will also be suspended and resumed, with the resume operation requiring re-validating the device link and the device itself. In this case, issuing a VERIFY command to spinup the disk must be done before starting to revalidate the device, when the ata port is being resumed. In such case, we must not allow the SCSI disk driver to issue START STOP UNIT commands. Allow a low level driver to refine the SCSI disk start/stop management by differentiating system and runtime cases with two new SCSI device flags: manage_system_start_stop and manage_runtime_start_stop. These new flags replace the current manage_start_stop flag. Drivers setting the manage_start_stop are modifed to set both new flags, thus preserving the existing start/stop management behavior. For backward compatibility, the old manage_start_stop sysfs device attribute is kept as a read-only attribute showing a value of 1 for devices enabling both new flags and 0 otherwise. Fixes: 0a85890 ("ata,scsi: do not issue START STOP UNIT on resume") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Hannes Reinecke <hare@suse.de> Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
1 parent fb99ef1 commit 3cc2ffe

File tree

4 files changed

+84
-23
lines changed

4 files changed

+84
-23
lines changed

drivers/ata/libata-scsi.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,8 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
10561056
* will be woken up by ata_port_pm_resume() with a port reset
10571057
* and device revalidation.
10581058
*/
1059-
sdev->manage_start_stop = 1;
1059+
sdev->manage_system_start_stop = true;
1060+
sdev->manage_runtime_start_stop = true;
10601061
sdev->no_start_on_resume = 1;
10611062
}
10621063

drivers/firewire/sbp2.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
8181
*
8282
* - power condition
8383
* Set the power condition field in the START STOP UNIT commands sent by
84-
* sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
84+
* sd_mod on suspend, resume, and shutdown (if manage_system_start_stop or
85+
* manage_runtime_start_stop is on).
8586
* Some disks need this to spin down or to resume properly.
8687
*
8788
* - override internal blacklist
@@ -1517,8 +1518,10 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
15171518

15181519
sdev->use_10_for_rw = 1;
15191520

1520-
if (sbp2_param_exclusive_login)
1521-
sdev->manage_start_stop = 1;
1521+
if (sbp2_param_exclusive_login) {
1522+
sdev->manage_system_start_stop = true;
1523+
sdev->manage_runtime_start_stop = true;
1524+
}
15221525

15231526
if (sdev->type == TYPE_ROM)
15241527
sdev->use_10_for_ms = 1;

drivers/scsi/sd.c

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -201,18 +201,32 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
201201
}
202202

203203
static ssize_t
204-
manage_start_stop_show(struct device *dev, struct device_attribute *attr,
205-
char *buf)
204+
manage_start_stop_show(struct device *dev,
205+
struct device_attribute *attr, char *buf)
206206
{
207207
struct scsi_disk *sdkp = to_scsi_disk(dev);
208208
struct scsi_device *sdp = sdkp->device;
209209

210-
return sprintf(buf, "%u\n", sdp->manage_start_stop);
210+
return sysfs_emit(buf, "%u\n",
211+
sdp->manage_system_start_stop &&
212+
sdp->manage_runtime_start_stop);
211213
}
214+
static DEVICE_ATTR_RO(manage_start_stop);
212215

213216
static ssize_t
214-
manage_start_stop_store(struct device *dev, struct device_attribute *attr,
215-
const char *buf, size_t count)
217+
manage_system_start_stop_show(struct device *dev,
218+
struct device_attribute *attr, char *buf)
219+
{
220+
struct scsi_disk *sdkp = to_scsi_disk(dev);
221+
struct scsi_device *sdp = sdkp->device;
222+
223+
return sysfs_emit(buf, "%u\n", sdp->manage_system_start_stop);
224+
}
225+
226+
static ssize_t
227+
manage_system_start_stop_store(struct device *dev,
228+
struct device_attribute *attr,
229+
const char *buf, size_t count)
216230
{
217231
struct scsi_disk *sdkp = to_scsi_disk(dev);
218232
struct scsi_device *sdp = sdkp->device;
@@ -224,11 +238,42 @@ manage_start_stop_store(struct device *dev, struct device_attribute *attr,
224238
if (kstrtobool(buf, &v))
225239
return -EINVAL;
226240

227-
sdp->manage_start_stop = v;
241+
sdp->manage_system_start_stop = v;
228242

229243
return count;
230244
}
231-
static DEVICE_ATTR_RW(manage_start_stop);
245+
static DEVICE_ATTR_RW(manage_system_start_stop);
246+
247+
static ssize_t
248+
manage_runtime_start_stop_show(struct device *dev,
249+
struct device_attribute *attr, char *buf)
250+
{
251+
struct scsi_disk *sdkp = to_scsi_disk(dev);
252+
struct scsi_device *sdp = sdkp->device;
253+
254+
return sysfs_emit(buf, "%u\n", sdp->manage_runtime_start_stop);
255+
}
256+
257+
static ssize_t
258+
manage_runtime_start_stop_store(struct device *dev,
259+
struct device_attribute *attr,
260+
const char *buf, size_t count)
261+
{
262+
struct scsi_disk *sdkp = to_scsi_disk(dev);
263+
struct scsi_device *sdp = sdkp->device;
264+
bool v;
265+
266+
if (!capable(CAP_SYS_ADMIN))
267+
return -EACCES;
268+
269+
if (kstrtobool(buf, &v))
270+
return -EINVAL;
271+
272+
sdp->manage_runtime_start_stop = v;
273+
274+
return count;
275+
}
276+
static DEVICE_ATTR_RW(manage_runtime_start_stop);
232277

233278
static ssize_t
234279
allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -560,6 +605,8 @@ static struct attribute *sd_disk_attrs[] = {
560605
&dev_attr_FUA.attr,
561606
&dev_attr_allow_restart.attr,
562607
&dev_attr_manage_start_stop.attr,
608+
&dev_attr_manage_system_start_stop.attr,
609+
&dev_attr_manage_runtime_start_stop.attr,
563610
&dev_attr_protection_type.attr,
564611
&dev_attr_protection_mode.attr,
565612
&dev_attr_app_tag_own.attr,
@@ -3771,13 +3818,20 @@ static void sd_shutdown(struct device *dev)
37713818
sd_sync_cache(sdkp, NULL);
37723819
}
37733820

3774-
if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) {
3821+
if (system_state != SYSTEM_RESTART &&
3822+
sdkp->device->manage_system_start_stop) {
37753823
sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
37763824
sd_start_stop_device(sdkp, 0);
37773825
}
37783826
}
37793827

3780-
static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
3828+
static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime)
3829+
{
3830+
return (sdev->manage_system_start_stop && !runtime) ||
3831+
(sdev->manage_runtime_start_stop && runtime);
3832+
}
3833+
3834+
static int sd_suspend_common(struct device *dev, bool runtime)
37813835
{
37823836
struct scsi_disk *sdkp = dev_get_drvdata(dev);
37833837
struct scsi_sense_hdr sshdr;
@@ -3809,12 +3863,12 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors)
38093863
}
38103864
}
38113865

3812-
if (sdkp->device->manage_start_stop) {
3866+
if (sd_do_start_stop(sdkp->device, runtime)) {
38133867
if (!sdkp->device->silence_suspend)
38143868
sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n");
38153869
/* an error is not worth aborting a system sleep */
38163870
ret = sd_start_stop_device(sdkp, 0);
3817-
if (ignore_stop_errors)
3871+
if (!runtime)
38183872
ret = 0;
38193873
}
38203874

@@ -3826,23 +3880,23 @@ static int sd_suspend_system(struct device *dev)
38263880
if (pm_runtime_suspended(dev))
38273881
return 0;
38283882

3829-
return sd_suspend_common(dev, true);
3883+
return sd_suspend_common(dev, false);
38303884
}
38313885

38323886
static int sd_suspend_runtime(struct device *dev)
38333887
{
3834-
return sd_suspend_common(dev, false);
3888+
return sd_suspend_common(dev, true);
38353889
}
38363890

3837-
static int sd_resume(struct device *dev)
3891+
static int sd_resume(struct device *dev, bool runtime)
38383892
{
38393893
struct scsi_disk *sdkp = dev_get_drvdata(dev);
3840-
int ret = 0;
3894+
int ret;
38413895

38423896
if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */
38433897
return 0;
38443898

3845-
if (!sdkp->device->manage_start_stop)
3899+
if (!sd_do_start_stop(sdkp->device, runtime))
38463900
return 0;
38473901

38483902
if (!sdkp->device->no_start_on_resume) {
@@ -3860,7 +3914,7 @@ static int sd_resume_system(struct device *dev)
38603914
if (pm_runtime_suspended(dev))
38613915
return 0;
38623916

3863-
return sd_resume(dev);
3917+
return sd_resume(dev, false);
38643918
}
38653919

38663920
static int sd_resume_runtime(struct device *dev)
@@ -3887,7 +3941,7 @@ static int sd_resume_runtime(struct device *dev)
38873941
"Failed to clear sense data\n");
38883942
}
38893943

3890-
return sd_resume(dev);
3944+
return sd_resume(dev, true);
38913945
}
38923946

38933947
static const struct dev_pm_ops sd_pm_ops = {

include/scsi/scsi_device.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ struct scsi_device {
161161
* pass settings from slave_alloc to scsi
162162
* core. */
163163
unsigned int eh_timeout; /* Error handling timeout */
164+
165+
bool manage_system_start_stop; /* Let HLD (sd) manage system start/stop */
166+
bool manage_runtime_start_stop; /* Let HLD (sd) manage runtime start/stop */
167+
164168
unsigned removable:1;
165169
unsigned changed:1; /* Data invalid due to media change */
166170
unsigned busy:1; /* Used to prevent races */
@@ -193,7 +197,6 @@ struct scsi_device {
193197
unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */
194198
unsigned no_start_on_add:1; /* do not issue start on add */
195199
unsigned allow_restart:1; /* issue START_UNIT in error handler */
196-
unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */
197200
unsigned no_start_on_resume:1; /* Do not issue START_STOP_UNIT on resume */
198201
unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */
199202
unsigned no_uld_attach:1; /* disable connecting to upper level drivers */

0 commit comments

Comments
 (0)