* [PATCH v4 0/4]: block layer runtime pm
@ 2012-07-05 7:26 Lin Ming
2012-07-05 7:26 ` [PATCH v4 1/4] block: add a flag to identify PM request Lin Ming
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Lin Ming @ 2012-07-05 7:26 UTC (permalink / raw)
To: Jens Axboe, Alan Stern, Shaohua Li; +Cc: linux-kernel, linux-pm, linux-scsi
In August 2010, Jens and Alan discussed about "Runtime PM and the block
layer". http://marc.info/?t=128259108400001&r=1&w=2
Here are the v4 patches to implement the ideas discussed.
Welcome to give it a try.
git pull git://git.kernel.org/pub/scm/linux/kernel/git/mlin/linux.git block_pm
The test steps, for example
# ls -l /sys/block/sda
/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
# echo auto > /sys/devices/pci0000:00/0000:00:1f.2/ata1/power/control
# echo auto > /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/power/control
# echo 10000 > /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/power/autosuspend_delay_ms
Then you'll see sda is suspended after 10secs idle.
# cat /sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/power/runtime_status
suspended
And if you do some IO, it will resume immediately.
v4:
- add CONFIG_PM_RUNTIME check
- update queue runtime pm status after system resume
- use pm_runtime_autosuspend instead of pm_request_autosuspend in scsi_runtime_idle
- always count PM request
v3:
- remove block layer suspend/resume callbacks
- add block layer runtime pm helper functions
v2:
- remove queue idle timer, use runtime pm core's auto suspend
Lin Ming (4):
block: add a flag to identify PM request
block: add runtime pm helpers
block: implement runtime pm strategy
[SCSI] sd: change to auto suspend mode
block/blk-core.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
block/elevator.c | 13 +++++++++++++
drivers/scsi/scsi_lib.c | 25 ++++++++++++++++++++++---
drivers/scsi/scsi_pm.c | 32 +++++++++++++++++++++++++-------
drivers/scsi/scsi_sysfs.c | 2 ++
drivers/scsi/sd.c | 16 +++++-----------
include/linux/blk_types.h | 2 ++
include/linux/blkdev.h | 27 +++++++++++++++++++++++++++
include/scsi/scsi_device.h | 4 ++++
9 files changed, 179 insertions(+), 21 deletions(-)
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 1/4] block: add a flag to identify PM request
2012-07-05 7:26 [PATCH v4 0/4]: block layer runtime pm Lin Ming
@ 2012-07-05 7:26 ` Lin Ming
2012-07-05 13:09 ` Rafael J. Wysocki
2012-07-05 7:26 ` [PATCH v4 2/4] block: add runtime pm helpers Lin Ming
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Lin Ming @ 2012-07-05 7:26 UTC (permalink / raw)
To: Jens Axboe, Alan Stern, Shaohua Li; +Cc: linux-kernel, linux-pm, linux-scsi
Add a flag REQ_PM to identify the request is PM related.
As an example, modify scsi code to use this flag.
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
---
drivers/scsi/scsi_lib.c | 25 ++++++++++++++++++++++---
drivers/scsi/sd.c | 9 +++++----
include/linux/blk_types.h | 2 ++
include/scsi/scsi_device.h | 4 ++++
4 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 6dfb978..45538e5 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -256,10 +256,10 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
EXPORT_SYMBOL(scsi_execute);
-int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
+static int __scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
struct scsi_sense_hdr *sshdr, int timeout, int retries,
- int *resid)
+ int *resid, int flags)
{
char *sense = NULL;
int result;
@@ -270,15 +270,34 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0, resid);
+ sense, timeout, retries, flags, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
kfree(sense);
return result;
}
+
+int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
+ int data_direction, void *buffer, unsigned bufflen,
+ struct scsi_sense_hdr *sshdr, int timeout, int retries,
+ int *resid)
+{
+ return __scsi_execute_req(sdev, cmd, data_direction, buffer, bufflen,
+ sshdr, timeout, retries, resid, 0);
+}
EXPORT_SYMBOL(scsi_execute_req);
+int scsi_execute_req_flag(struct scsi_device *sdev, const unsigned char *cmd,
+ int data_direction, void *buffer, unsigned bufflen,
+ struct scsi_sense_hdr *sshdr, int timeout, int retries,
+ int *resid, int flags)
+{
+ return __scsi_execute_req(sdev, cmd, data_direction, buffer, bufflen,
+ sshdr, timeout, retries, resid, flags);
+}
+EXPORT_SYMBOL(scsi_execute_req_flag);
+
/*
* Function: scsi_init_cmd_errh()
*
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 6f72b80..bf8413a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1270,8 +1270,9 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* Leave the rest of the command zero to indicate
* flush everything.
*/
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flag(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL,
+ REQ_PM);
if (res == 0)
break;
}
@@ -2815,8 +2816,8 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
if (!scsi_device_online(sdp))
return -ENODEV;
- res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES, NULL);
+ res = scsi_execute_req_flag(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 0edb65d..ed53dca 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -160,6 +160,7 @@ enum rq_flag_bits {
__REQ_FLUSH_SEQ, /* request for flush sequence */
__REQ_IO_STAT, /* account I/O stat */
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
+ __REQ_PM, /* runtime pm request */
__REQ_NR_BITS, /* stops here */
};
@@ -201,5 +202,6 @@ enum rq_flag_bits {
#define REQ_IO_STAT (1 << __REQ_IO_STAT)
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
#define REQ_SECURE (1 << __REQ_SECURE)
+#define REQ_PM (1 << __REQ_PM)
#endif /* __LINUX_BLK_TYPES_H */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index ba96988..e7fdff5 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -388,6 +388,10 @@ 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 int scsi_execute_req_flag(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, int flags);
#ifdef CONFIG_PM_RUNTIME
extern int scsi_autopm_get_device(struct scsi_device *);
--
1.7.10
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 2/4] block: add runtime pm helpers
2012-07-05 7:26 [PATCH v4 0/4]: block layer runtime pm Lin Ming
2012-07-05 7:26 ` [PATCH v4 1/4] block: add a flag to identify PM request Lin Ming
@ 2012-07-05 7:26 ` Lin Ming
2012-07-05 13:11 ` Rafael J. Wysocki
2012-07-05 7:26 ` [PATCH v4 3/4] block: implement runtime pm strategy Lin Ming
2012-07-05 7:26 ` [PATCH v4 4/4] [SCSI] sd: change to auto suspend mode Lin Ming
3 siblings, 1 reply; 9+ messages in thread
From: Lin Ming @ 2012-07-05 7:26 UTC (permalink / raw)
To: Jens Axboe, Alan Stern, Shaohua Li; +Cc: linux-kernel, linux-pm, linux-scsi
Add runtime pm helper functions:
blk_pm_runtime_init()
blk_pre_runtime_suspend()
blk_post_runtime_suspend()
blk_pre_runtime_resume()
blk_post_runtime_suspend()
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
---
v4:
- Adds CONFIG_PM_RUNTIME check
block/blk-core.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/blkdev.h | 27 +++++++++++++++++++++
2 files changed, 90 insertions(+)
diff --git a/block/blk-core.c b/block/blk-core.c
index 3c923a7..b94ec5f 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -30,6 +30,7 @@
#include <linux/list_sort.h>
#include <linux/delay.h>
#include <linux/ratelimit.h>
+#include <linux/pm_runtime.h>
#define CREATE_TRACE_POINTS
#include <trace/events/block.h>
@@ -2982,6 +2983,68 @@ void blk_finish_plug(struct blk_plug *plug)
}
EXPORT_SYMBOL(blk_finish_plug);
+#ifdef CONFIG_PM_RUNTIME
+void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
+{
+ q->dev = dev;
+ q->rpm_status = RPM_ACTIVE;
+
+ pm_runtime_use_autosuspend(q->dev);
+ pm_runtime_mark_last_busy(q->dev);
+ pm_runtime_autosuspend(q->dev);
+}
+EXPORT_SYMBOL(blk_pm_runtime_init);
+
+int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ int ret = 0;
+
+ spin_lock_irq(q->queue_lock);
+ if (q->nr_pending)
+ ret = -EBUSY;
+ else
+ q->rpm_status = RPM_SUSPENDING;
+ spin_unlock_irq(q->queue_lock);
+ return ret;
+}
+EXPORT_SYMBOL(blk_pre_runtime_suspend);
+
+void blk_post_runtime_suspend(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err)
+ q->rpm_status = RPM_SUSPENDED;
+ else {
+ q->rpm_status = RPM_ACTIVE;
+ pm_runtime_mark_last_busy(q->dev);
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_suspend);
+
+void blk_pre_runtime_resume(struct request_queue *q)
+{
+ spin_lock_irq(q->queue_lock);
+ q->rpm_status = RPM_RESUMING;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_pre_runtime_resume);
+
+void blk_post_runtime_resume(struct request_queue *q, int err)
+{
+ spin_lock_irq(q->queue_lock);
+ if (!err) {
+ q->rpm_status = RPM_ACTIVE;
+ __blk_run_queue(q);
+ pm_runtime_mark_last_busy(q->dev);
+ pm_request_autosuspend(q->dev);
+ } else
+ q->rpm_status = RPM_SUSPENDED;
+ spin_unlock_irq(q->queue_lock);
+}
+EXPORT_SYMBOL(blk_post_runtime_resume);
+#endif
+
int __init blk_dev_init(void)
{
BUILD_BUG_ON(__REQ_NR_BITS > 8 *
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index ba43f40..a5b255e 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -346,6 +346,12 @@ struct request_queue {
*/
struct kobject kobj;
+#ifdef CONFIG_PM_RUNTIME
+ struct device *dev;
+ int rpm_status;
+ unsigned int nr_pending;
+#endif
+
/*
* queue settings
*/
@@ -893,6 +899,27 @@ struct request_queue *blk_alloc_queue_node(gfp_t, int);
extern void blk_put_queue(struct request_queue *);
/*
+ * block layer runtime pm functions
+ */
+#ifdef CONFIG_PM_RUNTIME
+extern void blk_pm_runtime_init(struct request_queue *q, struct device *dev);
+extern int blk_pre_runtime_suspend(struct request_queue *q);
+extern void blk_post_runtime_suspend(struct request_queue *q, int err);
+extern void blk_pre_runtime_resume(struct request_queue *q);
+extern void blk_post_runtime_resume(struct request_queue *q, int err);
+#else
+static inline void blk_pm_runtime_init(struct request_queue *q,
+ struct device *dev) {}
+static inline int blk_pre_runtime_suspend(struct request_queue *q)
+{
+ return -ENOSYS;
+}
+static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
+static inline void blk_pre_runtime_resume(struct request_queue *q) {}
+static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
+#endif
+
+/*
* blk_plug permits building a queue of related requests by holding the I/O
* fragments for a short period. This allows merging of sequential requests
* into single larger request. As the requests are moved from a per-task list to
--
1.7.10
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 3/4] block: implement runtime pm strategy
2012-07-05 7:26 [PATCH v4 0/4]: block layer runtime pm Lin Ming
2012-07-05 7:26 ` [PATCH v4 1/4] block: add a flag to identify PM request Lin Ming
2012-07-05 7:26 ` [PATCH v4 2/4] block: add runtime pm helpers Lin Ming
@ 2012-07-05 7:26 ` Lin Ming
2012-07-05 13:54 ` Alan Stern
2012-07-05 7:26 ` [PATCH v4 4/4] [SCSI] sd: change to auto suspend mode Lin Ming
3 siblings, 1 reply; 9+ messages in thread
From: Lin Ming @ 2012-07-05 7:26 UTC (permalink / raw)
To: Jens Axboe, Alan Stern, Shaohua Li; +Cc: linux-kernel, linux-pm, linux-scsi
When a request is added:
If device is suspended or is suspending and the request is not a
PM request, resume the device.
When a request finishes:
Call pm_runtime_mark_last_busy().
When pick a request:
If device is resuming/suspending, then only PM request is allowed to go.
Return NULL for other cases.
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
---
v4:
- Adds CONFIG_PM_RUNTIME check
block/blk-core.c | 16 ++++++++++++++++
block/elevator.c | 13 +++++++++++++
2 files changed, 29 insertions(+)
diff --git a/block/blk-core.c b/block/blk-core.c
index b94ec5f..4720bec 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1225,6 +1225,11 @@ void __blk_put_request(struct request_queue *q, struct request *req)
if (unlikely(--req->ref_count))
return;
+#ifdef CONFIG_PM_RUNTIME
+ if (!(--q->nr_pending) && q->dev)
+ pm_runtime_mark_last_busy(q->dev);
+#endif
+
elv_completed_request(q, req);
/* this is a bio leak */
@@ -2013,6 +2018,17 @@ struct request *blk_peek_request(struct request_queue *q)
int ret;
while ((rq = __elv_next_request(q)) != NULL) {
+
+#ifdef CONFIG_PM_RUNTIME
+ if (q->rpm_status == RPM_SUSPENDED ||
+ (q->rpm_status != RPM_ACTIVE
+ && !(rq->cmd_flags & REQ_PM))) {
+
+ rq = NULL;
+ break;
+ }
+#endif
+
if (!(rq->cmd_flags & REQ_STARTED)) {
/*
* This is the first time the device driver
diff --git a/block/elevator.c b/block/elevator.c
index 6a55d41..9f82e77 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -34,6 +34,7 @@
#include <linux/blktrace_api.h>
#include <linux/hash.h>
#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
#include <trace/events/block.h>
@@ -536,6 +537,11 @@ void elv_requeue_request(struct request_queue *q, struct request *rq)
rq->cmd_flags &= ~REQ_STARTED;
+#ifdef CONFIG_PM_RUNTIME
+ /* __elv_add_request will increment the count */
+ if (!(rq->cmd_flags & REQ_PM))
+ q->nr_pending--;
+#endif
__elv_add_request(q, rq, ELEVATOR_INSERT_REQUEUE);
}
@@ -558,6 +564,13 @@ void __elv_add_request(struct request_queue *q, struct request *rq, int where)
{
trace_block_rq_insert(q, rq);
+#ifdef CONFIG_PM_RUNTIME
+ if (q->nr_pending++ == 0 && !(rq->cmd_flags & REQ_PM) &&
+ (q->rpm_status == RPM_SUSPENDED ||
+ q->rpm_status == RPM_SUSPENDING) && q->dev)
+ pm_request_resume(q->dev);
+#endif
+
rq->q = q;
if (rq->cmd_flags & REQ_SOFTBARRIER) {
--
1.7.10
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v4 4/4] [SCSI] sd: change to auto suspend mode
2012-07-05 7:26 [PATCH v4 0/4]: block layer runtime pm Lin Ming
` (2 preceding siblings ...)
2012-07-05 7:26 ` [PATCH v4 3/4] block: implement runtime pm strategy Lin Ming
@ 2012-07-05 7:26 ` Lin Ming
3 siblings, 0 replies; 9+ messages in thread
From: Lin Ming @ 2012-07-05 7:26 UTC (permalink / raw)
To: Jens Axboe, Alan Stern, Shaohua Li; +Cc: linux-kernel, linux-pm, linux-scsi
Uses block layer runtime pm helper functions in scsi_runtime_suspend/resume.
Remove scsi_autopm_* from sd open/release path.
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
---
v4:
- Update queue pm status in scsi_bus_resume_common
drivers/scsi/scsi_pm.c | 32 +++++++++++++++++++++++++-------
drivers/scsi/scsi_sysfs.c | 2 ++
drivers/scsi/sd.c | 7 -------
3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index d2a80de..aa43fa2 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -77,6 +77,9 @@ static int scsi_bus_resume_common(struct device *dev)
int err = 0;
if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct request_queue *q = sdev->request_queue;
+
/*
* Parent device may have runtime suspended as soon as
* it is woken up during the system resume.
@@ -84,12 +87,14 @@ static int scsi_bus_resume_common(struct device *dev)
* Resume it on behalf of child.
*/
pm_runtime_get_sync(dev->parent);
+ blk_pre_runtime_resume(q);
err = scsi_dev_type_resume(dev);
if (err == 0) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
}
+ blk_post_runtime_resume(q, err);
pm_runtime_put_sync(dev->parent);
}
@@ -142,10 +147,16 @@ static int scsi_runtime_suspend(struct device *dev)
dev_dbg(dev, "scsi_runtime_suspend\n");
if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct request_queue *q = sdev->request_queue;
+
+ err = blk_pre_runtime_suspend(q);
+ if (err)
+ return err;
+
err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
- if (err == -EAGAIN)
- pm_schedule_suspend(dev, jiffies_to_msecs(
- round_jiffies_up_relative(HZ/10)));
+
+ blk_post_runtime_suspend(q, err);
}
/* Insert hooks here for targets, hosts, and transport classes */
@@ -158,8 +169,14 @@ static int scsi_runtime_resume(struct device *dev)
int err = 0;
dev_dbg(dev, "scsi_runtime_resume\n");
- if (scsi_is_sdev_device(dev))
+ if (scsi_is_sdev_device(dev)) {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct request_queue *q = sdev->request_queue;
+
+ blk_pre_runtime_resume(q);
err = scsi_dev_type_resume(dev);
+ blk_post_runtime_resume(q, err);
+ }
/* Insert hooks here for targets, hosts, and transport classes */
@@ -174,9 +191,10 @@ static int scsi_runtime_idle(struct device *dev)
/* Insert hooks here for targets, hosts, and transport classes */
- if (scsi_is_sdev_device(dev))
- err = pm_schedule_suspend(dev, 100);
- else
+ if (scsi_is_sdev_device(dev)) {
+ pm_runtime_mark_last_busy(dev);
+ err = pm_runtime_autosuspend(dev);
+ } else
err = pm_runtime_suspend(dev);
return err;
}
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 04c2a27..1e8975f 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -895,6 +895,8 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
*/
scsi_autopm_get_device(sdev);
+ blk_pm_runtime_init(rq, &sdev->sdev_gendev);
+
error = device_add(&sdev->sdev_gendev);
if (error) {
sdev_printk(KERN_INFO, sdev,
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index bf8413a..bf9f4e8 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -967,10 +967,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
sdev = sdkp->device;
- retval = scsi_autopm_get_device(sdev);
- if (retval)
- goto error_autopm;
-
/*
* If the device is in error recovery, wait until it is done.
* If the device is offline, then disallow any access to it.
@@ -1015,8 +1011,6 @@ static int sd_open(struct block_device *bdev, fmode_t mode)
return 0;
error_out:
- scsi_autopm_put_device(sdev);
-error_autopm:
scsi_disk_put(sdkp);
return retval;
}
@@ -1051,7 +1045,6 @@ static int sd_release(struct gendisk *disk, fmode_t mode)
* XXX is followed by a "rmmod sd_mod"?
*/
- scsi_autopm_put_device(sdev);
scsi_disk_put(sdkp);
return 0;
}
--
1.7.10
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/4] block: add a flag to identify PM request
2012-07-05 7:26 ` [PATCH v4 1/4] block: add a flag to identify PM request Lin Ming
@ 2012-07-05 13:09 ` Rafael J. Wysocki
0 siblings, 0 replies; 9+ messages in thread
From: Rafael J. Wysocki @ 2012-07-05 13:09 UTC (permalink / raw)
To: Lin Ming
Cc: Jens Axboe, Alan Stern, Shaohua Li, linux-kernel, linux-pm,
linux-scsi
On Thursday, July 05, 2012, Lin Ming wrote:
> Add a flag REQ_PM to identify the request is PM related.
> As an example, modify scsi code to use this flag.
>
> Signed-off-by: Lin Ming <ming.m.lin@intel.com>
> ---
> drivers/scsi/scsi_lib.c | 25 ++++++++++++++++++++++---
> drivers/scsi/sd.c | 9 +++++----
> include/linux/blk_types.h | 2 ++
> include/scsi/scsi_device.h | 4 ++++
> 4 files changed, 33 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
> index 6dfb978..45538e5 100644
> --- a/drivers/scsi/scsi_lib.c
> +++ b/drivers/scsi/scsi_lib.c
> @@ -256,10 +256,10 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
> EXPORT_SYMBOL(scsi_execute);
>
>
> -int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
> +static int __scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
> int data_direction, void *buffer, unsigned bufflen,
> struct scsi_sense_hdr *sshdr, int timeout, int retries,
> - int *resid)
> + int *resid, int flags)
> {
> char *sense = NULL;
> int result;
> @@ -270,15 +270,34 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
> return DRIVER_ERROR << 24;
> }
> result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
> - sense, timeout, retries, 0, resid);
> + sense, timeout, retries, flags, resid);
> if (sshdr)
> scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
>
> kfree(sense);
> return result;
> }
> +
> +int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
> + int data_direction, void *buffer, unsigned bufflen,
> + struct scsi_sense_hdr *sshdr, int timeout, int retries,
> + int *resid)
> +{
> + return __scsi_execute_req(sdev, cmd, data_direction, buffer, bufflen,
> + sshdr, timeout, retries, resid, 0);
> +}
> EXPORT_SYMBOL(scsi_execute_req);
>
> +int scsi_execute_req_flag(struct scsi_device *sdev, const unsigned char *cmd,
> + int data_direction, void *buffer, unsigned bufflen,
> + struct scsi_sense_hdr *sshdr, int timeout, int retries,
> + int *resid, int flags)
> +{
> + return __scsi_execute_req(sdev, cmd, data_direction, buffer, bufflen,
> + sshdr, timeout, retries, resid, flags);
> +}
> +EXPORT_SYMBOL(scsi_execute_req_flag);
Hmm. You're only adding one item to the call chain this way and with many
arguments. I think it would be better to export __scsi_execute_req()
directly and define the wrappers as static inlines in a header.
In which case you won't need the second wrapper even. So perhaps it would be
a good idea to call the function scsi_execute_req_flags() and add a static
inline wrapper called scsi_execute_req() around it that will pass 0 as flags?
Rafael
> +
> /*
> * Function: scsi_init_cmd_errh()
> *
> diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
> index 6f72b80..bf8413a 100644
> --- a/drivers/scsi/sd.c
> +++ b/drivers/scsi/sd.c
> @@ -1270,8 +1270,9 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
> * Leave the rest of the command zero to indicate
> * flush everything.
> */
> - res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
> - SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL);
> + res = scsi_execute_req_flag(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
> + SD_FLUSH_TIMEOUT, SD_MAX_RETRIES, NULL,
> + REQ_PM);
> if (res == 0)
> break;
> }
> @@ -2815,8 +2816,8 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
> if (!scsi_device_online(sdp))
> return -ENODEV;
>
> - res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
> - SD_TIMEOUT, SD_MAX_RETRIES, NULL);
> + res = scsi_execute_req_flag(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
> + SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
> if (res) {
> sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
> sd_print_result(sdkp, res);
> diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> index 0edb65d..ed53dca 100644
> --- a/include/linux/blk_types.h
> +++ b/include/linux/blk_types.h
> @@ -160,6 +160,7 @@ enum rq_flag_bits {
> __REQ_FLUSH_SEQ, /* request for flush sequence */
> __REQ_IO_STAT, /* account I/O stat */
> __REQ_MIXED_MERGE, /* merge of different types, fail separately */
> + __REQ_PM, /* runtime pm request */
> __REQ_NR_BITS, /* stops here */
> };
>
> @@ -201,5 +202,6 @@ enum rq_flag_bits {
> #define REQ_IO_STAT (1 << __REQ_IO_STAT)
> #define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
> #define REQ_SECURE (1 << __REQ_SECURE)
> +#define REQ_PM (1 << __REQ_PM)
>
> #endif /* __LINUX_BLK_TYPES_H */
> diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
> index ba96988..e7fdff5 100644
> --- a/include/scsi/scsi_device.h
> +++ b/include/scsi/scsi_device.h
> @@ -388,6 +388,10 @@ 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 int scsi_execute_req_flag(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, int flags);
>
> #ifdef CONFIG_PM_RUNTIME
> extern int scsi_autopm_get_device(struct scsi_device *);
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/4] block: add runtime pm helpers
2012-07-05 7:26 ` [PATCH v4 2/4] block: add runtime pm helpers Lin Ming
@ 2012-07-05 13:11 ` Rafael J. Wysocki
2012-07-06 1:13 ` Lin Ming
0 siblings, 1 reply; 9+ messages in thread
From: Rafael J. Wysocki @ 2012-07-05 13:11 UTC (permalink / raw)
To: Lin Ming
Cc: Jens Axboe, Alan Stern, Shaohua Li, linux-kernel, linux-pm,
linux-scsi
On Thursday, July 05, 2012, Lin Ming wrote:
> Add runtime pm helper functions:
>
> blk_pm_runtime_init()
> blk_pre_runtime_suspend()
> blk_post_runtime_suspend()
> blk_pre_runtime_resume()
> blk_post_runtime_suspend()
What exactly do you need these things for? Please be specific.
Thanks,
Rafael
> Signed-off-by: Lin Ming <ming.m.lin@intel.com>
> ---
>
> v4:
> - Adds CONFIG_PM_RUNTIME check
>
> block/blk-core.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/blkdev.h | 27 +++++++++++++++++++++
> 2 files changed, 90 insertions(+)
>
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 3c923a7..b94ec5f 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -30,6 +30,7 @@
> #include <linux/list_sort.h>
> #include <linux/delay.h>
> #include <linux/ratelimit.h>
> +#include <linux/pm_runtime.h>
>
> #define CREATE_TRACE_POINTS
> #include <trace/events/block.h>
> @@ -2982,6 +2983,68 @@ void blk_finish_plug(struct blk_plug *plug)
> }
> EXPORT_SYMBOL(blk_finish_plug);
>
> +#ifdef CONFIG_PM_RUNTIME
> +void blk_pm_runtime_init(struct request_queue *q, struct device *dev)
> +{
> + q->dev = dev;
> + q->rpm_status = RPM_ACTIVE;
> +
> + pm_runtime_use_autosuspend(q->dev);
> + pm_runtime_mark_last_busy(q->dev);
> + pm_runtime_autosuspend(q->dev);
> +}
> +EXPORT_SYMBOL(blk_pm_runtime_init);
> +
> +int blk_pre_runtime_suspend(struct request_queue *q)
> +{
> + int ret = 0;
> +
> + spin_lock_irq(q->queue_lock);
> + if (q->nr_pending)
> + ret = -EBUSY;
> + else
> + q->rpm_status = RPM_SUSPENDING;
> + spin_unlock_irq(q->queue_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL(blk_pre_runtime_suspend);
> +
> +void blk_post_runtime_suspend(struct request_queue *q, int err)
> +{
> + spin_lock_irq(q->queue_lock);
> + if (!err)
> + q->rpm_status = RPM_SUSPENDED;
> + else {
> + q->rpm_status = RPM_ACTIVE;
> + pm_runtime_mark_last_busy(q->dev);
> + }
> + spin_unlock_irq(q->queue_lock);
> +}
> +EXPORT_SYMBOL(blk_post_runtime_suspend);
> +
> +void blk_pre_runtime_resume(struct request_queue *q)
> +{
> + spin_lock_irq(q->queue_lock);
> + q->rpm_status = RPM_RESUMING;
> + spin_unlock_irq(q->queue_lock);
> +}
> +EXPORT_SYMBOL(blk_pre_runtime_resume);
> +
> +void blk_post_runtime_resume(struct request_queue *q, int err)
> +{
> + spin_lock_irq(q->queue_lock);
> + if (!err) {
> + q->rpm_status = RPM_ACTIVE;
> + __blk_run_queue(q);
> + pm_runtime_mark_last_busy(q->dev);
> + pm_request_autosuspend(q->dev);
> + } else
> + q->rpm_status = RPM_SUSPENDED;
> + spin_unlock_irq(q->queue_lock);
> +}
> +EXPORT_SYMBOL(blk_post_runtime_resume);
> +#endif
> +
> int __init blk_dev_init(void)
> {
> BUILD_BUG_ON(__REQ_NR_BITS > 8 *
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index ba43f40..a5b255e 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -346,6 +346,12 @@ struct request_queue {
> */
> struct kobject kobj;
>
> +#ifdef CONFIG_PM_RUNTIME
> + struct device *dev;
> + int rpm_status;
> + unsigned int nr_pending;
> +#endif
> +
> /*
> * queue settings
> */
> @@ -893,6 +899,27 @@ struct request_queue *blk_alloc_queue_node(gfp_t, int);
> extern void blk_put_queue(struct request_queue *);
>
> /*
> + * block layer runtime pm functions
> + */
> +#ifdef CONFIG_PM_RUNTIME
> +extern void blk_pm_runtime_init(struct request_queue *q, struct device *dev);
> +extern int blk_pre_runtime_suspend(struct request_queue *q);
> +extern void blk_post_runtime_suspend(struct request_queue *q, int err);
> +extern void blk_pre_runtime_resume(struct request_queue *q);
> +extern void blk_post_runtime_resume(struct request_queue *q, int err);
> +#else
> +static inline void blk_pm_runtime_init(struct request_queue *q,
> + struct device *dev) {}
> +static inline int blk_pre_runtime_suspend(struct request_queue *q)
> +{
> + return -ENOSYS;
> +}
> +static inline void blk_post_runtime_suspend(struct request_queue *q, int err) {}
> +static inline void blk_pre_runtime_resume(struct request_queue *q) {}
> +static inline void blk_post_runtime_resume(struct request_queue *q, int err) {}
> +#endif
> +
> +/*
> * blk_plug permits building a queue of related requests by holding the I/O
> * fragments for a short period. This allows merging of sequential requests
> * into single larger request. As the requests are moved from a per-task list to
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 3/4] block: implement runtime pm strategy
2012-07-05 7:26 ` [PATCH v4 3/4] block: implement runtime pm strategy Lin Ming
@ 2012-07-05 13:54 ` Alan Stern
0 siblings, 0 replies; 9+ messages in thread
From: Alan Stern @ 2012-07-05 13:54 UTC (permalink / raw)
To: Lin Ming; +Cc: Jens Axboe, Shaohua Li, linux-kernel, linux-pm, linux-scsi
On Thu, 5 Jul 2012, Lin Ming wrote:
> When a request is added:
> If device is suspended or is suspending and the request is not a
> PM request, resume the device.
>
> When a request finishes:
> Call pm_runtime_mark_last_busy().
>
> When pick a request:
> If device is resuming/suspending, then only PM request is allowed to go.
> Return NULL for other cases.
>
> Signed-off-by: Lin Ming <ming.m.lin@intel.com>
These changes would look better if you didn't have "#ifdef
CONFIG_PM_RUNTIME" sprinkled throughout the block-layer routines.
Instead, define static helper functions to do your work, and put the
definitions inside a #ifdef block. If CONFIG_PM_RUNTIME isn't enabled,
the helper functions can be empty static inlines.
> --- a/block/elevator.c +++ b/block/elevator.c @@ -536,6 +537,11 @@
> void elv_requeue_request(struct request_queue *q, struct request *rq)
>
> rq->cmd_flags &= ~REQ_STARTED;
>
> +#ifdef CONFIG_PM_RUNTIME
> + /* __elv_add_request will increment the count */
> + if (!(rq->cmd_flags & REQ_PM))
> + q->nr_pending--;
> +#endif
__elv_add_request increments nr_pending even when REQ_PM is set. You
need to be consistent with that behavior.
Alan Stern
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/4] block: add runtime pm helpers
2012-07-05 13:11 ` Rafael J. Wysocki
@ 2012-07-06 1:13 ` Lin Ming
0 siblings, 0 replies; 9+ messages in thread
From: Lin Ming @ 2012-07-06 1:13 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Jens Axboe, Alan Stern, Shaohua Li, linux-kernel, linux-pm,
linux-scsi
On Thu, 2012-07-05 at 15:11 +0200, Rafael J. Wysocki wrote:
> On Thursday, July 05, 2012, Lin Ming wrote:
> > Add runtime pm helper functions:
> >
> > blk_pm_runtime_init()
> > blk_pre_runtime_suspend()
> > blk_post_runtime_suspend()
> > blk_pre_runtime_resume()
> > blk_post_runtime_suspend()
>
> What exactly do you need these things for? Please be specific.
Alan described these functions nicely, copied here.
http://marc.info/?l=linux-kernel&m=133727953025955&w=2
I'll add these function descriptions to the patch log.
====
This is not the way to do it. The block subsystem should not use
suspend/resume callbacks.
Instead, there should be block functions that can be called by client
drivers: block_pre_runtime_suspend, block_post_runtime_suspend,
bock_pre_runtime_resume, and block_post_runtime_resume.
They should do something like this:
block_pre_runtime_suspend:
If any requests are in the queue, return -EBUSY.
Otherwise set q->rpm_status to RPM_SUSPENDING and
return 0.
block_post_runtime_suspend:
If the suspend succeeded then set q->rpm_status to
RPM_SUSPENDED. Otherwise set it to RPM_ACTIVE and
call pm_runtime_mark_last_busy().
block_pre_runtime_resume:
Set q->rpm_status to RPM_RESUMING.
block_post_runtime_resume:
If the resume succeeded then set q->rpm_status to
RPM_ACTIVE and call pm_runtime_mark_last_busy() and
pm_runtime_request_autosuspend().
Otherwise set q->rpm_status to RPM_SUSPENDED.
There should also be an initialization function for client drivers to
call. block_runtime_pm_init() should call pm_runtime_mark_last_busy(),
pm_runtime_use_autosuspend(), and pm_runtime_autosuspend().
Next, you have to modify the parts of the block layer that run when a
new request is added to the queue or a request is removed.
When a request is added:
If q->rpm_status is RPM_SUSPENDED, or if q->rpm_status
is RPM_SUSPENDING and the REQ_PM flag isn't set, call
pm_runtime_request_resume().
When a request finishes:
Call pm_runtime_mark_last_busy().
Next, you have to change the parts of the block layer responsible for
taking a request from the queue and handing it to the lower-level
driver (both peek and get). If q->rpm_status is RPM_SUSPENDED, they
shouldn't do anything -- act as though the queue is empty. If
q->rpm_status is RPM_SUSPENDING or RPM_RESUMING, they should hand over
the request only if it has the REQ_PM flag set.
For this to work, the block layer has to know what struct device
pointer to pass to the pm_runtime_* routines. You'll have to add that
information to the request_queue structure; I guess q->dev can get set
by block_pm_runtime_init(). In fact, when that's done you won't need
q->rpm_status any more. You'll be able to use q->dev->power.rpm_status
directly, and you won't have to update it because the PM core does that
for you.
(Or maybe it would be easier to make q->rpm_status be a pointer to
q->dev->power.rpm_status. That way, if CONFIG_PM_RUNTIME isn't enabled
or block_runtime_pm_init() hasn't been called, you can have
q->rpm_status simply point to a static value that is permanently set to
RPM_ACTIVE.)
I may have left some parts out from this brief description. Hopefully
you'll be able to figure out the general idea and get it to work.
====
Thanks,
Lin Ming
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-07-06 1:13 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-05 7:26 [PATCH v4 0/4]: block layer runtime pm Lin Ming
2012-07-05 7:26 ` [PATCH v4 1/4] block: add a flag to identify PM request Lin Ming
2012-07-05 13:09 ` Rafael J. Wysocki
2012-07-05 7:26 ` [PATCH v4 2/4] block: add runtime pm helpers Lin Ming
2012-07-05 13:11 ` Rafael J. Wysocki
2012-07-06 1:13 ` Lin Ming
2012-07-05 7:26 ` [PATCH v4 3/4] block: implement runtime pm strategy Lin Ming
2012-07-05 13:54 ` Alan Stern
2012-07-05 7:26 ` [PATCH v4 4/4] [SCSI] sd: change to auto suspend mode Lin Ming
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).