Skip to content

Commit a730e3f

Browse files
arighihtejun
authored andcommitted
sched_ext: idle: Consolidate default idle CPU selection kfuncs
There is no reason to restrict scx_bpf_select_cpu_dfl() invocations to ops.select_cpu() while allowing scx_bpf_select_cpu_and() to be used from multiple contexts, as both provide equivalent functionality, with the latter simply accepting an additional "allowed" cpumask. Therefore, unify the two APIs, enabling both kfuncs to be used from ops.select_cpu(), ops.enqueue(), and unlocked contexts (e.g., via BPF test_run). This allows schedulers to implement a consistent idle CPU selection policy and helps reduce code duplication. Signed-off-by: Andrea Righi <arighi@nvidia.com> Signed-off-by: Tejun Heo <tj@kernel.org>
1 parent e764295 commit a730e3f

File tree

1 file changed

+71
-85
lines changed

1 file changed

+71
-85
lines changed

kernel/sched/ext_idle.c

Lines changed: 71 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,68 @@ static bool check_builtin_idle_enabled(void)
854854
return false;
855855
}
856856

857+
s32 select_cpu_from_kfunc(struct task_struct *p, s32 prev_cpu, u64 wake_flags,
858+
const struct cpumask *allowed, u64 flags)
859+
{
860+
struct rq *rq;
861+
struct rq_flags rf;
862+
s32 cpu;
863+
864+
if (!kf_cpu_valid(prev_cpu, NULL))
865+
return -EINVAL;
866+
867+
if (!check_builtin_idle_enabled())
868+
return -EBUSY;
869+
870+
/*
871+
* If called from an unlocked context, acquire the task's rq lock,
872+
* so that we can safely access p->cpus_ptr and p->nr_cpus_allowed.
873+
*
874+
* Otherwise, allow to use this kfunc only from ops.select_cpu()
875+
* and ops.select_enqueue().
876+
*/
877+
if (scx_kf_allowed_if_unlocked()) {
878+
rq = task_rq_lock(p, &rf);
879+
} else {
880+
if (!scx_kf_allowed(SCX_KF_SELECT_CPU | SCX_KF_ENQUEUE))
881+
return -EPERM;
882+
rq = scx_locked_rq();
883+
}
884+
885+
/*
886+
* Validate locking correctness to access p->cpus_ptr and
887+
* p->nr_cpus_allowed: if we're holding an rq lock, we're safe;
888+
* otherwise, assert that p->pi_lock is held.
889+
*/
890+
if (!rq)
891+
lockdep_assert_held(&p->pi_lock);
892+
893+
#ifdef CONFIG_SMP
894+
/*
895+
* This may also be called from ops.enqueue(), so we need to handle
896+
* per-CPU tasks as well. For these tasks, we can skip all idle CPU
897+
* selection optimizations and simply check whether the previously
898+
* used CPU is idle and within the allowed cpumask.
899+
*/
900+
if (p->nr_cpus_allowed == 1) {
901+
if (cpumask_test_cpu(prev_cpu, allowed ?: p->cpus_ptr) &&
902+
scx_idle_test_and_clear_cpu(prev_cpu))
903+
cpu = prev_cpu;
904+
else
905+
cpu = -EBUSY;
906+
} else {
907+
cpu = scx_select_cpu_dfl(p, prev_cpu, wake_flags,
908+
allowed ?: p->cpus_ptr, flags);
909+
}
910+
#else
911+
cpu = -EBUSY;
912+
#endif
913+
if (scx_kf_allowed_if_unlocked())
914+
task_rq_unlock(rq, p, &rf);
915+
916+
return cpu;
917+
}
918+
857919
/**
858920
* scx_bpf_cpu_node - Return the NUMA node the given @cpu belongs to, or
859921
* trigger an error if @cpu is invalid
@@ -878,38 +940,26 @@ __bpf_kfunc int scx_bpf_cpu_node(s32 cpu)
878940
* @wake_flags: %SCX_WAKE_* flags
879941
* @is_idle: out parameter indicating whether the returned CPU is idle
880942
*
881-
* Can only be called from ops.select_cpu() if the built-in CPU selection is
882-
* enabled - ops.update_idle() is missing or %SCX_OPS_KEEP_BUILTIN_IDLE is set.
883-
* @p, @prev_cpu and @wake_flags match ops.select_cpu().
943+
* Can be called from ops.select_cpu(), ops.enqueue(), or from an unlocked
944+
* context such as a BPF test_run() call, as long as built-in CPU selection
945+
* is enabled: ops.update_idle() is missing or %SCX_OPS_KEEP_BUILTIN_IDLE
946+
* is set.
884947
*
885948
* Returns the picked CPU with *@is_idle indicating whether the picked CPU is
886949
* currently idle and thus a good candidate for direct dispatching.
887950
*/
888951
__bpf_kfunc s32 scx_bpf_select_cpu_dfl(struct task_struct *p, s32 prev_cpu,
889952
u64 wake_flags, bool *is_idle)
890953
{
891-
#ifdef CONFIG_SMP
892954
s32 cpu;
893-
#endif
894-
if (!kf_cpu_valid(prev_cpu, NULL))
895-
goto prev_cpu;
896-
897-
if (!check_builtin_idle_enabled())
898-
goto prev_cpu;
899-
900-
if (!scx_kf_allowed(SCX_KF_SELECT_CPU))
901-
goto prev_cpu;
902955

903-
#ifdef CONFIG_SMP
904-
cpu = scx_select_cpu_dfl(p, prev_cpu, wake_flags, NULL, 0);
956+
cpu = select_cpu_from_kfunc(p, prev_cpu, wake_flags, NULL, 0);
905957
if (cpu >= 0) {
906958
*is_idle = true;
907959
return cpu;
908960
}
909-
#endif
910-
911-
prev_cpu:
912961
*is_idle = false;
962+
913963
return prev_cpu;
914964
}
915965

