Skip to content

Commit f666241

Browse files
author
Peter Zijlstra
committed
sched/fair: Unify cfs_rq throttling via account_cfs_rq_runtime()
assign_cfs_rq_runtime() during update_curr() sets the resched indicator and relies on check_cfs_rq_runtime() during pick_next_task() / put_prev_entity() to throttle the hierarchy once current task is preempted / blocks. Per-task throttle, on the other hand, uses throttle_cfs_rq() to simply propagate the throttle signals, and then relies on task work to individually throttle the runnable tasks on their way out to the userspace. Remove check_cfs_rq_runtime() and unify throttling into account_cfs_rq_runtime() which only sets the cfs_rq->throttled, cfs_rq->throttle_count indicators via throttle_cfs_rq() and optionally adds the task work to the current task (donor) it is on the throttled hierarchy. throttle_cfs_rq() requests for sched_cfs_bandwidth_slice() worth of bandwidth for the current hierarchy that enable it to continue running uninterrupted when selected. For the rest, it requests a bare minimum of "1" to ensure some bandwidth is available and pass the "runtime_remaining > 0" checks once selected. For SCHED_PROXY_EXEC, a mutex holder cannot exit to userspace without dropping it first and the mutex_unlock() ensures proxy is stopped before the mutex handoff which preserves the current semantics for running a throttled task until it exits to the userspace even if it acts as a donor. [ prateek: rebased on tip, comments, commit message. ] Reviewed-By: Benjamin Segall <bsegall@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Tested-by: Aaron Lu <ziqianlu@bytedance.com> Link: https://patch.msgid.link/20260602071005.11942-1-kprateek.nayak@amd.com
1 parent 102a283 commit f666241

1 file changed

Lines changed: 46 additions & 55 deletions

File tree

kernel/sched/fair.c

Lines changed: 46 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ static int se_is_idle(struct sched_entity *se)
525525
#endif /* !CONFIG_FAIR_GROUP_SCHED */
526526

527527
static __always_inline
528-
void account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec);
528+
bool account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec);
529529

530530
/**************************************************************
531531
* Scheduling class tree data structure manipulation methods:
@@ -6388,8 +6388,6 @@ pick_next_entity(struct rq *rq, struct cfs_rq *cfs_rq, bool protect)
63886388
return se;
63896389
}
63906390

6391-
static bool check_cfs_rq_runtime(struct cfs_rq *cfs_rq);
6392-
63936391
static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
63946392
{
63956393
/*
@@ -6399,9 +6397,6 @@ static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev)
63996397
if (prev->on_rq)
64006398
update_curr(cfs_rq);
64016399

6402-
/* throttle cfs_rqs exceeding runtime */
6403-
check_cfs_rq_runtime(cfs_rq);
6404-
64056400
if (prev->on_rq) {
64066401
update_stats_wait_start_fair(cfs_rq, prev);
64076402
/* Put 'current' back into the tree. */
@@ -6536,41 +6531,32 @@ static int __assign_cfs_rq_runtime(struct cfs_bandwidth *cfs_b,
65366531
return cfs_rq->runtime_remaining > 0;
65376532
}
65386533

6539-
/* returns 0 on failure to allocate runtime */
6540-
static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
6541-
{
6542-
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
6543-
6544-
guard(raw_spinlock)(&cfs_b->lock);
6534+
static bool throttle_cfs_rq(struct cfs_rq *cfs_rq);
65456535

6546-
return __assign_cfs_rq_runtime(cfs_b, cfs_rq, sched_cfs_bandwidth_slice());
6547-
}
6548-
6549-
static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
6536+
static bool __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
65506537
{
65516538
/* dock delta_exec before expiring quota (as it could span periods) */
65526539
cfs_rq->runtime_remaining -= delta_exec;
65536540

65546541
if (likely(cfs_rq->runtime_remaining > 0))
6555-
return;
6542+
return false;
65566543

65576544
if (cfs_rq->throttled)
6558-
return;
6545+
return true;
65596546
/*
6560-
* if we're unable to extend our runtime we resched so that the active
6561-
* hierarchy can be throttled
6547+
* throttle_cfs_rq() will try to extend the runtime first
6548+
* before throttling the hierarchy.
65626549
*/
6563-
if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr))
6564-
resched_curr(rq_of(cfs_rq));
6550+
return throttle_cfs_rq(cfs_rq);
65656551
}
65666552

