#include <scx/common.bpf.h>
#include <bpf_arena_common.bpf.h>
#include <bpf_experimental.h>
#include "intf.h"
#include "lavd.bpf.h"
#include "util.bpf.h"
#include "power.bpf.h"
#include <errno.h>
#include <stdbool.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <lib/cgroup.h>
char _license[] SEC("license") = "GPL";
u64 cur_logical_clk = LAVD_DL_COMPETE_WINDOW;
static u64 cur_svc_time_wwgt;
const volatile u64 slice_min_ns = LAVD_SLICE_MIN_NS_DFL;
const volatile u64 slice_max_ns = LAVD_SLICE_MAX_NS_DFL;
const volatile u8 mig_delta_pct = 0;
const volatile u64 lb_low_util_wall = 0;
const volatile u64 lb_local_dsq_util_wall = 0;
const volatile u64 pinned_slice_ns = 0;
static volatile u64 nr_cpus_big;
static pid_t lavd_pid;
static void advance_cur_logical_clk(struct task_struct *p)
{
u64 vlc, clc, ret_clc;
u64 nr_queued, delta, new_clk;
int i;
vlc = READ_ONCE(p->scx.dsq_vtime);
clc = READ_ONCE(cur_logical_clk);
bpf_for(i, 0, LAVD_MAX_RETRY) {
if (vlc <= clc)
return;
nr_queued = max(sys_stat.nr_queued_task, 1);
delta = (vlc - clc) / nr_queued;
new_clk = clc + delta;
ret_clc = __sync_val_compare_and_swap(&cur_logical_clk, clc, new_clk);
if (ret_clc == clc)
return;
clc = ret_clc;
}
}
static u64 calc_time_slice(task_ctx *taskc, struct cpu_ctx *cpuc)
{
if (!taskc || !cpuc)
return LAVD_SLICE_MAX_NS_DFL;
if (pinned_slice_ns && cpuc->nr_pinned_tasks) {
taskc->slice_wall = min(pinned_slice_ns, sys_stat.slice_wall);
reset_task_flag(taskc, LAVD_FLAG_SLICE_BOOST);
return taskc->slice_wall;
}
if (!no_slice_boost && !cpuc->nr_pinned_tasks &&
(taskc->avg_runtime_wall >= sys_stat.slice_wall)) {
if (can_boost_slice()) {
u64 s = taskc->avg_runtime_wall + LAVD_SLICE_BOOST_BONUS;
taskc->slice_wall = clamp(s, slice_min_ns,
LAVD_SLICE_BOOST_MAX);
set_task_flag(taskc, LAVD_FLAG_SLICE_BOOST);
return taskc->slice_wall;
}
if (taskc->lat_cri > sys_stat.avg_lat_cri) {
u64 b = (sys_stat.slice_wall * taskc->lat_cri) /
(sys_stat.avg_lat_cri + 1);
u64 s = sys_stat.slice_wall + b;
taskc->slice_wall = clamp(s, slice_min_ns,
min(taskc->avg_runtime_wall,
sys_stat.slice_wall * 2));
set_task_flag(taskc, LAVD_FLAG_SLICE_BOOST);
return taskc->slice_wall;
}
}
taskc->slice_wall = sys_stat.slice_wall;
reset_task_flag(taskc, LAVD_FLAG_SLICE_BOOST);
return taskc->slice_wall;
}
static void update_stat_for_running(struct task_struct *p,
task_ctx *taskc,
struct cpu_ctx *cpuc, u64 now)
{
u64 wait_period, interval;
struct cpu_ctx *prev_cpuc;
if (have_scheduled(taskc)) {
wait_period = time_delta(now, taskc->last_quiescent_clk);
interval = taskc->avg_runtime_wall + wait_period;
if (interval > 0)
taskc->run_freq = calc_avg_freq(taskc->run_freq, interval);
}
if (is_monitored) {
taskc->resched_interval_wall = time_delta(now,
taskc->last_running_clk);
}
taskc->prev_cpu_id = taskc->cpu_id;
taskc->cpu_id = cpuc->cpu_id;
reset_task_flag(taskc, LAVD_FLAG_IS_WAKEUP);
reset_task_flag(taskc, LAVD_FLAG_IS_SYNC_WAKEUP);
taskc->last_running_clk = now;
taskc->last_measured_wall_clk = now;
taskc->last_measured_task_clk = scx_clock_task(cpuc->cpu_id);
reset_lock_futex_boost(taskc, cpuc);
if (cpuc->max_lat_cri < taskc->lat_cri)
cpuc->max_lat_cri = taskc->lat_cri;
cpuc->sum_lat_cri += taskc->lat_cri;
cpuc->nr_sched++;
if (have_little_core) {
if (cpuc->max_perf_cri < taskc->perf_cri)
cpuc->max_perf_cri = taskc->perf_cri;
if (cpuc->min_perf_cri > taskc->perf_cri)
cpuc->min_perf_cri = taskc->perf_cri;
cpuc->sum_perf_cri += taskc->perf_cri;
}
cpuc->flags = taskc->flags;
cpuc->lat_cri = taskc->lat_cri;
cpuc->running_clk = now;
cpuc->est_stopping_clk = get_est_stopping_clk(taskc, now);
if (is_lat_cri(taskc))
cpuc->nr_lat_cri++;
if (is_perf_cri(taskc))
cpuc->nr_perf_cri++;
prev_cpuc = get_cpu_ctx_id(taskc->prev_cpu_id);
if (prev_cpuc && prev_cpuc->cpdom_id != cpuc->cpdom_id)
cpuc->nr_x_migration++;
reset_suspended_duration(cpuc);
}
static void account_task_runtime(struct task_struct *p,
task_ctx *taskc,
struct cpu_ctx *cpuc,
u64 now)
{
u64 task_time_wall, task_time_wwgt, task_time_invr;
u64 suspended_wall, duration_wall, now_task;
suspended_wall = get_suspended_duration_and_reset(cpuc);
duration_wall = time_delta(now, taskc->last_measured_wall_clk + suspended_wall);
now_task = scx_clock_task(cpuc->cpu_id);
task_time_wall = time_delta(now_task, taskc->last_measured_task_clk);
cpuc->stolen_time_wall += time_delta(duration_wall, task_time_wall);
task_time_wwgt = task_time_wall / p->scx.weight;
task_time_invr = conv_wall_to_invr(task_time_wall, cpuc);
WRITE_ONCE(cpuc->tot_task_time_wall, cpuc->tot_task_time_wall + task_time_wall);
WRITE_ONCE(cpuc->tot_task_time_wwgt, cpuc->tot_task_time_wwgt + task_time_wwgt);
WRITE_ONCE(cpuc->tot_task_time_invr, cpuc->tot_task_time_invr + task_time_invr);
taskc->acc_runtime_wall += task_time_wall;
taskc->svc_time_wwgt += task_time_wwgt;
taskc->last_measured_wall_clk = now;
taskc->last_measured_task_clk = now_task;
if (enable_cpu_bw && (p->pid != lavd_pid)) {
struct cgroup *cgrp = bpf_cgroup_from_id(taskc->cgrp_id);
if (cgrp) {
scx_cgroup_bw_consume(cgrp, task_time_wall);
bpf_cgroup_release(cgrp);
}
}
}
static void update_stat_for_stopping(struct task_struct *p,
task_ctx *taskc,
struct cpu_ctx *cpuc)
{
u64 now = scx_bpf_now();
account_task_runtime(p, taskc, cpuc, now);
taskc->avg_runtime_wall = calc_avg(taskc->avg_runtime_wall,
taskc->acc_runtime_wall);
taskc->last_slice_used_wall = time_delta(now, taskc->last_running_clk);
if (READ_ONCE(cur_svc_time_wwgt) < taskc->svc_time_wwgt)
WRITE_ONCE(cur_svc_time_wwgt, taskc->svc_time_wwgt);
reset_lock_futex_boost(taskc, cpuc);
}
static void update_stat_for_refill(struct task_struct *p,
task_ctx *taskc,
struct cpu_ctx *cpuc)
{
u64 now = scx_bpf_now();
account_task_runtime(p, taskc, cpuc, now);
taskc->avg_runtime_wall = calc_avg(taskc->avg_runtime_wall,
taskc->acc_runtime_wall);
}
static bool can_direct_dispatch(struct cpu_ctx *cpuc, bool is_cpu_idle)
{
return (is_cpu_idle && !queued_on_cpu(cpuc)) ||
(lb_local_dsq_util_wall > 0 &&
sys_stat.avg_util_wall < lb_local_dsq_util_wall);
}
s32 BPF_STRUCT_OPS(lavd_select_cpu, struct task_struct *p, s32 prev_cpu,
u64 wake_flags)
{
struct pick_ctx ictx = {
.p = p,
.taskc = get_task_ctx(p),
.prev_cpu = prev_cpu,
.cpuc_cur = get_cpu_ctx(),
.wake_flags = wake_flags,
};
struct task_struct *waker;
bool found_idle = false;
s32 cpu_id;
if (!ictx.taskc || !ictx.cpuc_cur)
return prev_cpu;
if (wake_flags & SCX_WAKE_SYNC)
set_task_flag(ictx.taskc, LAVD_FLAG_IS_SYNC_WAKEUP);
else
reset_task_flag(ictx.taskc, LAVD_FLAG_IS_SYNC_WAKEUP);
if (unlikely((bpf_in_hardirq() || bpf_in_nmi()) &&
!bpf_in_serving_softirq())) {
set_task_flag(ictx.taskc, LAVD_FLAG_WOKEN_BY_HARDIRQ);
reset_task_flag(ictx.taskc, LAVD_FLAG_WOKEN_BY_SOFTIRQ);
} else if (unlikely(bpf_in_serving_softirq() ||
((waker = bpf_get_current_task_btf()) &&
is_ksoftirqd(waker)))) {
set_task_flag(ictx.taskc, LAVD_FLAG_WOKEN_BY_SOFTIRQ);
reset_task_flag(ictx.taskc, LAVD_FLAG_WOKEN_BY_HARDIRQ);
}
cpu_id = pick_idle_cpu(&ictx, &found_idle);
cpu_id = cpu_id >= 0 ? cpu_id : prev_cpu;
ictx.taskc->suggested_cpu_id = cpu_id;
if (found_idle) {
struct cpu_ctx *cpuc;
set_task_flag(ictx.taskc, LAVD_FLAG_IDLE_CPU_PICKED);
cpuc = get_cpu_ctx_id(cpu_id);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx: %d", cpu_id);
goto out;
}
if (can_direct_dispatch(cpuc, true)) {
p->scx.dsq_vtime = calc_when_to_run(p, ictx.taskc);
p->scx.slice = LAVD_SLICE_MAX_NS_DFL;
scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL, p->scx.slice, 0);
goto out;
}
} else {
reset_task_flag(ictx.taskc, LAVD_FLAG_IDLE_CPU_PICKED);
}
out:
return cpu_id;
}
static int cgroup_throttled(struct task_struct *p, task_ctx *taskc, bool put_aside)
{
struct cgroup *cgrp;
int ret, ret2;
cgrp = bpf_cgroup_from_id(taskc->cgrp_id);
if (!cgrp) {
debugln("Failed to lookup a cgroup: %llu", taskc->cgrp_id);
return -ESRCH;
}
ret = scx_cgroup_bw_throttled(cgrp);
if ((ret == -EAGAIN) && put_aside) {
ret2 = scx_cgroup_bw_put_aside(p, (u64)taskc, p->scx.dsq_vtime, cgrp);
if (ret2) {
bpf_cgroup_release(cgrp);
return ret2;
}
}
bpf_cgroup_release(cgrp);
return ret;
}
void BPF_STRUCT_OPS(lavd_enqueue, struct task_struct *p, u64 enq_flags)
{
struct cpu_ctx *cpuc, *cpuc_cur;
s32 task_cpu, cpu = -ENOENT;
bool is_idle = false;
task_ctx *taskc;
u64 dsq_id;
taskc = get_task_ctx(p);
cpuc_cur = get_cpu_ctx();
if (!taskc || !cpuc_cur) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
if (!(enq_flags & SCX_ENQ_REENQ)) {
if (enq_flags & SCX_ENQ_WAKEUP)
set_task_flag(taskc, LAVD_FLAG_IS_WAKEUP);
else
reset_task_flag(taskc, LAVD_FLAG_IS_WAKEUP);
p->scx.dsq_vtime = calc_when_to_run(p, taskc);
}
p->scx.slice = LAVD_SLICE_MIN_NS_DFL;
task_cpu = scx_bpf_task_cpu(p);
if (likely(!__COMPAT_is_enq_cpu_selected(enq_flags))) {
struct pick_ctx ictx = {
.p = p,
.taskc = taskc,
.prev_cpu = task_cpu,
.cpuc_cur = cpuc_cur,
.wake_flags = 0,
};
cpu = pick_idle_cpu(&ictx, &is_idle);
} else {
cpu = task_cpu;
is_idle = test_task_flag(taskc, LAVD_FLAG_IDLE_CPU_PICKED);
reset_task_flag(taskc, LAVD_FLAG_IDLE_CPU_PICKED);
}
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
taskc->suggested_cpu_id = cpu;
taskc->cpdom_id = cpuc->cpdom_id;
if (unlikely(test_task_flag_mask(taskc, LAVD_MASK_MIGRATION)))
reset_task_flag(taskc, LAVD_MASK_MIGRATION);
if (enable_cpu_bw && (p->pid != lavd_pid) &&
(cgroup_throttled(p, taskc, true) == -EAGAIN)) {
debugln("Task %s[pid%d/cgid%llu] is throttled.",
p->comm, p->pid, taskc->cgrp_id);
return;
}
if (is_pinned(p) && (taskc->pinned_cpu_id == -ENOENT)) {
taskc->pinned_cpu_id = cpu;
__sync_fetch_and_add(&cpuc->nr_pinned_tasks, 1);
debugln("cpu%d [%d] -- %s:%d -- %s:%d", cpuc->cpu_id,
cpuc->nr_pinned_tasks, p->comm, p->pid, __func__,
__LINE__);
}
if (can_direct_dispatch(cpuc, is_idle)) {
scx_bpf_dsq_insert(p, SCX_DSQ_LOCAL_ON | cpu, p->scx.slice,
enq_flags);
} else {
dsq_id = get_target_dsq_id(p, cpuc);
scx_bpf_dsq_insert_vtime(p, dsq_id, p->scx.slice,
p->scx.dsq_vtime, enq_flags);
}
if (is_idle) {
scx_bpf_kick_cpu(cpu, SCX_KICK_IDLE);
return;
}
if (!no_preemption)
try_find_and_kick_victim_cpu(p, taskc, cpu, cpdom_to_dsq(cpuc->cpdom_id));
}
static
int enqueue_cb(struct task_struct __arg_trusted *p)
{
struct cpu_ctx *cpuc, *cpuc_cur;
task_ctx *taskc;
u64 dsq_id;
s32 cpu;
taskc = get_task_ctx(p);
cpuc_cur = get_cpu_ctx();
if (!taskc || !cpuc_cur) {
scx_bpf_error("Failed to lookup a task context: %d", p->pid);
return 0;
}
p->scx.dsq_vtime = calc_when_to_run(p, taskc);
cpu = taskc->suggested_cpu_id;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return 0;
}
if (is_pinned(p))
__sync_fetch_and_add(&cpuc->nr_pinned_tasks, 1);
dsq_id = get_target_dsq_id(p, cpuc);
scx_bpf_dsq_insert_vtime(p, dsq_id, p->scx.slice, p->scx.dsq_vtime, 0);
return 0;
}
void BPF_STRUCT_OPS(lavd_dequeue, struct task_struct *p, u64 deq_flags)
{
task_ctx *taskc;
int ret;
if (!enable_cpu_bw)
return;
taskc = get_task_ctx(p);
if (!taskc) {
debugln("Failed to lookup task_ctx for task %d", p->pid);
return;
}
if ((ret = scx_cgroup_bw_cancel((u64)taskc)))
debugln("Failed to cancel task %d with %d", p->pid, ret);
}
__hidden __attribute__ ((noinline))
void consume_prev(struct task_struct *prev, task_ctx *taskc_prev, struct cpu_ctx *cpuc)
{
if (!prev || !(prev->scx.flags & SCX_TASK_QUEUED))
return;
taskc_prev = taskc_prev ?: get_task_ctx(prev);
if (!taskc_prev)
return;
update_stat_for_refill(prev, taskc_prev, cpuc);
if (enable_cpu_bw && (prev->pid != lavd_pid) &&
(cgroup_throttled(prev, taskc_prev, false) == -EAGAIN))
return;
prev->scx.slice = calc_time_slice(taskc_prev, cpuc);
if (is_lock_holder_running(cpuc))
reset_lock_futex_boost(taskc_prev, cpuc);
cpuc->flags = taskc_prev->flags;
}
void BPF_STRUCT_OPS(lavd_dispatch, s32 cpu, struct task_struct *prev)
{
struct bpf_cpumask *active, *ovrflw;
u64 cpu_dsq_id, cpdom_dsq_id;
task_ctx *taskc_prev = NULL;
bool try_consume = false;
struct task_struct *p;
struct cpu_ctx *cpuc;
int ret;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
cpu_dsq_id = cpu_to_dsq(cpu);
cpdom_dsq_id = cpdom_to_dsq(cpuc->cpdom_id);
if (enable_cpu_bw && (ret = scx_cgroup_bw_reenqueue())) {
scx_bpf_error("Failed to reenqueue backlogged tasks: %d", ret);
}
if (prev && (prev->scx.flags & SCX_TASK_QUEUED) &&
is_lock_holder_running(cpuc)) {
consume_prev(prev, NULL, cpuc);
return;
}
if (use_full_cpus())
goto consume_out;
bpf_rcu_read_lock();
active = active_cpumask;
ovrflw = ovrflw_cpumask;
if (!active || !ovrflw) {
scx_bpf_error("Failed to prepare cpumasks.");
bpf_rcu_read_unlock();
return;
}
if (bpf_cpumask_test_cpu(cpu, cast_mask(active)) ||
bpf_cpumask_test_cpu(cpu, cast_mask(ovrflw))) {
bpf_rcu_read_unlock();
goto consume_out;
}
if (use_per_cpu_dsq() && scx_bpf_dsq_nr_queued(cpu_dsq_id)) {
bpf_cpumask_set_cpu(cpu, ovrflw);
bpf_rcu_read_unlock();
goto consume_out;
}
if (prev) {
if (is_pinned(prev)) {
bpf_cpumask_set_cpu(cpu, ovrflw);
bpf_rcu_read_unlock();
goto consume_out;
} else if (is_migration_disabled(prev)) {
bpf_rcu_read_unlock();
goto consume_out;
}
taskc_prev = get_task_ctx(prev);
if (taskc_prev &&
test_task_flag(taskc_prev, LAVD_FLAG_IS_AFFINITIZED) &&
bpf_cpumask_test_cpu(cpu, prev->cpus_ptr) &&
!bpf_cpumask_intersects(cast_mask(active), prev->cpus_ptr) &&
!bpf_cpumask_intersects(cast_mask(ovrflw), prev->cpus_ptr)) {
bpf_cpumask_set_cpu(cpu, ovrflw);
bpf_rcu_read_unlock();
goto consume_out;
}
}
if (!use_cpdom_dsq()) {
bpf_rcu_read_unlock();
return;
}
bpf_for_each(scx_dsq, p, cpdom_dsq_id, 0) {
task_ctx *taskc;
s32 new_cpu;
p = bpf_task_from_pid(p->pid);
if (!p)
break;
if (is_pinned(p)) {
new_cpu = scx_bpf_task_cpu(p);
if (new_cpu == cpu) {
bpf_cpumask_set_cpu(new_cpu, ovrflw);
bpf_task_release(p);
try_consume = true;
break;
}
if (!bpf_cpumask_test_and_set_cpu(new_cpu, ovrflw))
scx_bpf_kick_cpu(new_cpu, SCX_KICK_IDLE);
bpf_task_release(p);
continue;
} else if (is_migration_disabled(p)) {
new_cpu = scx_bpf_task_cpu(p);
if (new_cpu == cpu) {
bpf_task_release(p);
try_consume = true;
break;
}
bpf_task_release(p);
continue;
}
taskc = get_task_ctx(p);
if(taskc &&
(!test_task_flag(taskc, LAVD_FLAG_IS_AFFINITIZED) ||
bpf_cpumask_intersects(cast_mask(active), p->cpus_ptr) ||
bpf_cpumask_intersects(cast_mask(ovrflw), p->cpus_ptr))) {
bpf_task_release(p);
continue;
}
new_cpu = find_cpu_in(p->cpus_ptr, cpuc);
if (new_cpu >= 0) {
if (new_cpu == cpu) {
bpf_cpumask_set_cpu(new_cpu, ovrflw);
bpf_task_release(p);
try_consume = true;
break;
}
else if (!bpf_cpumask_test_and_set_cpu(new_cpu, ovrflw))
scx_bpf_kick_cpu(new_cpu, SCX_KICK_IDLE);
}
bpf_task_release(p);
}
bpf_rcu_read_unlock();
if (!try_consume)
return;
consume_out:
if (consume_task(cpu_dsq_id, cpdom_dsq_id))
return;
consume_prev(prev, taskc_prev, cpuc);
}
void BPF_STRUCT_OPS(lavd_runnable, struct task_struct *p, u64 enq_flags)
{
struct task_struct *waker;
task_ctx *p_taskc, *waker_taskc;
u64 now, interval;
int i;
p_taskc = get_task_ctx(p);
if (!p_taskc) {
scx_bpf_error("Failed to lookup task_ctx for task %d", p->pid);
return;
}
p_taskc->acc_runtime_wall = 0;
if (!(enq_flags & SCX_ENQ_WAKEUP))
return;
if (enq_flags & (SCX_ENQ_PREEMPT | SCX_ENQ_REENQ | SCX_ENQ_LAST))
return;
if (bpf_in_hardirq() || bpf_in_serving_softirq() || bpf_in_nmi())
return;
waker = bpf_get_current_task_btf();
if (is_ksoftirqd(waker))
return;
if ((p->real_parent != waker->real_parent))
return;
if (is_kernel_task(p) != is_kernel_task(waker))
return;
if (rt_or_dl_task(waker))
set_task_flag(p_taskc, LAVD_FLAG_WOKEN_BY_RT_DL);
else
reset_task_flag(p_taskc, LAVD_FLAG_WOKEN_BY_RT_DL);
waker_taskc = get_task_ctx(waker);
if (!waker_taskc) {
return;
}
now = scx_bpf_now();
interval = time_delta(now, READ_ONCE(waker_taskc->last_runnable_clk));
if (interval >= LAVD_LC_WAKE_INTERVAL_MIN) {
WRITE_ONCE(waker_taskc->wake_freq,
calc_avg_freq(waker_taskc->wake_freq, interval));
WRITE_ONCE(waker_taskc->last_runnable_clk, now);
}
p_taskc->lat_cri_waker = waker_taskc->lat_cri;
if (waker_taskc->lat_cri_wakee < p_taskc->lat_cri)
waker_taskc->lat_cri_wakee = p_taskc->lat_cri;
if (is_monitored) {
p_taskc->waker_pid = waker->pid;
for (i = 0; i < TASK_COMM_LEN && can_loop; i++)
p_taskc->waker_comm[i] = waker->comm[i];
}
}
void BPF_STRUCT_OPS(lavd_running, struct task_struct *p)
{
struct cpu_ctx *cpuc;
task_ctx *taskc;
u64 now = scx_bpf_now();
cpuc = get_cpu_ctx_task(p);
taskc = get_task_ctx(p);
if (!cpuc || !taskc) {
scx_bpf_error("Failed to lookup context for task %d", p->pid);
return;
}
if (p->scx.slice == SCX_SLICE_DFL)
p->scx.dsq_vtime = READ_ONCE(cur_logical_clk);
p->scx.slice = calc_time_slice(taskc, cpuc);
advance_cur_logical_clk(p);
update_stat_for_running(p, taskc, cpuc, now);
update_cpuperf_target(cpuc);
try_proc_introspec_cmd(p, taskc);
}
void BPF_STRUCT_OPS(lavd_tick, struct task_struct *p)
{
struct cpu_ctx *cpuc;
task_ctx *taskc;
u64 now;
cpuc = get_cpu_ctx_task(p);
taskc = get_task_ctx(p);
if (!cpuc || !taskc) {
scx_bpf_error("Failed to lookup context for task %d", p->pid);
return;
}
now = scx_bpf_now();
account_task_runtime(p, taskc, cpuc, now);
if (enable_cpu_bw && (cgroup_throttled(p, taskc, false) == -EAGAIN)) {
preempt_at_tick(p, cpuc);
return;
}
if (cpuc->nr_pinned_tasks)
shrink_slice_at_tick(p, cpuc, now);
}
void BPF_STRUCT_OPS(lavd_stopping, struct task_struct *p, bool runnable)
{
struct cpu_ctx *cpuc;
task_ctx *taskc;
cpuc = get_cpu_ctx_task(p);
taskc = get_task_ctx(p);
if (!cpuc || !taskc) {
scx_bpf_error("Failed to lookup context for task %d", p->pid);
return;
}
update_stat_for_stopping(p, taskc, cpuc);
}
void BPF_STRUCT_OPS(lavd_quiescent, struct task_struct *p, u64 deq_flags)
{
struct cpu_ctx *cpuc;
task_ctx *taskc;
u64 now, interval;
cpuc = get_cpu_ctx_task(p);
taskc = get_task_ctx(p);
if (!cpuc || !taskc) {
scx_bpf_error("Failed to lookup context for task %d", p->pid);
return;
}
cpuc->flags = 0;
if (is_pinned(p) && (taskc->pinned_cpu_id != -ENOENT)) {
__sync_fetch_and_sub(&cpuc->nr_pinned_tasks, 1);
taskc->pinned_cpu_id = -ENOENT;
debugln("%d [%d] -- %s:%d -- %s:%d", cpuc->cpu_id,
cpuc->nr_pinned_tasks, p->comm, p->pid, __func__,
__LINE__);
}
if (!(deq_flags & SCX_DEQ_SLEEP))
return;
now = scx_bpf_now();
interval = time_delta(now, taskc->last_quiescent_clk);
if (interval > 0) {
taskc->wait_freq = calc_avg_freq(taskc->wait_freq, interval);
taskc->last_quiescent_clk = now;
}
}
static void cpu_ctx_init_online(struct cpu_ctx *cpuc, u32 cpu_id, u64 now)
{
struct bpf_cpumask *cd_cpumask;
bpf_rcu_read_lock();
cd_cpumask = MEMBER_VPTR(cpdom_cpumask, [cpuc->cpdom_id]);
if (!cd_cpumask)
goto unlock_out;
bpf_cpumask_set_cpu(cpu_id, cd_cpumask);
unlock_out:
bpf_rcu_read_unlock();
cpuc->flags = 0;
cpuc->idle_start_clk = 0;
cpuc->lat_cri = 0;
cpuc->running_clk = 0;
cpuc->est_stopping_clk = SCX_SLICE_INF;
WRITE_ONCE(cpuc->online_clk, now);
barrier();
cpuc->is_online = true;
}
static void cpu_ctx_init_offline(struct cpu_ctx *cpuc, u32 cpu_id, u64 now)
{
struct bpf_cpumask *cd_cpumask;
bpf_rcu_read_lock();
cd_cpumask = MEMBER_VPTR(cpdom_cpumask, [cpuc->cpdom_id]);
if (!cd_cpumask)
goto unlock_out;
bpf_cpumask_clear_cpu(cpu_id, cd_cpumask);
unlock_out:
bpf_rcu_read_unlock();
cpuc->flags = 0;
cpuc->idle_start_clk = 0;
WRITE_ONCE(cpuc->offline_clk, now);
cpuc->is_online = false;
barrier();
cpuc->lat_cri = 0;
cpuc->running_clk = 0;
cpuc->est_stopping_clk = SCX_SLICE_INF;
}
void BPF_STRUCT_OPS(lavd_cpu_online, s32 cpu)
{
u64 now = scx_bpf_now();
struct cpu_ctx *cpuc;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
cpu_ctx_init_online(cpuc, cpu, now);
__sync_fetch_and_add(&nr_cpus_onln, 1);
__sync_fetch_and_add(&total_max_capacity, cpuc->max_capacity);
update_autopilot_high_cap();
update_sys_stat();
}
void BPF_STRUCT_OPS(lavd_cpu_offline, s32 cpu)
{
u64 now = scx_bpf_now();
struct cpu_ctx *cpuc;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
cpu_ctx_init_offline(cpuc, cpu, now);
__sync_fetch_and_sub(&nr_cpus_onln, 1);
__sync_fetch_and_sub(&total_max_capacity, cpuc->max_capacity);
update_autopilot_high_cap();
update_sys_stat();
}
void BPF_STRUCT_OPS(lavd_update_idle, s32 cpu, bool idle)
{
struct cpu_ctx *cpuc;
u64 now;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
now = scx_bpf_now();
if (idle) {
cpuc->idle_start_clk = now;
reset_cpu_preemption_info(cpuc, false);
}
else {
for (int i = 0; i < LAVD_MAX_RETRY; i++) {
u64 old_clk = cpuc->idle_start_clk;
if (old_clk == 0)
break;
bool ret = __sync_bool_compare_and_swap(
&cpuc->idle_start_clk, old_clk, 0);
if (ret) {
if (time_after(old_clk, now))
break;
u64 duration_wall = time_delta(now, old_clk);
__sync_fetch_and_add(&cpuc->idle_total_wall,
duration_wall);
break;
}
}
}
}
void BPF_STRUCT_OPS(lavd_set_cpumask, struct task_struct *p,
const struct cpumask *cpumask)
{
task_ctx *taskc;
taskc = get_task_ctx(p);
if (!taskc) {
scx_bpf_error("task_ctx_stor first lookup failed");
return;
}
if (bpf_cpumask_weight(p->cpus_ptr) != nr_cpu_ids)
set_task_flag(taskc, LAVD_FLAG_IS_AFFINITIZED);
else
reset_task_flag(taskc, LAVD_FLAG_IS_AFFINITIZED);
set_on_core_type(taskc, cpumask);
}
void BPF_STRUCT_OPS(lavd_cpu_acquire, s32 cpu,
struct scx_cpu_acquire_args *args)
{
struct cpu_ctx *cpuc;
u64 duration_wall, duration_invr;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
duration_wall = time_delta(scx_bpf_now(), cpuc->cpu_release_clk);
duration_invr = conv_wall_to_invr(duration_wall, cpuc);
cpuc->tot_task_time_invr += duration_invr;
cpuc->cpuperf_cur = scx_bpf_cpuperf_cur(cpu);
}
void BPF_STRUCT_OPS(lavd_cpu_release, s32 cpu,
struct scx_cpu_release_args *args)
{
struct cpu_ctx *cpuc;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx %d", cpu);
return;
}
cpuc->flags = 0;
reset_cpu_preemption_info(cpuc, true);
scx_bpf_reenqueue_local();
reset_cpuperf_target(cpuc);
cpuc->cpu_release_clk = scx_bpf_now();
}
void BPF_STRUCT_OPS(lavd_enable, struct task_struct *p)
{
task_ctx *taskc;
taskc = get_task_ctx(p);
if (!taskc) {
scx_bpf_error("task_ctx_stor first lookup failed");
return;
}
taskc->svc_time_wwgt = READ_ONCE(cur_svc_time_wwgt);
}
s32 BPF_STRUCT_OPS_SLEEPABLE(lavd_init_task, struct task_struct *p,
struct scx_init_task_args *args)
{
task_ctx *taskc, *taskc_parent;
struct task_struct *parent;
int i;
u64 now;
if (!p) {
scx_bpf_error("NULL task_struct pointer received");
return -ESRCH;
}
taskc = scx_task_alloc(p);
if (!taskc) {
scx_bpf_error("task_ctx_stor first lookup failed");
return -ENOMEM;
}
parent = bpf_task_from_pid(p->real_parent->pid);
if (parent && (taskc_parent = get_task_ctx(parent))) {
for (i = 0; i < sizeof(*taskc) && can_loop; i++)
((char __arena *)taskc)[i] = ((char __arena *)taskc_parent)[i];
} else {
for (i = 0; i < sizeof(*taskc) && can_loop; i++)
((char __arena *)taskc)[i] = 0;
now = scx_bpf_now();
taskc->last_runnable_clk = now;
taskc->last_running_clk = now;
taskc->last_quiescent_clk = now;
taskc->avg_runtime_wall = sys_stat.slice_wall;
taskc->svc_time_wwgt = sys_stat.avg_svc_time_wwgt;
}
taskc->pinned_cpu_id = -ENOENT;
taskc->pid = p->pid;
taskc->cgrp_id = args->cgroup->kn->id;
bpf_rcu_read_lock();
if (bpf_cpumask_weight(p->cpus_ptr) != nr_cpu_ids)
set_task_flag(taskc, LAVD_FLAG_IS_AFFINITIZED);
else
reset_task_flag(taskc, LAVD_FLAG_IS_AFFINITIZED);
set_on_core_type(taskc, p->cpus_ptr);
bpf_rcu_read_unlock();
if (is_ksoftirqd(p))
set_task_flag(taskc, LAVD_FLAG_KSOFTIRQD);
if (parent)
bpf_task_release(parent);
return 0;
}
s32 BPF_STRUCT_OPS(lavd_exit_task, struct task_struct *p,
struct scx_exit_task_args *args)
{
scx_task_free(p);
return 0;
}
void BPF_STRUCT_OPS(lavd_dump_task, struct scx_dump_ctx *dctx,
struct task_struct *p)
{
int cgroup_throttled = 0, task_throttled = 0;
char cgrp_name[64] = "unknown";
struct kernfs_node *kn;
struct cgroup *cgrp;
task_ctx *taskc;
taskc = get_task_ctx(p);
if (!taskc)
return;
if (enable_cpu_bw) {
cgroup_throttled = scx_cgroup_bw_is_cgroup_throttled(taskc->cgrp_id);
task_throttled = scx_cgroup_bw_is_task_throttled((u64)taskc);
}
cgrp = bpf_cgroup_from_id(taskc->cgrp_id);
if (cgrp) {
kn = BPF_CORE_READ(cgrp, kn);
bpf_probe_read_kernel_str(cgrp_name, sizeof(cgrp_name), BPF_CORE_READ(kn, name));
bpf_cgroup_release(cgrp);
}
scx_bpf_dump(" + given_slice: %llu lat_cri: %d/%d perf_cri: %d/%d\n",
taskc->slice_wall,
taskc->lat_cri, sys_stat.avg_lat_cri,
taskc->perf_cri, sys_stat.avg_perf_cri);
scx_bpf_dump(" + cpdom_id: %d cgroup: %s[%llu] (%s) task_status: %s\n",
taskc->cpdom_id,
cgrp_name, taskc->cgrp_id,
(cgroup_throttled) ? "throttled" : "not throttled",
(task_throttled) ? "throttled" : "not throttled");
}
static s32 init_cpdoms(u64 now)
{
struct cpdom_ctx *cpdomc;
int err;
for (int i = 0; i < LAVD_CPDOM_MAX_NR; i++) {
cpdomc = MEMBER_VPTR(cpdom_ctxs, [i]);
if (!cpdomc) {
scx_bpf_error("Failed to lookup cpdom_ctx for %d", i);
return -ESRCH;
}
if (!cpdomc->is_valid)
continue;
if (use_cpdom_dsq()) {
err = scx_bpf_create_dsq(cpdom_to_dsq(cpdomc->id),
cpdomc->numa_id);
if (err) {
scx_bpf_error("Failed to create a DSQ for cpdom %llu on NUMA node %d",
cpdomc->id, cpdomc->numa_id);
return err;
}
}
nr_cpdoms = i + 1;
}
return 0;
}
static int calloc_cpumask(struct bpf_cpumask **p_cpumask)
{
struct bpf_cpumask *cpumask;
cpumask = bpf_cpumask_create();
if (!cpumask)
return -ENOMEM;
cpumask = bpf_kptr_xchg(p_cpumask, cpumask);
if (cpumask)
bpf_cpumask_release(cpumask);
return 0;
}
static int init_cpumasks(void)
{
const struct cpumask *online_cpumask;
struct bpf_cpumask *active;
int err = 0;
bpf_rcu_read_lock();
err = calloc_cpumask(&active_cpumask);
active = active_cpumask;
if (err || !active)
goto out;
online_cpumask = scx_bpf_get_online_cpumask();
nr_cpus_onln = bpf_cpumask_weight(online_cpumask);
bpf_cpumask_copy(active, online_cpumask);
scx_bpf_put_cpumask(online_cpumask);
err = calloc_cpumask(&ovrflw_cpumask);
if (err)
goto out;
err = calloc_cpumask(&turbo_cpumask);
if (err)
goto out;
err = calloc_cpumask(&big_cpumask);
if (err)
goto out;
out:
bpf_rcu_read_unlock();
return err;
}
static s32 init_per_cpu_ctx(u64 now)
{
struct cpu_ctx *cpuc;
struct bpf_cpumask *turbo, *big, *active, *ovrflw, *cd_cpumask;
const struct cpumask *online_cpumask;
struct cpdom_ctx *cpdomc;
int cpu, i, j, k, err = 0;
u64 cpdom_id;
u32 sum_capacity = 0, big_capacity = 0;
bpf_rcu_read_lock();
online_cpumask = scx_bpf_get_online_cpumask();
turbo = turbo_cpumask;
big = big_cpumask;
active = active_cpumask;
ovrflw = ovrflw_cpumask;
if (!turbo || !big || !active || !ovrflw) {
scx_bpf_error("Failed to prepare cpumasks.");
err = -ENOMEM;
goto unlock_out;
}
one_little_max_capacity = LAVD_SCALE;
bpf_for(cpu, 0, nr_cpu_ids) {
if (cpu >= LAVD_CPU_ID_MAX)
break;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx: %d", cpu);
err = -ESRCH;
goto unlock_out;
}
err = calloc_cpumask(&cpuc->tmp_a_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_o_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_l_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_i_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_t_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_t2_mask);
if (err)
goto unlock_out;
err = calloc_cpumask(&cpuc->tmp_t3_mask);
if (err)
goto unlock_out;
cpuc->cpu_id = cpu;
cpuc->idle_start_clk = 0;
cpuc->lat_cri = 0;
cpuc->running_clk = 0;
cpuc->est_stopping_clk = SCX_SLICE_INF;
cpuc->online_clk = now;
cpuc->offline_clk = now;
cpuc->cpu_release_clk = now;
cpuc->is_online = bpf_cpumask_test_cpu(cpu, online_cpumask);
cpuc->max_capacity = cpu_capacity[cpu];
cpuc->effective_capacity = cpuc->max_capacity;
cpuc->big_core = cpu_big[cpu];
cpuc->turbo_core = cpu_turbo[cpu];
cpuc->min_perf_cri = LAVD_SCALE;
cpuc->max_freq = LAVD_SCALE;
cpuc->futex_op = LAVD_FUTEX_OP_INVALID;
sum_capacity += cpuc->max_capacity;
if (cpuc->big_core) {
nr_cpus_big++;
big_capacity += cpuc->max_capacity;
bpf_cpumask_set_cpu(cpu, big);
}
else {
have_little_core = true;
}
if (cpuc->turbo_core) {
bpf_cpumask_set_cpu(cpu, turbo);
have_turbo_core = true;
}
if (cpuc->max_capacity < one_little_max_capacity)
one_little_max_capacity = cpuc->max_capacity;
}
default_big_core_scale = (big_capacity << LAVD_SHIFT) / sum_capacity;
total_max_capacity = sum_capacity;
bpf_for(cpdom_id, 0, nr_cpdoms) {
if (cpdom_id >= LAVD_CPDOM_MAX_NR)
break;
cpdomc = MEMBER_VPTR(cpdom_ctxs, [cpdom_id]);
cd_cpumask = MEMBER_VPTR(cpdom_cpumask, [cpdom_id]);
if (!cpdomc || !cd_cpumask) {
scx_bpf_error("Failed to lookup cpdom_ctx for %llu", cpdom_id);
err = -ESRCH;
goto unlock_out;
}
if (!cpdomc->is_valid)
continue;
bpf_for(i, 0, LAVD_CPU_ID_MAX/64) {
u64 cpumask = cpdomc->__cpumask[i];
bpf_for(k, 0, 64) {
j = cpumask_next_set_bit(&cpumask);
if (j < 0)
break;
cpu = (i * 64) + j;
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx: %d", cpu);
err = -ESRCH;
goto unlock_out;
}
cpuc->llc_id = cpdomc->llc_id;
cpuc->cpdom_id = cpdomc->id;
cpuc->cpdom_alt_id = cpdomc->alt_id;
if (bpf_cpumask_test_cpu(cpu, online_cpumask)) {
bpf_cpumask_set_cpu(cpu, cd_cpumask);
cpdomc->nr_active_cpus++;
cpdomc->cap_sum_active_cpus += cpuc->effective_capacity;
}
}
}
}
bpf_for(cpu, 0, nr_cpu_ids) {
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx: %d", cpu);
err = -ESRCH;
goto unlock_out;
}
debugln("cpu[%d] max_capacity: %d, big_core: %d, turbo_core: %d, "
"cpdom_id: %llu, alt_id: %llu",
cpu, cpuc->max_capacity, cpuc->big_core, cpuc->turbo_core,
cpuc->cpdom_id, cpuc->cpdom_alt_id);
}
unlock_out:
scx_bpf_put_cpumask(online_cpumask);
bpf_rcu_read_unlock();
return err;
}
static int init_per_cpu_dsqs(void)
{
struct cpdom_ctx *cpdomc;
struct cpu_ctx *cpuc;
int cpu, err = 0;
bpf_for(cpu, 0, nr_cpu_ids) {
cpuc = get_cpu_ctx_id(cpu);
if (!cpuc) {
scx_bpf_error("Failed to lookup cpu_ctx: %d", cpu);
return -ESRCH;
}
cpdomc = MEMBER_VPTR(cpdom_ctxs, [cpuc->cpdom_id]);
if (!cpdomc) {
scx_bpf_error("Failed to lookup cpdom_ctx for %hhu", cpuc->cpdom_id);
return -ESRCH;
}
if (is_smt_active && (cpu != get_primary_cpu(cpu)))
continue;
err = scx_bpf_create_dsq(cpu_to_dsq(cpu), cpdomc->numa_id);
if (err) {
scx_bpf_error("Failed to create a DSQ for cpu %d on NUMA node %d",
cpu, cpdomc->numa_id);
return err;
}
}
return 0;
}
s32 BPF_STRUCT_OPS_SLEEPABLE(lavd_cgroup_init, struct cgroup *cgrp,
struct scx_cgroup_init_args *args)
{
int ret;
if (!enable_cpu_bw)
return 0;
ret = scx_cgroup_bw_init(cgrp, args);
if (ret)
scx_bpf_error("Failed to init a cgroup: %d", ret);
return ret;
}
void BPF_STRUCT_OPS(lavd_cgroup_exit, struct cgroup *cgrp)
{
int ret;
if (!enable_cpu_bw)
return;
ret = scx_cgroup_bw_exit(cgrp);
if (ret)
scx_bpf_error("Failed to exit a cgroup: %d", ret);
}
void BPF_STRUCT_OPS(lavd_cgroup_move, struct task_struct *p,
struct cgroup *from, struct cgroup *to)
{
task_ctx *taskc;
taskc = get_task_ctx(p);
if (!taskc)
scx_bpf_error("Failed to get a task context: %d", p->pid);
taskc->cgrp_id = to->kn->id;
}
void BPF_STRUCT_OPS(lavd_cgroup_set_bandwidth, struct cgroup *cgrp,
u64 period_us, u64 quota_us, u64 burst_us)
{
int ret;
if (!enable_cpu_bw)
return;
ret = scx_cgroup_bw_set(cgrp, period_us, quota_us, burst_us);
if (ret)
scx_bpf_error("Failed to set bandwidth of a cgroup: %d", ret);
}
int lavd_enqueue_cb(u64 ctx)
{
task_ctx *taskc = (task_ctx *)ctx;
struct task_struct *p;
if (!enable_cpu_bw)
return 0;
if ((p = bpf_task_from_pid(taskc->pid))) {
enqueue_cb(p);
bpf_task_release(p);
}
return 0;
}
REGISTER_SCX_CGROUP_BW_ENQUEUE_CB(lavd_enqueue_cb);
s32 BPF_STRUCT_OPS_SLEEPABLE(lavd_init)
{
u64 now = scx_bpf_now();
int err;
err = init_cpdoms(now);
if (err)
return err;
err = init_cpumasks();
if (err)
return err;
err = init_per_cpu_ctx(now);
if (err)
return err;
if (use_per_cpu_dsq()) {
err = init_per_cpu_dsqs();
if (err)
return err;
}
err = init_sys_stat(now);
if (err)
return err;
init_autopilot_caps();
WRITE_ONCE(cur_logical_clk, 0);
WRITE_ONCE(cur_svc_time_wwgt, 0);
if (enable_cpu_bw) {
struct scx_cgroup_bw_config bw_config = {
.verbose = verbose > 2,
};
err = scx_cgroup_bw_lib_init(&bw_config);
}
lavd_pid = (u32)bpf_get_current_pid_tgid();
return err;
}
void BPF_STRUCT_OPS(lavd_exit, struct scx_exit_info *ei)
{
UEI_RECORD(uei, ei);
}
static
int set_aggressive_migration(void)
{
struct task_struct *curr;
struct cpdom_ctx *cpdc;
struct cpu_ctx *cpuc;
task_ctx *taskc;
u32 cpu;
if (nr_cpdoms == 1)
return 0;
cpu = bpf_get_smp_processor_id();
if ((curr = bpf_get_current_task_btf()) &&
(taskc = get_task_ctx(curr)) &&
(cpuc = get_cpu_ctx_id(cpu)) &&
(cpdc = MEMBER_VPTR(cpdom_ctxs, [cpuc->cpdom_id])) &&
READ_ONCE(cpdc->is_stealee)) {
set_task_flag(taskc, LAVD_FLAG_MIGRATION_AGGRESSIVE);
scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT);
}
return 0;
}
SEC("?tracepoint/syscalls/sys_enter_execve")
int BPF_PROG(cond_hook_sys_enter_execve)
{
set_aggressive_migration();
return 0;
}
SEC("?tracepoint/syscalls/sys_enter_execveat")
int BPF_PROG(cond_hook_sys_enter_execveat)
{
set_aggressive_migration();
return 0;
}
SCX_OPS_DEFINE(lavd_ops,
.select_cpu = (void *)lavd_select_cpu,
.enqueue = (void *)lavd_enqueue,
.dequeue = (void *)lavd_dequeue,
.dispatch = (void *)lavd_dispatch,
.runnable = (void *)lavd_runnable,
.running = (void *)lavd_running,
.tick = (void *)lavd_tick,
.stopping = (void *)lavd_stopping,
.quiescent = (void *)lavd_quiescent,
.cpu_online = (void *)lavd_cpu_online,
.cpu_offline = (void *)lavd_cpu_offline,
.update_idle = (void *)lavd_update_idle,
.set_cpumask = (void *)lavd_set_cpumask,
.cpu_acquire = (void *)lavd_cpu_acquire,
.cpu_release = (void *)lavd_cpu_release,
.enable = (void *)lavd_enable,
.init_task = (void *)lavd_init_task,
.exit_task = (void *)lavd_exit_task,
.dump_task = (void *)lavd_dump_task,
.cgroup_init = (void *)lavd_cgroup_init,
.cgroup_exit = (void *)lavd_cgroup_exit,
.cgroup_move = (void *)lavd_cgroup_move,
.cgroup_set_bandwidth = (void *)lavd_cgroup_set_bandwidth,
.init = (void *)lavd_init,
.exit = (void *)lavd_exit,
.timeout_ms = 30000U,
.name = "lavd");