swactor/crates/mvp-system/src/gpu_worker_ctl.rs

700 lines
19 KiB
Rust
Raw Normal View History

2026-06-23 13:51:34 +00:00
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ProcessId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WorkerGeneration(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RingId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StepId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ObjectId(pub u64);
2026-06-25 12:30:18 +00:00
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RoleId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeId(pub u64);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PortId(pub String);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Sequence(pub u64);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerJson {
Null,
Bool(bool),
Number(i64),
String(String),
Array(Vec<WorkerJson>),
Object(std::collections::BTreeMap<String, WorkerJson>),
}
impl WorkerJson {
pub fn empty() -> Self {
Self::Object(std::collections::BTreeMap::new())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RingDirection {
Ingress,
Egress,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ObjectLayout {
Token,
Tensor,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RingLayout {
pub offset: u64,
pub byte_len: u64,
pub header_bytes: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ObjectSpec {
pub max_extent: u64,
pub alignment: u64,
pub layout: ObjectLayout,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UninstallReason {
Reconfigure,
Shutdown,
Fault,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ShutdownMode {
Graceful,
AbortInFlight,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConfigureRole {
pub generation: WorkerGeneration,
pub role_id: RoleId,
pub config: WorkerJson,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InstallRing {
pub generation: WorkerGeneration,
pub ring_id: RingId,
pub edge_id: EdgeId,
pub port_id: PortId,
pub direction: RingDirection,
pub layout: RingLayout,
pub object_spec: ObjectSpec,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UninstallRing {
pub generation: WorkerGeneration,
pub ring_id: RingId,
pub reason: UninstallReason,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InputBinding {
pub port_id: PortId,
pub object_id: ObjectId,
pub sequence: Sequence,
pub device_handle: DeviceHandle,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OutputBinding {
pub port_id: PortId,
pub ring_id: RingId,
pub object_id: ObjectId,
pub sequence: Sequence,
pub extent: u64,
pub flags: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExecuteStep {
pub generation: WorkerGeneration,
pub role_id: RoleId,
pub step_id: StepId,
pub inputs: Vec<InputBinding>,
pub outputs: Vec<OutputBinding>,
pub runtime: WorkerJson,
pub release_inputs_after: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerFatalReason {
UnsupportedHelperAbi,
BackendInitializationFailed,
ProtocolViolation,
RoleUnavailable,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerStoppedReason {
Graceful,
AbortInFlight,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RoleFailure {
InvalidConfig,
BackendRejected,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RingFaultReason {
HelperFailed,
InvalidLayout,
WorkerCrashed,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ObjectFailure {
InvalidRecord,
DeviceCopyFailed,
RingFaulted,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StepFailure {
RoleUnavailable,
InvalidInputHandle,
RuntimeFailed,
OutputValidationFailed,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ReleaseFailure {
UnknownHandle,
InUse,
}
2026-06-23 13:51:34 +00:00
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DeviceHandle {
pub generation: WorkerGeneration,
pub id: u64,
}
impl DeviceHandle {
pub fn new(generation: WorkerGeneration, id: u64) -> Self {
Self { generation, id }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ArenaEnv {
pub arena_fd: i32,
pub arena_bytes: u64,
}
impl ArenaEnv {
pub fn test_default() -> Self {
Self {
arena_fd: 3,
arena_bytes: 4096,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WorkerConfig {
pub node_id: NodeId,
pub arena_env: ArenaEnv,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ActorCommand {
2026-06-25 12:30:18 +00:00
ConfigureRole(ConfigureRole),
2026-06-23 13:51:34 +00:00
InstallRing {
generation: WorkerGeneration,
ring_id: RingId,
},
2026-06-25 12:30:18 +00:00
InstallRingSpec(InstallRing),
UninstallRing(UninstallRing),
RingReadable {
generation: WorkerGeneration,
ring_id: RingId,
},
RingWritable {
generation: WorkerGeneration,
ring_id: RingId,
},
2026-06-23 13:51:34 +00:00
ExecuteStep {
generation: WorkerGeneration,
step_id: StepId,
input: DeviceHandle,
},
2026-06-25 12:30:18 +00:00
ExecuteStepSpec(ExecuteStep),
2026-06-23 13:51:34 +00:00
ReleaseDeviceObject {
generation: WorkerGeneration,
handle: DeviceHandle,
},
2026-06-25 12:30:18 +00:00
ShutdownWorker {
generation: WorkerGeneration,
mode: ShutdownMode,
},
2026-06-23 13:51:34 +00:00
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerCommand {
InitializeWorker {
generation: WorkerGeneration,
},
2026-06-25 12:30:18 +00:00
ConfigureRole(ConfigureRole),
2026-06-23 13:51:34 +00:00
InstallRing {
ring_id: RingId,
},
2026-06-25 12:30:18 +00:00
InstallRingSpec(InstallRing),
UninstallRing(UninstallRing),
RingReadable {
ring_id: RingId,
},
RingWritable {
ring_id: RingId,
},
2026-06-23 13:51:34 +00:00
ExecuteStep {
step_id: StepId,
input: DeviceHandle,
},
2026-06-25 12:30:18 +00:00
ExecuteStepSpec(ExecuteStep),
2026-06-23 13:51:34 +00:00
ReleaseDeviceObject {
handle: DeviceHandle,
},
2026-06-25 12:30:18 +00:00
ShutdownWorker {
mode: ShutdownMode,
},
2026-06-23 13:51:34 +00:00
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerEvent {
2026-06-25 12:30:18 +00:00
WorkerReady {
pid: ProcessId,
generation: WorkerGeneration,
ring_helper_abi: u16,
backend: WorkerJson,
},
WorkerFatal {
reason: WorkerFatalReason,
},
WorkerStopped {
reason: WorkerStoppedReason,
},
RoleConfigured {
role_id: RoleId,
},
RoleFailed {
role_id: RoleId,
reason: RoleFailure,
},
RingInstalled {
ring_id: RingId,
},
RingInstalledForEdge {
ring_id: RingId,
edge_id: EdgeId,
port_id: PortId,
},
RingFault {
ring_id: RingId,
edge_id: EdgeId,
port_id: PortId,
reason: RingFaultReason,
},
RingQuiesced {
ring_id: RingId,
},
ObjectLoaded {
object_id: ObjectId,
sequence: u64,
},
ObjectLoadedFromRing {
ring_id: RingId,
edge_id: EdgeId,
port_id: PortId,
object_id: ObjectId,
sequence: Sequence,
extent: u64,
device_handle: DeviceHandle,
},
ObjectProduced {
object_id: ObjectId,
sequence: u64,
},
ObjectProducedToRing {
ring_id: RingId,
edge_id: EdgeId,
port_id: PortId,
object_id: ObjectId,
sequence: Sequence,
extent: u64,
},
ObjectFailed {
ring_id: RingId,
edge_id: EdgeId,
port_id: PortId,
object_id: Option<ObjectId>,
sequence: Option<Sequence>,
reason: ObjectFailure,
},
StepCompleted {
step_id: StepId,
},
StepCompletedForRole {
role_id: RoleId,
step_id: StepId,
},
StepFailed {
role_id: RoleId,
step_id: StepId,
reason: StepFailure,
},
DeviceObjectReleased {
device_handle: DeviceHandle,
},
ReleaseFailed {
device_handle: DeviceHandle,
reason: ReleaseFailure,
},
RingReadable {
ring_id: RingId,
},
RingWritable {
ring_id: RingId,
},
2026-06-23 13:51:34 +00:00
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExitStatus {
Code(i32),
Signal(i32),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerCtlEvent {
StartWorker,
ProcessStarted { pid: ProcessId },
WorkerReady { generation: WorkerGeneration },
ActorCommand(ActorCommand),
StdoutEvent(WorkerEvent),
ProcessExited { status: ExitStatus },
RestartRequested,
ShutdownRequested,
WorkerStopped { generation: WorkerGeneration },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WorkerFailure {
ProcessExited,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommandRejection {
NotRunning,
OldGenerationHandle,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerCtlOut {
WorkerRunning {
generation: WorkerGeneration,
},
WorkerFailed {
generation: WorkerGeneration,
reason: WorkerFailure,
},
CommandRejected {
reason: CommandRejection,
},
RingFaulted {
ring_id: RingId,
},
WorkerStopped {
generation: WorkerGeneration,
},
RingQuiesced {
ring_id: RingId,
},
TerminalStopped {
generation: WorkerGeneration,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WorkerCtlCommand {
SpawnProcessActor { node_id: NodeId },
StopDriverPump { ring_id: RingId },
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RoutedEvent {
ToEdgeEstablisher(WorkerEvent),
ToRxOrRole(WorkerEvent),
ToTxOrRole(WorkerEvent),
ToStageController(WorkerEvent),
ToDriverOrWorkerSide(WorkerEvent),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CtlState {
Idle,
Starting,
Running,
Crashed,
ShuttingDown,
Stopped,
}
pub struct GpuWorkerCtl {
2026-06-23 13:51:34 +00:00
config: WorkerConfig,
state: CtlState,
current_generation: WorkerGeneration,
commands: Vec<WorkerCtlCommand>,
serialized: Vec<WorkerCommand>,
events: Vec<WorkerCtlOut>,
routed: Vec<RoutedEvent>,
installed_rings: std::collections::BTreeSet<RingId>,
}
impl GpuWorkerCtl {
2026-06-23 13:51:34 +00:00
pub fn new(config: WorkerConfig) -> Self {
Self {
config,
state: CtlState::Idle,
current_generation: WorkerGeneration(1),
commands: Vec::new(),
serialized: Vec::new(),
events: Vec::new(),
routed: Vec::new(),
installed_rings: std::collections::BTreeSet::new(),
}
}
pub fn observe(&mut self, event: WorkerCtlEvent) {
match event {
WorkerCtlEvent::StartWorker => self.start(),
WorkerCtlEvent::ProcessStarted { .. } => {
self.state = CtlState::Starting;
self.serialized.push(WorkerCommand::InitializeWorker {
generation: self.current_generation,
});
}
WorkerCtlEvent::WorkerReady { generation } => {
self.current_generation = generation;
self.state = CtlState::Running;
self.events.push(WorkerCtlOut::WorkerRunning { generation });
}
WorkerCtlEvent::ActorCommand(command) => self.actor_command(command),
WorkerCtlEvent::StdoutEvent(event) => self.route(event),
WorkerCtlEvent::ProcessExited { status } => self.process_exited(status),
WorkerCtlEvent::RestartRequested => self.restart(),
WorkerCtlEvent::ShutdownRequested => self.shutdown(),
WorkerCtlEvent::WorkerStopped { generation } => {
self.events.push(WorkerCtlOut::WorkerStopped { generation });
}
}
}
pub fn commands(&self) -> &[WorkerCtlCommand] {
&self.commands
}
pub fn serialized_worker_commands(&self) -> &[WorkerCommand] {
&self.serialized
}
pub fn events(&self) -> &[WorkerCtlOut] {
&self.events
}
pub fn routed(&self) -> &[RoutedEvent] {
&self.routed
}
pub fn current_generation(&self) -> WorkerGeneration {
self.current_generation
}
fn start(&mut self) {
self.state = CtlState::Starting;
self.commands.push(WorkerCtlCommand::SpawnProcessActor {
node_id: self.config.node_id,
});
}
fn actor_command(&mut self, command: ActorCommand) {
if self.state != CtlState::Running {
self.events.push(WorkerCtlOut::CommandRejected {
reason: CommandRejection::NotRunning,
});
return;
}
2026-06-25 12:30:18 +00:00
let command_generation = match &command {
ActorCommand::ConfigureRole(configure) => configure.generation,
2026-06-23 13:51:34 +00:00
ActorCommand::InstallRing { generation, .. }
2026-06-25 12:30:18 +00:00
| ActorCommand::RingReadable { generation, .. }
| ActorCommand::RingWritable { generation, .. }
2026-06-23 13:51:34 +00:00
| ActorCommand::ExecuteStep { generation, .. }
2026-06-25 12:30:18 +00:00
| ActorCommand::ReleaseDeviceObject { generation, .. }
| ActorCommand::ShutdownWorker { generation, .. } => *generation,
ActorCommand::InstallRingSpec(install) => install.generation,
ActorCommand::UninstallRing(uninstall) => uninstall.generation,
ActorCommand::ExecuteStepSpec(step) => step.generation,
2026-06-23 13:51:34 +00:00
};
if command_generation != self.current_generation {
self.events.push(WorkerCtlOut::CommandRejected {
reason: CommandRejection::OldGenerationHandle,
});
return;
}
match command {
2026-06-25 12:30:18 +00:00
ActorCommand::ConfigureRole(configure) => {
self.serialized
.push(WorkerCommand::ConfigureRole(configure));
}
2026-06-23 13:51:34 +00:00
ActorCommand::InstallRing { ring_id, .. } => {
self.installed_rings.insert(ring_id);
self.serialized.push(WorkerCommand::InstallRing { ring_id });
}
2026-06-25 12:30:18 +00:00
ActorCommand::InstallRingSpec(install) => {
self.installed_rings.insert(install.ring_id);
self.serialized
.push(WorkerCommand::InstallRingSpec(install));
}
ActorCommand::UninstallRing(uninstall) => {
self.installed_rings.remove(&uninstall.ring_id);
self.serialized
.push(WorkerCommand::UninstallRing(uninstall));
}
ActorCommand::RingReadable { ring_id, .. } => {
self.serialized
.push(WorkerCommand::RingReadable { ring_id });
}
ActorCommand::RingWritable { ring_id, .. } => {
self.serialized
.push(WorkerCommand::RingWritable { ring_id });
}
2026-06-23 13:51:34 +00:00
ActorCommand::ExecuteStep { step_id, input, .. } => {
if input.generation != self.current_generation {
self.events.push(WorkerCtlOut::CommandRejected {
reason: CommandRejection::OldGenerationHandle,
});
} else {
self.serialized
.push(WorkerCommand::ExecuteStep { step_id, input });
}
}
2026-06-25 12:30:18 +00:00
ActorCommand::ExecuteStepSpec(step) => {
if step
.inputs
.iter()
.any(|input| input.device_handle.generation != self.current_generation)
{
self.events.push(WorkerCtlOut::CommandRejected {
reason: CommandRejection::OldGenerationHandle,
});
} else {
self.serialized.push(WorkerCommand::ExecuteStepSpec(step));
}
}
2026-06-23 13:51:34 +00:00
ActorCommand::ReleaseDeviceObject { handle, .. } => {
if handle.generation != self.current_generation {
self.events.push(WorkerCtlOut::CommandRejected {
reason: CommandRejection::OldGenerationHandle,
});
} else {
self.serialized
.push(WorkerCommand::ReleaseDeviceObject { handle });
}
}
2026-06-25 12:30:18 +00:00
ActorCommand::ShutdownWorker { mode, .. } => {
self.serialized.push(WorkerCommand::ShutdownWorker { mode });
self.state = CtlState::ShuttingDown;
}
2026-06-23 13:51:34 +00:00
}
}
fn route(&mut self, event: WorkerEvent) {
match event.clone() {
2026-06-25 12:30:18 +00:00
WorkerEvent::RingInstalled { .. }
| WorkerEvent::RingInstalledForEdge { .. }
| WorkerEvent::RingFault { .. }
| WorkerEvent::RingQuiesced { .. } => {
2026-06-23 13:51:34 +00:00
self.routed.push(RoutedEvent::ToEdgeEstablisher(event))
}
2026-06-25 12:30:18 +00:00
WorkerEvent::ObjectLoaded { .. }
| WorkerEvent::ObjectLoadedFromRing { .. }
| WorkerEvent::ObjectFailed { .. } => self.routed.push(RoutedEvent::ToRxOrRole(event)),
WorkerEvent::ObjectProduced { .. } | WorkerEvent::ObjectProducedToRing { .. } => {
self.routed.push(RoutedEvent::ToTxOrRole(event))
}
WorkerEvent::StepCompleted { .. }
| WorkerEvent::StepCompletedForRole { .. }
| WorkerEvent::StepFailed { .. }
| WorkerEvent::RoleConfigured { .. }
| WorkerEvent::RoleFailed { .. } => {
2026-06-23 13:51:34 +00:00
self.routed.push(RoutedEvent::ToStageController(event))
}
2026-06-25 12:30:18 +00:00
WorkerEvent::RingReadable { .. }
| WorkerEvent::RingWritable { .. }
| WorkerEvent::DeviceObjectReleased { .. }
| WorkerEvent::ReleaseFailed { .. }
| WorkerEvent::WorkerReady { .. }
| WorkerEvent::WorkerFatal { .. }
| WorkerEvent::WorkerStopped { .. } => {
2026-06-23 13:51:34 +00:00
self.routed.push(RoutedEvent::ToDriverOrWorkerSide(event))
}
}
}
fn process_exited(&mut self, status: ExitStatus) {
match (self.state, status) {
(CtlState::ShuttingDown, ExitStatus::Code(0)) => {
for ring_id in &self.installed_rings {
self.events
.push(WorkerCtlOut::RingQuiesced { ring_id: *ring_id });
}
self.events.push(WorkerCtlOut::TerminalStopped {
generation: self.current_generation,
});
self.state = CtlState::Stopped;
}
(_, _) => {
for ring_id in &self.installed_rings {
self.events
.push(WorkerCtlOut::RingFaulted { ring_id: *ring_id });
self.commands
.push(WorkerCtlCommand::StopDriverPump { ring_id: *ring_id });
}
self.events.push(WorkerCtlOut::WorkerFailed {
generation: self.current_generation,
reason: WorkerFailure::ProcessExited,
});
self.state = CtlState::Crashed;
}
}
}
fn restart(&mut self) {
self.state = CtlState::Starting;
self.current_generation = WorkerGeneration(self.current_generation.0 + 1);
}
fn shutdown(&mut self) {
if self.state == CtlState::Running {
2026-06-25 12:30:18 +00:00
self.serialized.push(WorkerCommand::ShutdownWorker {
mode: ShutdownMode::Graceful,
});
2026-06-23 13:51:34 +00:00
self.state = CtlState::ShuttingDown;
}
}
}
pub type GpuWorkerCtlHarness = GpuWorkerCtl;