65676553
static __always_inline
6568-
void account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
6554+
bool account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
65696555
{
65706556
if (!cfs_bandwidth_used() || !cfs_rq->runtime_enabled)
6571-
return;
6557+
return false;
65726558

6573-
__account_cfs_rq_runtime(cfs_rq, delta_exec);
6559+
return __account_cfs_rq_runtime(cfs_rq, delta_exec);
65746560
}
65756561

65766562
static inline int cfs_rq_throttled(struct cfs_rq *cfs_rq)
@@ -6858,10 +6844,24 @@ static int tg_throttle_down(struct task_group *tg, void *data)
68586844

68596845
static bool throttle_cfs_rq(struct cfs_rq *cfs_rq)
68606846
{
6861-
struct rq *rq = rq_of(cfs_rq);
68626847
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
6848+
struct sched_entity *curr = cfs_rq->curr;
6849+
struct rq *rq = rq_of(cfs_rq);
68636850

68646851
scoped_guard(raw_spinlock, &cfs_b->lock) {
6852+
u64 target_runtime = 1;
6853+
6854+
/*
6855+
* If cfs_rq->curr is still runnable, we are here from an
6856+
* update_curr(). Request sysctl_sched_cfs_bandwidth_slice
6857+
* worth of bandwidth to continue running.
6858+
*
6859+
* If the curr is not runnable, just request enough bandwidth
6860+
* to be runnable next time the pick selects this cfs_rq.
6861+
*/
6862+
if (curr && curr->on_rq)
6863+
target_runtime = sched_cfs_bandwidth_slice();
6864+
68656865
/*
68666866
* Check if We have raced with bandwidth becoming available. If
68676867
* we actually throttled the timer might not unthrottle us for
@@ -6872,7 +6872,7 @@ static bool throttle_cfs_rq(struct cfs_rq *cfs_rq)
68726872
*
68736873
* This will start the period timer if necessary.
68746874
*/
6875-
if (__assign_cfs_rq_runtime(cfs_b, cfs_rq, 1))
6875+
if (__assign_cfs_rq_runtime(cfs_b, cfs_rq, target_runtime))
68766876
return false;
68776877

68786878
/*
@@ -6893,6 +6893,17 @@ static bool throttle_cfs_rq(struct cfs_rq *cfs_rq)
68936893
*/
68946894
cfs_rq->throttled = 1;
68956895
WARN_ON_ONCE(cfs_rq->throttled_clock);
6896+
6897+
/*
6898+
* If current hierarchy was throttled, add throttle work to the
6899+
* current donor. In case of proxy-execution, the execution
6900+
* context cannot exit to the userspace while holding a mutex
6901+
* and the rule of throttle deferral to only throttle the
6902+
* throttled context at exit to userspace is still preserved.
6903+
*/
6904+
if (curr && curr->on_rq)
6905+
task_throttle_setup_work(rq->donor);
6906+
68966907
return true;
68976908
}
68986909

@@ -7283,7 +7294,7 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq)
72837294
if (!cfs_bandwidth_used())
72847295
return;
72857296

7286-
/* an active group must be handled by the update_curr()->put() path */
7297+
/* an active group must be handled by the update_curr() path */
72877298
if (!cfs_rq->runtime_enabled || cfs_rq->curr)
72887299
return;
72897300

@@ -7293,8 +7304,6 @@ static void check_enqueue_throttle(struct cfs_rq *cfs_rq)
72937304

