* [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
* 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
* [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
* 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 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
* [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
* 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
* [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
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).