[PATCH] sched/fair: Restart hrtick after same-task repicks

Shubhang Kaushik (Ampere) posted 1 patch 1 month, 2 weeks ago
kernel/sched/core.c  |  2 ++
kernel/sched/fair.c  | 29 ++++++++++++++++++++++++++++-
kernel/sched/sched.h | 21 ++++++++++++++++++++-
3 files changed, 50 insertions(+), 2 deletions(-)
[PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Shubhang Kaushik (Ampere) 1 month, 2 weeks ago
Fair hrtick is implemented with a one-shot timer, so each precise
preemption point has to be programmed explicitly. The usual fair path
does this from set_next_task_fair(), which calls hrtick_start_fair().

The missed path is:

  hrtick
    -> task_tick_fair(..., queued=1)
       -> entity_tick()
	  -> resched_curr()
    -> schedule()
       -> pick_task_fair() picks current again
       -> put_prev_set_next_task()
	  -> next == prev
	  -> return

Since set_next_task_fair() is skipped, hrtick_start_fair() is not called
and no new fair hrtick is started.

Record when a queued fair hrtick may need a restart, and consume that
state only from the same-task fast path. Limit this to cases where more
than one fair entity is runnable and all queued fair entities are
runnable, avoiding extra hrticks for delayed-dequeue and pipe-like cases
where queued entities are not all competing for CPU time.

Signed-off-by: Shubhang Kaushik (Ampere) <sh@gentwo.org>
---
On v7.2-rc7 mainline (3aa1dcaa4f6f), with HRTICK enabled,
base_slice_ns=3000000, and two CPU-bound tasks pinned to one CPU, the
nice-0 task's runtime intervals above 8ms dropped from 228 in a 10s
perf sched capture to 34-38 across repeated runs with this change.

A similar missed hrtick start was previously reported for the older
pick_next_task_fair() flow:
  Message-ID: <20241111074841.8802-1-shijie@os.amperecomputing.com>
---
 kernel/sched/core.c  |  2 ++
 kernel/sched/fair.c  | 29 ++++++++++++++++++++++++++++-
 kernel/sched/sched.h | 21 ++++++++++++++++++++-
 3 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f6135341aa779b8262f113e103d8ad..5ec8c3f752fa48149469907edb595ede0769e94a 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1013,12 +1013,14 @@ static inline void hrtick_schedule_exit(struct rq *rq)
 		__hrtimer_rearm_deferred();
 
 	rq->hrtick_sched = HRTICK_SCHED_NONE;
+	rq->hrtick_rearm_fair = false;
 }
 
 static void hrtick_rq_init(struct rq *rq)
 {
 	INIT_CSD(&rq->hrtick_csd, __hrtick_start, rq);
 	rq->hrtick_sched = HRTICK_SCHED_NONE;
+	rq->hrtick_rearm_fair = false;
 	hrtimer_setup(&rq->hrtick_timer, hrtick, CLOCK_MONOTONIC,
 		      HRTIMER_MODE_REL_HARD | HRTIMER_MODE_LAZY_REARM);
 }
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d78467ec6ee1343050fcc2794dafb38ade3599e5..2d90a9a84175833bdb78f6f78d23b124105fcb82 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7681,6 +7681,22 @@ static void hrtick_start_fair(struct rq *rq, struct task_struct *p)
 	hrtick_start(rq, (scale * delta) / 1024);
 }
 
+void __hrtick_rearm_fair(struct rq *rq, struct task_struct *p)
+{
+	rq->hrtick_rearm_fair = false;
+
+	if (!hrtick_enabled_fair(rq))
+		return;
+
+	if (hrtick_active(rq))
+		return;
+
+	if (p->sched_class != &fair_sched_class)
+		return;
+
+	hrtick_start_fair(rq, p);
+}
+
 /*
  * Called on enqueue to start the hrtick when h_nr_queued becomes more than 1.
  */
@@ -14858,8 +14874,19 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
 		entity_tick(cfs_rq, se, queued);
 	}
 
-	if (queued)
+	if (queued) {
+		/*
+		 * Fair hrtick is one-shot. If this hrtick-triggered
+		 * reschedule picks the same task again, set_next_task_fair()
+		 * will be skipped. Mark that path for a possible restart, but
+		 * avoid delayed-dequeue cases where queued entities are not all
+		 * runnable.
+		 */
+		rq->hrtick_rearm_fair = hrtick_enabled_fair(rq) &&
+					rq->cfs.h_nr_runnable > 1 &&
+					rq->cfs.h_nr_runnable == rq->cfs.h_nr_queued;
 		return;
+	}
 
 	if (static_branch_unlikely(&sched_numa_balancing))
 		task_tick_numa(rq, curr);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 56acf502ba260ab18bacd7a4c2efdec612d50125..faf63eea233981fbd7e0a13b652f0c37d292ef35 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1313,6 +1313,7 @@ struct rq {
 	ktime_t			hrtick_time;
 	ktime_t			hrtick_delay;
 	unsigned int		hrtick_sched;
+	bool			hrtick_rearm_fair;
 #endif
 
 #ifdef CONFIG_SCHEDSTATS
@@ -2745,6 +2746,18 @@ static inline void set_next_task(struct rq *rq, struct task_struct *next)
 	next->sched_class->set_next_task(rq, next, false);
 }
 
