pub struct CgroupDef {Show 15 fields
pub name: Cow<'static, str>,
pub cpuset: Option<CpusetSpec>,
pub works: Vec<WorkSpec>,
pub swappable: bool,
pub payload: Option<&'static Payload>,
pub cpuset_mems: Option<BTreeSet<usize>>,
pub cpu: Option<CpuLimits>,
pub memory: Option<MemoryLimits>,
pub io: Option<IoLimits>,
pub pids: Option<PidsLimits>,
pub default_nice: Option<i32>,
pub default_comm: Option<Cow<'static, str>>,
pub default_uid: Option<u32>,
pub default_gid: Option<u32>,
pub default_numa_node: Option<u32>,
}Expand description
Declarative cgroup definition: name + cpuset + synthetic
WorkSpec groups + optional userspace Payload.
Bundles the ops that always go together (AddCgroup + SetCpuset +
Spawn) into a single value. The executor creates the cgroup, optionally
sets its cpuset, spawns workers for each WorkSpec entry, and moves
them into the cgroup.
Multiple WorkSpec entries run in parallel within the cgroup. Each
entry spawns its own set of worker processes. The optional
Self::payload slot is a single userspace binary that runs
alongside those synthetic WorkSpec groups (hence “plural works,
singular payload” — the pluralization in the legacy “workload(s)”
prose elided this distinction).
Use CgroupDef in Step::with_defs for scenarios where cgroups are
created once and run for the step duration. Use Op::add_cgroup +
Op::spawn(SpawnPlacement::cgroup(name), work) directly when you
need mid-step cgroup creation, removal, or other dynamic operations
between spawn and collect.
§Resource controllers overview
CgroupDef exposes one builder method per cgroup v2 controller
knob, each writing the corresponding cgroup.* / *.max /
*.weight file at apply_setup time. The full surface:
| Controller | One-line description | Builder methods | Underlying file(s) |
|---|---|---|---|
| cpuset | Bind to a CPU subset and NUMA-node memory affinity. | Self::cpuset, Self::cpuset_mems | cpuset.cpus, cpuset.mems |
| cpu | Bandwidth ceiling (cpu.max quota/period) plus relative-share weight. | Self::cpu_quota_pct, Self::cpu_quota, Self::cpu_unlimited, Self::cpu_weight | cpu.max, cpu.weight |
| memory | Hard ceiling, soft throttle threshold, soft protection floor, swap cap. | Self::memory_max, Self::memory_high, Self::memory_low, Self::memory_swap_max, Self::memory_swap_unlimited, Self::memory_unlimited | memory.max, memory.high, memory.low, memory.swap.max |
| io | Relative IO share (BFQ / io.cost) when the io controller is enabled. | Self::io_weight | io.weight |
| pids | Task-count ceiling — fork(2)/clone(2) returns EAGAIN once the cap is hit. | Self::pids_max, Self::pids_unlimited | pids.max |
| freeze | Pause/resume every task in the cgroup mid-run via the JOBCTL freeze path. | (Op-level) Op::freeze_cgroup, Op::unfreeze_cgroup | cgroup.freeze |
CgroupDef covers steady-state resource limits — knobs that
hold for the cgroup’s whole lifetime. The freeze knob is
intentionally exposed at the Op layer instead, because
freeze/unfreeze describe transitions over time (suspend
mid-step, resume later) rather than the cgroup’s identity; see
the “See also” section below for the full Op-variants list.
All builders are additive — a CgroupDef accumulates an
optional CpuLimits / MemoryLimits / IoLimits /
PidsLimits block. When a block is set (e.g. def.memory
is Some), all knobs in that block are written —
None-valued fields emit their kernel-default sentinel
("max" for memory.max/memory.high, "0" for
memory.low). Only memory.swap.max is gated: None means
no write (for CONFIG_SWAP=n compatibility). The “*_unlimited”
builders explicitly rewind a knob to its sentinel value
("max" / "0") so a base CgroupDef factory can cap a
resource and a per-test extension can clear that cap without
rewriting the whole CgroupDef.
Validation runs at apply_setup time (before any worker
spawn): out-of-range weights, cpu.max period == 0, and
pids.max == Some(0) all produce actionable bails before the
syscall fires. The kernel is the final authority on
per-controller numeric ranges; framework-level checks catch
only the foot-cannons documented per-builder.
§Builder semantics
The setters fall into three groups:
Group I — per-WorkSpec fan to works[0]:
workers, workers_pct,
work_type, sched_policy,
affinity, mem_policy,
mpol_flags. Each mutates self.works[0],
auto-inserting a default WorkSpec when works is empty. There
is NO cgroup-level default for these knobs — per-group identity (or
per-group cpuset validation) makes fan-out semantically ambiguous.
Use work + per-WorkSpec setters for multi-group
cgroups.
Group II — cgroup-level default_* merge:
nice, comm, uid,
gid, numa_node. Each stores a
value in a default_* field on CgroupDef. Every WorkSpec in
works whose corresponding Option-typed field is
None inherits the default at merged_works
time — ORDER-INDEPENDENT with work. Some(_)
(including Some(0)) opts out.
Group III — pcomm: mutates works in-place
at call time, NOT order-independent — by design. See
pcomm for the coalescing rationale.
Other setters (cpuset,
cpuset_mems, the
cpu_quota / memory_max
/ io_weight / pids_max
controller families, workload,
swappable) set cgroup-level state directly
and do not participate in either merge pattern.
§See also
CgroupDef only expresses the steady-state shape of a cgroup
(name, cpuset, work groups, payload). State changes that need
to happen DURING a step — without tearing the cgroup down and
recreating it — go through dedicated Op variants instead:
Op::FreezeCgroup/Op::UnfreezeCgroup— pause and resume every task in the cgroup viacgroup.freeze(the kernel-side asynchronous freeze path; not a SIGSTOP). Useful for scheduler suspend/resume tests that observe how the scheduler handles a workload that goes idle mid-step. Do not freeze a cgroup hosting the test’s own observers — see the deadlock warning onOp::FreezeCgroup.Op::SetCpuset— re-pin an existing cgroup’s cpuset to exercise the scheduler’s response to a moving CPU mask without disrupting the worker tasks themselves.Op::AddCgroup/Op::RemoveCgroup— add or destroy cgroups mid-step when aCgroupDef’s lifecycle is tied to step duration but the test wants a different (e.g. nested) cgroup to appear or disappear partway through.
These describe transitions over time rather than the cgroup’s
identity, which is why they live as Op variants alongside
the rest of the operation vocabulary rather than as
CgroupDef builders.
// Single work group via convenience methods.
let def = CgroupDef::named("workers")
.cpuset(CpusetSpec::disjoint(0, 2))
.workers(4)
.work_type(WorkType::SpinWait);
assert_eq!(def.name, "workers");
assert_eq!(def.works[0].num_workers, Some(4));
// Multiple concurrent work groups via .work().
let def = CgroupDef::named("mixed")
.work(WorkSpec::default().workers(4).work_type(WorkType::SpinWait))
.work(WorkSpec::default().workers(2).work_type(WorkType::YieldHeavy));
assert_eq!(def.works.len(), 2);
// Synthetic work + userspace binary side-by-side via .workload(&X).
// The binary runs inside the same cgroup as the WorkSpec handles;
// both spawn in apply_setup, the WorkSpec groups first, then the
// Payload after the cpuset settles.
let def = CgroupDef::named("io_and_spin")
.cpuset(CpusetSpec::disjoint(0, 2))
.workers(2)
.work_type(WorkType::SpinWait)
.workload(&BENCH);
assert!(def.payload.is_some());
assert_eq!(def.works[0].num_workers, Some(2));Fields§
§name: Cow<'static, str>Cgroup name relative to the scenario’s parent cgroup. Must be a valid cgroupfs filename.
cpuset: Option<CpusetSpec>Optional cpuset assignment. None inherits the parent cgroup’s
cpuset (typically the scenario’s usable CPU set).
works: Vec<WorkSpec>WorkSpec groups to spawn. Empty means use a single default WorkSpec
(SpinWait, Normal, ctx.workers_per_cgroup workers — defaults to 1
from CtxBuilder unless the scenario overrides it explicitly).
swappable: boolWhen true, the gauntlet work_type override replaces each WorkSpec’s work_type (applied per-WorkSpec via resolve_work_type).
payload: Option<&'static Payload>Optional userspace Payload to
launch inside this cgroup.
Spawn order within apply_setup: the cgroup is created
(add_cgroup_no_cpuset), its cpuset is resolved + set, then
each WorkSpec entry is spawned and moved into the cgroup in
declaration order, and finally — after every synthetic
WorkSpec handle has started — the Payload is spawned via
PayloadRun::new(ctx, p).in_cgroup(name).spawn(). This
fixed order lets the cgroup cpuset and mempolicy settle on
the WorkSpec handles before the binary inherits placement, so
the binary sees a stable topology. Once spawned, all three
(cgroup, works, payload) run concurrently until teardown.
Only
PayloadKind::Binary
payloads are accepted — scheduler-kind payloads are rejected
at construction time via Self::workload. The payload is
killed at step-teardown (before cgroup removal) so the cgroup
removal does not fail with EBUSY.
cpuset_mems: Option<BTreeSet<usize>>Optional cpuset.mems NUMA node binding. None inherits the
parent cgroup’s cpuset.mems. Set via
Self::cpuset_mems.
cpu: Option<CpuLimits>Optional cpu controller limits (cpu.max, cpu.weight).
None leaves both kernel defaults in place. Set via
Self::cpu_quota_pct / Self::cpu_quota /
Self::cpu_weight.
memory: Option<MemoryLimits>Optional memory controller limits (memory.max,
memory.high, memory.low, memory.swap.max). None
leaves all four at the kernel defaults. Set via
Self::memory_max / Self::memory_high /
Self::memory_low / Self::memory_swap_max.
io: Option<IoLimits>Optional io controller limits (io.weight). None leaves
the kernel default in place. Set via Self::io_weight.
pids: Option<PidsLimits>Optional pids controller limits (pids.max). None leaves
the kernel default in place (no ceiling). Set via
Self::pids_max.
default_nice: Option<i32>Cgroup-level default for WorkSpec::nice. When Some(n),
every WorkSpec in Self::works whose own nice field
is None (the framework’s “skip setpriority(2)” state — see
WorkloadConfig::nice)
inherits Some(n) at apply-setup time. Set via Self::nice;
merged in Self::merged_works.
Order-independent with Self::work: def.work(spec).nice(n)
and def.nice(n).work(spec) produce identical effective
WorkSpec values because the merge runs at merged_works()
call time, not at builder-method call time.
default_comm: Option<Cow<'static, str>>Cgroup-level default for WorkSpec::comm. Merged into any
WorkSpec whose own comm is None at apply-setup time.
Set via Self::comm; merged in Self::merged_works.
default_uid: Option<u32>Cgroup-level default for WorkSpec::uid. Merged into any
WorkSpec whose own uid is None at apply-setup time.
Set via Self::uid; merged in Self::merged_works.
default_gid: Option<u32>Cgroup-level default for WorkSpec::gid. Merged into any
WorkSpec whose own gid is None at apply-setup time.
Set via Self::gid; merged in Self::merged_works.
default_numa_node: Option<u32>Cgroup-level default for WorkSpec::numa_node. Merged into
any WorkSpec whose own numa_node is None at
apply-setup time. Set via Self::numa_node; merged in
Self::merged_works.
Implementations§
Source§impl CgroupDef
impl CgroupDef
Sourcepub fn named(name: impl Into<Cow<'static, str>>) -> Self
pub fn named(name: impl Into<Cow<'static, str>>) -> Self
Create a CgroupDef with defaults (empty works, no cpuset).
apply_setup fills an empty works slice with one default
WorkSpec (SpinWait, SCHED_NORMAL, ctx.workers_per_cgroup
workers — defaults to 1 from CtxBuilder). For an empty
move-target cgroup with no workers, declare it via
Op::AddCgroup at step or Backdrop level. For the common
CgroupDef::named(name).workers(ctx.workers_per_cgroup)
pattern use Ctx::cgroup_def.
Sourcepub fn cpuset(self, cpus: CpusetSpec) -> Self
pub fn cpuset(self, cpus: CpusetSpec) -> Self
Set Self::cpuset; see Op::SetCpuset for mid-run changes.
Sourcepub fn work(self, w: WorkSpec) -> Self
pub fn work(self, w: WorkSpec) -> Self
Append a WorkSpec group (multiple calls yield concurrent groups within this cgroup).
Sourcepub fn workers(self, n: usize) -> Self
pub fn workers(self, n: usize) -> Self
Set WorkSpec::num_workers on works[0] (Group I).
n MUST be >= 1. n == 0 is rejected at apply-setup time
by resolve_num_workers (before any worker spawn) with an
actionable diagnostic naming the cgroup;
WorkloadConfig::validate
is the downstream defense-in-depth gate. A zero-worker spawn
would silently produce no workload load, vacuously passing
scheduler assertions that rely on observable contention. Pass
n >= 1; for fraction-of-cpuset sizing use Self::workers_pct.
Sourcepub fn workers_pct(self, pct: f64) -> Self
pub fn workers_pct(self, pct: f64) -> Self
Set WorkSpec::workers_pct on works[0] (Group I). Resolved
against the cgroup’s cpuset at apply-setup via
ceil(cpuset_cpus * pct). Mutually exclusive with
Self::workers — see WorkSpec::workers_pct.
§Panics
Panics when pct is NaN, infinite, or <= 0.0. Extreme
finite values (e.g. 1e100) pass the gate and saturate to
usize::MAX via the as cast in resolve_workers_pct
(RFC 2484 / Rust 1.45+) — attempting to spawn that many
workers would OOM the host. Keep pct near the intended
oversubscription factor (e.g. 1.0, 2.0, 4.0).
Sourcepub fn work_type(self, wt: WorkType) -> Self
pub fn work_type(self, wt: WorkType) -> Self
Set WorkSpec::work_type on works[0] (Group I).
Sourcepub fn sched_policy(self, p: SchedPolicy) -> Self
pub fn sched_policy(self, p: SchedPolicy) -> Self
Set WorkSpec::sched_policy on works[0] (Group I).
Sourcepub fn affinity(self, a: AffinityIntent) -> Self
pub fn affinity(self, a: AffinityIntent) -> Self
Set WorkSpec::affinity on works[0] (Group I).
Sourcepub fn mem_policy(self, p: MemPolicy) -> Self
pub fn mem_policy(self, p: MemPolicy) -> Self
Set WorkSpec::mem_policy on works[0] (Group I). Validated
against the resolved cpuset per-group.
Sourcepub fn mpol_flags(self, f: MpolFlags) -> Self
pub fn mpol_flags(self, f: MpolFlags) -> Self
Set WorkSpec::mpol_flags on works[0] (Group I).
Sourcepub const fn nice(self, n: i32) -> Self
pub const fn nice(self, n: i32) -> Self
Set Self::default_nice (Group II). Note: WorkSpec::nice(0)
stores Some(0) and opts out of this default — the worker’s
nice is explicitly set to 0 via setpriority(2) rather than
inheriting.
Sourcepub fn comm(self, name: impl Into<Cow<'static, str>>) -> Self
pub fn comm(self, name: impl Into<Cow<'static, str>>) -> Self
Set Self::default_comm (Group II).
§Panics
Panics on programmer-error inputs — mirrors
crate::workload::WorkSpec::pcomm’s # Panics:
- Empty string.
- Interior NUL byte.
- More than 15 bytes (
TASK_COMM_LEN - 1cap).
See
validate_task_comm_string (in crate::workload)
for the centralized rationale; name.len() is the BYTE
length (UTF-8 multi-byte chars count as their byte width).
Sourcepub const fn uid(self, uid: u32) -> Self
pub const fn uid(self, uid: u32) -> Self
Set Self::default_uid (Group II).
Sourcepub const fn gid(self, gid: u32) -> Self
pub const fn gid(self, gid: u32) -> Self
Set Self::default_gid (Group II).
Sourcepub fn pcomm(self, name: impl Into<Cow<'static, str>>) -> Self
pub fn pcomm(self, name: impl Into<Cow<'static, str>>) -> Self
Set the thread-group leader’s comm on every WorkSpec in
this CgroupDef. Each affected WorkSpec gets pcomm = Some(name); existing per-WorkSpec pcomm values (set
before this call) are overwritten. Calling on an empty
works list pushes a default WorkSpec carrying the value.
The pcomm string is applied via prctl(PR_SET_NAME) on
the forked thread-group leader. The builder rejects > 15
bytes (TASK_COMM_LEN-1) at construction so the
task->group_leader->comm == pcomm invariant the framework
relies on holds exactly.
Setting this triggers the fork-then-thread spawn path in
apply_setup: WorkSpecs sharing a pcomm value coalesce
into ONE thread-group leader per group; every worker
thread inside observes task->group_leader->comm == pcomm.
Each worker thread additionally sets its own task->comm
via .comm() on the per-WorkSpec WorkSpec::comm at
thread creation time.
pcomm lives ONLY on WorkSpec — there is no
CgroupDef-level field. This builder writes the value into
every WorkSpec directly so apply_setup has a single
authoritative source per WorkSpec.
Not order-independent with Self::work — by design.
Unlike Group II setters, pcomm mutates works in-place when
called: it stamps every WorkSpec that EXISTS at call time and
then returns. WorkSpecs added via subsequent Self::work
calls are not retroactively touched, and a WorkSpec that
already carried its own pcomm is OVERWRITTEN if it was
pushed before .pcomm(..) ran. This is intentional — pcomm
determines the thread-group leader’s coalescing key in
apply_setup, so the framework needs the value baked onto
each WorkSpec by the time merged_works() runs. Storing it
as a default and merging at read time would break the
coalescing contract for the empty-works case (the synthesised
WorkSpec::default() would have to carry the pcomm without
distinguishing “default” from “explicit override”).
§Panics
Panics on programmer-error inputs — mirrors
crate::workload::WorkSpec::pcomm’s # Panics:
- Empty string.
- Interior NUL byte.
- More than 15 bytes (
TASK_COMM_LEN - 1cap).
See
validate_task_comm_string (in crate::workload)
for the centralized rationale; name.len() is the BYTE
length (UTF-8 multi-byte chars count as their byte width).
Sourcepub const fn numa_node(self, node: u32) -> Self
pub const fn numa_node(self, node: u32) -> Self
Set Self::default_numa_node (Group II).
Sourcepub const fn swappable(self, swappable: bool) -> Self
pub const fn swappable(self, swappable: bool) -> Self
Set Self::swappable (gauntlet work_type override).
Sourcepub fn workload(self, p: &'static Payload) -> Self
pub fn workload(self, p: &'static Payload) -> Self
Attach a userspace payload binary that runs inside this cgroup
alongside any synthetic WorkSpec groups. The payload spawns
when the step enters apply_setup and is killed during
step-teardown so the cgroup can be removed cleanly.
§Panics
Panics when p.is_scheduler() (i.e. p is a scheduler-kind
Payload — KERNEL_DEFAULT
or any other PayloadKind::Scheduler* variant). Only
PayloadKind::Binary
payloads are accepted; CgroupDef.workload is for userspace
binary payloads only, and scheduler placement uses
#[ktstr_test(scheduler = ...)] instead.
Why panic at declaration time, not at spawn time? Three reasons, all of which favor failing fast:
- Discovery-time surfacing.
CgroupDefbuilders run during test construction, which nextest’s--listinvocation reaches BEFORE any VM boot. A panic here emits a full backtrace inside the test binary and surfaces the offending call site immediately; a deferred runtime error would require a KVM-capable host + a kernel image + an initramfs build to observe — a 30+ second feedback loop for what is purely a typed-API misuse. - No side effects. The panic happens before
CgroupDef.payload = Some(p)assignment runs, so the in-progress builder is left in its prior (no-payload) state. A caller that catches the panic viacatch_unwindsees a valid CgroupDef either way. - Scheduler-kind is always a programming error here.
Payload::KERNEL_DEFAULTinCgroupDef::workloadis never a legitimate use case — it means the author confused theschedulerslot (test-level) with theworkloadslot (cgroup-level). There is no recovery path; the only resolution is editing the source.
Scheduler-kind payloads in the step-level Op::RunPayload
path bail with an anyhow::Error instead of panicking —
that path runs during scenario execution where one bad op
should not crash a whole test run.
Sourcepub fn cpuset_mems(self, nodes: BTreeSet<usize>) -> Self
pub fn cpuset_mems(self, nodes: BTreeSet<usize>) -> Self
Bind cpuset.mems for this cgroup. Mirrors Self::cpuset
for NUMA memory placement: the cgroup’s tasks may only
allocate memory on the listed NUMA nodes. None (default)
inherits the parent’s cpuset.mems.
Required when the cgroup spans CPUs on a NUMA node whose
memory is NOT in the parent’s cpuset.mems — allocations
from the cgroup’s tasks are constrained to the parent’s
allowed nodes per kernel/cgroup/cpuset.c. The framework
writes cpuset.mems immediately after cpuset.cpus so the
binding is in effect before any worker is moved in.
Sourcepub fn cpu_quota_pct(self, pct: u32) -> Self
pub fn cpu_quota_pct(self, pct: u32) -> Self
Set cpu.max quota as a percentage of one CPU’s
throughput, with a default 100 ms period. 100 means
“one full CPU” (quota=100_000, period=100_000); 200 means
“two CPUs”. Use Self::cpu_quota for non-default periods.
Sourcepub fn cpu_quota(self, quota: Duration, period: Duration) -> Self
pub fn cpu_quota(self, quota: Duration, period: Duration) -> Self
Set cpu.max quota and period directly. quota may exceed
period (multi-CPU concurrency, see CpuLimits::max_quota_us).
Both arguments are converted to microseconds; sub-microsecond
fractions in the supplied Durations are truncated.
Sourcepub fn cpu_unlimited(self) -> Self
pub fn cpu_unlimited(self) -> Self
Clear any previously-set cpu.max quota (writes "max"),
leaving cpu.weight (if set) intact. Useful when a base
CgroupDef builder applied a default cap and the test wants
only weight-based bias.
Sourcepub fn cpu_weight(self, weight: u32) -> Self
pub fn cpu_weight(self, weight: u32) -> Self
Set cpu.weight (CGROUP_WEIGHT_MIN..=CGROUP_WEIGHT_MAX,
1..=10000; CGROUP_WEIGHT_DFL = 100; enforced by
cpu_weight_write_u64 in kernel/sched/core.c). Larger values
get a larger share under contention. Independent of cpu.max.
Sourcepub fn memory_max(self, bytes: u64) -> Self
pub fn memory_max(self, bytes: u64) -> Self
Set memory.max hard ceiling in bytes. Crossing this
triggers reclaim first (try_charge_memcg in
mm/memcontrol.c); the cgroup OOM killer fires only after
MAX_RECLAIM_RETRIES failed retries, and is skipped when
the allocation carries __GFP_NORETRY or
__GFP_RETRY_MAYFAIL.
Sourcepub fn memory_high(self, bytes: u64) -> Self
pub fn memory_high(self, bytes: u64) -> Self
Set memory.high soft throttle threshold in bytes. Crossing
this triggers reclaim throttling but NOT OOM-kill — per
__mem_cgroup_handle_over_high in mm/memcontrol.c:
“memory.high enforcement isn’t as strict, and there is no
OOM killer involved”.
Sourcepub fn memory_low(self, bytes: u64) -> Self
pub fn memory_low(self, bytes: u64) -> Self
Set memory.low soft protection threshold in bytes.
Reclaim prefers other cgroups before this one’s memory
drops below low.
Sourcepub fn memory_unlimited(self) -> Self
pub fn memory_unlimited(self) -> Self
Clear all three memory limits (writes "max" for max/high
and "0" for low). Equivalent to leaving memory unset
at construction; provided for symmetry with
Self::cpu_unlimited.
Sourcepub fn io_weight(self, weight: u16) -> Self
pub fn io_weight(self, weight: u16) -> Self
Set io.weight (CGROUP_WEIGHT_MIN..=CGROUP_WEIGHT_MAX,
1..=10000; CGROUP_WEIGHT_DFL = 100; enforced by
ioc_weight_write in block/blk-iocost.c). Biases relative
IO share when the io controller is enabled. io.max
per-device caps are not surfaced — see IoLimits.
Sourcepub fn memory_swap_max(self, bytes: u64) -> Self
pub fn memory_swap_max(self, bytes: u64) -> Self
Set memory.swap.max ceiling in bytes. The kernel parses the
wire value via page_counter_memparse and accepts a decimal
byte count (swap_max_write in mm/memcontrol.c). Distinct
from memory.max: this caps how much of the cgroup’s memory
can spill to swap, separate from total memory consumption.
Sourcepub fn memory_swap_unlimited(self) -> Self
pub fn memory_swap_unlimited(self) -> Self
Clear any previously-set memory.swap.max (writes "max").
Mirrors Self::cpu_unlimited / Self::memory_unlimited
for a single memory-knob unset; useful when a base
CgroupDef builder applied a swap cap and the test wants to
remove only that knob while preserving memory.max/high/
low.
No-ops when self.memory == None — the default state already
means “no swap cap” (apply_setup emits no memory writes for an
unset memory field), so creating a fresh MemoryLimits just
to set swap_max = None would (a) be redundant and (b)
trigger 3 unwanted writes for memory.max / memory.high /
memory.low at apply_setup time. The no-op short-circuit
keeps “fresh CgroupDef + memory_swap_unlimited()” semantically
identical to “fresh CgroupDef”.
Sourcepub fn pids_max(self, n: u64) -> Self
pub fn pids_max(self, n: u64) -> Self
Set pids.max task-count ceiling. n is the maximum number
of processes the cgroup may host before subsequent
fork() / clone() calls return EAGAIN. Existing tasks are
NOT killed when the limit lands below the current count
(per the pids_max_write kernel comment: “Limit updates
don’t need to be mutex’d, since it isn’t critical that any
racing fork()s follow the new limit”).
n = 0 is rejected at apply_setup time: a 0-limit cgroup
halts every fork/clone inside, including the worker spawn
under CloneMode::Fork and the ForkExit per-iteration
child fork. There is no kernel sentinel for “no fork ever”;
pids_max=0 silently fails every fork() inside with
EAGAIN, which is almost certainly a configuration bug.
Sourcepub fn pids_unlimited(self) -> Self
pub fn pids_unlimited(self) -> Self
Clear any previously-set pids.max (writes "max").
Mirrors Self::cpu_unlimited / Self::memory_unlimited.
Sourcepub fn merged_works(&self) -> Vec<WorkSpec>
pub fn merged_works(&self) -> Vec<WorkSpec>
Materialize Self::works with cgroup-level defaults
merged into each entry. Called by apply_setup to resolve
the per-WorkSpec values before spawning workers.
For every WorkSpec in Self::works (or a single
WorkSpec::default() when works is empty, matching
apply_setup’s default-substitution rule), each cgroup-level
default in Self::default_nice / Self::default_comm /
Self::default_uid / Self::default_gid /
Self::default_numa_node fills the corresponding
WorkSpec field when that field is “unset” at the WorkSpec
level.
“Unset” means None for every Option-typed field —
nice, comm, uid, gid, numa_node are all
Option<_>. The framework’s “skip setpriority(2)” state per
WorkloadConfig::nice
is None. A WorkSpec that explicitly sets Some(n)
(including Some(0)) keeps its value; the cgroup-level
default applies only when the WorkSpec is at the framework
default of None.
pcomm is NOT propagated through merged_works. The
Self::pcomm convenience method writes pcomm directly
into every WorkSpec at builder time so coalescing in
apply_setup reads the per-WorkSpec value (the
authoritative source).
Decoupling this merge from the convenience-method call sites
makes the builder order-independent —
def.nice(5).work(spec) and def.work(spec).nice(5)
produce identical effective WorkSpec values.
Trait Implementations§
Source§impl FromIterator<CgroupDef> for Backdrop
Backdrop::from_iter(cgroups) / cgroups.into_iter().collect()
shortcuts for the common “build a Backdrop from a Vec of cgroup
defs” pattern test fixtures repeat. Equivalent to
Backdrop::from_cgroups; declaration order is preserved.
impl FromIterator<CgroupDef> for Backdrop
Backdrop::from_iter(cgroups) / cgroups.into_iter().collect()
shortcuts for the common “build a Backdrop from a Vec of cgroup
defs” pattern test fixtures repeat. Equivalent to
Backdrop::from_cgroups; declaration order is preserved.
Auto Trait Implementations§
impl Freeze for CgroupDef
impl RefUnwindSafe for CgroupDef
impl Send for CgroupDef
impl Sync for CgroupDef
impl Unpin for CgroupDef
impl UnwindSafe for CgroupDef
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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