This mechanism use the PM QoS framework to add device frequency constraints during Block IO is running. When critical processing I/O requests that could block latency-sensitive threads, it dynamically applies frequency restrictions. These constraints will be removed through a timeout-based reset mechanism. Signed-off-by: Wang Jianzheng <wangjianzheng@xxxxxxxx> --- block/blk-mq.c | 58 ++++++++++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 9 +++++++ include/linux/pm_qos.h | 6 +++++ 3 files changed, 73 insertions(+) diff --git a/block/blk-mq.c b/block/blk-mq.c index ba3a4b77f578..fcb4034287d3 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -11,6 +11,7 @@ #include <linux/bio.h> #include <linux/blkdev.h> #include <linux/blk-integrity.h> +#include <linux/blk-pm.h> #include <linux/kmemleak.h> #include <linux/mm.h> #include <linux/init.h> @@ -28,6 +29,7 @@ #include <linux/prefetch.h> #include <linux/blk-crypto.h> #include <linux/part_stat.h> +#include <linux/pm_qos.h> #include <linux/sched/isolation.h> #include <trace/events/block.h> @@ -3095,6 +3097,54 @@ static bool bio_unaligned(const struct bio *bio, struct request_queue *q) return false; } +#ifdef CONFIG_PM +static void blk_mq_dev_frequency_work(struct work_struct *work) +{ + struct request_queue *q = + container_of(work, struct request_queue, dev_freq_work.work); + unsigned long timeout; + struct dev_pm_qos_request *qos = q->dev_freq_qos; + + timeout = msecs_to_jiffies(q->disk->dev_freq_timeout); + if (!q || IS_ERR_OR_NULL(q->dev) || IS_ERR_OR_NULL(qos)) + return; + + if (q->pm_qos_status == PM_QOS_ACTIVE) { + q->pm_qos_status = PM_QOS_FREQ_SET; + dev_pm_qos_add_request(q->dev, qos, DEV_PM_QOS_MIN_FREQUENCY, + FREQ_QOS_MAX_DEFAULT_VALUE); + } else { + if (time_after(jiffies, READ_ONCE(q->last_active) + timeout)) + q->pm_qos_status = PM_QOS_FREQ_REMOV; + } + + if (q->pm_qos_status == PM_QOS_FREQ_REMOV) { + dev_pm_qos_remove_request(qos); + q->pm_qos_status = PM_QOS_ACTIVE; + } else { + schedule_delayed_work(&q->dev_freq_work, + q->last_active + timeout - jiffies); + } +} + +static void blk_pm_qos_dev_freq_update(struct request_queue *q, struct bio *bio) +{ + if (IS_ERR_OR_NULL(q->dev) || !q->disk->dev_freq_timeout) + return; + + if ((bio->bi_opf & REQ_SYNC || bio_op(bio) == REQ_OP_READ)) { + WRITE_ONCE(q->last_active, jiffies); + if (q->pm_qos_status == PM_QOS_ACTIVE) + schedule_delayed_work(&q->dev_freq_work, 0); + } +} +#else +static void blk_pm_qos_dev_freq_update(struct request_queue *q, struct bio *bio) +{ + return; +} +#endif + /** * blk_mq_submit_bio - Create and send a request to block device. * @bio: Bio pointer. @@ -3161,6 +3211,8 @@ void blk_mq_submit_bio(struct bio *bio) goto queue_exit; } + blk_pm_qos_dev_freq_update(q, bio); + bio = __bio_split_to_limits(bio, &q->limits, &nr_segs); if (!bio) goto queue_exit; @@ -4601,6 +4653,12 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, q->queue_flags |= QUEUE_FLAG_MQ_DEFAULT; INIT_DELAYED_WORK(&q->requeue_work, blk_mq_requeue_work); +#ifdef CONFIG_PM + INIT_DELAYED_WORK(&q->dev_freq_work, blk_mq_dev_frequency_work); + q->dev_freq_qos = kzalloc(sizeof(*q->dev_freq_qos), GFP_KERNEL); + if (IS_ERR_OR_NULL(q->dev_freq_qos)) + goto err_hctxs; +#endif INIT_LIST_HEAD(&q->flush_list); INIT_LIST_HEAD(&q->requeue_list); spin_lock_init(&q->requeue_lock); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 950ad047dd81..ea6dfff6b0f5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -26,6 +26,7 @@ #include <linux/xarray.h> #include <linux/file.h> #include <linux/lockdep.h> +#include <linux/pm_qos.h> struct module; struct request_queue; @@ -522,6 +523,14 @@ struct request_queue { #ifdef CONFIG_PM struct device *dev; enum rpm_status rpm_status; + enum pm_qos_status pm_qos_status; + unsigned long last_active; + + /** @dev_freq_work: Work to handle dev frequency PM QoS limits. */ + struct delayed_work dev_freq_work; + + /** @dev_freq_qos: PM QoS frequency limits for dev. */ + struct dev_pm_qos_request *dev_freq_qos; #endif /* diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 4a69d4af3ff8..e0d77ff65942 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -95,6 +95,12 @@ struct freq_qos_request { struct freq_constraints *qos; }; +enum pm_qos_status { + PM_QOS_INVALID = -1, + PM_QOS_ACTIVE = 0, + PM_QOS_FREQ_SET, + PM_QOS_FREQ_REMOV, +}; enum dev_pm_qos_req_type { DEV_PM_QOS_RESUME_LATENCY = 1, -- 2.34.1