+#ifdef CONFIG_SCHED_HRTICK
+void __hrtick_rearm_fair(struct rq *rq, struct task_struct *p);
+
+static inline void hrtick_rearm_fair(struct rq *rq, struct task_struct *p)
+{
+	if (rq->hrtick_rearm_fair)
+		__hrtick_rearm_fair(rq, p);
+}
+#else
+static inline void hrtick_rearm_fair(struct rq *rq, struct task_struct *p) { }
+#endif
+
 static inline void
 __put_prev_set_next_dl_server(struct rq *rq,
 			      struct task_struct *prev,
@@ -2763,8 +2776,14 @@ static inline void put_prev_set_next_task(struct rq *rq,
 
 	__put_prev_set_next_dl_server(rq, prev, next);
 
-	if (next == prev)
+	if (next == prev) {
+		/*
+		 * Same-task repicks skip class callbacks. Restart fair hrtick
+		 * if the queued tick path marked it as needed.
+		 */
+		hrtick_rearm_fair(rq, next);
 		return;
+	}
 
 	prev->sched_class->put_prev_task(rq, prev, next);
 	next->sched_class->set_next_task(rq, next, true);

---
base-commit: 3aa1dcaa4f6f5ae08936491e08bd456f331f2d40
change-id: 20260813-sched-fair-hrtick-restart-ab9d3d47ef78

Best regards,
-- 
Shubhang Kaushik (Ampere) <sh@gentwo.org>
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Peter Zijlstra 2 weeks, 3 days ago
On Thu, Aug 13, 2026 at 02:23:48PM -0700, Shubhang Kaushik (Ampere) wrote:
> Fair hrtick is implemented with a one-shot timer, so each precise
> preemption point has to be programmed explicitly. The usual fair path
> does this from set_next_task_fair(), which calls hrtick_start_fair().
> 
> The missed path is:
> 
>   hrtick
>     -> task_tick_fair(..., queued=1)
>        -> entity_tick()
> 	  -> resched_curr()
>     -> schedule()
>        -> pick_task_fair() picks current again
>        -> put_prev_set_next_task()
> 	  -> next == prev
> 	  -> return
> 
> Since set_next_task_fair() is skipped, hrtick_start_fair() is not called
> and no new fair hrtick is started.

Indeed. However, you missed this is also true for DL.

Does something like the below work for you?

---
 kernel/sched/core.c      |  2 +-
 kernel/sched/deadline.c  |  8 ++++++--
 kernel/sched/ext/ext.c   |  5 ++++-
 kernel/sched/fair.c      | 15 ++++++++++-----
 kernel/sched/idle.c      |  5 ++++-
 kernel/sched/rt.c        |  7 +++++--
 kernel/sched/sched.h     | 16 ++++++++++++----
 kernel/sched/stop_task.c |  5 ++++-
 8 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 91f059a55695..a39d12d38070 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7241,7 +7241,7 @@ static void __sched notrace __schedule(int sched_mode)
 			 * on_cpu.
 			 */
 			donor->sched_class->put_prev_task(rq, donor, donor);
-			donor->sched_class->set_next_task(rq, donor, true);
+			donor->sched_class->set_next_task(rq, donor, SNT_PICK);
 		}
 	} else {
 		rq_set_donor(rq, next);
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index de6a361a87c7..21d904d92c64 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -2773,11 +2773,14 @@ static void start_hrtick_dl(struct rq *rq, struct sched_dl_entity *dl_se)
  * DL keeps current in tree, because ->deadline is not typically changed while
  * a task is runnable.
  */
-static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
+static void set_next_task_dl(struct rq *rq, struct task_struct *p, enum snt_e type)
 {
 	struct sched_dl_entity *dl_se = &p->dl;
 	struct dl_rq *dl_rq = &rq->dl;
 
+	if (type == SNT_REPICK)
+		goto repick;
+
 	p->se.exec_start = rq_clock_task(rq);
 	if (on_dl_rq(&p->dl))
 		update_stats_wait_end_dl(dl_rq, dl_se);
@@ -2788,7 +2791,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
 	WARN_ON_ONCE(dl_rq->curr);
 	dl_rq->curr = dl_se;
 
-	if (!first)
+	if (type != SNT_PICK)
 		return;
 
 	if (rq->donor->sched_class != &dl_sched_class)
@@ -2796,6 +2799,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
 
 	deadline_queue_push_tasks(rq);
 
+repick:
 	if (hrtick_enabled_dl(rq))
 		start_hrtick_dl(rq, &p->dl);
 }
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 51de1d8b72a1..31a300f2d3b2 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3003,10 +3003,13 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
 	return verdict;
 }
 
