declare_scheduler

Macro declare_scheduler 

Source
declare_scheduler!() { /* proc-macro */ }
Expand description

Function-style macro that registers a Scheduler const.

§Syntax

use ktstr::prelude::*;

declare_scheduler!(MITOSIS, {
    name = "mitosis",
    binary = "scx_mitosis",
    cgroup_parent = "/ktstr",
    sched_args = ["--exit-dump-len", "1048576"],
    kernels = ["6.14", "7.0..=7.2"],
    constraints = TopologyConstraints {
        min_llcs: 1, max_llcs: Some(8), max_cpus: Some(64),
        ..TopologyConstraints::DEFAULT
    },
});

§Generated items

Given declare_scheduler!(MITOSIS, { ... }):

  • pub static MITOSIS: ::ktstr::test_support::Scheduler — the declared scheduler value. No _PAYLOAD suffix; the const IS the Scheduler.
  • A hidden static __KTSTR_SCHED_REG_MITOSIS: &'static Scheduler registered in KTSTR_SCHEDULERS (ktstr::test_support::KTSTR_SCHEDULERS) via linkme so the verifier can discover the declaration by spawning the test binary with --ktstr-list-schedulers.

§Visibility prefix

An optional Rust visibility prefix may precede the const name:

declare_scheduler!(MY_SCHED, { ... });             // defaults to `pub`
declare_scheduler!(pub MY_SCHED, { ... });          // explicit `pub`
declare_scheduler!(pub(crate) MY_SCHED, { ... });   // crate-local
declare_scheduler!(pub(super) MY_SCHED, { ... });   // parent-module
declare_scheduler!(pub(in crate::test_support) MY_SCHED, { ... });

Omitting the prefix defaults to pub — schedulers are normally public so the verifier and other crates can reference them; an explicit prefix is needed only when the declaration sits inside a module that wants to narrow the exposed name. (Field content shown above as { ... } is elided; consult the Syntax example for the required fields.) The hidden registry static (see Generated items above) is always static (private) regardless of the user-facing const’s visibility — linkme gathers it via link-section walking, not Rust name resolution, so the slice mechanism works at every visibility level.

§Accepted fields

Exactly one scheduler-source must be declared: binary, binary_path, or the kernel_builtin_enable + kernel_builtin_disable pair. The three options select between the matching SchedulerSpec variants. To run under the kernel default instead, reference ktstr::test_support::Scheduler::EEVDF directly rather than declaring a new scheduler.

FieldRequiredDescription
name = "..."yesScheduler name (sidecar / logs).
binary = "..."one sourceBinary name → SchedulerSpec::Discover(...). Matched against [[bin]] names in target/{debug,release}/, the test binary’s directory, or KTSTR_SCHEDULER env var. Often equal to the cargo package name but not required to be.
binary_path = "/abs/path"one sourceAbsolute filesystem path → SchedulerSpec::Path(...). The runtime does not auto-build this variant: the file must already exist at the path when the test runs. Use for prebuilt binaries that live outside the cargo discovery cascade. Macro-time validation rejects empty strings, relative paths, and ~-prefixed paths (no compile-time tilde expansion); existence is the runtime’s job.
kernel_builtin_enable = [..] + kernel_builtin_disable = [..]one sourceTwo string-array literals that together select SchedulerSpec::KernelBuiltin { enable: &[..], disable: &[..] }. The framework writes the enable commands to the guest’s /sched_enable and the disable commands to /sched_disable (see src/vmm/initramfs.rs), and the guest interpreter runs each entry once at scenario start / teardown. Both fields must be set together — setting only one is rejected. The interpreter (src/vmm/rust_init/dump.rs) accepts EXACTLY ONE shell-line shape: echo VALUE > /path (plus blank lines and # comments). Pipes, >>, ;, variable expansion, and any other syntax silently no-ops at runtime, so the macro rejects entries that don’t match echo … > /… at expand time. At least one of the two arrays must be non-empty: a pair that supplies neither enable nor disable commands is equivalent to the EEVDF baseline — reference Scheduler::EEVDF for that. Note: cargo ktstr export currently bails on KernelBuiltin schedulers (src/export.rs); declarations using this variant cannot be reproduced via the export-to-shar workflow until that limitation is lifted.
topology = (numa, llcs, cores, threads)noDefault VM topology. Default: (1, 1, 2, 1) (from Scheduler::named). Validated at compile time: each value must be non-zero, and llcs must be a multiple of numa.
cgroup_parent = "..."noCgroup parent path (must begin with /).
sched_args = [..]noScheduler CLI args prepended before per-test extra_sched_args.
sysctls = [Sysctl::new("k", "v"), ..]noGuest sysctls.
kargs = [..]noExtra guest kernel cmdline args.
kernels = ["6.14", "7.0..=7.2", ..]noKernel specs the verifier sweeps. Same parser as the --kernel CLI flag — accepts exact versions, ranges (.. or ..=, both inclusive), git refs (git+URL#tag=NAME), paths, and cache keys. Each entry is validated at macro-expand time via the same KernelId::parse + validate the verifier uses at runtime; empty entries, inverted ranges, and ..-containing strings whose endpoints aren’t version-shaped (e.g. "abc..def") are rejected.
constraints = TopologyConstraints { .. }noGauntlet preset constraints — maps directly onto Scheduler::constraints. Filters which gauntlet topology presets exercise this scheduler. When given as a struct literal, the macro additionally cross-checks each literal field against the effective topology (explicit topology field if present, otherwise the (1, 1, 2, 1) default from Scheduler::named) and rejects infeasible pairings; non-struct-literal forms (e.g. OTHER::CONST_CONSTRAINTS) skip that check.
assert = Assert::NO_OVERRIDES.method().chain()noScheduler-wide assertion overrides — maps directly onto Scheduler::assert. Merged with Assert::default_checks() and the per-test assert at runtime (default ← scheduler ← per-test). Accepts any const-evaluable expression: a const path like Assert::NO_OVERRIDES, a const-fn call like Assert::default_checks(), or a chain of const-fn setters like Assert::NO_OVERRIDES.check_not_starved().max_gap_ms(50). The macro accepts MethodCall chains and Path-rooted (type/module-prefixed) Calls — only bare single-segment lowercase Calls like helper() are rejected as non-const free-fn patterns; non-const methods on a Path receiver slip through and surface as a deep const-eval failure at the spread site.
config_file = "..."noHost-side config file path.
config_file_def = ("--config {file}", "/include-files/cfg.json")noInline-config plumbing — maps directly onto Scheduler::config_file_def. 2-tuple of string literals: arg_template (CLI arg with {file} placeholder substituted at run time) and guest_path (absolute path where the framework writes the JSON inside the guest). Distinct from config_file (which references a pre-existing host file). The macro validates: tuple-arity = 2, both elements non-empty string literals, {file} placeholder present in arg_template, guest_path absolute.

§Const naming rules

The first argument must be a SCREAMING_SNAKE_CASE identifier and must NOT be one of the reserved built-in names (EEVDF, KERNEL_DEFAULT). The macro emits a compile_error! if either rule is violated.