Release a dead process's device claims (M17.1)
Every path out of a process (exit, fault, kill) now releases its device claims alongside its IRQ and MSI bindings, so a restarted driver can claim its hardware again — the cleanup half of process-lifecycle.md's iron rule 1. MSI vectors were already swept by irq.releaseOwner; claims were the gap. The claim-release test proves kill -> release -> re-claim, plus the broker release in isolation.
This commit is contained in:
@@ -132,6 +132,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
processKillTest(boot_information);
|
||||
} else if (eql(case, "supervision")) {
|
||||
supervisionTest(boot_information);
|
||||
} else if (eql(case, "claim-release")) {
|
||||
claimReleaseTest(boot_information);
|
||||
} else if (eql(case, "initial-ramdisk")) {
|
||||
initialRamdiskTest(boot_information);
|
||||
} else if (eql(case, "vfs")) {
|
||||
@@ -1454,6 +1456,61 @@ fn processKillTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
}
|
||||
|
||||
/// M17.1: a dead process's device claims are released by the reap, so a restarted
|
||||
/// driver can claim its hardware again (docs/process-lifecycle.md iron rule 1).
|
||||
/// First the broker release in isolation — two owners, one released, the other's
|
||||
/// claim must survive. Then the death-path wiring with a real child: the claim is
|
||||
/// made on the child's behalf (the broker is kernel-callable), the child is
|
||||
/// killed, and once the exit notification arrives — posted last, after release —
|
||||
/// the device must be unclaimed and claimable again.
|
||||
fn claimReleaseTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: claim-release\n", .{});
|
||||
|
||||
var buffer: [2]device_abi.DeviceDescriptor = undefined;
|
||||
const total = devices_broker.enumerate(&buffer);
|
||||
check("the device tree is seeded (>= 2 devices)", total >= 2);
|
||||
if (total < 2) {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
|
||||
// The broker release in isolation.
|
||||
check("device 0 claimed by owner 111", devices_broker.claim(0, 111));
|
||||
check("device 1 claimed by owner 222", devices_broker.claim(1, 222));
|
||||
devices_broker.releaseAllOwnedBy(111);
|
||||
check("owner 111's claim is released", devices_broker.ownerOf(0) == null);
|
||||
check("owner 222's claim survives", (devices_broker.ownerOf(1) orelse 0) == 222);
|
||||
devices_broker.releaseAllOwnedBy(222);
|
||||
check("cleanup released owner 222", devices_broker.ownerOf(1) == null);
|
||||
|
||||
// The death-path wiring: a real process dies holding a claim.
|
||||
check("bootloader handed over /system/services/init", boot_information.init_len != 0);
|
||||
if (boot_information.init_len == 0) {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const me = scheduler.currentId();
|
||||
const endpoint = ipcsync.createIpcEndpoint() orelse {
|
||||
check("exit endpoint allocated", false);
|
||||
result();
|
||||
return;
|
||||
};
|
||||
const child = process.spawnProcessSupervised(image, 4, &.{"/system/services/init"}, me, endpoint) catch 0;
|
||||
check("supervised child spawned", child != 0);
|
||||
check("device 0 claimed on the child's behalf", devices_broker.claim(0, child));
|
||||
|
||||
check("the kill is accepted", process.killProcess(me, child) == 0);
|
||||
var badge: u64 = 0;
|
||||
var received_cap: u64 = 0;
|
||||
_ = ipcsync.replyWait(endpoint, 0, 0, 0, 0, abi.no_cap, &badge, &received_cap);
|
||||
check("the exit notification arrived", badge == abi.notify_badge_bit | abi.notify_exit_bit | child);
|
||||
check("death released the child's claim", devices_broker.ownerOf(0) == null);
|
||||
check("the device is claimable again", devices_broker.claim(0, me));
|
||||
devices_broker.releaseAllOwnedBy(me);
|
||||
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