Add a new flag may_power_off to scsi_device to let user decide if they want the scsi device to be powered off when runtime suspended. For ODD, if the ODD is capable of zero power, a device attribute file will be created for user to control the value. And if user sets 0 to may_power_off, we will disable runtime suspend for this ODD since there is no benefit if power can't be removed when suspended. Apply on top of the previous sent ZPODD scsi tree patches: https://lkml.org/lkml/2012/7/27/71 Signed-off-by: Aaron Lu <aaron.lu@xxxxxxxxx> --- drivers/scsi/sr.c | 55 +++++++++++++++++++++++++++++++++++++++++----- include/scsi/scsi_device.h | 1 + 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index e6e5549..bc9df62 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -158,7 +158,7 @@ static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk) kref_get(&cd->kref); if (scsi_device_get(cd->device)) goto out_put; - if (cd->device->can_power_off && scsi_autopm_get_device(cd->device)) + if (scsi_autopm_get_device(cd->device)) goto out_pm; goto out; @@ -179,11 +179,50 @@ static void scsi_cd_put(struct scsi_cd *cd) mutex_lock(&sr_ref_mutex); kref_put(&cd->kref, sr_kref_release); scsi_device_put(sdev); - if (sdev->can_power_off) - scsi_autopm_put_device_autosuspend(sdev); + scsi_autopm_put_device_autosuspend(sdev); mutex_unlock(&sr_ref_mutex); } +static ssize_t +may_power_off_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + return snprintf(buf, 10, "%d\n", sdev->may_power_off); +} + +static ssize_t +may_power_off_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = -EINVAL; + struct scsi_device *sdev = to_scsi_device(dev); + struct scsi_cd *cd = dev_get_drvdata(dev); + + if (buf[1] == '\0' || (buf[1] == '\n' && buf[2] == '\0')) { + if (buf[0] == '1') + value = 1; + else if (buf[0] == '0') + value = 0; + } + + if (value >= 0) { + if (sdev->may_power_off != value) { + if (value == 0) { + if (!atomic_dec_and_test(&cd->suspend_count)) + scsi_autopm_get_device(cd->device); + } else + atomic_set(&cd->suspend_count, 1); + sdev->may_power_off = value; + } + value = count; + } + + return value; +} +DEVICE_ATTR(may_power_off, S_IRUGO | S_IWUSR, + may_power_off_show, may_power_off_store); + static int sr_suspend(struct device *dev, pm_message_t msg) { int poweroff; @@ -342,7 +381,7 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, return 0; /* if the logical unit just finished loading/unloading, do a TUR */ - if (cd->device->can_power_off && cd->dbml && sr_unit_load_done(cd)) { + if (cd->device->may_power_off && cd->dbml && sr_unit_load_done(cd)) { events = 0; goto do_tur; } @@ -396,7 +435,7 @@ do_tur: cd->tur_changed = true; } - if (cd->device->can_power_off && !cd->media_present) { + if (cd->device->may_power_off && !cd->media_present) { if (cd->cdi.mask & CDC_CLOSE_TRAY) poweroff = 1; else @@ -850,6 +889,8 @@ static int sr_probe(struct device *dev) pm_runtime_set_autosuspend_delay(dev, 180 * 1000); pm_runtime_use_autosuspend(dev); atomic_set(&cd->suspend_count, 1); + sdev->may_power_off = 1; + device_create_file(dev, &dev_attr_may_power_off); } disk->driverfs_dev = &sdev->sdev_gendev; @@ -1138,10 +1179,12 @@ static int sr_remove(struct device *dev) struct scsi_cd *cd = dev_get_drvdata(dev); /* disable runtime pm and possibly resume the device */ - if (cd->device->can_power_off && + if (cd->device->may_power_off && !atomic_dec_and_test(&cd->suspend_count)) scsi_autopm_get_device(cd->device); + device_remove_file(dev, &dev_attr_may_power_off); + blk_queue_prep_rq(cd->device->request_queue, scsi_prep_fn); del_gendisk(cd->disk); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 4bc4ac4..0bfc17f 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -157,6 +157,7 @@ struct scsi_device { unsigned can_power_off:1; /* Device supports runtime power off */ unsigned wakeup_by_user:1; /* User wakes up the ODD */ unsigned wce_default_on:1; /* Cache is ON by default */ + unsigned may_power_off:1; /* power off is allowed by user */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ struct list_head event_list; /* asserted events */ -- 1.7.11.5 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html