1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Guest-side verifier workload: a scheduler-agnostic "did the scheduler
//! dispatch?" probe run at [`super::init`]'s Phase 5 when the guest was
//! booted with `--ktstr-verifier-workload` (the `cargo ktstr verifier`
//! sweep path — a VM with no `#[ktstr_test]` body).
//!
//! After the scheduler has attached (Phase 3) and Phase 5 has emitted the
//! `PayloadStarting` frame, this spawns a SpinWait workload — as
//! SCHED_EXT tasks, so the BPF scheduler dispatches them under any switch
//! mode (full or `SCX_OPS_SWITCH_PARTIAL`) — sized to the guest's online
//! CPU count, waits (by polling live per-worker iteration counts, not a
//! blind fixed sleep) until every worker has advanced at least one
//! iteration or a bounded deadline elapses, then stops the workload. It
//! emits a [`LifecyclePhase::WorkloadDispatched`] frame
//! only for a worker that BOTH advanced non-zero `iterations` AND had its
//! SCHED_EXT set succeed (`sched_policy_error` is None) — so a fair-class
//! fallback cannot false-confirm — proof the scheduler actually
//! dispatched a task onto a CPU. The host
//! verdict ([`crate::verifier::collect_verifier_output`]) PASSes a cell
//! only when BOTH `PayloadStarting` (attached) AND `WorkloadDispatched`
//! (dispatched) frames arrive.
//!
//! On any failure — workload spawn error, or zero progress within the
//! deadline — NO frame is emitted and the function returns quietly. The
//! host reads the absence, given `PayloadStarting`, as "attached but did
//! not dispatch": a distinct, worse failure than a failed attach. It
//! never panics; a guest panic reboots via `panic=-1` (an i8042 reset →
//! `ExitAction::Shutdown`), which would strand the run with no clean
//! verdict signal.
//!
//! Safe from guest init because the fork workers' orphan self-exit guard
//! (`getppid() == 1` under pid-1 driver) is suppressed by the
//! `KTSTR_GUEST_INIT` env var, which [`super::init`] sets before Phase 5
//! and which is inherited across fork.
use crateLifecyclePhase;
use crate;
use ;
/// Longest we wait for the scheduler to dispatch a worker before
/// concluding it did not. Bounded so a wedged scheduler fails the cell
/// quickly rather than hanging until the host watchdog (120s).
const DISPATCH_DEADLINE: Duration = from_secs;
/// Inter-poll pause. `snapshot_iterations` is a polled shared-memory
/// counter (no eventfd to block on), so the wait is a bounded poll that
/// breaks the instant a worker advances — not a fixed sleep-then-stop.
const POLL_INTERVAL: Duration = from_millis;
/// Run the SpinWait probe and, on confirmed dispatch, emit
/// [`LifecyclePhase::WorkloadDispatched`]. See the module docs.
pub