mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 12:34:02 +02:00
sched_ext: Fixes for v7.3-rc1
- The task ownership check in the dispatch queue move operation raced against the task exiting or moving to a different sub-scheduler, spuriously triggering scheduler aborts. Fix by moving the check under the queue lock. - The cgroup bandwidth change callback runs in a sleepable context but sleepable implementations were rejected at load time. Allow them and add a marker so userspace can detect the capability. - Sync tooling headers with the scx repo for accumulated compatibility improvements. - Example scheduler fixes: ignored timer re-arm failures and vtime credit loss on cgroup migration. - Documentation and comment fixes. -----BEGIN PGP SIGNATURE----- iIQEABYKACwWIQTfIjM1kS57o3GsC/uxYfJx3gVYGQUCapXcug4cdGpAa2VybmVs Lm9yZwAKCRCxYfJx3gVYGZFfAQCMdpcFMqLdaHkRMiWK+SopQ703AnnpoX9xC81t vy81mQD/QlIEZimidIS2xju3/HkHfVXX6hHxAbMy8hdclhYHlgM= =256l -----END PGP SIGNATURE----- Merge tag 'sched_ext-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext Pull sched_ext fixes from Tejun Heo: - The task ownership check in the dispatch queue move operation raced against the task exiting or moving to a different sub-scheduler, spuriously triggering scheduler aborts. Fix by moving the check under the queue lock - The cgroup bandwidth change callback runs in a sleepable context but sleepable implementations were rejected at load time. Allow them and add a marker so userspace can detect the capability - Sync tooling headers with the scx repo for accumulated compatibility improvements - Example scheduler fixes: ignored timer re-arm failures and vtime credit loss on cgroup migration - Documentation and comment fixes * tag 'sched_ext-for-7.3-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext: sched_ext: Fix missing @slice and @vtime descriptions in finish_dispatch() kernel-doc sched_ext: Fix several comment issues sched_ext: Check bpf_timer_start return values in scx_qmap sched_ext: Fix vtime delta loss in scx_flatcg cgroup migration sched_ext: Fix timer pinning and return value in scx_central docs/sched_ext: document that cgroup CPU knobs are scheduler-dependent sched_ext: Fix spurious aborts in scx_bpf_dsq_move() on ownership change races sched_ext: Sync common and compat headers from the scx repo sched_ext: Sync tools autogen enum headers from the scx repo Docs/admin-guide/cgroup-v2: document BPF scheduler callbacks for cpu.max and cpu.idle sched_ext: Fix nonexistent field in sched-ext.rst example sched_ext: Allow ops.cgroup_set_bandwidth() to be sleepable
This commit is contained in:
commit
bf1079577a
|
|
@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle
|
|||
processes can be categorized as follows:
|
||||
|
||||
* Processes under the fair-class scheduler
|
||||
* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback
|
||||
* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback
|
||||
* Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler
|
||||
without the ``cgroup_set_weight`` callback
|
||||
without the corresponding ``cgroup_set_*`` callback
|
||||
|
||||
For details on when a process is under the fair-class scheduler or a BPF scheduler,
|
||||
check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`.
|
||||
|
|
@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds.
|
|||
$PERIOD duration. "max" for $MAX indicates no limit. If only
|
||||
one number is written, $MAX is updated.
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
|
||||
the callback actually does.
|
||||
|
||||
cpu.max.burst
|
||||
A read-write single value file which exists on non-root
|
||||
|
|
@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds.
|
|||
|
||||
The burst in the range [0, $MAX].
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_bandwidth`` callback depending on what
|
||||
the callback actually does.
|
||||
|
||||
cpu.pressure
|
||||
A read-write nested-keyed file.
|
||||
|
|
@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds.
|
|||
own relative priorities, but the cgroup itself will be treated as
|
||||
very low priority relative to its peers.
|
||||
|
||||
This file affects only processes under the fair-class scheduler.
|
||||
This file affects only processes under the fair-class scheduler and a BPF
|
||||
scheduler with the ``cgroup_set_idle`` callback depending on what the
|
||||
callback actually does.
|
||||
|
||||
Memory
|
||||
------
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ optional. The following modified excerpt is from
|
|||
|
||||
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
exit_type = ei->type;
|
||||
exit_type = ei->kind;
|
||||
}
|
||||
|
||||
SEC(".struct_ops")
|
||||
|
|
@ -242,6 +242,21 @@ optional. The following modified excerpt is from
|
|||
.name = "simple",
|
||||
};
|
||||
|
||||
Scheduler-Dependent Knobs
|
||||
-------------------------
|
||||
|
||||
The fair-class scheduler enforces CPU controller settings such as
|
||||
``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the
|
||||
scheduler core communicates these settings to the BPF scheduler
|
||||
through ``ops.cgroup_init()`` and reports subsequent changes through
|
||||
the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task
|
||||
nice changes are converted to weights and reported through
|
||||
``ops.set_weight()``.
|
||||
|
||||
Each BPF scheduler is responsible for implementing the scheduling
|
||||
semantics of these settings and may choose to ignore them. Consult the
|
||||
loaded scheduler's documentation before relying on these controls.
|
||||
|
||||
Dispatch Queues
|
||||
---------------
|
||||
|
||||
|
|
|
|||
|
|
@ -876,9 +876,9 @@ struct task_struct *scx_task_iter_next_locked(struct scx_task_iter *iter)
|
|||
* unloading. The init_tasks ("swappers") should be excluded
|
||||
* from the iteration because:
|
||||
*
|
||||
* - It's unsafe to use __setschduler_prio() on an init_task to
|
||||
* determine the sched_class to use as it won't preserve its
|
||||
* idle_sched_class.
|
||||
* - It's unsafe to use __setscheduler_class() on an init_task
|
||||
* to determine the sched_class to use as it won't preserve
|
||||
* its idle_sched_class.
|
||||
*
|
||||
* - ops.init/exit_task() can easily be confused if called with
|
||||
* init_tasks as they, e.g., share PID 0.
|
||||
|
|
@ -2806,6 +2806,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
|
|||
* @p: task to finish dispatching
|
||||
* @qseq_at_dispatch: qseq when @p started getting dispatched
|
||||
* @dsq_id: destination DSQ ID
|
||||
* @slice: slice carried by the insert verdict, 0 keeps the current value
|
||||
* @vtime: vtime carried by the insert verdict, committed on PRIQ inserts
|
||||
* @enq_flags: %SCX_ENQ_*
|
||||
*
|
||||
* Dispatching to local DSQs may need to wait for queueing to complete or
|
||||
|
|
@ -5514,7 +5516,7 @@ static const struct kset_uevent_ops scx_uevent_ops = {
|
|||
};
|
||||
|
||||
/*
|
||||
* Used by sched_fork() and __setscheduler_prio() to pick the matching
|
||||
* Used by sched_fork() and __setscheduler_class() to pick the matching
|
||||
* sched_class. dl/rt are already handled.
|
||||
*/
|
||||
bool task_should_scx(int policy)
|
||||
|
|
@ -7694,7 +7696,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
|||
/*
|
||||
* Enable ops for every task. Fork is excluded by scx_fork_rwsem
|
||||
* preventing new tasks from being added. No need to exclude tasks
|
||||
* leaving as sched_ext_free() can handle both prepped and enabled
|
||||
* leaving as sched_ext_dead() can handle both prepped and enabled
|
||||
* tasks. Prep all tasks first and then enable them with preemption
|
||||
* disabled.
|
||||
*
|
||||
|
|
@ -7786,7 +7788,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
|
|||
|
||||
/*
|
||||
* We're fully committed and can't fail. The task READY -> ENABLED
|
||||
* transitions here are synchronized against sched_ext_free() through
|
||||
* transitions here are synchronized against sched_ext_dead() through
|
||||
* scx_tasks_lock.
|
||||
*/
|
||||
percpu_down_write(&scx_fork_rwsem);
|
||||
|
|
@ -8079,6 +8081,7 @@ static int bpf_scx_check_member(const struct btf_type *t,
|
|||
case offsetof(struct sched_ext_ops, cgroup_init):
|
||||
case offsetof(struct sched_ext_ops, cgroup_exit):
|
||||
case offsetof(struct sched_ext_ops, cgroup_prep_move):
|
||||
case offsetof(struct sched_ext_ops, cgroup_set_bandwidth):
|
||||
#endif
|
||||
case offsetof(struct sched_ext_ops, cpu_online):
|
||||
case offsetof(struct sched_ext_ops, cpu_offline):
|
||||
|
|
@ -9003,12 +9006,6 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
|
|||
if (unlikely(READ_ONCE(sch->aborting)))
|
||||
return false;
|
||||
|
||||
if (unlikely(!scx_task_on_sched(sch, p))) {
|
||||
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
|
||||
p->comm, p->pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can be called from either ops.dispatch() holding the dispatched rq's
|
||||
* lock or any context where no rq lock is held. If latter, lock @p's
|
||||
|
|
@ -9040,6 +9037,17 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* @p has been on $src_dsq and can't move anymore. If @p is not on @sch,
|
||||
* the caller didn't have authority over @p at the time of the call.
|
||||
*/
|
||||
if (unlikely(!scx_task_on_sched(sch, p))) {
|
||||
scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
|
||||
p->comm, p->pid);
|
||||
raw_spin_unlock(&src_dsq->lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* @p is still on $src_dsq and stable, determine the destination */
|
||||
dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p));
|
||||
|
||||
|
|
@ -9765,7 +9773,7 @@ __bpf_kfunc struct task_struct *bpf_iter_scx_dsq_next(struct bpf_iter_scx_dsq *i
|
|||
* bpf_iter_scx_dsq_destroy - Destroy a DSQ iterator
|
||||
* @it: iterator to destroy
|
||||
*
|
||||
* Undo scx_iter_scx_dsq_new().
|
||||
* Undo bpf_iter_scx_dsq_new().
|
||||
*/
|
||||
__bpf_kfunc void bpf_iter_scx_dsq_destroy(struct bpf_iter_scx_dsq *it)
|
||||
{
|
||||
|
|
@ -11041,3 +11049,16 @@ static int __init scx_init(void)
|
|||
return 0;
|
||||
}
|
||||
__initcall(scx_init);
|
||||
|
||||
/*
|
||||
* Compatibility markers for userspace. Existence of a marker function
|
||||
* represents that the kernel supports that sched-ext feature.
|
||||
*/
|
||||
|
||||
/*
|
||||
* scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that
|
||||
* ops.cgroup_set_bandwidth() may be implemented as a sleepable callback.
|
||||
*/
|
||||
#ifdef CONFIG_EXT_GROUP_SCHED
|
||||
DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep);
|
||||
#endif /* CONFIG_EXT_GROUP_SCHED */
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ struct sched_ext_ops {
|
|||
*
|
||||
* Note that this callback may be called from a CPU other than the
|
||||
* one the task is going to run on. This can happen when a task
|
||||
* property is changed (i.e., affinity), since scx_next_task_scx(),
|
||||
* property is changed (i.e., affinity), since set_next_task_scx(),
|
||||
* which triggers this callback, may run on a CPU different from
|
||||
* the task's assigned CPU.
|
||||
*
|
||||
|
|
@ -753,7 +753,7 @@ struct sched_ext_ops {
|
|||
* @burst_us: bandwidth control burst
|
||||
*
|
||||
* Update @cgrp's bandwidth control parameters. This is from the cpu.max
|
||||
* cgroup interface.
|
||||
* cgroup interface. This operation may block.
|
||||
*
|
||||
* @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled
|
||||
* to. For example, if @period_us is 1_000_000 and @quota_us is
|
||||
|
|
@ -2001,6 +2001,27 @@ struct scx_bstr_buf {
|
|||
char line[SCX_EXIT_MSG_LEN];
|
||||
};
|
||||
|
||||
/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */
|
||||
#define DECLARE_SCX_COMPAT_MARKER(func) \
|
||||
extern void scx_compat_marker_##func(void)
|
||||
|
||||
/**
|
||||
* DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker
|
||||
* @func: marker suffix; the defined symbol is scx_compat_marker_@func
|
||||
*
|
||||
* Emit an empty, callerless function that is retained in the kernel's BTF.
|
||||
* Its presence is part of the kernel<->userspace contract: userspace probes
|
||||
* scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports
|
||||
* the corresponding feature.
|
||||
*
|
||||
* The leading declaration suppresses the missing-prototype warning; the
|
||||
* trailing declaration consumes the semicolon at the use site.
|
||||
*/
|
||||
#define DEFINE_SCX_COMPAT_MARKER(func) \
|
||||
DECLARE_SCX_COMPAT_MARKER(func); \
|
||||
__used __retain void scx_compat_marker_##func(void) {} \
|
||||
DECLARE_SCX_COMPAT_MARKER(func)
|
||||
|
||||
extern struct scx_sched __rcu *scx_root;
|
||||
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
extern int LINUX_KERNEL_VERSION __kconfig;
|
||||
extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak;
|
||||
extern const char CONFIG_LOCALVERSION[64] __kconfig __weak;
|
||||
extern bool CONFIG_PREEMPT_RCU __kconfig __weak;
|
||||
|
||||
/*
|
||||
* Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can
|
||||
|
|
@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags
|
|||
s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym;
|
||||
bool scx_bpf_task_running(const struct task_struct *p) __ksym;
|
||||
s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym;
|
||||
struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak;
|
||||
struct rq *scx_bpf_locked_rq(void) __ksym;
|
||||
struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak;
|
||||
struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak;
|
||||
|
|
@ -527,32 +529,103 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask)
|
|||
return (const struct cpumask *)mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
|
||||
* migrate_disable() for the current task. Recorded once by
|
||||
* scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
|
||||
* the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
|
||||
*
|
||||
* Defaults to true (conservative). Over-reporting in is_migration_disabled()
|
||||
* causes local-only dispatch, which is safe. Under-reporting can crash the
|
||||
* scheduler, so we err high if the probe somehow fails to run.
|
||||
*/
|
||||
bool __scx_prolog_disables_migration __weak = true;
|
||||
|
||||
/*
|
||||
* scx_lib_init_probe - non-sleepable prolog probe.
|
||||
*
|
||||
* Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops
|
||||
* (kernel/sched/ext.c). The kernel's struct_ops machinery invokes
|
||||
* bpf_scx_reg when userspace creates the scheduler link, before
|
||||
* ops.init() fires. Its address is taken in the vtable, so the symbol
|
||||
* is non-inlinable and has been stable since introduction.
|
||||
*
|
||||
* Entering via fentry runs us through __bpf_prog_enter -- the
|
||||
* non-sleepable prolog that consumers of is_migration_disabled() live
|
||||
* under.
|
||||
*
|
||||
* Loud warning: the prolog adds at most 1 to migration_disabled.
|
||||
* Reading > 1 means something upstream in the
|
||||
* bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration
|
||||
* before the prolog ran, invalidating the probe; audit and adjust.
|
||||
*/
|
||||
SEC("fentry/bpf_scx_reg") __weak
|
||||
int scx_lib_init_probe(void *ctx)
|
||||
{
|
||||
if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) {
|
||||
const struct task_struct *p = bpf_get_current_task_btf();
|
||||
unsigned int md = p->migration_disabled;
|
||||
|
||||
if (md > 1)
|
||||
bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u "
|
||||
"upstream of BPF prolog; probe result unreliable",
|
||||
md);
|
||||
|
||||
__scx_prolog_disables_migration = md > 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if task @p cannot migrate to a different CPU, false
|
||||
* otherwise.
|
||||
*
|
||||
* IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable
|
||||
* contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"),
|
||||
* SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or
|
||||
* __bpf_prog_enter_sleepable_recur(), both of which unconditionally
|
||||
* call migrate_disable(); this helper can yield a false negative for
|
||||
* p == current there, which can crash the scheduler.
|
||||
*/
|
||||
static inline bool is_migration_disabled(const struct task_struct *p)
|
||||
{
|
||||
/*
|
||||
* Testing p->migration_disabled in a BPF code is tricky because the
|
||||
* migration is _always_ disabled while running the BPF code.
|
||||
* The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF
|
||||
* code execution disable and re-enable the migration of the current
|
||||
* task, respectively. So, the _current_ task of the sched_ext ops is
|
||||
* always migration-disabled. Moreover, p->migration_disabled could be
|
||||
* two or greater when a sched_ext ops BPF code (e.g., ops.tick) is
|
||||
* executed in the middle of the other BPF code execution.
|
||||
* Testing p->migration_disabled in BPF is tricky because the BPF prolog
|
||||
* (__bpf_prog_enter) may call migrate_disable() for the current task,
|
||||
* making migration_disabled == 1 even for tasks that are not truly
|
||||
* migration-disabled.
|
||||
*
|
||||
* Therefore, we should decide that the _current_ task is
|
||||
* migration-disabled only when its migration_disabled count is greater
|
||||
* than one. In other words, when p->migration_disabled == 1, there is
|
||||
* an ambiguity, so we should check if @p is the current task or not.
|
||||
* Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
|
||||
* trampoline.c"), the BPF prolog calls migrate_disable() only when
|
||||
* CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases:
|
||||
*
|
||||
* 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so
|
||||
* migration_disabled == 1 for the current task is ambiguous.
|
||||
* Disambiguate by checking p == current.
|
||||
*
|
||||
* 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls
|
||||
* migrate_disable(), so migration_disabled == 1 is unambiguously
|
||||
* a real migrate_disable() call.
|
||||
*
|
||||
* A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU,
|
||||
* where the prolog historically called migrate_disable() unconditionally
|
||||
* but a cherry-picked downstream kernel may not. The runtime-probed flag
|
||||
* __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes
|
||||
* the two cases without relying on the kernel version alone.
|
||||
*/
|
||||
if (bpf_core_field_exists(p->migration_disabled)) {
|
||||
if (p->migration_disabled == 1)
|
||||
return bpf_get_current_task_btf() != p;
|
||||
else
|
||||
return p->migration_disabled;
|
||||
if (p->migration_disabled == 1) {
|
||||
/* Fast path: prolog always disables migration */
|
||||
if (CONFIG_PREEMPT_RCU)
|
||||
return bpf_get_current_task_btf() != p;
|
||||
/* Fast path: prolog never disables migration */
|
||||
if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0))
|
||||
return true;
|
||||
/* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */
|
||||
return __scx_prolog_disables_migration ?
|
||||
bpf_get_current_task_btf() != p : true;
|
||||
}
|
||||
return p->migration_disabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1021,7 +1094,20 @@ static inline u64 scx_clock_task(u32 cpu)
|
|||
{
|
||||
struct rq___local *rq = get_current_rq(cpu);
|
||||
|
||||
/* Equivalent to the kernel's rq_clock_task(). */
|
||||
/*
|
||||
* Equivalent to the kernel's rq_clock_task(): wall-clock time minus
|
||||
* cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor
|
||||
* steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs,
|
||||
* it equals rq->clock.
|
||||
*
|
||||
* Conceptually this clock advances during idle (the idle task counts
|
||||
* as a running task), but rq->clock_task is only updated on scheduling
|
||||
* events. With NO_HZ_IDLE (the default), the periodic tick is stopped
|
||||
* on idle CPUs, so rq->clock_task is not refreshed while a CPU is
|
||||
* idle. Reading this clock for a remote idle CPU from a BPF timer
|
||||
* callback returns the value from when the CPU last went idle, making
|
||||
* the delta over an idle interval effectively zero.
|
||||
*/
|
||||
return rq ? rq->clock_task : 0;
|
||||
}
|
||||
|
||||
|
|
@ -1032,9 +1118,23 @@ static inline u64 scx_clock_pelt(u32 cpu)
|
|||
/*
|
||||
* Equivalent to the kernel's rq_clock_pelt(): subtracts
|
||||
* lost_idle_time from clock_pelt to absorb the jump that occurs
|
||||
* when clock_pelt resyncs with clock_task at idle exit. The result
|
||||
* is a continuous, capacity-invariant clock safe for both task
|
||||
* execution time stamping and cross-idle measurements.
|
||||
* when clock_pelt resyncs with clock_task at idle exit. The intent
|
||||
* is a continuous, capacity- and frequency-invariant clock that is
|
||||
* frozen during idle, IRQ, and hypervisor steal.
|
||||
*
|
||||
* However, like scx_clock_task(), this clock has a stale-read issue
|
||||
* for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt
|
||||
* itself advances at wall-clock rate (hardware-clock based), but
|
||||
* lost_idle_time is only updated via update_rq_clock_pelt(), which
|
||||
* requires update_rq_clock() to be called. With NO_HZ_IDLE, the
|
||||
* periodic tick is stopped on idle CPUs, so lost_idle_time is not
|
||||
* refreshed during idle. Reading this clock for a remote idle CPU
|
||||
* from a BPF timer callback therefore returns a value that drifts
|
||||
* at wall-clock rate -- the same stale behaviour as scx_clock_task().
|
||||
*
|
||||
* Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in
|
||||
* sync (stale by at most one tick period, ~1 ms), so the result is
|
||||
* accurate.
|
||||
*/
|
||||
return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k
|
|||
|
||||
/*
|
||||
* v6.19: Introduce lockless peek API for user DSQs.
|
||||
* v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty
|
||||
* FIFO DSQs (2f2ea7709266).
|
||||
*
|
||||
* Preserve the following macro until v6.21.
|
||||
* The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ
|
||||
* before the v7.1 fix. Require kernel version >= 7.1.0 before calling it;
|
||||
* otherwise fall through to the bpf_iter_scx_dsq fallback below.
|
||||
*/
|
||||
static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id)
|
||||
{
|
||||
struct task_struct *p = NULL;
|
||||
struct bpf_iter_scx_dsq it;
|
||||
|
||||
if (bpf_ksym_exists(scx_bpf_dsq_peek))
|
||||
if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
|
||||
LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
|
||||
return scx_bpf_dsq_peek(dsq_id);
|
||||
if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0))
|
||||
p = bpf_iter_scx_dsq_next(&it);
|
||||
|
|
@ -238,6 +243,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags)
|
|||
scx_bpf_pick_any_cpu_node(cpus_allowed, node, flags) : \
|
||||
scx_bpf_pick_any_cpu(cpus_allowed, flags))
|
||||
|
||||
/*
|
||||
* v6.18: Add a helper to retrieve the current task running on a CPU.
|
||||
*
|
||||
* The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in
|
||||
* this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't
|
||||
* resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until
|
||||
* pre-v6.18 kernels fall out of the support window.
|
||||
*/
|
||||
static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
|
||||
{
|
||||
struct rq *rq;
|
||||
|
||||
if (bpf_ksym_exists(scx_bpf_cpu_curr))
|
||||
return scx_bpf_cpu_curr(cpu);
|
||||
|
||||
rq = scx_bpf_cpu_rq(cpu);
|
||||
|
||||
return rq ? rq->curr : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* v6.19: To work around BPF maximum parameter limit, the following kfuncs are
|
||||
* replaced with variants that pack scalar arguments in a struct. Wrappers are
|
||||
|
|
@ -378,6 +403,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime)
|
|||
p->scx.dsq_vtime = vtime;
|
||||
}
|
||||
|
||||
/*
|
||||
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
|
||||
* will eventually deprecate scx_bpf_reenqueue_local().
|
||||
*/
|
||||
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
|
||||
|
||||
static inline bool __COMPAT_has_generic_reenq(void)
|
||||
{
|
||||
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
|
||||
}
|
||||
|
||||
/*
|
||||
* v6.19: The new void variant can be called from anywhere while the older v1
|
||||
* variant can only be called from ops.cpu_release(). The double ___ prefixes on
|
||||
|
|
@ -395,21 +431,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void)
|
|||
|
||||
static inline void scx_bpf_reenqueue_local(void)
|
||||
{
|
||||
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
|
||||
if (__COMPAT_has_generic_reenq())
|
||||
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
|
||||
else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
|
||||
scx_bpf_reenqueue_local___v2___compat();
|
||||
else
|
||||
scx_bpf_reenqueue_local___v1();
|
||||
}
|
||||
|
||||
/*
|
||||
* v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
|
||||
* will eventually deprecate scx_bpf_reenqueue_local().
|
||||
*/
|
||||
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
|
||||
|
||||
static inline bool __COMPAT_has_generic_reenq(void)
|
||||
static inline int scx_bpf_reenqueue_local_from_anywhere(void)
|
||||
{
|
||||
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
|
||||
/*
|
||||
* The generic reenq kfunc and the v2 reenqueue-local variant can both be
|
||||
* called from anywhere; v1 cannot. Test each ksym in its own branch with a
|
||||
* distinct call: combining them with || would fold into a bitwise OR of the
|
||||
* two ksym addresses, which the verifier rejects.
|
||||
*/
|
||||
if (__COMPAT_has_generic_reenq()) {
|
||||
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
|
||||
return 0;
|
||||
}
|
||||
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
|
||||
scx_bpf_reenqueue_local___v2___compat();
|
||||
return 0;
|
||||
}
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@
|
|||
#include <bpf/btf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "enums_abi.autogen.h"
|
||||
|
||||
struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
|
||||
|
||||
static inline void __COMPAT_load_vmlinux_btf(void)
|
||||
|
|
@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recover the true value of a 64-bit enum enumerator whose kernel BTF entry
|
||||
* was truncated to its low 32 bits.
|
||||
*
|
||||
* Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode
|
||||
* 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only
|
||||
* carry the low 32 bits. This happens with pahole < 1.24, which predates
|
||||
* ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's
|
||||
* Container-Optimized OS / GKE kernels deliberately pass it for backward
|
||||
* compatibility with older BTF consumers). The high bits
|
||||
* can't be recovered from kernel BTF, so substitute the value from the
|
||||
* vmlinux.h this tree was built against, cross-checked against the low 32
|
||||
* bits the kernel did provide.
|
||||
*
|
||||
* Note that this is a best-effort recovery, not a ground truth. The
|
||||
* substitution assumes the running kernel agrees with this tree's vmlinux.h
|
||||
* on the high 32 bits, but only the low 32 bits can actually be verified.
|
||||
* The cross-check is vacuous for enumerators whose value has no low bits
|
||||
* set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK,
|
||||
* SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything,
|
||||
* so those substitutions rest entirely on the high bits never moving. An
|
||||
* enumerator missing from the table (a kernel newer than this tree's
|
||||
* vmlinux.h, or a stale autogen table) can't be recovered at all. If a
|
||||
* substitution is ever wrong, the scheduler operates on bogus values (e.g.
|
||||
* dispatching to nonexistent DSQ ids or silently dropping flags) and can
|
||||
* wildly malfunction, which is why the mismatch and table-miss paths refuse
|
||||
* instead of guessing.
|
||||
*/
|
||||
static inline bool __COMPAT_recover_truncated_enum64(const char *type,
|
||||
const char *name,
|
||||
u32 lo32, u64 *v)
|
||||
{
|
||||
static bool warned;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) {
|
||||
const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i];
|
||||
|
||||
if (strcmp(e->type, type) || strcmp(e->name, name))
|
||||
continue;
|
||||
|
||||
if (e->val <= (u64)UINT32_MAX) {
|
||||
*v = lo32;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((u32)e->val != lo32) {
|
||||
fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n",
|
||||
type, name, lo32, (unsigned long long)e->val);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!warned) {
|
||||
fprintf(stderr,
|
||||
"WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n"
|
||||
"WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n"
|
||||
"WARNING: scx enum values are truncated to their low 32 bits in kernel\n"
|
||||
"WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n"
|
||||
"WARNING: this binary was built against, cross-checked against the low\n"
|
||||
"WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n"
|
||||
"WARNING: verified: if the running kernel's actual values differ from\n"
|
||||
"WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n"
|
||||
"WARNING: kernel), the scheduler will operate on bogus values, such as\n"
|
||||
"WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n");
|
||||
warned = true;
|
||||
}
|
||||
*v = e->val;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unknown enumerator (likely a stale autogen table). Fail
|
||||
* pessimistically to avoid returning an invalid value.
|
||||
*/
|
||||
fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n",
|
||||
type, name, lo32);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
|
|
@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v
|
|||
n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
|
||||
SCX_BUG_ON(!n, "btf__name_by_offset()");
|
||||
if (!strcmp(n, name)) {
|
||||
/*
|
||||
* Try to recover a 64-bit enum from an 8-byte
|
||||
* BTF_KIND_ENUM that was encoded without ENUM64
|
||||
* support (old pahole or
|
||||
* --skip_encoding_btf_enum64). Only scx_*
|
||||
* types are covered by the substitution table;
|
||||
* non-scx types fall through to the raw value
|
||||
* so this generic utility keeps working for
|
||||
* them.
|
||||
*/
|
||||
if (t->size == 8 && !strncmp(type, "scx_", 4))
|
||||
return __COMPAT_recover_truncated_enum64(type, name,
|
||||
(u32)e[i].val, v);
|
||||
*v = e[i].val;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@
|
|||
#define HAVE_SCX_DEQ_SLEEP
|
||||
#define HAVE_SCX_DEQ_CORE_SCHED_EXEC
|
||||
#define HAVE_SCX_DEQ_SCHED_CHANGE
|
||||
#define HAVE_SCX_DSP_NONE
|
||||
#define HAVE_SCX_DSP_LOCAL
|
||||
#define HAVE_SCX_DSP_PREV
|
||||
#define HAVE_SCX_DSP_RETRY
|
||||
#define HAVE_SCX_DSQ_FLAG_BUILTIN
|
||||
#define HAVE_SCX_DSQ_FLAG_LOCAL_ON
|
||||
#define HAVE_SCX_DSQ_INVALID
|
||||
|
|
@ -188,7 +192,6 @@
|
|||
#define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY
|
||||
#define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY
|
||||
#define HAVE_SCX_RQ_IN_WAKEUP
|
||||
#define HAVE_SCX_RQ_IN_BALANCE
|
||||
#define HAVE_SCX_RQ_IN_DISPATCH
|
||||
#define HAVE_SCX_SCHED_PCPU_BYPASSING
|
||||
#define HAVE_SCX_SLICE_OOB_DUR_BITS
|
||||
|
|
|
|||
223
tools/sched_ext/include/scx/enums_abi.autogen.h
Normal file
223
tools/sched_ext/include/scx/enums_abi.autogen.h
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* WARNING: This file is autogenerated from gen_enum_defs.py [1].
|
||||
*
|
||||
* scx enumerator values from the vmlinux.h this tree is built against.
|
||||
* Used as the substitution source when the running kernel's BTF lacks
|
||||
* BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated.
|
||||
*
|
||||
* [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py
|
||||
*/
|
||||
|
||||
#ifndef __ENUMS_ABI_AUTOGEN_H__
|
||||
#define __ENUMS_ABI_AUTOGEN_H__
|
||||
|
||||
struct __scx_enum_abi_val {
|
||||
const char *type;
|
||||
const char *name;
|
||||
u64 val;
|
||||
};
|
||||
|
||||
static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
|
||||
__attribute__((unused)) = {
|
||||
{ "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU },
|
||||
{ "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU },
|
||||
{ "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU },
|
||||
{ "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU },
|
||||
{ "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU },
|
||||
{ "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU },
|
||||
{ "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU },
|
||||
{ "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU },
|
||||
{ "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU },
|
||||
{ "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU },
|
||||
{ "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU },
|
||||
{ "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU },
|
||||
{ "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU },
|
||||
{ "scx_consts", "SCX_TID_CHUNK", 0x400LLU },
|
||||
{ "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU },
|
||||
{ "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU },
|
||||
{ "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU },
|
||||
{ "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU },
|
||||
{ "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU },
|
||||
{ "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU },
|
||||
{ "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU },
|
||||
{ "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU },
|
||||
{ "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU },
|
||||
{ "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU },
|
||||
{ "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU },
|
||||
{ "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU },
|
||||
{ "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU },
|
||||
{ "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU },
|
||||
{ "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU },
|
||||
{ "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU },
|
||||
{ "scx_enable_state", "SCX_ENABLING", 0x0LLU },
|
||||
{ "scx_enable_state", "SCX_ENABLED", 0x1LLU },
|
||||
{ "scx_enable_state", "SCX_DISABLING", 0x2LLU },
|
||||
{ "scx_enable_state", "SCX_DISABLED", 0x3LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU },
|
||||
{ "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU },
|
||||
{ "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU },
|
||||
{ "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_READY", 0x300LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU },
|
||||
{ "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU },
|
||||
{ "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU },
|
||||
{ "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU },
|
||||
{ "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU },
|
||||
{ "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
|
||||
{ "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
|
||||
{ "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
|
||||
{ "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
|
||||
{ "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU },
|
||||
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU },
|
||||
{ "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU },
|
||||
{ "scx_opi", "SCX_OPI_END", 0x23LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
|
||||
{ "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
|
||||
{ "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU },
|
||||
{ "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU },
|
||||
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU },
|
||||
{ "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU },
|
||||
{ "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU },
|
||||
{ "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU },
|
||||
{ "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU },
|
||||
{ "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU },
|
||||
{ "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU },
|
||||
{ "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU },
|
||||
{ "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_INITED", 0x2LLU },
|
||||
{ "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU },
|
||||
{ "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU },
|
||||
};
|
||||
|
||||
#endif /* __ENUMS_ABI_AUTOGEN_H__ */
|
||||
|
|
@ -299,6 +299,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
|
|||
u64 now = scx_bpf_now();
|
||||
u64 nr_to_kick = nr_queued;
|
||||
s32 i, curr_cpu;
|
||||
int ret;
|
||||
|
||||
curr_cpu = bpf_get_smp_processor_id();
|
||||
if (timer_pinned && (curr_cpu != central_cpu)) {
|
||||
|
|
@ -332,7 +333,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
|
|||
scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT);
|
||||
}
|
||||
|
||||
bpf_timer_start(timer, TIMER_INTERVAL_NS, BPF_F_TIMER_CPU_PIN);
|
||||
ret = bpf_timer_start(timer, TIMER_INTERVAL_NS,
|
||||
timer_pinned ? BPF_F_TIMER_CPU_PIN : 0);
|
||||
if (ret)
|
||||
scx_bpf_error("bpf_timer_start failed (%d)", ret);
|
||||
__sync_fetch_and_add(&nr_timers, 1);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -937,7 +937,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,
|
|||
if (!(from_cgc = find_cgrp_ctx(from)) || !(to_cgc = find_cgrp_ctx(to)))
|
||||
return;
|
||||
|
||||
delta = time_delta(p->scx.dsq_vtime, from_cgc->tvtime_now);
|
||||
delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now);
|
||||
scx_bpf_task_set_dsq_vtime(p, to_cgc->tvtime_now + delta);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1246,7 +1246,8 @@ static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
|
|||
scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
|
||||
}
|
||||
|
||||
bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
|
||||
if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
|
||||
scx_bpf_error("failed to re-arm stats timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1268,7 +1269,8 @@ struct {
|
|||
static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
{
|
||||
scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
|
||||
bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
|
||||
if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
|
||||
scx_bpf_error("failed to re-arm lowpri timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1747,7 +1749,8 @@ static void rr_advance(void)
|
|||
static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
|
||||
{
|
||||
rr_advance();
|
||||
bpf_timer_start(timer, round_robin_ns, 0);
|
||||
if (bpf_timer_start(timer, round_robin_ns, 0))
|
||||
scx_bpf_error("failed to re-arm round-robin timer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user