From 6c7f1e2f12f6f37ddda01180c9e5c407eccce616 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 23 Jan 2013 15:09:31 +0800 Subject: [SCSI] sr: support runtime pm This patch adds runtime pm support for sr. It did this by increasing the runtime usage_count of the device when its block device is accessed. And decreasing the runtime usage_count of the device when the access is done. If there is media inside, runtime suspend is not allowed as we don't always know if the ODD is being used or not. The idea is discussed here: http://thread.gmane.org/gmane.linux.acpi.devel/55243/focus=52703 and the restriction to check media inside is discussed here: http://thread.gmane.org/gmane.linux.ide/53665/focus=58836 Signed-off-by: Aaron Lu Acked-by: Alan Stern Acked-by: James Bottomley Signed-off-by: Jeff Garzik --- drivers/scsi/sr.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 5fc97d2ba2fd..2e8ddd77366f 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,11 @@ static DEFINE_MUTEX(sr_mutex); static int sr_probe(struct device *); static int sr_remove(struct device *); static int sr_done(struct scsi_cmnd *); +static int sr_runtime_suspend(struct device *dev); + +static struct dev_pm_ops sr_pm_ops = { + .runtime_suspend = sr_runtime_suspend, +}; static struct scsi_driver sr_template = { .owner = THIS_MODULE, @@ -86,6 +92,7 @@ static struct scsi_driver sr_template = { .name = "sr", .probe = sr_probe, .remove = sr_remove, + .pm = &sr_pm_ops, }, .done = sr_done, }; @@ -131,6 +138,16 @@ static inline struct scsi_cd *scsi_cd(struct gendisk *disk) return container_of(disk->private_data, struct scsi_cd, driver); } +static int sr_runtime_suspend(struct device *dev) +{ + struct scsi_cd *cd = dev_get_drvdata(dev); + + if (cd->media_present) + return -EBUSY; + else + return 0; +} + /* * The get and put routines for the struct scsi_cd. Note this entity * has a scsi_device pointer and owns a reference to this. @@ -146,7 +163,8 @@ static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk) kref_get(&cd->kref); if (scsi_device_get(cd->device)) goto out_put; - goto out; + if (!scsi_autopm_get_device(cd->device)) + goto out; out_put: kref_put(&cd->kref, sr_kref_release); @@ -162,6 +180,7 @@ static void scsi_cd_put(struct scsi_cd *cd) mutex_lock(&sr_ref_mutex); kref_put(&cd->kref, sr_kref_release); + scsi_autopm_put_device(sdev); scsi_device_put(sdev); mutex_unlock(&sr_ref_mutex); } @@ -540,6 +559,8 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, void __user *argp = (void __user *)arg; int ret; + scsi_autopm_get_device(cd->device); + mutex_lock(&sr_mutex); /* @@ -571,6 +592,7 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, out: mutex_unlock(&sr_mutex); + scsi_autopm_put_device(cd->device); return ret; } @@ -578,7 +600,13 @@ static unsigned int sr_block_check_events(struct gendisk *disk, unsigned int clearing) { struct scsi_cd *cd = scsi_cd(disk); - return cdrom_check_events(&cd->cdi, clearing); + unsigned int ret; + + scsi_autopm_get_device(cd->device); + ret = cdrom_check_events(&cd->cdi, clearing); + scsi_autopm_put_device(cd->device); + + return ret; } static int sr_block_revalidate_disk(struct gendisk *disk) @@ -586,12 +614,16 @@ static int sr_block_revalidate_disk(struct gendisk *disk) struct scsi_cd *cd = scsi_cd(disk); struct scsi_sense_hdr sshdr; + scsi_autopm_get_device(cd->device); + /* if the unit is not ready, nothing more to do */ if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr)) - return 0; + goto out; sr_cd_check(&cd->cdi); get_sectorsize(cd); +out: + scsi_autopm_put_device(cd->device); return 0; } @@ -718,6 +750,8 @@ static int sr_probe(struct device *dev) sdev_printk(KERN_DEBUG, sdev, "Attached scsi CD-ROM %s\n", cd->cdi.name); + scsi_autopm_put_device(cd->device); + return 0; fail_put: @@ -965,6 +999,8 @@ static int sr_remove(struct device *dev) { struct scsi_cd *cd = dev_get_drvdata(dev); + scsi_autopm_get_device(cd->device); + blk_queue_prep_rq(cd->device->request_queue, scsi_prep_fn); del_gendisk(cd->disk); -- cgit 1.4.1 From 6f4c827e68a78731c6c75df69bf7b75b029ec70c Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 23 Jan 2013 15:09:32 +0800 Subject: [libata] scsi: no poll when ODD is powered off When the ODD is powered off, any action the user did to the ODD that would generate a media event will trigger an ACPI interrupt, so the poll for media event is no longer necessary. And the poll will also cause a runtime status change, which will stop the ODD from staying in powered off state, so the poll should better be stopped. But since we don't have access to the gendisk structure in LLDs, here comes the disk_events_disable_depth for scsi device. This field is a hint set by LLDs to convey information to upper layer drivers. A value of 0 means media poll is necessary for the device, while values above 0 means media poll is not needed and should better be skipped. So we can increase its value when we are to power off the ODD in ATA layer and decrease its value when the ODD is powered on, effectively silence the media events poll. Signed-off-by: Aaron Lu Signed-off-by: Jeff Garzik --- drivers/ata/libata-zpodd.c | 7 +++++++ drivers/scsi/scsi_lib.c | 14 ++++++++++++++ drivers/scsi/sr.c | 10 +++++++--- include/scsi/scsi_device.h | 4 ++++ 4 files changed, 32 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 540b0b7904fb..a7df60383532 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -178,11 +178,16 @@ bool zpodd_zpready(struct ata_device *dev) * Enable runtime wake capability through ACPI and set the powered_off flag, * this flag will be used during resume to decide what operations are needed * to take. + * + * Also, media poll needs to be silenced, so that it doesn't bring the ODD + * back to full power state every few seconds. */ void zpodd_enable_run_wake(struct ata_device *dev) { struct zpodd *zpodd = dev->zpodd; + sdev_disable_disk_events(dev->sdev); + zpodd->powered_off = true; device_set_run_wake(&dev->sdev->sdev_gendev, true); acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true); @@ -231,6 +236,8 @@ void zpodd_post_poweron(struct ata_device *dev) zpodd->zp_sampled = false; zpodd->zp_ready = false; + + sdev_enable_disk_events(dev->sdev); } static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index f1bf5aff68ed..765398c063c7 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2617,3 +2617,17 @@ void scsi_kunmap_atomic_sg(void *virt) kunmap_atomic(virt); } EXPORT_SYMBOL(scsi_kunmap_atomic_sg); + +void sdev_disable_disk_events(struct scsi_device *sdev) +{ + atomic_inc(&sdev->disk_events_disable_depth); +} +EXPORT_SYMBOL(sdev_disable_disk_events); + +void sdev_enable_disk_events(struct scsi_device *sdev) +{ + if (WARN_ON_ONCE(atomic_read(&sdev->disk_events_disable_depth) <= 0)) + return; + atomic_dec(&sdev->disk_events_disable_depth); +} +EXPORT_SYMBOL(sdev_enable_disk_events); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 2e8ddd77366f..f2884ee90710 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -602,9 +602,13 @@ static unsigned int sr_block_check_events(struct gendisk *disk, struct scsi_cd *cd = scsi_cd(disk); unsigned int ret; - scsi_autopm_get_device(cd->device); - ret = cdrom_check_events(&cd->cdi, clearing); - scsi_autopm_put_device(cd->device); + if (atomic_read(&cd->device->disk_events_disable_depth) == 0) { + scsi_autopm_get_device(cd->device); + ret = cdrom_check_events(&cd->cdi, clearing); + scsi_autopm_put_device(cd->device); + } else { + ret = 0; + } return ret; } diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index e65c62e82c5a..bb1371bf171d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -161,6 +161,8 @@ struct scsi_device { unsigned wce_default_on:1; /* Cache is ON by default */ unsigned no_dif:1; /* T10 PI (DIF) should be disabled */ + atomic_t disk_events_disable_depth; /* disable depth for disk events */ + DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ struct list_head event_list; /* asserted events */ struct work_struct event_work; @@ -397,6 +399,8 @@ extern int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, struct scsi_sense_hdr *, int timeout, int retries, int *resid); +extern void sdev_disable_disk_events(struct scsi_device *sdev); +extern void sdev_enable_disk_events(struct scsi_device *sdev); #ifdef CONFIG_PM_RUNTIME extern int scsi_autopm_get_device(struct scsi_device *); -- cgit 1.4.1