[PATCH 2/3] block: add support for device frequency PM QoS tuning

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux