The device manager supervises: hello, backoff, and the crash-loop cap (M18.1)
The manager is now a harness service on the well-known .device_manager endpoint. Every driver spawns supervised; drivers with an assignment must hello (device-manager-protocol, versioned) within a deadline enforced by a timer sweep. Exit reasons drive the restart decision: clean exits stay down, faults restart with 300/600/1200ms backoff, and three fast deaths mark a driver failed instead of respawning forever. usb-xhci-bus is the first conforming driver; the crash-test fixture claims a device, hellos, and faults on purpose — each respawn re-proving claim release on death through the manager's own path. maximum_tasks grows 16 -> 32: the initial-ramdisk sweep (15 binaries at once) was intermittently overflowing the static pool.
This commit is contained in:
@@ -138,6 +138,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
vfsClientDeathTest(boot_information);
|
||||
} else if (eql(case, "signals")) {
|
||||
signalsTest(boot_information);
|
||||
} else if (eql(case, "driver-restart")) {
|
||||
driverRestartTest(boot_information);
|
||||
} else if (eql(case, "initial-ramdisk")) {
|
||||
initialRamdiskTest(boot_information);
|
||||
} else if (eql(case, "vfs")) {
|
||||
@@ -1654,6 +1656,44 @@ fn signalsTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
}
|
||||
|
||||
/// M18.1: the device manager's restart machinery, end to end. In test-restart
|
||||
/// mode the manager also supervises crash-test: a fixture that claims device 0,
|
||||
/// hellos, and faults. The scenario asserts three markers in order — the real
|
||||
/// xHCI driver hellos clean and stays; crash-test is restarted with backoff
|
||||
/// (each respawn re-claiming the device the dead instance held, M17.1 through
|
||||
/// the manager's path); the crash loop caps and the manager gives up.
|
||||
fn driverRestartTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: driver-restart\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over an initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
return;
|
||||
};
|
||||
|
||||
process.setInitialRamdisk(image); // the manager system_spawns drivers by name
|
||||
process.write_count = 0;
|
||||
var manager: u32 = 0;
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
const item = rd.entry(i) orelse continue;
|
||||
if (!eql(item.name, "device-manager")) continue;
|
||||
manager = process.spawnProcessSupervised(item.blob, 4, &.{ "device-manager", "test-restart" }, scheduler.currentId(), null) catch 0;
|
||||
break;
|
||||
}
|
||||
check("device-manager spawned in test-restart mode", manager != 0);
|
||||
// The assertions live in the harness: its expect regex requires, in order,
|
||||
// the xHCI hello ack, a crash-test restart, and the crash-loop cap — read
|
||||
// from the whole serial capture, immune to the transient-line races a
|
||||
// write_buffer poll would have here (many processes log concurrently).
|
||||
result();
|
||||
}
|
||||
|
||||
/// The whole user-side surface at once: spawn process-test's supervisor role,
|
||||
/// which — entirely from ring 3 — creates an exit endpoint, spawns its two
|
||||
/// children supervised, sees them in process_enumerate, kills them (one blocked,
|
||||
|
||||
Reference in New Issue
Block a user