From 998970b614991fee35f6777fab073966a8e549ba Mon Sep 17 00:00:00 2001 From: David Vernet Date: Tue, 4 Jun 2024 21:35:21 -0500 Subject: [PATCH] scx: Call directly into ext.c for hotplug events The rq_online() and rq_offline() sched_class callbacks may be invoked on either the domain creation path, or on the hotplug path. ext.c would previously only invoke its hotplug logic when those callbacks are invoked on the hotplug path. This turned out to be a bit brittle, as hotplug could break for sched_ext if we first invoked rq_online() from the domain path, as it would cause us to skip invoking it on the hotplug path due to hotplug implementation details. To avoid this frailty (and fix a hotplug regression for us after merging v6.10), let's instead just call directly into ext.c on the hotplug path. This could be made into a sched_class callback if that's what schedulers want, but that's kind of what rq_online() and rq_offline() are supposed to be, so it's a bit awkward as is. It would have been ideal if we could have just used a hotplug event notifier, but as far as I know that doesn't exist. Signed-off-by: David Vernet --- kernel/sched/core.c | 20 ++++++++++++++++++++ kernel/sched/ext.c | 7 ++----- kernel/sched/ext.h | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 4a08e1c6c56db..a18430adf3f08 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -9790,6 +9790,21 @@ int sched_cpu_activate(unsigned int cpu) if (rq->rd) { BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); set_rq_online(rq); + /* + * Unfortunately, we can't use the rq_online() and rq_offline() + * callbacks to pass hotplug events down to ext.c because those + * callbacks may be invoked on the domain rebuild path first, + * thus causing us to skip invoking rq_online() in the + * set_rq_online() call above. While we could just treat all + * rq_online() / rq_offline() calls as hotplug events, that + * would cause a lot of noise in BPF schedulers who would + * receive several callbacks for every hotplug event as the + * domains are torn down and recreated. Instead, just call + * directly into the scheduler on the hotplug activation and + * deactivation paths so there's no ambiguity in calling + * context. + */ + scx_rq_activate(rq); } rq_unlock_irqrestore(rq, &rf); @@ -9833,6 +9848,11 @@ int sched_cpu_deactivate(unsigned int cpu) rq_lock_irqsave(rq, &rf); if (rq->rd) { BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); + /* + * See the comment at scx_rq_activate() for why we can't simply + * use the rq_online() and rq_offline() callbacks. + */ + scx_rq_deactivate(rq); set_rq_offline(rq); } rq_unlock_irqrestore(rq, &rf); diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 08d4acba66f95..7adbac5f7dfe1 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -3218,13 +3218,13 @@ static void handle_hotplug(struct rq *rq, bool online) online ? "online" : "offline"); } -static void rq_online_scx(struct rq *rq) +void scx_rq_activate(struct rq *rq) { handle_hotplug(rq, true); rq->scx.flags |= SCX_RQ_ONLINE; } -static void rq_offline_scx(struct rq *rq) +void scx_rq_deactivate(struct rq *rq) { rq->scx.flags &= ~SCX_RQ_ONLINE; handle_hotplug(rq, false); @@ -3861,9 +3861,6 @@ DEFINE_SCHED_CLASS(ext) = { .balance = balance_scx, .select_task_rq = select_task_rq_scx, .set_cpus_allowed = set_cpus_allowed_scx, - - .rq_online = rq_online_scx, - .rq_offline = rq_offline_scx, #endif #ifdef CONFIG_SCHED_CORE diff --git a/kernel/sched/ext.h b/kernel/sched/ext.h index 97064e53f2995..52d9b7df2a253 100644 --- a/kernel/sched/ext.h +++ b/kernel/sched/ext.h @@ -45,6 +45,8 @@ int scx_check_setscheduler(struct task_struct *p, int policy); bool scx_can_stop_tick(struct rq *rq); bool task_should_scx(struct task_struct *p); void init_sched_ext_class(void); +void scx_rq_activate(struct rq *rq); +void scx_rq_deactivate(struct rq *rq); static inline u32 scx_cpuperf_target(s32 cpu) { @@ -100,6 +102,8 @@ static inline bool scx_can_stop_tick(struct rq *rq) { return true; } static inline bool task_on_scx(const struct task_struct *p) { return false; } static inline void init_sched_ext_class(void) {} static inline u32 scx_cpuperf_target(s32 cpu) { return 0; } +static inline void scx_rq_activate(struct rq *rq) {} +static inline void scx_rq_deactivate(struct rq *rq) {} #define for_each_active_class for_each_class #define for_balance_class_range for_class_range