summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2026-07-24 09:09:28 -1000
committerTejun Heo <tj@kernel.org>2026-07-24 12:10:22 -1000
commit3a21e34eb2588e135190cf5c48e64397bc18d0b5 (patch)
tree7664b3a2ed5243869c37c6ffdc824e4429564d6f /kernel
parent49d6247d64aec7ba1590223d277c47ae09037d4d (diff)
sched_ext: Gate scx_bpf_cidperf_set() behind a new SCX_CAP_PERF
scx_bpf_cidperf_set() reaches cpufreq with no cap check, so any cid-form sub-sched can steer the frequency of any cid in its view, including ones it holds nothing on. Gate it behind a new SCX_CAP_PERF rather than SCX_CAP_BASE: hardware control is a separate axis from queue access - a parent may well delegate scheduling on a cid without handing over its frequency. PERF neither implies nor is implied by the other caps. The check runs under the target rq's lock, which ecaps updates are also folded under, so it is authoritative - a write can never land after a revoke has taken effect. Denials are counted in SCX_EV_SUB_CIDPERF_DENIED. The operation is synchronous and the outcome is reported to the caller: scx_bpf_cidperf_set() now returns 0 or -errno, -EACCES on denial. The cid-form interface is still under initial development, so the signature is changed in place without versioning. scx_qmap grants PERF alongside its existing cid grants so the cpuperf demo keeps working in sub-scheds. Signed-off-by: Tejun Heo <tj@kernel.org> Reviewed-by: Andrea Righi <arighi@nvidia.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sched/ext/ext.c35
-rw-r--r--kernel/sched/ext/internal.h16
2 files changed, 41 insertions, 10 deletions
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index e11c66bd23e7..aca8d2380509 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -5144,6 +5144,7 @@ static const char *scx_cap_names[__SCX_NR_CAPS] = {
[__SCX_CAP_ENQ_IMMED] = "enq_immed",
[__SCX_CAP_ENQ] = "enq",
[__SCX_CAP_PREEMPT] = "preempt",
+ [__SCX_CAP_PERF] = "perf",
};
static ssize_t scx_attr_caps_show(struct kobject *kobj,
@@ -9786,6 +9787,7 @@ static s32 scx_cpuperf_set(struct scx_sched *sch, s32 cpu, u32 perf)
{
struct rq *rq, *locked_rq;
struct rq_flags rf;
+ s32 ret;
if (unlikely(perf > SCX_CPUPERF_ONE)) {
scx_error(sch, "Invalid cpuperf target %u for CPU %d", perf, cpu);
@@ -9816,13 +9818,24 @@ static s32 scx_cpuperf_set(struct scx_sched *sch, s32 cpu, u32 perf)
update_rq_clock(rq);
}
- rq->scx.cpuperf_target = perf;
- cpufreq_update_util(rq, 0);
+ /*
+ * ecaps updates are folded under the rq lock, making this test
+ * authoritative: a write can never land after a revoke has taken
+ * effect on @cpu.
+ */
+ if (likely(!scx_missing_caps(sch, cpu, SCX_CAP_PERF))) {
+ rq->scx.cpuperf_target = perf;
+ cpufreq_update_util(rq, 0);
+ ret = 0;
+ } else {
+ __scx_add_event(sch, SCX_EV_SUB_CIDPERF_DENIED, 1);
+ ret = -EACCES;
+ }
if (!locked_rq)
rq_unlock_irqrestore(rq, &rf);
- return 0;
+ return ret;
}
/**
@@ -9859,10 +9872,13 @@ __bpf_kfunc void scx_bpf_cpuperf_set(s32 cpu, u32 perf, const struct bpf_prog_au
* @perf: target performance level [0, %SCX_CPUPERF_ONE]
* @aux: implicit BPF argument to access bpf_prog_aux hidden from BPF progs
*
- * cid-addressed equivalent of scx_bpf_cpuperf_set().
+ * cid-addressed equivalent of scx_bpf_cpuperf_set(). A sub-sched needs
+ * SCX_CAP_PERF on @cid. Returns 0 if the target was applied, -%EACCES if
+ * the write was denied for missing caps, other -errnos if @cid didn't
+ * resolve.
*/
-__bpf_kfunc void scx_bpf_cidperf_set(s32 cid, u32 perf,
- const struct bpf_prog_aux *aux)
+__bpf_kfunc s32 scx_bpf_cidperf_set(s32 cid, u32 perf,
+ const struct bpf_prog_aux *aux)
{
struct scx_sched *sch;
s32 cpu;
@@ -9871,11 +9887,12 @@ __bpf_kfunc void scx_bpf_cidperf_set(s32 cid, u32 perf,
sch = scx_prog_sched(aux);
if (unlikely(!sch))
- return;
+ return -ENODEV;
cpu = scx_cid_to_cpu(sch, cid);
if (cpu < 0)
- return;
- scx_bpf_cpuperf_set(cpu, perf, aux);
+ return cpu;
+
+ return scx_cpuperf_set(sch, cpu, perf);
}
/**
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 308d16320818..886f1d132e6b 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -1206,6 +1206,12 @@ struct scx_event_stats {
* sub-sched lacked baseline access on the target cid.
*/
s64 SCX_EV_SUB_REENQ_DENIED;
+
+ /*
+ * The number of times scx_bpf_cidperf_set() was denied because the
+ * sub-sched lacked SCX_CAP_PERF on the target cid.
+ */
+ s64 SCX_EV_SUB_CIDPERF_DENIED;
};
#define SCX_EVENTS_LIST(SCX_EVENT) \
@@ -1227,7 +1233,8 @@ struct scx_event_stats {
SCX_EVENT(SCX_EV_SUB_FORCED_ADMIT); \
SCX_EVENT(SCX_EV_SUB_PREEMPT_DENIED); \
SCX_EVENT(SCX_EV_SUB_KICK_DENIED); \
- SCX_EVENT(SCX_EV_SUB_REENQ_DENIED)
+ SCX_EVENT(SCX_EV_SUB_REENQ_DENIED); \
+ SCX_EVENT(SCX_EV_SUB_CIDPERF_DENIED)
struct scx_sched;
@@ -1353,6 +1360,11 @@ struct scx_sched_pnode {
* - SCX_ENQ_PREEMPT inserts
* - SCX_KICK_PREEMPT kicks
*
+ * PERF control the cid's cpu power/perf management state, currently the
+ * cpufreq target set through scx_bpf_cidperf_set(). Hardware
+ * control is a separate axis from queue access: PERF neither
+ * implies nor is implied by the caps above.
+ *
* Implied caps apply to the holder's own use of a cid, not to delegation.
* scx_bpf_sub_grant() delegates literally-held caps, so a cap held only through
* implication is usable but cannot be re-delegated to a child. When granting a
@@ -1363,6 +1375,7 @@ enum scx_cap_flags {
__SCX_CAP_ENQ_IMMED = 0,
__SCX_CAP_ENQ = 1,
__SCX_CAP_PREEMPT = 2,
+ __SCX_CAP_PERF = 3,
__SCX_NR_CAPS,
__SCX_CAP_ALL = BIT_U64(__SCX_NR_CAPS) - 1,
@@ -1370,6 +1383,7 @@ enum scx_cap_flags {
SCX_CAP_ENQ_IMMED = BIT_U64(__SCX_CAP_ENQ_IMMED),
SCX_CAP_ENQ = BIT_U64(__SCX_CAP_ENQ),
SCX_CAP_PREEMPT = BIT_U64(__SCX_CAP_PREEMPT),
+ SCX_CAP_PERF = BIT_U64(__SCX_CAP_PERF),
/* alias for minimal cap to make any use of a cpu */
SCX_CAP_BASE = SCX_CAP_ENQ_IMMED,