[PATCH 2/2] sched/rt: Block nohz tick_stop when rt bandwidth in use

From: Hao Jia
Date: Mon Aug 21 2023 - 05:50:23 EST


The commit 88c56cfeaec4 ("sched/fair: Block nohz tick_stop
when cfs bandwidth in use") has been merged. It can handle
conflicts between NOHZ full and cfs_bandwidth, and the
scheduler feature HZ_BW allows us to choose which one to prefer.

This problem also exists between NOHZ full and rt_bandwidth.
Now when there is only one RR task or only FIFO tasks on
the NOHZ full cpu, NOHZ full will stop tick even though
those tasks are constrained by rt_bandwidth. This makes It
very easy for RT tasks to run longer than the rt_bandwidth
runtime limit. This may cause rt bandwidth limit not timely
and CFS task starvation for a long time.

Similar to the commit above, we also add a check in
pick_next_task_rt(), which updates the tick dependency
status according to whether the task to be run is
constrained by rt_bandwidth.

Add check in sched_can_stop_tick() to cover some
edge cases such as rq nr_running going from 2->1 without
going through pick_next_task_rt() and the 1 remains
the running RT task.

Signed-off-by: Hao Jia <jiahao.os@xxxxxxxxxxxxx>
---
kernel/sched/rt.c | 61 +++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 0b9e9467ef61..f55ce6935a80 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1741,7 +1741,31 @@ static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flag
}

#ifdef CONFIG_NO_HZ_FULL
-static bool can_stop_tick_rt(struct rq *rq, int *stop_next)
+/*
+ * If the scheduler feature HZ_BW is enabled, we need to further
+ * check whether task @p is constrained by RT bandwidth to decide
+ * whether to stop tick.
+ */
+static inline bool rt_task_bw_constrained(struct task_struct *p)
+{
+ struct rt_rq *rt_rq;
+
+ if (!sched_feat(HZ_BW))
+ return false;
+
+ if (rt_bandwidth_enabled())
+ return true;
+
+ if (p->sched_class == &rt_sched_class && task_on_rq_queued(p)) {
+ rt_rq = rt_rq_of_se(&p->rt);
+ if (sched_rt_runtime(rt_rq) != RUNTIME_INF)
+ return true;
+ }
+
+ return false;
+}
+
+static bool __can_stop_tick_rt(struct rq *rq, struct task_struct *p, int *stop_next)
{
int fifo_nr_running;

@@ -1751,7 +1775,7 @@ static bool can_stop_tick_rt(struct rq *rq, int *stop_next)
*/
if (rq->rt.rr_nr_running) {
*stop_next = 1;
- if (rq->rt.rr_nr_running == 1)
+ if (rq->rt.rr_nr_running == 1 && !rt_task_bw_constrained(p))
return true;
else
return false;
@@ -1764,11 +1788,38 @@ static bool can_stop_tick_rt(struct rq *rq, int *stop_next)
fifo_nr_running = rq->rt.rt_nr_running - rq->rt.rr_nr_running;
if (fifo_nr_running) {
*stop_next = 1;
- return true;
+ if (!rt_task_bw_constrained(p))
+ return true;
+ else
+ return false;
}

return true;
}
+
+static bool can_stop_tick_rt(struct rq *rq, int *stop_next)
+{
+ bool ret;
+
+ ret = __can_stop_tick_rt(rq, rq->curr, stop_next);
+ if (stop_next)
+ return ret;
+
+ return true;
+}
+static void sched_rt_update_stop_tick(struct rq *rq, struct task_struct *p)
+{
+ int cpu = cpu_of(rq);
+ int unused;
+
+ if (!sched_feat(HZ_BW) || !tick_nohz_full_cpu(cpu))
+ return;
+
+ if (!__can_stop_tick_rt(rq, p, &unused))
+ tick_nohz_dep_set_cpu(cpu, TICK_DEP_BIT_SCHED);
+}
+#else /* CONFIG_NO_HZ_FULL */
+static inline void sched_rt_update_stop_tick(struct rq *rq, struct task_struct *p) {}
#endif

static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool first)
@@ -1846,8 +1897,10 @@ static struct task_struct *pick_next_task_rt(struct rq *rq)
{
struct task_struct *p = pick_task_rt(rq);

- if (p)
+ if (p) {
+ sched_rt_update_stop_tick(rq, p);
set_next_task_rt(rq, p, true);
+ }

return p;
}
--
2.39.2