@@ -936,62 +986,7 @@ __bpf_kfunc s32 scx_bpf_select_cpu_dfl(struct task_struct *p, s32 prev_cpu,
936986
__bpf_kfunc s32 scx_bpf_select_cpu_and(struct task_struct *p, s32 prev_cpu, u64 wake_flags,
937987
const struct cpumask *cpus_allowed, u64 flags)
938988
{
939-
struct rq *rq;
940-
struct rq_flags rf;
941-
s32 cpu;
942-
943-
if (!kf_cpu_valid(prev_cpu, NULL))
944-
return -EINVAL;
945-
946-
if (!check_builtin_idle_enabled())
947-
return -EBUSY;
948-
949-
/*
950-
* If called from an unlocked context, acquire the task's rq lock,
951-
* so that we can safely access p->cpus_ptr and p->nr_cpus_allowed.
952-
*
953-
* Otherwise, allow to use this kfunc only from ops.select_cpu()
954-
* and ops.select_enqueue().
955-
*/
956-
if (scx_kf_allowed_if_unlocked()) {
957-
rq = task_rq_lock(p, &rf);
958-
} else {
959-
if (!scx_kf_allowed(SCX_KF_SELECT_CPU | SCX_KF_ENQUEUE))
960-
return -EPERM;
961-
rq = scx_locked_rq();
962-
}
963-
964-
/*
965-
* Validate locking correctness to access p->cpus_ptr and
966-
* p->nr_cpus_allowed: if we're holding an rq lock, we're safe;
967-
* otherwise, assert that p->pi_lock is held.
968-
*/
969-
if (!rq)
970-
lockdep_assert_held(&p->pi_lock);
971-
972-
#ifdef CONFIG_SMP
973-
/*
974-
* This may also be called from ops.enqueue(), so we need to handle
975-
* per-CPU tasks as well. For these tasks, we can skip all idle CPU
976-
* selection optimizations and simply check whether the previously
977-
* used CPU is idle and within the allowed cpumask.
978-
*/
979-
if (p->nr_cpus_allowed == 1) {
980-
if (cpumask_test_cpu(prev_cpu, cpus_allowed) &&
981-
scx_idle_test_and_clear_cpu(prev_cpu))
982-
cpu = prev_cpu;
983-
else
984-
cpu = -EBUSY;
985-
} else {
986-
cpu = scx_select_cpu_dfl(p, prev_cpu, wake_flags, cpus_allowed, flags);
987-
}
988-
#else
989-
cpu = -EBUSY;
990-
#endif
991-
if (scx_kf_allowed_if_unlocked())
992-
task_rq_unlock(rq, p, &rf);
993-
994-
return cpu;
989+
return select_cpu_from_kfunc(p, prev_cpu, wake_flags, cpus_allowed, flags);
995990
}
996991

997992
/**
@@ -1294,28 +1289,19 @@ BTF_ID_FLAGS(func, scx_bpf_pick_idle_cpu, KF_RCU)
12941289
BTF_ID_FLAGS(func, scx_bpf_pick_any_cpu_node, KF_RCU)
12951290
BTF_ID_FLAGS(func, scx_bpf_pick_any_cpu, KF_RCU)
12961291
BTF_ID_FLAGS(func, scx_bpf_select_cpu_and, KF_RCU)
1292+
BTF_ID_FLAGS(func, scx_bpf_select_cpu_dfl, KF_RCU)
12971293
BTF_KFUNCS_END(scx_kfunc_ids_idle)
12981294

12991295
static const struct btf_kfunc_id_set scx_kfunc_set_idle = {
13001296
.owner = THIS_MODULE,
13011297
.set = &scx_kfunc_ids_idle,
13021298
};
13031299

1304-
BTF_KFUNCS_START(scx_kfunc_ids_select_cpu)
1305-
BTF_ID_FLAGS(func, scx_bpf_select_cpu_dfl, KF_RCU)
1306-
BTF_KFUNCS_END(scx_kfunc_ids_select_cpu)
1307-
1308-
static const struct btf_kfunc_id_set scx_kfunc_set_select_cpu = {
1309-
.owner = THIS_MODULE,
1310-
.set = &scx_kfunc_ids_select_cpu,
1311-
};
1312-
13131300
int scx_idle_init(void)
13141301
{
13151302
int ret;
13161303

1317-
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_select_cpu) ||
1318-
register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_idle) ||
1304+
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &scx_kfunc_set_idle) ||
13191305
register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &scx_kfunc_set_idle) ||
13201306
register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &scx_kfunc_set_idle);
13211307

0 commit comments

Comments
 (0)