72947305
/* update runtime allocation */
72957306
account_cfs_rq_runtime(cfs_rq, 0);
7296-
if (cfs_rq->runtime_remaining <= 0)
7297-
throttle_cfs_rq(cfs_rq);
72987307
}
72997308

73007309
static void sync_throttle(struct task_group *tg, int cpu)
@@ -7324,25 +7333,6 @@ static void sync_throttle(struct task_group *tg, int cpu)
73247333
cfs_rq->pelt_clock_throttled = 1;
73257334
}
73267335

7327-
/* conditionally throttle active cfs_rq's from put_prev_entity() */
7328-
static bool check_cfs_rq_runtime(struct cfs_rq *cfs_rq)
7329-
{
7330-
if (!cfs_bandwidth_used())
7331-
return false;
7332-
7333-
if (likely(!cfs_rq->runtime_enabled || cfs_rq->runtime_remaining > 0))
7334-
return false;
7335-
7336-
/*
7337-
* it's possible for a throttled entity to be forced into a running
7338-
* state (e.g. set_curr_task), in this case we're finished.
7339-
*/
7340-
if (cfs_rq_throttled(cfs_rq))
7341-
return true;
7342-
7343-
return throttle_cfs_rq(cfs_rq);
7344-
}
7345-
73467336
static enum hrtimer_restart sched_cfs_slack_timer(struct hrtimer *timer)
73477337
{
73487338
struct cfs_bandwidth *cfs_b =
@@ -7596,8 +7586,7 @@ static void sched_fair_update_stop_tick(struct rq *rq, struct task_struct *p)
75967586

75977587
#else /* !CONFIG_CFS_BANDWIDTH: */
75987588

7599-
static void account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) {}
7600-
static bool check_cfs_rq_runtime(struct cfs_rq *cfs_rq) { return false; }
7589+
static bool account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) { return false; }
76017590
static void check_enqueue_throttle(struct cfs_rq *cfs_rq) {}
76027591
static inline void sync_throttle(struct task_group *tg, int cpu) {}
76037592
static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq) {}
@@ -9934,8 +9923,6 @@ struct task_struct *pick_task_fair(struct rq *rq, struct rq_flags *rf)
99349923
if (cfs_rq->curr && cfs_rq->curr->on_rq)
99359924
update_curr(cfs_rq);
99369925

9937-
throttled |= check_cfs_rq_runtime(cfs_rq);
9938-
99399926
se = pick_next_entity(rq, cfs_rq, true);
99409927
if (!se)
99419928
goto again;
@@ -14853,8 +14840,8 @@ static inline void task_tick_core(struct rq *rq, struct task_struct *curr) {}
1485314840
*/
1485414841
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
1485514842
{
14856-
struct cfs_rq *cfs_rq;
1485714843
struct sched_entity *se = &curr->se;
14844+
struct cfs_rq *cfs_rq;
1485814845

1485914846
for_each_sched_entity(se) {
1486014847
cfs_rq = cfs_rq_of(se);
@@ -15036,6 +15023,7 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
1503615023
static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
1503715024
{
1503815025
struct sched_entity *se = &p->se;
15026+
bool throttled = false;
1503915027

1504015028
for_each_sched_entity(se) {
1504115029
struct cfs_rq *cfs_rq = cfs_rq_of(se);
@@ -15046,9 +15034,12 @@ static void set_next_task_fair(struct rq *rq, struct task_struct *p, bool first)
1504615034

1504715035
set_next_entity(cfs_rq, se, first);
1504815036
/* ensure bandwidth has been allocated on our new cfs_rq */
15049-
account_cfs_rq_runtime(cfs_rq, 0);
15037+
throttled |= account_cfs_rq_runtime(cfs_rq, 0);
1505015038
}
1505115039

15040+
if (throttled)
15041+
task_throttle_setup_work(p);
15042+
1505215043
se = &p->se;
1505315044

1505415045
if (task_on_rq_queued(p)) {

0 commit comments

Comments
 (0)