Linux block layer
 help / color / mirror / Atom feed
* [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire
@ 2026-08-31 11:01 Ye Bin
  2026-08-31 11:01 ` [PATCH v2 1/4] block/mq-deadline: reject zero prio_aging_expire Ye Bin
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Ye Bin @ 2026-08-31 11:01 UTC (permalink / raw)
  To: axboe, linux-block; +Cc: ming.lei

From: Ye Bin <yebin10@huawei.com>

v2 diff vs v1:
1. Rejects zero and negative prio_aging_expire values with -EINVAL.
2. Introduce a per-queue prio_enable boolean sysfs attribute to disable
io priority.
3. Adds a module parameter prio_enable to disable io priority.
4. Documents both prio_enable and prio_aging_expire in
Documentation/block/deadline-iosched.rst.

This series adds a way to disable I/O priority (RT/BE/IDLE) distinction
in the mq-deadline scheduler and hardens the prio_aging_expire tunable
to prevent priority inversion.

Since mq-deadline introduced support for I/O priorities, processes
without an explicit I/O priority are bound to their scheduling priority.
Many applications do not care about I/O priorities and have no way to
disable the distinction, which adds overhead and can cause unexpected
latency behaviour.  This series gives operators a per-queue sysfs
switch (and a module parameter default) to opt out of the RT/BE/IDLE
classification entirely.

Patch 1 rejects zero and negative prio_aging_expire values with
-EINVAL.  A value of zero does not disable I/O priority; instead the
priority aging path in dd_dispatch_prio_aged_requests() is invoked with
"now - 0 == now", which dispatches best-effort and idle requests ahead
of pending real-time requests -- a classic priority inversion.

Patch 2 adds a per-queue prio_enable boolean sysfs attribute.  When
disabled, every request is filed in the best-effort bucket and the
priority aging path is bypassed.  The sysfs store drains in-flight I/O
through queue freeze and quiesce (the same sequence used by
elevator_switch()) to avoid priority inversion while toggling at
runtime.  As requests may now be filed in a bucket that does not match
their ioprio, dd_start_request(), dd_request_merged() and
dd_merged_requests() are updated to look up the per-priority bucket
from rq->elv.priv[0] instead of recomputing it from the request ioprio;
the now-unused dd_rq_ioclass() helper is removed.

Patch 3 adds a module parameter prio_enable so the default can be
overridden at load time (mq_deadline.prio_enable=0 on the kernel
command line or modprobe mq_deadline prio_enable=0), letting systems
opt out of I/O priority from boot without writing to sysfs after every
queue creation.

Patch 4 documents both prio_enable and prio_aging_expire in
Documentation/block/deadline-iosched.rst.

Ye Bin (4):
  block/mq-deadline: reject zero prio_aging_expire
  block/mq-deadline: add prio_enable switch for I/O priority control
  block/mq-deadline: add module parameter for prio_enable
  docs: block: document prio_enable and prio_aging_expire in
    deadline-iosched

 Documentation/block/deadline-iosched.rst |  32 +++++
 block/mq-deadline.c                      | 148 ++++++++++++++++++-----
 2 files changed, 151 insertions(+), 29 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/4] block/mq-deadline: reject zero prio_aging_expire
  2026-08-31 11:01 [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire Ye Bin
@ 2026-08-31 11:01 ` Ye Bin
  2026-08-31 11:01 ` [PATCH v2 2/4] block/mq-deadline: add prio_enable switch for I/O priority control Ye Bin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Ye Bin @ 2026-08-31 11:01 UTC (permalink / raw)
  To: axboe, linux-block; +Cc: ming.lei

From: Ye Bin <yebin10@huawei.com>

A prio_aging_expire of zero does not disable I/O priority in
mq-deadline.  Instead the priority aging path in
dd_dispatch_prio_aged_requests() is invoked with "now - 0 == now",
which causes best-effort and idle requests to be dispatched ahead of
pending real-time requests -- a classic priority inversion, not the
"priority disabled" behavior users may expect when writing zero.

Reject zero (and negative) values in the sysfs store with -EINVAL so
that a misconfiguration is reported rather than silently accepted.

Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 block/mq-deadline.c | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index 5f643c0ce2a8..a4358625cfc8 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -31,7 +31,10 @@ static const int read_expire = HZ / 2;  /* max time before a read is submitted.
 static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */
 /*
  * Time after which to dispatch lower priority requests even if higher
- * priority requests are pending.
+ * priority requests are pending.  Must be > 0: a value of zero would make
+ * the priority aging path dispatch best-effort and idle requests ahead of
+ * pending real-time requests through "now - 0 == now", a classic priority
+ * inversion.
  */
 static const int prio_aging_expire = 10 * HZ;
 static const int writes_starved = 2;    /* max times reads can starve a write */
@@ -770,7 +773,6 @@ static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count)
 	STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, msecs_to_jiffies)
 STORE_JIFFIES(deadline_read_expire_store, &dd->fifo_expire[DD_READ], 0, INT_MAX);
 STORE_JIFFIES(deadline_write_expire_store, &dd->fifo_expire[DD_WRITE], 0, INT_MAX);
-STORE_JIFFIES(deadline_prio_aging_expire_store, &dd->prio_aging_expire, 0, INT_MAX);
 STORE_INT(deadline_writes_starved_store, &dd->writes_starved, INT_MIN, INT_MAX);
 STORE_INT(deadline_front_merges_store, &dd->front_merges, 0, 1);
 STORE_INT(deadline_fifo_batch_store, &dd->fifo_batch, 0, INT_MAX);
@@ -778,6 +780,28 @@ STORE_INT(deadline_fifo_batch_store, &dd->fifo_batch, 0, INT_MAX);
 #undef STORE_INT
 #undef STORE_JIFFIES
 
+/*
+ * prio_aging_expire must be positive: a value of zero would make the
+ * priority aging path dispatch best-effort and idle requests ahead of
+ * pending real-time requests through "now - 0 == now", a classic priority
+ * inversion.  Reject zero and negative values instead of clamping, so that
+ * a misconfiguration is reported rather than silently accepted.
+ */
+static ssize_t deadline_prio_aging_expire_store(struct elevator_queue *e,
+						 const char *page, size_t count)
+{
+	struct deadline_data *dd = e->elevator_data;
+	int val, ret;
+
+	ret = kstrtoint(page, 0, &val);
+	if (ret < 0)
+		return ret;
+	if (val <= 0)
+		return -EINVAL;
+	dd->prio_aging_expire = msecs_to_jiffies(val);
+	return count;
+}
+
 #define DD_ATTR(name) \
 	__ATTR(name, 0644, deadline_##name##_show, deadline_##name##_store)
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/4] block/mq-deadline: add prio_enable switch for I/O priority control
  2026-08-31 11:01 [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire Ye Bin
  2026-08-31 11:01 ` [PATCH v2 1/4] block/mq-deadline: reject zero prio_aging_expire Ye Bin
@ 2026-08-31 11:01 ` Ye Bin
  2026-08-31 11:01 ` [PATCH v2 3/4] block/mq-deadline: add module parameter for prio_enable Ye Bin
  2026-08-31 11:01 ` [PATCH v2 4/4] docs: block: document prio_enable and prio_aging_expire in deadline-iosched Ye Bin
  3 siblings, 0 replies; 5+ messages in thread
From: Ye Bin @ 2026-08-31 11:01 UTC (permalink / raw)
  To: axboe, linux-block; +Cc: ming.lei

From: Ye Bin <yebin10@huawei.com>

Since mq-deadline introduced support for I/O priorities, processes
without an explicit I/O priority are bound to their scheduling
priority.  This forces applications to plan their I/O priorities,
but many applications do not care about I/O priorities and have no
way to disable the distinction.

Add a per-queue prio_enable boolean to control whether I/O priority
(RT/BE/IDLE) support is active.  When disabled, every request is
filed in the best-effort bucket and the priority aging path is
bypassed.

To avoid priority inversion while toggling the switch at runtime,
the sysfs store follows the same sequence as elevator_switch():
freeze the queue, quiesce, flip the flag, then unquiesce and
unfreeze.  This drains in-flight I/O so that requests already queued
in RT or IDLE buckets complete before the mode changes.

Since requests may now be filed in a bucket that does not match
their ioprio, update dd_start_request(), dd_request_merged() and
dd_merged_requests() to look up the per-priority bucket from
rq->elv.priv[0] instead of recomputing it from the request ioprio.
Remove the now-unused dd_rq_ioclass() helper.

Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 block/mq-deadline.c | 109 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 82 insertions(+), 27 deletions(-)

diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index a4358625cfc8..276ce249d326 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -86,6 +86,7 @@ struct deadline_data {
 	 * run time data
 	 */
 
+	struct request_queue *q;	/* associated request queue */
 	struct list_head dispatch;
 	struct dd_per_prio per_prio[DD_PRIO_COUNT];
 
@@ -102,6 +103,7 @@ struct deadline_data {
 	int writes_starved;
 	int front_merges;
 	int prio_aging_expire;
+	bool prio_enable;
 
 	spinlock_t lock;
 };
@@ -120,15 +122,6 @@ deadline_rb_root(struct dd_per_prio *per_prio, struct request *rq)
 	return &per_prio->sort_list[rq_data_dir(rq)];
 }
 
-/*
- * Returns the I/O priority class (IOPRIO_CLASS_*) that has been assigned to a
- * request.
- */
-static u8 dd_rq_ioclass(struct request *rq)
-{
-	return IOPRIO_PRIO_CLASS(req_get_ioprio(rq));
-}
-
 /*
  * Return the first request for which blk_rq_pos() >= @pos.
  */
@@ -187,10 +180,7 @@ static void deadline_remove_request(struct request_queue *q,
 static void dd_request_merged(struct request_queue *q, struct request *req,
 			      enum elv_merge type)
 {
-	struct deadline_data *dd = q->elevator->elevator_data;
-	const u8 ioprio_class = dd_rq_ioclass(req);
-	const enum dd_prio prio = ioprio_class_to_prio[ioprio_class];
-	struct dd_per_prio *per_prio = &dd->per_prio[prio];
+	struct dd_per_prio *per_prio = req->elv.priv[0];
 
 	/*
 	 * if the merge was a front merge, we need to reposition request
@@ -208,12 +198,11 @@ static void dd_merged_requests(struct request_queue *q, struct request *req,
 			       struct request *next)
 {
 	struct deadline_data *dd = q->elevator->elevator_data;
-	const u8 ioprio_class = dd_rq_ioclass(next);
-	const enum dd_prio prio = ioprio_class_to_prio[ioprio_class];
+	struct dd_per_prio *per_prio = next->elv.priv[0];
 
 	lockdep_assert_held(&dd->lock);
 
-	dd->per_prio[prio].stats.merged++;
+	per_prio->stats.merged++;
 
 	/*
 	 * if next expires before rq, assign its expire time to rq
@@ -230,7 +219,7 @@ static void dd_merged_requests(struct request_queue *q, struct request *req,
 	/*
 	 * kill knowledge of next, this one is a goner
 	 */
-	deadline_remove_request(q, &dd->per_prio[prio], next);
+	deadline_remove_request(q, per_prio, next);
 }
 
 /*
@@ -305,15 +294,13 @@ static bool started_after(struct deadline_data *dd, struct request *rq,
 	return time_after(start_time, latest_start);
 }
 
-static struct request *dd_start_request(struct deadline_data *dd,
-					enum dd_data_dir data_dir,
+static struct request *dd_start_request(enum dd_data_dir data_dir,
 					struct request *rq)
 {
-	u8 ioprio_class = dd_rq_ioclass(rq);
-	enum dd_prio prio = ioprio_class_to_prio[ioprio_class];
+	struct dd_per_prio *per_prio = rq->elv.priv[0];
 
-	dd->per_prio[prio].latest_pos[data_dir] = blk_rq_pos(rq);
-	dd->per_prio[prio].stats.dispatched++;
+	per_prio->latest_pos[data_dir] = blk_rq_pos(rq);
+	per_prio->stats.dispatched++;
 	rq->rq_flags |= RQF_STARTED;
 	return rq;
 }
@@ -410,7 +397,7 @@ static struct request *__dd_dispatch_request(struct deadline_data *dd,
 	 */
 	dd->batching++;
 	deadline_move_request(per_prio, rq);
-	return dd_start_request(dd, data_dir, rq);
+	return dd_start_request(data_dir, rq);
 }
 
 /*
@@ -426,6 +413,16 @@ static struct request *dd_dispatch_prio_aged_requests(struct deadline_data *dd,
 
 	lockdep_assert_held(&dd->lock);
 
+	/*
+	 * When I/O priority is disabled every request is filed in the
+	 * DD_BE_PRIO bucket, so the priority aging path must be bypassed to
+	 * avoid dispatching best-effort (or idle) requests ahead of pending
+	 * real-time requests through "now - prio_aging_expire", which would
+	 * cause priority inversion.
+	 */
+	if (!dd->prio_enable)
+		return NULL;
+
 	prio_cnt = !!dd_queued(dd, DD_RT_PRIO) + !!dd_queued(dd, DD_BE_PRIO) +
 		   !!dd_queued(dd, DD_IDLE_PRIO);
 	if (prio_cnt < 2)
@@ -461,7 +458,17 @@ static struct request *dd_dispatch_request(struct blk_mq_hw_ctx *hctx)
 	if (!list_empty(&dd->dispatch)) {
 		rq = list_first_entry(&dd->dispatch, struct request, queuelist);
 		list_del_init(&rq->queuelist);
-		dd_start_request(dd, rq_data_dir(rq), rq);
+		dd_start_request(rq_data_dir(rq), rq);
+		goto unlock;
+	}
+
+	/*
+	 * When I/O priority is disabled every request is filed in the
+	 * best-effort bucket, so skip the priority aging path and the
+	 * multi-priority loop and dispatch directly from that single bucket.
+	 */
+	if (!dd->prio_enable) {
+		rq = __dd_dispatch_request(dd, &dd->per_prio[DD_BE_PRIO], now);
 		goto unlock;
 	}
 
@@ -536,6 +543,7 @@ static int dd_init_sched(struct request_queue *q, struct elevator_queue *eq)
 		return -ENOMEM;
 
 	eq->elevator_data = dd;
+	dd->q = q;
 
 	INIT_LIST_HEAD(&dd->dispatch);
 	for (prio = 0; prio <= DD_PRIO_MAX; prio++) {
@@ -553,6 +561,7 @@ static int dd_init_sched(struct request_queue *q, struct elevator_queue *eq)
 	dd->last_dir = DD_WRITE;
 	dd->fifo_batch = fifo_batch;
 	dd->prio_aging_expire = prio_aging_expire;
+	dd->prio_enable = true;
 	spin_lock_init(&dd->lock);
 
 	/* We dispatch from request queue wide instead of hw queue */
@@ -573,7 +582,8 @@ static int dd_request_merge(struct request_queue *q, struct request **rq,
 {
 	struct deadline_data *dd = q->elevator->elevator_data;
 	const u8 ioprio_class = IOPRIO_PRIO_CLASS(bio->bi_ioprio);
-	const enum dd_prio prio = ioprio_class_to_prio[ioprio_class];
+	const enum dd_prio prio = !dd->prio_enable ? DD_BE_PRIO :
+					  ioprio_class_to_prio[ioprio_class];
 	struct dd_per_prio *per_prio = &dd->per_prio[prio];
 	sector_t sector = bio_end_sector(bio);
 	struct request *__rq;
@@ -633,7 +643,15 @@ static void dd_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
 
 	lockdep_assert_held(&dd->lock);
 
-	prio = ioprio_class_to_prio[ioprio_class];
+	/*
+	 * When I/O priority is disabled, file every request in the best-effort
+	 * bucket so that the dispatch path no longer distinguishes between RT,
+	 * BE and IDLE classes.
+	 */
+	if (!dd->prio_enable)
+		prio = DD_BE_PRIO;
+	else
+		prio = ioprio_class_to_prio[ioprio_class];
 	per_prio = &dd->per_prio[prio];
 	if (!rq->elv.priv[0])
 		per_prio->stats.inserted++;
@@ -748,6 +766,7 @@ SHOW_JIFFIES(deadline_prio_aging_expire_show, dd->prio_aging_expire);
 SHOW_INT(deadline_writes_starved_show, dd->writes_starved);
 SHOW_INT(deadline_front_merges_show, dd->front_merges);
 SHOW_INT(deadline_fifo_batch_show, dd->fifo_batch);
+SHOW_INT(deadline_prio_enable_show, dd->prio_enable);
 #undef SHOW_INT
 #undef SHOW_JIFFIES
 
@@ -802,6 +821,41 @@ static ssize_t deadline_prio_aging_expire_store(struct elevator_queue *e,
 	return count;
 }
 
+/*
+ * Writing zero to prio_enable disables I/O priority: all requests are
+ * treated as best-effort. To avoid priority inversion while the mode is
+ * being switched, first drain all in-flight I/O by following the same
+ * sequence used by elevator_switch(): freeze the queue so that new
+ * upper-layer I/O is blocked and all outstanding requests complete,
+ * quiesce the queue so that no dispatch is in progress, then flip
+ * prio_enable to false. New I/O queued after the switch lands in the
+ * best-effort bucket.
+ */
+static ssize_t deadline_prio_enable_store(struct elevator_queue *e,
+					 const char *page, size_t count)
+{
+	struct deadline_data *dd = e->elevator_data;
+	int val, ret;
+
+	ret = kstrtoint(page, 0, &val);
+	if (ret < 0)
+		return ret;
+	if (val < 0)
+		val = 0;
+
+	if (!!val != dd->prio_enable) {
+		unsigned int memflags;
+
+		memflags = blk_mq_freeze_queue(dd->q);
+		blk_mq_quiesce_queue(dd->q);
+		dd->prio_enable = !!val;
+		blk_mq_unquiesce_queue(dd->q);
+		blk_mq_unfreeze_queue(dd->q, memflags);
+	}
+
+	return count;
+}
+
 #define DD_ATTR(name) \
 	__ATTR(name, 0644, deadline_##name##_show, deadline_##name##_store)
 
@@ -812,6 +866,7 @@ static const struct elv_fs_entry deadline_attrs[] = {
 	DD_ATTR(front_merges),
 	DD_ATTR(fifo_batch),
 	DD_ATTR(prio_aging_expire),
+	DD_ATTR(prio_enable),
 	__ATTR_NULL
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 3/4] block/mq-deadline: add module parameter for prio_enable
  2026-08-31 11:01 [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire Ye Bin
  2026-08-31 11:01 ` [PATCH v2 1/4] block/mq-deadline: reject zero prio_aging_expire Ye Bin
  2026-08-31 11:01 ` [PATCH v2 2/4] block/mq-deadline: add prio_enable switch for I/O priority control Ye Bin
@ 2026-08-31 11:01 ` Ye Bin
  2026-08-31 11:01 ` [PATCH v2 4/4] docs: block: document prio_enable and prio_aging_expire in deadline-iosched Ye Bin
  3 siblings, 0 replies; 5+ messages in thread
From: Ye Bin @ 2026-08-31 11:01 UTC (permalink / raw)
  To: axboe, linux-block; +Cc: ming.lei

From: Ye Bin <yebin10@huawei.com>

Allow the default of prio_enable to be overridden at load time:

  - built-in:  mq_deadline.prio_enable=0 on the kernel command line
  - module:    modprobe mq_deadline prio_enable=0

A value of zero disables I/O priority from boot/load: every request is
filed in the best-effort bucket and the priority aging path is
bypassed, so systems that do not want RT/BE/IDLE distinction can opt
out without writing to sysfs after every queue creation.

Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 block/mq-deadline.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index 276ce249d326..b30924e0b297 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -37,6 +37,17 @@ static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SO
  * inversion.
  */
 static const int prio_aging_expire = 10 * HZ;
+
+/*
+ * Whether to enable I/O priority support (RT/BE/IDLE distinction).
+ * When false every request is filed in the best-effort bucket and the
+ * priority aging path is bypassed, so systems that do not want RT/BE/IDLE
+ * distinction can opt out.
+ */
+static bool prio_enable = true;
+module_param(prio_enable, bool, 0644);
+MODULE_PARM_DESC(prio_enable,
+		 "Enable I/O priority (RT/BE/IDLE); 0 = best-effort only.");
 static const int writes_starved = 2;    /* max times reads can starve a write */
 static const int fifo_batch = 16;       /* # of sequential requests treated as one
 				     by the above parameters. For throughput. */
@@ -561,7 +572,7 @@ static int dd_init_sched(struct request_queue *q, struct elevator_queue *eq)
 	dd->last_dir = DD_WRITE;
 	dd->fifo_batch = fifo_batch;
 	dd->prio_aging_expire = prio_aging_expire;
-	dd->prio_enable = true;
+	dd->prio_enable = prio_enable;
 	spin_lock_init(&dd->lock);
 
 	/* We dispatch from request queue wide instead of hw queue */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 4/4] docs: block: document prio_enable and prio_aging_expire in deadline-iosched
  2026-08-31 11:01 [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire Ye Bin
                   ` (2 preceding siblings ...)
  2026-08-31 11:01 ` [PATCH v2 3/4] block/mq-deadline: add module parameter for prio_enable Ye Bin
@ 2026-08-31 11:01 ` Ye Bin
  3 siblings, 0 replies; 5+ messages in thread
From: Ye Bin @ 2026-08-31 11:01 UTC (permalink / raw)
  To: axboe, linux-block; +Cc: ming.lei

From: Ye Bin <yebin10@huawei.com>

The mq-deadline scheduler exposes two sysfs tunables, prio_enable and
prio_aging_expire, that control its I/O priority (RT/BE/IDLE) support,
but neither was described in the deadline-iosched documentation.

Add sections covering:

  - prio_enable: enables/disables RT/BE/IDLE distinction; when disabled
    all requests fall into the best-effort bucket and the priority aging
    path is bypassed.  Switching the value drains in-flight I/O (queue
    freeze and quiesce) to avoid priority inversion during the transition.
    Also available as a module parameter.

  - prio_aging_expire: the time after which a waiting best-effort or idle
    request may be dispatched despite pending real-time requests, to
    prevent indefinite starvation.  Defaults to 10000 ms; only effective
    when prio_enable is on and at least two priority buckets are
    populated.  Zero and negative values are rejected with -EINVAL to
    avoid the "now - 0 == now" priority inversion.

Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 Documentation/block/deadline-iosched.rst | 32 ++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/Documentation/block/deadline-iosched.rst b/Documentation/block/deadline-iosched.rst
index 9f5c5a4c370e..ffa88cbf2951 100644
--- a/Documentation/block/deadline-iosched.rst
+++ b/Documentation/block/deadline-iosched.rst
@@ -69,4 +69,36 @@ that comes at basically 0 cost we leave that on. We simply disable the
 rbtree front sector lookup when the io scheduler merge function is called.
 
 
+prio_enable	(bool)
+----------------------
+
+Whether to enable I/O priority support that distinguishes real-time (RT),
+best-effort (BE) and idle requests.  When enabled (the default), requests are
+filed into separate per-priority buckets and dispatched in priority order: lower
+priority requests are deferred while any higher priority requests are pending,
+subject to the prio_aging_expire aging mechanism described below.  When disabled,
+every request is filed in the best-effort bucket, the priority aging path is
+bypassed, and the scheduler dispatches from that single bucket.  This lets
+systems that do not want RT/BE/IDLE distinction opt out of the extra overhead.
+Switching the value drains all in-flight I/O (queue freeze and quiesce) to avoid
+priority inversion during the transition.  This parameter can also be set at
+module load time via the prio_enable module parameter.
+
+
+prio_aging_expire	(in ms)
+------------------------------
+
+To prevent lower priority requests from being starved indefinitely by a steady
+stream of higher priority requests, the deadline scheduler ages pending
+requests.  prio_aging_expire is the time after which a best-effort or idle
+request that has been waiting longer than this threshold may be dispatched even
+though real-time requests are still pending.  The default is 10000 ms (10 s).
+
+This parameter only takes effect when prio_enable is enabled and there are
+requests queued in at least two distinct priority buckets.  The value must be
+positive: zero or negative values are rejected with -EINVAL, since a value of
+zero would dispatch best-effort and idle requests ahead of pending real-time
+requests through "now - 0 == now", a classic priority inversion.
+
+
 Nov 11 2002, Jens Axboe <jens.axboe@oracle.com>
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-31 11:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 11:01 [PATCH v2 0/4] block/mq-deadline: add prio_enable switch and harden prio_aging_expire Ye Bin
2026-08-31 11:01 ` [PATCH v2 1/4] block/mq-deadline: reject zero prio_aging_expire Ye Bin
2026-08-31 11:01 ` [PATCH v2 2/4] block/mq-deadline: add prio_enable switch for I/O priority control Ye Bin
2026-08-31 11:01 ` [PATCH v2 3/4] block/mq-deadline: add module parameter for prio_enable Ye Bin
2026-08-31 11:01 ` [PATCH v2 4/4] docs: block: document prio_enable and prio_aging_expire in deadline-iosched Ye Bin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox