sched_ext: Add the SCX_CAP_ENQ cap

Add SCX_CAP_ENQ, which gates inserting tasks onto a cid's local DSQ. Unlike
IMMED enqueue, plain enqueues can pile up, so ENQ is the stronger cap and
implies ENQ_IMMED. Losing ENQ also triggers the reenq scan. The scan tests
each queued task and the running task against the cap each needs via
scx_caps_for_task(), so an ENQ-only loss reenqueues plain tasks, evicting a
running one, while IMMED tasks, which need only ENQ_IMMED, stay put.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reviewed-by: Andrea Righi <arighi@nvidia.com>
This commit is contained in:
Tejun Heo 2026-07-13 22:18:43 -10:00
parent 46a85ae6fe
commit 701b7bcad8
3 changed files with 22 additions and 5 deletions

View File

@ -4963,6 +4963,7 @@ SCX_ATTR(events);
#ifdef CONFIG_EXT_SUB_SCHED
static const char *scx_cap_names[__SCX_NR_CAPS] = {
[__SCX_CAP_ENQ_IMMED] = "enq_immed",
[__SCX_CAP_ENQ] = "enq",
};
static ssize_t scx_attr_caps_show(struct kobject *kobj,

View File

@ -1224,8 +1224,8 @@ struct scx_sched_pcpu {
/*
* pshard->caps[cap_bit] is the set of cids the sched holds that one
* cap on. ecaps is its transpose: the set of SCX_CAP_* bits the sched
* holds on this cpu, collected so that the hot-path check is a single
* read.
* effectively holds on this cpu, with implied caps folded in, so that
* the hot-path check is a single read.
*
* While pshard->caps[] under pshard->lock is the target configuration,
* ecaps is the effective copy owned by the cpu. It is written under the
@ -1287,20 +1287,28 @@ struct scx_sched_pnode {
* the allocation pattern.
*
* ENQ_IMMED insert an IMMED task onto the cid's local DSQ
*
* ENQ insert any task onto the cid's local DSQ (implies ENQ_IMMED)
*
* 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.
*/
enum scx_cap_flags {
__SCX_CAP_ENQ_IMMED = 0,
__SCX_CAP_ENQ = 1,
__SCX_NR_CAPS,
__SCX_CAP_ALL = BIT_U64(__SCX_NR_CAPS) - 1,
SCX_CAP_ENQ_IMMED = BIT_U64(__SCX_CAP_ENQ_IMMED),
SCX_CAP_ENQ = BIT_U64(__SCX_CAP_ENQ),
/* alias for minimal cap to make any use of a cpu */
SCX_CAP_BASE = SCX_CAP_ENQ_IMMED,
/* caps whose loss strands queued tasks, see scx_process_sync_ecaps() */
SCX_CAPS_REENQ_ON_LOSS = SCX_CAP_ENQ_IMMED,
SCX_CAPS_REENQ_ON_LOSS = SCX_CAP_ENQ_IMMED | SCX_CAP_ENQ,
};
#ifdef CONFIG_EXT_SUB_SCHED

View File

@ -115,18 +115,26 @@ static inline u64 scx_caps_for_enq(u64 enq_flags)
/* a restored task must be put into the local DSQ regardless of caps */
if (enq_flags & SCX_ENQ_IGNORE_CAPS)
return 0;
return SCX_CAP_ENQ_IMMED;
if (enq_flags & SCX_ENQ_IMMED)
return SCX_CAP_ENQ_IMMED;
return SCX_CAP_ENQ;
}
/* map queued @p to the SCX_CAP_* bit required to stay on its local DSQ */
static inline u64 scx_caps_for_task(struct task_struct *p)
{
return SCX_CAP_ENQ_IMMED;
if (p->scx.flags & SCX_TASK_IMMED)
return SCX_CAP_ENQ_IMMED;
return SCX_CAP_ENQ;
}
/* caps implied by holding @cap */
static inline u64 scx_caps_implied(u64 cap)
{
switch (cap) {
case SCX_CAP_ENQ:
return SCX_CAP_ENQ_IMMED;
}
return 0;
}