-static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
+static void set_next_task_scx(struct rq *rq, struct task_struct *p, enum snt_e type)
 {
 	struct scx_sched *sch = scx_task_sched(p);
 
+	if (type == SNT_REPICK)
+		return;
+
 	if (p->scx.flags & SCX_TASK_QUEUED) {
 		/*
 		 * Core-sched might decide to execute @p before it is
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 4d0b94465d19..f469e469b402 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15242,14 +15242,18 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
 	}
 }
 
-static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
+static void set_next_task_fair(struct rq *rq, struct task_struct *p, enum snt_e type)
 {
 	struct sched_entity *se = &p->se;
-	bool throttled = false;
 	struct cfs_rq *cfs_rq = &rq->cfs;
 	unsigned long weight = NICE_0_LOAD;
+	bool first = type == SNT_PICK;
+	bool throttled = false;
 	bool on_rq = se->on_rq;
 
+	if (type == SNT_REPICK)
+		goto repick;
+
 	clear_buddies(cfs_rq, se);
 
 	if (on_rq)
@@ -15293,11 +15297,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
 
 	WARN_ON_ONCE(se->sched_delayed);
 
-	if (hrtick_enabled_fair(rq))
-		hrtick_start_fair(rq, p);
-
 	update_misfit_status(p, rq);
 	sched_fair_update_stop_tick(rq, p);
+
+repick:
+	if (hrtick_enabled_fair(rq))
+		hrtick_start_fair(rq, p);
 }
 
 void init_cfs_rq(struct cfs_rq *cfs_rq)
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index eb73b65ce6c4..76f3c84ca684 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -487,8 +487,11 @@ static void put_prev_task_idle(struct rq *rq, struct task_struct *prev, struct t
 	update_rq_avg_idle(rq);
 }
 
-static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool first)
+static void set_next_task_idle(struct rq *rq, struct task_struct *next, enum snt_e type)
 {
+	if (type == SNT_REPICK)
+		return;
+
 	update_idle_core(rq);
 	scx_update_idle(rq, true, true);
 	schedstat_inc(rq->sched_goidle);
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 85303add726d..1535046a23ff 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1654,11 +1654,14 @@ static void wakeup_preempt_rt(struct rq *rq, struct task_struct *p, int flags)
 		check_preempt_equal_prio(rq, p);
 }
 
-static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool first)
+static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, enum snt_e type)
 {
 	struct sched_rt_entity *rt_se = &p->rt;
 	struct rt_rq *rt_rq = &rq->rt;
 
+	if (type == SNT_REPICK)
+		return;
+
 	p->se.exec_start = rq_clock_task(rq);
 	if (on_rt_rq(&p->rt))
 		update_stats_wait_end_rt(rt_rq, rt_se);
@@ -1666,7 +1669,7 @@ static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool f
 	/* The running task is never eligible for pushing */
 	dequeue_pushable_task(rq, p);
 
-	if (!first)
+	if (type != SNT_PICK)
 		return;
 
 	/*
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 6c3ad70e58b8..944366e2d142 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2630,6 +2630,12 @@ struct affinity_context {
 
 extern s64 update_curr_common(struct rq *rq);
 
+enum snt_e {
+	SNT_NORMAL,
+	SNT_PICK,
+	SNT_REPICK,
+};
+
 struct sched_class {
 
 #ifdef CONFIG_UCLAMP_TASK
@@ -2687,7 +2693,7 @@ struct sched_class {
 	 * __schedule: rq->lock
 	 */
 	void (*put_prev_task)(struct rq *rq, struct task_struct *p, struct task_struct *next);
-	void (*set_next_task)(struct rq *rq, struct task_struct *p, bool first);
+	void (*set_next_task)(struct rq *rq, struct task_struct *p, enum snt_e type);
 
 	/*
 	 * select_task_rq: p->pi_lock
@@ -2790,7 +2796,7 @@ static inline void put_prev_task(struct rq *rq, struct task_struct *prev)
 
 static inline void set_next_task(struct rq *rq, struct task_struct *next)
 {
-	next->sched_class->set_next_task(rq, next, false);
+	next->sched_class->set_next_task(rq, next, SNT_NORMAL);
 }
 
 static inline void
@@ -2811,11 +2817,13 @@ static inline void put_prev_set_next_task(struct rq *rq,
 
 	__put_prev_set_next_dl_server(rq, prev, next);
 
-	if (next == prev)
+	if (next == prev) {
+		next->sched_class->set_next_task(rq, next, SNT_REPICK);
 		return;
+	}
 
 	prev->sched_class->put_prev_task(rq, prev, next);
-	next->sched_class->set_next_task(rq, next, true);
+	next->sched_class->set_next_task(rq, next, SNT_PICK);
 }
 
 /*
diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
index c909ca0d8c87..1e0109ec36b3 100644
--- a/kernel/sched/stop_task.c
+++ b/kernel/sched/stop_task.c
@@ -27,8 +27,11 @@ wakeup_preempt_stop(struct rq *rq, struct task_struct *p, int flags)
 	/* we're never preempted */
 }
 
-static void set_next_task_stop(struct rq *rq, struct task_struct *stop, bool first)
+static void set_next_task_stop(struct rq *rq, struct task_struct *stop, enum snt_e type)
 {
+	if (type == SNT_REPICK)
+		return;
+
 	stop->se.exec_start = rq_clock_task(rq);
 }
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Shubhang 2 weeks, 3 days ago
Hi Peter,

On Fri, 11 Sep 2026, Peter Zijlstra wrote:

> On Thu, Aug 13, 2026 at 02:23:48PM -0700, Shubhang Kaushik (Ampere) wrote:
>> Fair hrtick is implemented with a one-shot timer, so each precise
>> preemption point has to be programmed explicitly. The usual fair path
>> does this from set_next_task_fair(), which calls hrtick_start_fair().
>>
>> The missed path is:
>>
>>   hrtick
>>     -> task_tick_fair(..., queued=1)
>>        -> entity_tick()
>> 	  -> resched_curr()
>>     -> schedule()
>>        -> pick_task_fair() picks current again
>>        -> put_prev_set_next_task()
>> 	  -> next == prev
>> 	  -> return
>>
>> Since set_next_task_fair() is skipped, hrtick_start_fair() is not called
>> and no new fair hrtick is started.
>
> Indeed. However, you missed this is also true for DL.
>
> Does something like the below work for you?
>

Yes, this works for me.

When schedule() selects the current task again, next == prev 
case returns from put_prev_set_next_task() without calling the selected
task's set_next_task() function. For a fair task, that means 
set_next_task_fair() is skipped, meaning hrtick_start_fair() is skipped.
For a deadline task, set_next_task_dl() and start_hrtick_dl() are skipped 
for the same reason.

Using SNT_REPICK makes this case explicit:

         next == prev
           -> next->sched_class->set_next_task(rq, next, SNT_REPICK)

set_next_task_fair(..., SNT_REPICK) can then bypass the normal
fair task selection work and restart only the fair hrtick. Likewise,
set_next_task_dl(..., SNT_REPICK) can restart only the DL hrtick.
This seems cleaner than the fair specific rq flag and runnable count 
checks in my patch. And it also fixes the DL case that I missed.

It also retains the reason I moved the restart to the pick side. This
call happens during schedule(), when rq->hrtick_sched is in its 
deferred state. Therefore hrtick_start() records the new delay and
hrtick_schedule_exit() rearms the hrtimer after scheduling completes,
rather than reprogramming it directly from the hrtick callback.

I will test this approach with the delayed dequeue workload and a DL
hrtick case, then send a v2 based on it.

On the vprot point, I agree with Vincent's follow-up i.e. SNT_REPICK
must not call set_protect_slice(). As doing so would extend the protected
minimum slice on every repick. Once vprot has passed, a newly eligible
task should be able to preempt before the current task reaches deadline. 
Will keep that as a separate fix.

Thanks,
Shubhang Kaushik

> ---
> kernel/sched/core.c      |  2 +-
> kernel/sched/deadline.c  |  8 ++++++--
> kernel/sched/ext/ext.c   |  5 ++++-
> kernel/sched/fair.c      | 15 ++++++++++-----
> kernel/sched/idle.c      |  5 ++++-
> kernel/sched/rt.c        |  7 +++++--
> kernel/sched/sched.h     | 16 ++++++++++++----
> kernel/sched/stop_task.c |  5 ++++-
> 8 files changed, 46 insertions(+), 17 deletions(-)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 91f059a55695..a39d12d38070 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -7241,7 +7241,7 @@ static void __sched notrace __schedule(int sched_mode)
> 			 * on_cpu.
> 			 */
> 			donor->sched_class->put_prev_task(rq, donor, donor);
> -			donor->sched_class->set_next_task(rq, donor, true);
> +			donor->sched_class->set_next_task(rq, donor, SNT_PICK);
> 		}
> 	} else {
> 		rq_set_donor(rq, next);
> diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
> index de6a361a87c7..21d904d92c64 100644
> --- a/kernel/sched/deadline.c
> +++ b/kernel/sched/deadline.c
> @@ -2773,11 +2773,14 @@ static void start_hrtick_dl(struct rq *rq, struct sched_dl_entity *dl_se)
>  * DL keeps current in tree, because ->deadline is not typically changed while
>  * a task is runnable.
>  */
> -static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_dl(struct rq *rq, struct task_struct *p, enum snt_e type)
> {
> 	struct sched_dl_entity *dl_se = &p->dl;
> 	struct dl_rq *dl_rq = &rq->dl;
>
> +	if (type == SNT_REPICK)
> +		goto repick;
> +
> 	p->se.exec_start = rq_clock_task(rq);
> 	if (on_dl_rq(&p->dl))
> 		update_stats_wait_end_dl(dl_rq, dl_se);
> @@ -2788,7 +2791,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
> 	WARN_ON_ONCE(dl_rq->curr);
> 	dl_rq->curr = dl_se;
>
> -	if (!first)
> +	if (type != SNT_PICK)
> 		return;
>
> 	if (rq->donor->sched_class != &dl_sched_class)
> @@ -2796,6 +2799,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
>
> 	deadline_queue_push_tasks(rq);
>
> +repick:
> 	if (hrtick_enabled_dl(rq))
> 		start_hrtick_dl(rq, &p->dl);
> }
> diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
> index 51de1d8b72a1..31a300f2d3b2 100644
> --- a/kernel/sched/ext/ext.c
> +++ b/kernel/sched/ext/ext.c
> @@ -3003,10 +3003,13 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
> 	return verdict;
> }
>
> -static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_scx(struct rq *rq, struct task_struct *p, enum snt_e type)
> {
> 	struct scx_sched *sch = scx_task_sched(p);
>
> +	if (type == SNT_REPICK)
> +		return;
> +
> 	if (p->scx.flags & SCX_TASK_QUEUED) {
> 		/*
> 		 * Core-sched might decide to execute @p before it is
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 4d0b94465d19..f469e469b402 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15242,14 +15242,18 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
> 	}
> }
>
> -static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_fair(struct rq *rq, struct task_struct *p, enum snt_e type)
> {
> 	struct sched_entity *se = &p->se;
> -	bool throttled = false;
> 	struct cfs_rq *cfs_rq = &rq->cfs;
> 	unsigned long weight = NICE_0_LOAD;
> +	bool first = type == SNT_PICK;
> +	bool throttled = false;
> 	bool on_rq = se->on_rq;
>
> +	if (type == SNT_REPICK)
> +		goto repick;
> +
> 	clear_buddies(cfs_rq, se);
>
> 	if (on_rq)
> @@ -15293,11 +15297,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
>
> 	WARN_ON_ONCE(se->sched_delayed);
>
> -	if (hrtick_enabled_fair(rq))
> -		hrtick_start_fair(rq, p);
> -
> 	update_misfit_status(p, rq);
> 	sched_fair_update_stop_tick(rq, p);
> +
> +repick:
> +	if (hrtick_enabled_fair(rq))
> +		hrtick_start_fair(rq, p);
> }
>
> void init_cfs_rq(struct cfs_rq *cfs_rq)
> diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
> index eb73b65ce6c4..76f3c84ca684 100644
> --- a/kernel/sched/idle.c
> +++ b/kernel/sched/idle.c
> @@ -487,8 +487,11 @@ static void put_prev_task_idle(struct rq *rq, struct task_struct *prev, struct t
> 	update_rq_avg_idle(rq);
> }
>
> -static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool first)
> +static void set_next_task_idle(struct rq *rq, struct task_struct *next, enum snt_e type)
> {
> +	if (type == SNT_REPICK)
> +		return;
> +
> 	update_idle_core(rq);
> 	scx_update_idle(rq, true, true);
> 	schedstat_inc(rq->sched_goidle);
> diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
> index 85303add726d..1535046a23ff 100644
> --- a/kernel/sched/rt.c
> +++ b/kernel/sched/rt.c
> @@ -1654,11 +1654,14 @@ static void wakeup_preempt_rt(struct rq *rq, struct task_struct *p, int flags)
> 		check_preempt_equal_prio(rq, p);
> }
>
> -static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool first)
> +static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, enum snt_e type)
> {
> 	struct sched_rt_entity *rt_se = &p->rt;
> 	struct rt_rq *rt_rq = &rq->rt;
>
> +	if (type == SNT_REPICK)
> +		return;
> +
> 	p->se.exec_start = rq_clock_task(rq);
> 	if (on_rt_rq(&p->rt))
> 		update_stats_wait_end_rt(rt_rq, rt_se);
> @@ -1666,7 +1669,7 @@ static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool f
> 	/* The running task is never eligible for pushing */
> 	dequeue_pushable_task(rq, p);
>
> -	if (!first)
> +	if (type != SNT_PICK)
> 		return;
>
> 	/*
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index 6c3ad70e58b8..944366e2d142 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -2630,6 +2630,12 @@ struct affinity_context {
>
> extern s64 update_curr_common(struct rq *rq);
>
> +enum snt_e {
> +	SNT_NORMAL,
> +	SNT_PICK,
> +	SNT_REPICK,
> +};
> +
> struct sched_class {
>
> #ifdef CONFIG_UCLAMP_TASK
> @@ -2687,7 +2693,7 @@ struct sched_class {
> 	 * __schedule: rq->lock
> 	 */
> 	void (*put_prev_task)(struct rq *rq, struct task_struct *p, struct task_struct *next);
> -	void (*set_next_task)(struct rq *rq, struct task_struct *p, bool first);
> +	void (*set_next_task)(struct rq *rq, struct task_struct *p, enum snt_e type);
>
> 	/*
> 	 * select_task_rq: p->pi_lock
> @@ -2790,7 +2796,7 @@ static inline void put_prev_task(struct rq *rq, struct task_struct *prev)
>
> static inline void set_next_task(struct rq *rq, struct task_struct *next)
> {
> -	next->sched_class->set_next_task(rq, next, false);
> +	next->sched_class->set_next_task(rq, next, SNT_NORMAL);
> }
>
> static inline void
> @@ -2811,11 +2817,13 @@ static inline void put_prev_set_next_task(struct rq *rq,
>
> 	__put_prev_set_next_dl_server(rq, prev, next);
>
> -	if (next == prev)
> +	if (next == prev) {
> +		next->sched_class->set_next_task(rq, next, SNT_REPICK);
> 		return;
> +	}
>
> 	prev->sched_class->put_prev_task(rq, prev, next);
> -	next->sched_class->set_next_task(rq, next, true);
> +	next->sched_class->set_next_task(rq, next, SNT_PICK);
> }
>
> /*
> diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
> index c909ca0d8c87..1e0109ec36b3 100644
> --- a/kernel/sched/stop_task.c
> +++ b/kernel/sched/stop_task.c
> @@ -27,8 +27,11 @@ wakeup_preempt_stop(struct rq *rq, struct task_struct *p, int flags)
> 	/* we're never preempted */
> }
>
> -static void set_next_task_stop(struct rq *rq, struct task_struct *stop, bool first)
> +static void set_next_task_stop(struct rq *rq, struct task_struct *stop, enum snt_e type)
> {
> +	if (type == SNT_REPICK)
> +		return;
> +
> 	stop->se.exec_start = rq_clock_task(rq);
> }
>
>
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Vincent Guittot 2 weeks, 3 days ago
On Fri, 11 Sept 2026 at 13:21, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Aug 13, 2026 at 02:23:48PM -0700, Shubhang Kaushik (Ampere) wrote:
> > Fair hrtick is implemented with a one-shot timer, so each precise
> > preemption point has to be programmed explicitly. The usual fair path
> > does this from set_next_task_fair(), which calls hrtick_start_fair().
> >
> > The missed path is:
> >
> >   hrtick
> >     -> task_tick_fair(..., queued=1)
> >        -> entity_tick()
> >         -> resched_curr()
> >     -> schedule()
> >        -> pick_task_fair() picks current again
> >        -> put_prev_set_next_task()
> >         -> next == prev
> >         -> return
> >
> > Since set_next_task_fair() is skipped, hrtick_start_fair() is not called
> > and no new fair hrtick is started.
>
> Indeed. However, you missed this is also true for DL.
>
> Does something like the below work for you?
>
> ---
>  kernel/sched/core.c      |  2 +-
>  kernel/sched/deadline.c  |  8 ++++++--
>  kernel/sched/ext/ext.c   |  5 ++++-
>  kernel/sched/fair.c      | 15 ++++++++++-----
>  kernel/sched/idle.c      |  5 ++++-
>  kernel/sched/rt.c        |  7 +++++--
>  kernel/sched/sched.h     | 16 ++++++++++++----
>  kernel/sched/stop_task.c |  5 ++++-
>  8 files changed, 46 insertions(+), 17 deletions(-)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 91f059a55695..a39d12d38070 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -7241,7 +7241,7 @@ static void __sched notrace __schedule(int sched_mode)
>                          * on_cpu.
>                          */
>                         donor->sched_class->put_prev_task(rq, donor, donor);
> -                       donor->sched_class->set_next_task(rq, donor, true);
> +                       donor->sched_class->set_next_task(rq, donor, SNT_PICK);
>                 }
>         } else {
>                 rq_set_donor(rq, next);
> diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
> index de6a361a87c7..21d904d92c64 100644
> --- a/kernel/sched/deadline.c
> +++ b/kernel/sched/deadline.c
> @@ -2773,11 +2773,14 @@ static void start_hrtick_dl(struct rq *rq, struct sched_dl_entity *dl_se)
>   * DL keeps current in tree, because ->deadline is not typically changed while
>   * a task is runnable.
>   */
> -static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_dl(struct rq *rq, struct task_struct *p, enum snt_e type)
>  {
>         struct sched_dl_entity *dl_se = &p->dl;
>         struct dl_rq *dl_rq = &rq->dl;
>
> +       if (type == SNT_REPICK)
> +               goto repick;
> +
>         p->se.exec_start = rq_clock_task(rq);
>         if (on_dl_rq(&p->dl))
>                 update_stats_wait_end_dl(dl_rq, dl_se);
> @@ -2788,7 +2791,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
>         WARN_ON_ONCE(dl_rq->curr);
>         dl_rq->curr = dl_se;
>
> -       if (!first)
> +       if (type != SNT_PICK)
>                 return;
>
>         if (rq->donor->sched_class != &dl_sched_class)
> @@ -2796,6 +2799,7 @@ static void set_next_task_dl(struct rq *rq, struct task_struct *p, bool first)
>
>         deadline_queue_push_tasks(rq);
>
> +repick:
>         if (hrtick_enabled_dl(rq))
>                 start_hrtick_dl(rq, &p->dl);
>  }
> diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
> index 51de1d8b72a1..31a300f2d3b2 100644
> --- a/kernel/sched/ext/ext.c
> +++ b/kernel/sched/ext/ext.c
> @@ -3003,10 +3003,13 @@ static enum scx_dsp_verdict dispatch_one(struct rq *rq, struct task_struct *prev
>         return verdict;
>  }
>
> -static void set_next_task_scx(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_scx(struct rq *rq, struct task_struct *p, enum snt_e type)
>  {
>         struct scx_sched *sch = scx_task_sched(p);
>
> +       if (type == SNT_REPICK)
> +               return;
> +
>         if (p->scx.flags & SCX_TASK_QUEUED) {
>                 /*
>                  * Core-sched might decide to execute @p before it is
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 4d0b94465d19..f469e469b402 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15242,14 +15242,18 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
>         }
>  }
>
> -static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
> +static void set_next_task_fair(struct rq *rq, struct task_struct *p, enum snt_e type)
>  {
>         struct sched_entity *se = &p->se;
> -       bool throttled = false;
>         struct cfs_rq *cfs_rq = &rq->cfs;
>         unsigned long weight = NICE_0_LOAD;
> +       bool first = type == SNT_PICK;
> +       bool throttled = false;
>         bool on_rq = se->on_rq;
>
> +       if (type == SNT_REPICK)
> +               goto repick;
> +
>         clear_buddies(cfs_rq, se);
>
>         if (on_rq)
> @@ -15293,11 +15297,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
>
>         WARN_ON_ONCE(se->sched_delayed);
>
> -       if (hrtick_enabled_fair(rq))
> -               hrtick_start_fair(rq, p);
> -
>         update_misfit_status(p, rq);
>         sched_fair_update_stop_tick(rq, p);
> +
> +repick:
> +       if (hrtick_enabled_fair(rq))
> +               hrtick_start_fair(rq, p);

While at it, you might want to replace:
vdelta = se->deadline - se->vruntime;
by
vdelta = se->vprot - se->vruntime;
in hrtick_start_fair()

>  }
>
>  void init_cfs_rq(struct cfs_rq *cfs_rq)
> diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
> index eb73b65ce6c4..76f3c84ca684 100644
> --- a/kernel/sched/idle.c
> +++ b/kernel/sched/idle.c
> @@ -487,8 +487,11 @@ static void put_prev_task_idle(struct rq *rq, struct task_struct *prev, struct t
>         update_rq_avg_idle(rq);
>  }
>
> -static void set_next_task_idle(struct rq *rq, struct task_struct *next, bool first)
> +static void set_next_task_idle(struct rq *rq, struct task_struct *next, enum snt_e type)
>  {
> +       if (type == SNT_REPICK)
> +               return;
> +
>         update_idle_core(rq);
>         scx_update_idle(rq, true, true);
>         schedstat_inc(rq->sched_goidle);
> diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
> index 85303add726d..1535046a23ff 100644
> --- a/kernel/sched/rt.c
> +++ b/kernel/sched/rt.c
> @@ -1654,11 +1654,14 @@ static void wakeup_preempt_rt(struct rq *rq, struct task_struct *p, int flags)
>                 check_preempt_equal_prio(rq, p);
>  }
>
> -static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool first)
> +static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, enum snt_e type)
>  {
>         struct sched_rt_entity *rt_se = &p->rt;
>         struct rt_rq *rt_rq = &rq->rt;
>
> +       if (type == SNT_REPICK)
> +               return;
> +
>         p->se.exec_start = rq_clock_task(rq);
>         if (on_rt_rq(&p->rt))
>                 update_stats_wait_end_rt(rt_rq, rt_se);
> @@ -1666,7 +1669,7 @@ static inline void set_next_task_rt(struct rq *rq, struct task_struct *p, bool f
>         /* The running task is never eligible for pushing */
>         dequeue_pushable_task(rq, p);
>
> -       if (!first)
> +       if (type != SNT_PICK)
>                 return;
>
>         /*
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index 6c3ad70e58b8..944366e2d142 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -2630,6 +2630,12 @@ struct affinity_context {
>
>  extern s64 update_curr_common(struct rq *rq);
>
> +enum snt_e {
> +       SNT_NORMAL,
> +       SNT_PICK,
> +       SNT_REPICK,
> +};
> +
>  struct sched_class {
>
>  #ifdef CONFIG_UCLAMP_TASK
> @@ -2687,7 +2693,7 @@ struct sched_class {
>          * __schedule: rq->lock
>          */
>         void (*put_prev_task)(struct rq *rq, struct task_struct *p, struct task_struct *next);
> -       void (*set_next_task)(struct rq *rq, struct task_struct *p, bool first);
> +       void (*set_next_task)(struct rq *rq, struct task_struct *p, enum snt_e type);
>
>         /*
>          * select_task_rq: p->pi_lock
> @@ -2790,7 +2796,7 @@ static inline void put_prev_task(struct rq *rq, struct task_struct *prev)
>
>  static inline void set_next_task(struct rq *rq, struct task_struct *next)
>  {
> -       next->sched_class->set_next_task(rq, next, false);
> +       next->sched_class->set_next_task(rq, next, SNT_NORMAL);
>  }
>
>  static inline void
> @@ -2811,11 +2817,13 @@ static inline void put_prev_set_next_task(struct rq *rq,
>
>         __put_prev_set_next_dl_server(rq, prev, next);
>
> -       if (next == prev)
> +       if (next == prev) {
> +               next->sched_class->set_next_task(rq, next, SNT_REPICK);
>                 return;
> +       }
>
>         prev->sched_class->put_prev_task(rq, prev, next);
> -       next->sched_class->set_next_task(rq, next, true);
> +       next->sched_class->set_next_task(rq, next, SNT_PICK);
>  }
>
>  /*
> diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
> index c909ca0d8c87..1e0109ec36b3 100644
> --- a/kernel/sched/stop_task.c
> +++ b/kernel/sched/stop_task.c
> @@ -27,8 +27,11 @@ wakeup_preempt_stop(struct rq *rq, struct task_struct *p, int flags)
>         /* we're never preempted */
>  }
>
> -static void set_next_task_stop(struct rq *rq, struct task_struct *stop, bool first)
> +static void set_next_task_stop(struct rq *rq, struct task_struct *stop, enum snt_e type)
>  {
> +       if (type == SNT_REPICK)
> +               return;
> +
>         stop->se.exec_start = rq_clock_task(rq);
>  }
>
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Peter Zijlstra 2 weeks, 3 days ago
On Fri, Sep 11, 2026 at 02:36:41PM +0200, Vincent Guittot wrote:

> > @@ -15293,11 +15297,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
> >
> >         WARN_ON_ONCE(se->sched_delayed);
> >
> > -       if (hrtick_enabled_fair(rq))
> > -               hrtick_start_fair(rq, p);
> > -
> >         update_misfit_status(p, rq);
> >         sched_fair_update_stop_tick(rq, p);
> > +
> > +repick:
> > +       if (hrtick_enabled_fair(rq))
> > +               hrtick_start_fair(rq, p);
> 
> While at it, you might want to replace:
> vdelta = se->deadline - se->vruntime;
> by
> vdelta = se->vprot - se->vruntime;
> in hrtick_start_fair()

That should be a separate patch.

> 
> >  }
> >
> >  void init_cfs_rq(struct cfs_rq *cfs_rq)

But that made me thing; do we want something like so folded in here?

Since we got picked again, we should set vprot again, no?

---
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15279,11 +15279,8 @@ static void set_next_task_fair(struct rq
 	se = &p->se;
 	cfs_rq->curr = se;
 
-	if (on_rq) {
+	if (on_rq)
 		reweight_eevdf(cfs_rq, se, weight, se->on_rq);
-		if (first)
-			set_protect_slice(cfs_rq, se);
-	}
 
 	if (task_on_rq_queued(p)) {
 		/*
@@ -15301,6 +15298,9 @@ static void set_next_task_fair(struct rq
 	sched_fair_update_stop_tick(rq, p);
 
 repick:
+	if (on_rq)
+		set_protect_slice(cfs_rq, se);
+
 	if (hrtick_enabled_fair(rq))
 		hrtick_start_fair(rq, p);
 }
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Vincent Guittot 2 weeks, 3 days ago
On Fri, 11 Sept 2026 at 15:59, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Fri, Sep 11, 2026 at 02:36:41PM +0200, Vincent Guittot wrote:
>
> > > @@ -15293,11 +15297,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
> > >
> > >         WARN_ON_ONCE(se->sched_delayed);
> > >
> > > -       if (hrtick_enabled_fair(rq))
> > > -               hrtick_start_fair(rq, p);
> > > -
> > >         update_misfit_status(p, rq);
> > >         sched_fair_update_stop_tick(rq, p);
> > > +
> > > +repick:
> > > +       if (hrtick_enabled_fair(rq))
> > > +               hrtick_start_fair(rq, p);
> >
> > While at it, you might want to replace:
> > vdelta = se->deadline - se->vruntime;
> > by
> > vdelta = se->vprot - se->vruntime;
> > in hrtick_start_fair()
>
> That should be a separate patch.

ok

>
> >
> > >  }
> > >
> > >  void init_cfs_rq(struct cfs_rq *cfs_rq)
>
> But that made me thing; do we want something like so folded in here?
>
> Since we got picked again, we should set vprot again, no?

No, because we want to allow picking another task as soon as the
current task has run at least its "min" slice and a new eligible task
is enqueued instead of waiting the end of the next period of slice

This also means that we can't simply replace deadline by vprot as i
proposed above because once we are after vprot we need deadline

>
> ---
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15279,11 +15279,8 @@ static void set_next_task_fair(struct rq
>         se = &p->se;
>         cfs_rq->curr = se;
>
> -       if (on_rq) {
> +       if (on_rq)
>                 reweight_eevdf(cfs_rq, se, weight, se->on_rq);
> -               if (first)
> -                       set_protect_slice(cfs_rq, se);
> -       }
>
>         if (task_on_rq_queued(p)) {
>                 /*
> @@ -15301,6 +15298,9 @@ static void set_next_task_fair(struct rq
>         sched_fair_update_stop_tick(rq, p);
>
>  repick:
> +       if (on_rq)
> +               set_protect_slice(cfs_rq, se);
> +
>         if (hrtick_enabled_fair(rq))
>                 hrtick_start_fair(rq, p);
>  }
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Zhan Xusheng 1 month ago
From: Zhan Xusheng <zhanxusheng@xiaomi.com>

On Thu, 13 Aug 2026 14:23:48 -0700, Shubhang Kaushik (Ampere) wrote:
> +		rq->hrtick_rearm_fair = hrtick_enabled_fair(rq) &&
> +				rq->cfs.h_nr_runnable > 1 &&
> +				rq->cfs.h_nr_runnable == rq->cfs.h_nr_queued;

The last term switches the fix off whenever anything on the rq sits in
delayed dequeue.  set_delayed() decrements h_nr_runnable and leaves
h_nr_queued alone (kernel/sched/fair.c:6398), clear_delayed() puts it
back (6418), so the two differ exactly while a delay-dequeued entity is
present.  With DELAY_DEQUEUE that is routine, and it says nothing about
whether the running task still needs its slice bounded.

Your test cannot show that either way: two CPU-bound tasks pinned to one
CPU never sleep, so nothing is ever delay-dequeued there and the term is
true for the whole run.  Adding a third task that sleeps in a loop should
bring the missed hrtick back while the term is false.

If the intent is only to skip rqs whose other queued entities are not
competing for the CPU, h_nr_runnable > 1 already says that by itself.

> +static inline void hrtick_rearm_fair(struct rq *rq, struct task_struct *p)
> +{
> +	if (rq->hrtick_rearm_fair)
> +		__hrtick_rearm_fair(rq, p);
> +}

What does the rq field buy?  __hrtick_rearm_fair() already tests
hrtick_enabled_fair(), hrtick_active() and the class, and a same-task
repick that finds no hrtick armed wants one regardless of what triggered
the repick.  If there is a same-task repick that must not arm one, the
changelog is the place to name it.

Last one is only a question.  entity_tick() -> update_curr() ->
update_deadline() has already pushed se->deadline by a slice before
task_tick_fair() reaches the queued branch, so hrtick_start_fair() would
compute a valid delay if called right there, with no new field and no
change to put_prev_set_next_task().  The difference I can see is that the
tick callback runs with rq->hrtick_sched == 0, so hrtick_start() would
program the hrtimer immediately from inside its own callback instead of
leaving it to hrtick_schedule_exit().  Is that what moved you to the pick
side?

Thanks,
Zhan Xusheng
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Shubhang 2 weeks, 4 days ago
Hi Zhan,

Thanks for the review.

My original intent behind h_nr_runnable == h_nr_queued was to avoid
rearming an hrtick merely because delayed dequeued entities remain
queued. However, that condition also suppresses the rearm when there is
a real competing runnable entity plus an unrelated delayed entity. Which 
makes it be restrictive. I will change this to h_nr_runnable > 1 and add
a mixed workload test that exercises delayed dequeue.

Ack, I will also rework the rq flag. The reason I put the rearm on the
same task repick path was to retain the normal schedule side deferred
hrtick programming, rather than start a timer directly from the hrtick
callback. But the extra state does not appear necessary: the same task
path can check whether fair hrtick is enabled, inactive, and has another
runnable fair entity, then rearm directly.

Thanks,
Shubhang Kaushik

On Wed, 26 Aug 2026, Zhan Xusheng wrote:

> From: Zhan Xusheng <zhanxusheng@xiaomi.com>
>
> On Thu, 13 Aug 2026 14:23:48 -0700, Shubhang Kaushik (Ampere) wrote:
>> +		rq->hrtick_rearm_fair = hrtick_enabled_fair(rq) &&
>> +				rq->cfs.h_nr_runnable > 1 &&
>> +				rq->cfs.h_nr_runnable == rq->cfs.h_nr_queued;
>
> The last term switches the fix off whenever anything on the rq sits in
> delayed dequeue.  set_delayed() decrements h_nr_runnable and leaves
> h_nr_queued alone (kernel/sched/fair.c:6398), clear_delayed() puts it
> back (6418), so the two differ exactly while a delay-dequeued entity is
> present.  With DELAY_DEQUEUE that is routine, and it says nothing about
> whether the running task still needs its slice bounded.
>
> Your test cannot show that either way: two CPU-bound tasks pinned to one
> CPU never sleep, so nothing is ever delay-dequeued there and the term is
> true for the whole run.  Adding a third task that sleeps in a loop should
> bring the missed hrtick back while the term is false.
>
> If the intent is only to skip rqs whose other queued entities are not
> competing for the CPU, h_nr_runnable > 1 already says that by itself.
>
>> +static inline void hrtick_rearm_fair(struct rq *rq, struct task_struct *p)
>> +{
>> +	if (rq->hrtick_rearm_fair)
>> +		__hrtick_rearm_fair(rq, p);
>> +}
>
> What does the rq field buy?  __hrtick_rearm_fair() already tests
> hrtick_enabled_fair(), hrtick_active() and the class, and a same-task
> repick that finds no hrtick armed wants one regardless of what triggered
> the repick.  If there is a same-task repick that must not arm one, the
> changelog is the place to name it.
>
> Last one is only a question.  entity_tick() -> update_curr() ->
> update_deadline() has already pushed se->deadline by a slice before
> task_tick_fair() reaches the queued branch, so hrtick_start_fair() would
> compute a valid delay if called right there, with no new field and no
> change to put_prev_set_next_task().  The difference I can see is that the
> tick callback runs with rq->hrtick_sched == 0, so hrtick_start() would
> program the hrtimer immediately from inside its own callback instead of
> leaving it to hrtick_schedule_exit().  Is that what moved you to the pick
> side?
>
> Thanks,
> Zhan Xusheng
>
Re: [PATCH] sched/fair: Restart hrtick after same-task repicks
Posted by Shubhang 1 month ago
Hello everyone,

A gentle ping for this patch, sent on August 13.

A fair hrtick is one-shot. When it expires and schedule() selects the
current fair task again, the next == prev path skips
set_next_task_fair(), which normally starts the next fair hrtick.
Consequently, no new fair hrtick is armed after that same-task repick.

The patch starts a new one-shot fair hrtick only in this next == prev
path, and only when all queued fair entities are runnable
(h_nr_runnable == h_nr_queued).

Could you please comment on whether this is the appropriate point and
condition for starting the next fair hrtick?

Thanks,
Shubhang Kaushik