mirror of
https://github.com/sched-ext/scx.git
synced 2024-12-05 00:37:11 +00:00
678b10133d
Introduce scx_flash (Fair Latency-Aware ScHeduler), a scheduler that
focuses on ensuring fairness among tasks and performance predictability.
This scheduler is introduced as a replacement of the "lowlatency" mode
in scx_bpfland, that has been dropped in commit 78101e4
("scx_bpfland:
drop lowlatency mode and the priority DSQ").
scx_flash operates based on an EDF (Earliest Deadline First) policy,
where each task is assigned a latency weight. This weight is adjusted
dynamically, influenced by the task's static weight and how often it
releases the CPU before its full assigned time slice is used: tasks that
release the CPU early receive a higher latency weight, granting them
a higher priority over tasks that fully use their time slice.
The combination of dynamic latency weights and EDF scheduling ensures
responsive and stable performance, even in overcommitted systems, making
the scheduler particularly well-suited for latency-sensitive workloads,
such as multimedia or real-time audio processing.
Tested-by: Peter Jung <ptr1337@cachyos.org>
Tested-by: Piotr Gorski <piotrgorski@cachyos.org>
Signed-off-by: Andrea Righi <arighi@nvidia.com>
471 lines
17 KiB
Meson
471 lines
17 KiB
Meson
project('sched_ext schedulers', 'c',
|
|
version: '1.0.6',
|
|
license: 'GPL-2.0',
|
|
meson_version : '>= 1.2.0',)
|
|
|
|
fs = import('fs')
|
|
|
|
cc = meson.get_compiler('c')
|
|
|
|
enable_rust = get_option('enable_rust')
|
|
|
|
bpf_clang = find_program(get_option('bpf_clang'))
|
|
|
|
run_veristat = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/veristat'))
|
|
run_veristat_diff = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/veristat_diff'))
|
|
|
|
enable_stress = get_option('enable_stress')
|
|
|
|
veristat_scheduler = get_option('veristat_scheduler')
|
|
|
|
veristat_diff_dir = get_option('veristat_diff_dir')
|
|
|
|
build_outside_src = get_option('build_outside_src')
|
|
|
|
if enable_stress
|
|
run_stress_tests = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/run_stress_tests'))
|
|
endif
|
|
|
|
get_clang_ver = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/get_clang_ver'))
|
|
get_bpftool_ver = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/get_bpftool_ver'))
|
|
bpftool_build_skel = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/bpftool_build_skel'))
|
|
get_sys_incls = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/get_sys_incls'))
|
|
test_sched = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/test_sched'))
|
|
fetch_libbpf = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/fetch_libbpf'))
|
|
build_libbpf = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/build_libbpf'))
|
|
fetch_bpftool = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/fetch_bpftool'))
|
|
build_bpftool = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/build_bpftool'))
|
|
|
|
bpf_clang_ver = run_command(get_clang_ver, bpf_clang, check: true).stdout().strip()
|
|
if bpf_clang_ver == ''
|
|
error('Unable to find clang version')
|
|
endif
|
|
|
|
bpf_clang_maj = bpf_clang_ver.split('.')[0].to_int()
|
|
|
|
if bpf_clang_maj < 16
|
|
error('clang < 16 loses high 32 bits of 64 bit enums when compiling BPF (@0@ ver=@1@)'
|
|
.format(bpf_clang.full_path(), bpf_clang_ver))
|
|
elif bpf_clang_maj < 17
|
|
warning('clang >= 17 recommended (@0@ ver=@1@)'
|
|
.format(bpf_clang.full_path(), bpf_clang_ver))
|
|
endif
|
|
|
|
# These are for building libbpf and/or bpftool
|
|
|
|
if meson.get_compiler('c').get_id() == 'gcc'
|
|
extra_args = ['-Wno-sign-compare', '-Wno-maybe-uninitialized', '-Wno-calloc-transposed-args']
|
|
else
|
|
extra_args = []
|
|
endif
|
|
|
|
executable('cc_cflags_probe', 'meson-scripts/cc_cflags_probe.c', install: false, pie: true, c_args : extra_args)
|
|
|
|
jq = find_program('jq')
|
|
make = find_program('make')
|
|
nproc = find_program('nproc')
|
|
|
|
make_jobs = 1
|
|
if nproc.found()
|
|
make_jobs = run_command(nproc, check: true).stdout().to_int()
|
|
endif
|
|
|
|
libbpf_path = '@0@/libbpf'.format(meson.current_build_dir())
|
|
libbpf_src_path = '@0@/src'.format(libbpf_path)
|
|
libbpf_a = '@0@/libbpf.a'.format(libbpf_src_path)
|
|
should_build_libbpf = true
|
|
libbpf_h = get_option('libbpf_h')
|
|
libbpf_local_h = get_option('libbpf_h')
|
|
|
|
if get_option('libbpf_a') == 'disabled'
|
|
libbpf_a = ''
|
|
should_build_libbpf = false
|
|
elif get_option('libbpf_a') != ''
|
|
libbpf_a = get_option('libbpf_a')
|
|
if not fs.exists(libbpf_a)
|
|
error('@0@ does not exist.'.format(libbpf_a))
|
|
endif
|
|
should_build_libbpf = false
|
|
endif
|
|
|
|
# WARNING! To build libbpf with the same compiler(CC) and CFLAGS
|
|
# as the schedulers we need to do this hack whereby we create a dummy exe
|
|
# then read the compiler and args from meson's compile_commands.json
|
|
# and re-set them when we build libbpf with make
|
|
if should_build_libbpf
|
|
if not jq.found() or not make.found()
|
|
error('To build the libbpf library "make" and "jq" are required')
|
|
endif
|
|
|
|
libbpf_header_paths = ['/src/usr/include', '/include/uapi']
|
|
|
|
libbpf_h = []
|
|
|
|
# This exists because meson doesn't like absolute paths for include_directories
|
|
# if they are found within the same directory as the source
|
|
libbpf_local_h = []
|
|
local_build_path = meson.current_build_dir().replace(meson.current_source_dir(), '')
|
|
|
|
foreach path : libbpf_header_paths
|
|
libbpf_h += ['@0@'.format(libbpf_path) + path]
|
|
if not build_outside_src
|
|
libbpf_local_h += ['.@0@/libbpf'.format(local_build_path) + path]
|
|
else
|
|
libbpf_local_h += ['@0@/libbpf'.format(local_build_path) + path]
|
|
endif
|
|
endforeach
|
|
|
|
message('Fetching libbpf repo')
|
|
libbpf_commit = '686f600bca59e107af4040d0838ca2b02c14ff50'
|
|
run_command(fetch_libbpf, meson.current_build_dir(), libbpf_commit, check: true)
|
|
|
|
make_jobs = 1
|
|
if nproc.found()
|
|
make_jobs = run_command(nproc, check: true).stdout().to_int()
|
|
endif
|
|
|
|
libbpf = custom_target('libbpf',
|
|
output: '@PLAINNAME@.__PHONY__',
|
|
input: 'meson-scripts/cc_cflags_probe.c',
|
|
command: [build_libbpf, jq, make, libbpf_src_path, '@0@'.format(make_jobs)],
|
|
build_by_default: true)
|
|
else
|
|
# this is a noop when we're not building libbpf ourselves
|
|
libbpf = custom_target('libbpf',
|
|
output: '@PLAINNAME@.__PHONY__',
|
|
input: 'meson-scripts/cc_cflags_probe.c',
|
|
command: ['echo'],
|
|
build_by_default: true)
|
|
endif
|
|
|
|
if libbpf_a != ''
|
|
libbpf_dep = [declare_dependency(
|
|
link_args: libbpf_a,
|
|
include_directories: libbpf_local_h),
|
|
cc.find_library('elf'),
|
|
cc.find_library('z'),
|
|
cc.find_library('zstd')]
|
|
else
|
|
libbpf_dep = dependency('libbpf', version: '>=1.4')
|
|
endif
|
|
|
|
if get_option('kernel_headers') != ''
|
|
kernel_headers = get_option('kernel_headers')
|
|
kernel_dep = [declare_dependency(include_directories: kernel_headers)]
|
|
else
|
|
kernel_dep = []
|
|
endif
|
|
|
|
bpftool_path = '@0@/bpftool/src'.format(meson.current_build_dir())
|
|
should_build_bpftool = true
|
|
bpftool_exe_path = '@0@/bpftool'.format(bpftool_path)
|
|
|
|
if get_option('bpftool') == 'disabled'
|
|
bpftool = find_program('bpftool')
|
|
should_build_bpftool = false
|
|
bpftool_exe_path = bpftool.full_path()
|
|
elif get_option('bpftool') != ''
|
|
bpftool = find_program(get_option('bpftool'))
|
|
should_build_bpftool = false
|
|
bpftool_exe_path = bpftool.full_path()
|
|
endif
|
|
|
|
if should_build_bpftool
|
|
message('Fetching bpftool repo')
|
|
bpftool_commit = '77a72987353fcae8ce330fd87d4c7afb7677a169'
|
|
run_command(fetch_bpftool, meson.current_build_dir(), bpftool_commit, check: true)
|
|
|
|
bpftool_target = custom_target('bpftool_target',
|
|
output: '@PLAINNAME@.__PHONY__',
|
|
input: 'meson-scripts/bpftool_dummy.c',
|
|
command: [build_bpftool, jq, make, bpftool_path, '@0@'.format(make_jobs)],
|
|
build_by_default: true)
|
|
else
|
|
bpftool_ver = run_command(get_bpftool_ver, bpftool_exe_path, check: true).stdout().strip()
|
|
bpftool_maj = bpftool_ver.split('.')[0].to_int()
|
|
bpftool_min = bpftool_ver.split('.')[1].to_int()
|
|
if bpftool_maj < 7 or (bpftool_maj == 7 and bpftool_min < 4)
|
|
error('bpftool >= 7.4 required (@0@ ver=@1@)'.format(bpftool_exe_path, bpftool_ver))
|
|
endif
|
|
# this is a noop when we're not building bpftool ourselves
|
|
bpftool_target = custom_target('bpftool_target',
|
|
output: '@PLAINNAME@.__PHONY__',
|
|
input: 'meson-scripts/bpftool_dummy.c',
|
|
command: ['echo'],
|
|
build_by_default: true)
|
|
endif
|
|
# end libbpf/bpftool stuff
|
|
|
|
#
|
|
# Determine bpf_base_cflags which will be used to compile all .bpf.o files.
|
|
# Note that "-target bpf" is not included to accommodate
|
|
# libbpf_cargo::SkeletonBuilder.
|
|
#
|
|
# Map https://mesonbuild.com/Reference-tables.html#cpu-families to the
|
|
# __TARGET_ARCH list in tools/lib/bpf/bpf_tracing.h in the kernel tree.
|
|
#
|
|
arch_dict = {
|
|
'x86': 'x86',
|
|
'x86_64': 'x86',
|
|
's390x': 's390',
|
|
'arm': 'arm',
|
|
'aarch64': 'arm64',
|
|
'mips': 'mips',
|
|
'mips64': 'mips',
|
|
'ppc': 'powerpc',
|
|
'ppc64': 'powerpc',
|
|
'sparc': 'sparc',
|
|
'sparc64': 'sparc',
|
|
'riscv32': 'riscv',
|
|
'riscv64': 'riscv',
|
|
'arc': 'arc',
|
|
'loongarch64': 'loongarch'
|
|
}
|
|
|
|
cpu = host_machine.cpu_family()
|
|
if cpu not in arch_dict
|
|
error('CPU family "@0@" is not in known arch dict'.format(cpu))
|
|
endif
|
|
|
|
sys_incls = run_command(get_sys_incls, bpf_clang, check: true).stdout().splitlines()
|
|
bpf_base_cflags = ['-g', '-O2', '-Wall', '-Wno-compare-distinct-pointer-types',
|
|
'-D__TARGET_ARCH_' + arch_dict[cpu], '-mcpu=v3',
|
|
'-m@0@-endian'.format(host_machine.endian())] + sys_incls
|
|
|
|
if get_option('werror')
|
|
bpf_base_cflags += '-Werror'
|
|
endif
|
|
|
|
message('cpu=@0@ bpf_base_cflags=@1@'.format(cpu, bpf_base_cflags))
|
|
|
|
libbpf_c_headers = []
|
|
|
|
if libbpf_a != ''
|
|
foreach header: libbpf_h
|
|
libbpf_c_headers += ['-I', header]
|
|
endforeach
|
|
endif
|
|
|
|
#
|
|
# Generators to build BPF skel file for C schedulers.
|
|
#
|
|
gen_bpf_o = generator(bpf_clang,
|
|
output: '@BASENAME@.o',
|
|
depends: [libbpf],
|
|
arguments: [bpf_base_cflags, '-target', 'bpf', libbpf_c_headers, '@EXTRA_ARGS@',
|
|
'-c', '@INPUT@', '-o', '@OUTPUT@'])
|
|
gen_bpf_skel = generator(bpftool_build_skel,
|
|
output: ['@BASENAME@.skel.h','@BASENAME@.subskel.h' ],
|
|
depends: [libbpf, bpftool_target],
|
|
arguments: [bpftool_exe_path, '@INPUT@', '@OUTPUT0@', '@OUTPUT1@'])
|
|
|
|
#
|
|
# For rust sub-projects.
|
|
#
|
|
cargo_build_args = ['--quiet']
|
|
if get_option('buildtype') == 'release' or get_option('buildtype') == 'plain'
|
|
cargo_build_args += '--release'
|
|
endif
|
|
|
|
if get_option('offline')
|
|
cargo_build_args += '--offline'
|
|
endif
|
|
|
|
if get_option('kernel') != ''
|
|
kernel = get_option('kernel')
|
|
endif
|
|
|
|
if enable_rust
|
|
cargo = find_program(get_option('cargo'))
|
|
cargo_fetch = find_program(join_paths(meson.current_source_dir(),
|
|
'meson-scripts/cargo_fetch'))
|
|
cargo_env = environment()
|
|
cargo_env.set('BPF_CLANG', bpf_clang.full_path())
|
|
|
|
meson.add_install_script('meson-scripts/install_rust_user_scheds')
|
|
|
|
foreach flag: bpf_base_cflags
|
|
cargo_env.append('BPF_BASE_CFLAGS', flag, separator: ' ')
|
|
endforeach
|
|
|
|
if libbpf_a != ''
|
|
foreach header: libbpf_h
|
|
cargo_env.append('BPF_EXTRA_CFLAGS_PRE_INCL', '-I' + header, separator: ' ')
|
|
endforeach
|
|
|
|
cargo_env.append('RUSTFLAGS',
|
|
'-C relocation-model=pic -C link-args=-lelf -C link-args=-lz -C link-args=-lzstd -L '
|
|
+ fs.parent(libbpf_a), separator: ' ')
|
|
|
|
#
|
|
# XXX - scx_rusty's original Cargo.toml contained a dependency matching
|
|
# the following. However, it doesn't seem necessary to enable linking to
|
|
# libbpf.a. Ask Dan Schatzberg about the role the dependency line plays.
|
|
#
|
|
#cargo_build_args += ['--config',
|
|
# 'dependencies.libbpf-sys.version="1.2"',
|
|
# '--config',
|
|
# 'dependencies.libbpf-sys.features=["novendor", "static"]']
|
|
endif
|
|
|
|
if get_option('cargo_home') != ''
|
|
cargo_env.set('CARGO_HOME', get_option('cargo_home'))
|
|
endif
|
|
|
|
run_target('fetch', command: [cargo_fetch, cargo], env: cargo_env)
|
|
|
|
rust_scheds = ['scx_lavd', 'scx_bpfland', 'scx_rustland', 'scx_rlfifo',
|
|
'scx_flash', 'scx_rusty',
|
|
'scx_layered', 'scx_mitosis']
|
|
rust_misc = ['scx_stats', 'scx_stats_derive', 'scx_utils',
|
|
'scx_rustland_core',
|
|
'scx_loader']
|
|
|
|
sched_deps = [libbpf, bpftool_target]
|
|
cargo_cmd = [cargo, 'build', '--manifest-path=@INPUT@', '--target-dir=@OUTDIR@',
|
|
cargo_build_args]
|
|
|
|
# target to compile all rust subprojects
|
|
custom_target('rust_all',
|
|
output: '@PLAINNAME@.__PHONY__',
|
|
input: 'Cargo.toml',
|
|
command: cargo_cmd,
|
|
env: cargo_env,
|
|
depends: sched_deps,
|
|
build_by_default: true,
|
|
build_always_stale: true)
|
|
|
|
# targets to build individual rust subprojects
|
|
foreach p : rust_scheds + rust_misc
|
|
custom_target(p,
|
|
output: p + '@PLAINNAME@.__PHONY__',
|
|
input: 'Cargo.toml',
|
|
command: cargo_cmd + ['-p', p],
|
|
env: cargo_env,
|
|
depends: sched_deps,
|
|
build_by_default: false,
|
|
build_always_stale: true)
|
|
endforeach
|
|
else
|
|
rust_scheds = []
|
|
endif
|
|
|
|
run_target('test_sched', command: [test_sched, kernel])
|
|
run_target('veristat', command: [run_veristat, meson.current_build_dir(),
|
|
get_option('veristat_scheduler'), get_option('kernel')])
|
|
|
|
if get_option('vng_rw_mount') == true
|
|
foreach s : rust_scheds
|
|
run_target('test_sched_'+s, command: [test_sched, kernel, s, 'VNG_RW=true'])
|
|
run_target('veristat_'+s, command: [run_veristat, meson.current_build_dir(),
|
|
get_option('veristat_scheduler'), get_option('kernel'), s, 'VNG_RW=true'])
|
|
endforeach
|
|
else
|
|
foreach s : rust_scheds
|
|
run_target('test_sched_'+s, command: [test_sched, kernel, s])
|
|
run_target('veristat_'+s, command: [run_veristat, meson.current_build_dir(),
|
|
get_option('veristat_scheduler'), get_option('kernel'), s])
|
|
endforeach
|
|
endif
|
|
|
|
run_target('veristat_diff', command: [run_veristat_diff, meson.current_build_dir(),
|
|
get_option('veristat_scheduler'), get_option('kernel'),
|
|
get_option('veristat_diff_dir')])
|
|
|
|
if enable_stress
|
|
# not sure there's a better way
|
|
# only different...
|
|
copys = [
|
|
custom_target('copy stress wrapper',
|
|
input : 'scripts/bpftrace_stress_wrapper.sh',
|
|
output : 'bpftrace_stress_wrapper.sh',
|
|
command : ['cp', '-a', '@INPUT@', '@OUTPUT@'],
|
|
install : false,
|
|
build_by_default : true),
|
|
custom_target('copy dsq_lat',
|
|
input : 'scripts/dsq_lat.bt',
|
|
output : 'dsq_lat.bt',
|
|
command : ['cp', '-a', '@INPUT@', '@OUTPUT@'],
|
|
install : false,
|
|
build_by_default : true),
|
|
custom_target('copy runq_lat',
|
|
input : 'scripts/process_runqlat.bt',
|
|
output : 'process_runqlat.bt',
|
|
command : ['cp', '-a', '@INPUT@', '@OUTPUT@'],
|
|
install : false,
|
|
build_by_default : true)
|
|
]
|
|
run_target('stress_tests', command: [run_stress_tests, '-k', kernel, '-b',
|
|
meson.current_build_dir()], depends: [copys])
|
|
|
|
if get_option('vng_rw_mount') == true
|
|
foreach s : rust_scheds
|
|
if get_option('kernel_headers') == ''
|
|
run_target('stress_tests_'+s, command: [run_stress_tests, '-k', kernel,
|
|
'-b', meson.current_build_dir(), '--sched', s,
|
|
'--rw', 'true',
|
|
'--extra-scheduler-args', get_option('extra_sched_args')],
|
|
depends: [copys])
|
|
else
|
|
run_target('stress_tests_'+s, command: [run_stress_tests, '-k', kernel,
|
|
'-b', meson.current_build_dir(), '--sched', s,
|
|
'--rw', 'true',
|
|
'--headers', get_option('kernel_headers'),
|
|
'--extra-scheduler-args', get_option('extra_sched_args')],
|
|
depends: [copys])
|
|
endif
|
|
endforeach
|
|
else
|
|
foreach s : rust_scheds
|
|
if get_option('kernel_headers') == ''
|
|
run_target('stress_tests_'+s, command: [run_stress_tests, '-k', kernel,
|
|
'-b', meson.current_build_dir(), '--sched', s,
|
|
'--extra-scheduler-args', get_option('extra_sched_args')],
|
|
depends: [copys])
|
|
else
|
|
run_target('stress_tests_'+s, command: [run_stress_tests, '-k', kernel,
|
|
'-b', meson.current_build_dir(), '--sched', s,
|
|
'--headers', get_option('kernel_headers'),
|
|
'--extra-scheduler-args', get_option('extra_sched_args')],
|
|
depends: [copys])
|
|
endif
|
|
endforeach
|
|
endif
|
|
endif
|
|
|
|
subdir('scheds')
|
|
|
|
systemd = dependency('systemd', required: get_option('systemd'))
|
|
|
|
if systemd.found()
|
|
subdir('services/systemd')
|
|
endif
|
|
|
|
openrc = dependency('openrc', required: get_option('openrc'))
|
|
|
|
if openrc.found()
|
|
subdir('services/openrc')
|
|
endif
|
|
|
|
libalpm = dependency('libalpm', required: get_option('libalpm'))
|
|
|
|
if libalpm.found() and systemd.found()
|
|
subdir('libalpm/systemd')
|
|
endif
|
|
|
|
if libalpm.found() and openrc.found()
|
|
subdir('libalpm/openrc')
|
|
endif
|