Payload

Struct Payload 

Source
#[non_exhaustive]
pub struct Payload { pub name: &'static str, pub kind: PayloadKind, pub output: OutputFormat, pub default_args: &'static [&'static str], pub default_checks: &'static [MetricCheck], pub metrics: &'static [MetricHint], pub include_files: &'static [&'static str], pub uses_parent_pgrp: bool, pub known_flags: Option<&'static [&'static str]>, }
Expand description

A test payload — either a scheduler or a userspace binary to run inside the guest VM.

Payload unifies the two launch modes under one #[ktstr_test] attribute surface: tests declare scheduler = SOME_SCHED for scheduler-centric runs, payload = SOME_BIN for binary runs, or both with workloads = [...] to compose binaries under a scheduler. See PayloadKind for the two variants.

Use Payload::KERNEL_DEFAULT as the default scheduler placeholder when a test doesn’t attach a sched_ext scheduler — it wraps the kernel’s default scheduler (EEVDF on Linux 6.6+) via Scheduler::EEVDF.

Payload intentionally does NOT implement serde::Serialize / serde::Deserialize. It is a compile-time-static definition that references &'static Scheduler and &'static [&'static str] slices — lifetimes that serialization cannot round-trip. Runtime telemetry (per-payload metrics, exit codes, names) is serialized via PayloadMetrics and Metric instead; those own their data.

#[non_exhaustive] reserves the right to add fields without breaking downstream code. Out-of-crate callers cannot construct Payload via struct literal — use the const-fn constructors (Payload::new, Payload::binary) or the #[derive(Payload)] macro, which routes through Payload::new under the hood.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§name: &'static str

Short, stable name used in logs and sidecar records.

§kind: PayloadKind

Launch kind — scheduler reference or binary name.

§output: OutputFormat

How the framework extracts metrics from the payload’s stdout, with stderr fallback when stdout yields no metrics. See OutputFormat for the per-variant contract and scenario::payload_run for the fallback mechanics.

§default_args: &'static [&'static str]

Default CLI args appended when this payload runs. Test bodies can extend via .arg(...) or replace via .clear_args() + .arg(...) on the runtime builder.

§default_checks: &'static [MetricCheck]

Author-declared default checks evaluated against extracted PayloadMetrics. Payloads that need exit-code gating should include MetricCheck::ExitCodeEq(0) here; the runtime evaluates ExitCodeEq as a pre-pass before metric checks.

§metrics: &'static [MetricHint]

Declared metric hints — polarity, unit. Unhinted metrics extracted from output land as Polarity::Unknown.

§include_files: &'static [&'static str]

Host-side file specs resolved at runtime. Each entry is resolved through the framework’s include-file pipeline — the same resolver used by CLI -i / --include-files arguments: bare names are searched in the host’s PATH, explicit paths (absolute, relative, or containing /) must exist on the host, and directories are walked recursively. The entry’s scheduler / payload / workloads / extra_include_files are aggregated at test time via KtstrTestEntry::all_include_files and resolved through the same pipeline the ktstr shell -i path uses. Populate via the #[include_files("helper", ...)] attribute on #[derive(Payload)] or by spelling the array in the struct literal.

§uses_parent_pgrp: bool

When true, the payload’s spawn path does NOT place the child into its own process group via CommandExt::process_group(0). The child inherits the parent ktstr process’s pgid. Default (false) keeps the existing “fresh pgrp → killpg-reaches-descendants” model — see src/scenario/payload_run.rs::build_command.

Opt-in for tty-dependent binaries: a shell-like tool that uses the controlling terminal’s foreground process group for signal delivery (job-control signals, SIGHUP on tty close) reads a fresh pgrp as “no job control”, which breaks interactive shells and less-style readers. Payloads that need tty job-control semantics set this true so they stay in the parent’s pgrp and keep the inherited controlling-terminal association.

Trade-off on the true branch: multi-process payloads can no longer be killed via killpg(child_pid, SIGKILL) because the child is not a pgrp leader; the kill path falls back to single-pid kill(pid, SIGKILL) and any descendants that the payload forks must either react to SIGHUP / pipe close or run the risk of orphaning. Most payloads should leave this false.

§known_flags: Option<&'static [&'static str]>

When Some, the listed flag names form an allowlist that Op::RunPayload validation checks against at scenario- execution time (inside apply_ops, before the payload spawn) — any user-supplied --flag whose name is not in the allowlist produces an error surfaced through the step executor, surfacing typos as loud errors instead of silent no-ops that only manifest as “feature didn’t activate” in the test output.

None (default) disables validation — the payload accepts arbitrary flag sets. Use None for payloads that wrap binaries with open-ended flag surfaces (stress-ng, fio, schbench) where enumerating every accepted flag is either impossible or high-churn.

Some(&[]) is legal but rarely intended: it rejects EVERY long flag, including ones the wrapped binary legitimately accepts. Use None for “no validation” and a non-empty slice for “validate against this allowlist” — an empty slice means “only positional args and short flags are acceptable”, which is almost never what a Payload author wants.

Flag names in the slice are bare (no leading --) and match the syntax of Op::RunPayload’s per-flag slot.

Implementations§

Source§

impl Payload

Source

pub const KERNEL_DEFAULT: Payload

Placeholder payload that wraps the current kernel-default scheduler — Scheduler::EEVDF on Linux 6.6+ (the “no scx scheduler attached” case). Used as the default value of the scheduler slot on KtstrTestEntry so tests without an explicit scheduler = ... attribute still get a valid, non-optional reference. Wire name is "kernel_default" — the Rust const and the serialized form agree, so the const describes what it selects for (the kernel’s default) rather than naming a specific scheduler that a future kernel release could replace.

§kernel_default vs eevdf in sidecars

KERNEL_DEFAULT.name is "kernel_default" (the intent-level label), while KERNEL_DEFAULT.scheduler_name() returns "eevdf" (the inner Scheduler::EEVDF’s .name). The two names answer different questions:

  • "kernel_default" answers “what did the test author select?” — a future kernel release replacing EEVDF keeps this label stable, so an in-memory match on author intent survives kernel upgrades.
  • "eevdf" answers “what scheduler actually ran?” — the concrete scheduling class in effect.

Sidecar serialization reads the scheduler-slot Scheduler directly. The SidecarResult.scheduler field (src/test_support/sidecar/mod.rs) is populated by reading entry.scheduler.name — a field access on the &'static Scheduler held in the entry’s scheduler slot, with no Payload indirection. When the scheduler slot holds &Scheduler::EEVDF (the framework default), the sidecar records "eevdf". The outer KERNEL_DEFAULT.name ("kernel_default") is NOT written to the sidecar — it stays in-memory only, used by logs, #[ktstr_test]-declaration lookups, and Payload::display_name() on the payload-slot surface. Cross-kernel-version comparisons via sidecar scheduler therefore see "eevdf" today and whatever future scheduling class replaces EEVDF tomorrow; author-intent filtering on "kernel_default" requires consulting the in-memory Payload::name directly, not the sidecar.

Source

pub const fn display_name(&self) -> &'static str

Short, human-readable name for logging and sidecar output.

Source

pub const fn as_scheduler(&self) -> Option<&'static Scheduler>

Return the inner Scheduler reference when this payload wraps one. Returns None for PayloadKind::Binary.

Source

pub const fn is_scheduler(&self) -> bool

True when this payload wraps a Scheduler (scheduler slot). False for binary payloads.

Source

pub const fn new( name: &'static str, kind: PayloadKind, output: OutputFormat, default_args: &'static [&'static str], default_checks: &'static [MetricCheck], metrics: &'static [MetricHint], include_files: &'static [&'static str], uses_parent_pgrp: bool, known_flags: Option<&'static [&'static str]>, ) -> Payload

Primary const constructor for a Payload.

Takes every field by position so the #[derive(Payload)] macro can emit a single call instead of a struct-literal. #[non_exhaustive] on the struct prevents out-of-crate struct-literal construction; this constructor — defined in the same crate as Payload — is not subject to that restriction, so the macro-expanded tokens that reach downstream crates compile cleanly.

For one-field constructions prefer Payload::binary — it calls into this helper and pins the non-identity fields to the exit-code-only defaults.

Source

pub const fn binary(name: &'static str, binary: &'static str) -> Payload

Minimal const constructor for a binary-kind Payload. Fills the non-identity fields with the exit-code-only defaults — no CLI args, no author-declared checks, no metric hints, and OutputFormat::ExitCode — so a #[ktstr_test] entry or a direct unit test can declare a runnable binary with one line instead of spelling out the full struct literal.

The binary string is the executable name passed to std::process::Command::new inside the guest. Supply it to the guest via -i / --include-files for CLI invocations or pre-install it in the initramfs for #[ktstr_test] entries — see PayloadKind::Binary for the full packaging contract.

Source

pub const fn scheduler_name(&self) -> &'static str

The scheduler’s display name.

Returns a compile-time-fixed LABEL, not a runtime reflection of the scheduling class the live kernel is actually running. A sidecar written on a kernel whose default is a successor scheduling class still records whatever string this method returns — the label comes from the Payload / inner Scheduler definition, nothing queries /proc or the live policy. Consumers that need to know the running kernel’s scheduling class must cross-reference the sidecar’s host.kernel_release with kernel-version-to-scheduler knowledge maintained outside the sidecar.

Branch behavior:

  • PayloadKind::Scheduler(s)s.name — the label attached to that specific scheduler, e.g. "eevdf" for Scheduler::EEVDF or "scx_rusty" for a scx_* scheduler. This is what scheduler-kind payloads (including Payload::KERNEL_DEFAULT, which wraps Scheduler::EEVDF) surface.
  • PayloadKind::Binary(_)"kernel_default" — a binary payload runs under whatever scheduler the test declares elsewhere (or the kernel default if it declares none), so the binary-kind payload carries no scheduler identity of its own. The returned string is a LABEL (“test author did not pin a scheduler here”), NOT a statement about which scheduling class the VM actually ran under — the live kernel may be running EEVDF, a successor class, or an scx scheduler the binary’s test harness attached separately; scheduler_name() does not observe any of that. Only a scheduler-kind payload explicitly wrapping Scheduler::EEVDF returns the "eevdf" label; every binary-kind payload returns "kernel_default" regardless of what class is running.
Source

pub const fn scheduler_binary(&self) -> Option<&'static SchedulerSpec>

The scheduler’s binary spec when scheduler-kind; None for binary-kind payloads. Consumers that dispatch on the SchedulerSpec variant (e.g. KernelBuiltin { enable, disable } hook invocation) use this rather than the scheduler_name shortcut.

Source

pub const fn has_active_scheduling(&self) -> bool

True when this payload drives an active scheduling policy (anything other than the kernel default EEVDF). Forwards to SchedulerSpec::has_active_scheduling for scheduler-kind payloads; binary-kind payloads always return false — a binary runs under whatever scheduler the test declares, and does not itself impose one.

Returns true for KernelBuiltin scheduler-kind payloads. See Self::has_bpf_scheduler for the narrower gate that excludes them.

Source

pub const fn has_bpf_scheduler(&self) -> bool

True when this payload drives a userspace BPF scheduler binary. Forwards to SchedulerSpec::has_bpf_scheduler for scheduler-kind payloads; binary-kind payloads always return false.

Distinct from Self::has_active_scheduling: this excludes KernelBuiltin scheduler-kind payloads (in-kernel policy, no userspace BPF binary). Use whenever the caller assumes a BPF binary is attached — verifier_stats wiring, BPF-attach monitor thresholds, or auto-repro probe gating.

Source

pub const fn sysctls(&self) -> &'static [Sysctl]

Guest sysctls applied before the scheduler starts. Empty slice for binary-kind payloads.

Source

pub const fn kargs(&self) -> &'static [&'static str]

Extra guest kernel command-line arguments appended when booting the VM. Empty slice for binary-kind payloads.

Source

pub const fn sched_args(&self) -> &'static [&'static str]

Scheduler CLI args prepended before per-test extra_sched_args. Empty slice for binary-kind payloads.

Source

pub const fn cgroup_parent(&self) -> Option<CgroupPath>

Cgroup parent path. None for binary-kind payloads and for scheduler-kind payloads that did not set one.

Source

pub const fn config_file(&self) -> Option<&'static str>

Host-side path to the scheduler config file. None for binary-kind payloads and for scheduler-kind payloads that did not set one.

Source

pub const fn config_file_def(&self) -> Option<(&'static str, &'static str)>

Inline config file definition. None for binary-kind payloads and for scheduler-kind payloads that did not set one.

Source

pub const fn assert(&self) -> &'static Assert

Scheduler-wide assertion overrides. For binary-kind payloads returns Assert::NO_OVERRIDES — the default identity value merge that leaves per-entry assertions untouched.

Source

pub const fn topology(&self) -> Topology

Default VM topology for this payload. Scheduler-kind payloads expose the topology declared on the inner Scheduler so tests that inherit from the scheduler slot stay consistent with the rest of the scheduler’s test surface; binary-kind payloads return a minimal placeholder (Topology::DEFAULT_FOR_PAYLOAD) — a pure binary workload has no scheduler-level topology opinion, so per-entry #[ktstr_test(...)] overrides are what actually drive the VM shape.

Source

pub const fn constraints(&self) -> TopologyConstraints

Gauntlet topology constraints. Scheduler-kind payloads forward to the inner Scheduler::constraints; binary-kind payloads return TopologyConstraints::DEFAULT.

Trait Implementations§

Source§

impl Clone for Payload

Source§

fn clone(&self) -> Payload

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Payload

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for Payload

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<T> MaybeSend for T
where T: Send,

§

impl<T> MaybeSend for T
where T: Send,