kernel: maximum_children_per_parent is gone

The second invented ceiling. It was written to stop a driver looping
device_register and exhausting a shared table — but there is no shared table
to exhaust any more, and each registrar already has its own allowance, so a
runaway costs only itself.

It never bounded a determined caller in the first place: 16 children per
parent, and nothing stopped it claiming more parents. What it reliably did
was refuse a real PCI bus with more than 16 functions, which is how an AMD
Ryzen booted with a working display, no USB and no storage.

The constant, its check, and the now-unused childCount all go. TooManyChildren
survives with one meaning instead of two: the caller is at its per-registrar
allowance.

This was unblocked from the moment D9 landed. The plan said so — "once the
quota exists the per-parent cap is redundant whether or not D6 has landed" —
in the same edit that left the step tagged "blocked on D6". Three iterations
were then spent re-reading that tag instead of the sentence beside it.

The containment test now asserts 64 children under one parent, four times
the old ceiling; restoring the cap fails it.

Suite 118/118.
This commit is contained in:
Daniel Samson
2026-08-08 21:12:20 +01:00
parent 3ae541214f
commit 7d8aa51234
3 changed files with 54 additions and 75 deletions
+18 -19
View File
@@ -4037,34 +4037,33 @@ fn containmentTest() void {
check("re-registering an identical child returns the same id", again != 0 and again == good);
check("re-registering grew nothing", devices_broker.enumerate(&buffer) == before + 1);
// Fill the parent to its child cap with distinct children (same window, different
// identity — the match is on identity, so each is a new device).
// **There is no per-parent cap.** `maximum_children_per_parent = 16` is gone: it
// was written to stop a driver looping `device_register` and exhausting a shared
// table, and there is no shared table to exhaust — the table grows, and each
// registrar has its own allowance, so a runaway costs only itself. The cap never
// bounded a determined caller anyway (16 per parent, but nothing stopped it
// claiming more parents); what it reliably did was refuse a real PCI bus with more
// than 16 functions, which is how an AMD Ryzen booted with no USB and no storage.
//
// 64 children under one parent — four times the old ceiling.
var filled: u32 = 0;
var capped = false;
var registered_children: u32 = 0;
while (filled < 64) : (filled += 1) {
var name: [4]u8 = .{ 'k', 0, 0, 0 };
name[1] = '0' + @as(u8, @intCast(filled / 10));
name[2] = '0' + @as(u8, @intCast(filled % 10));
var extra = childDescriptor(name[0..3], parent_window.start, 0x20);
_ = devices_broker.register(parent_id, me, &extra) catch |err| {
capped = err == error.TooManyChildren;
break;
};
if (devices_broker.register(parent_id, me, &extra)) |_| {
registered_children += 1;
} else |_| break;
}
check("the parent reaches its child cap (TooManyChildren)", capped);
check("one parent takes far more children than the old cap allowed", registered_children == 64);
// The regression this ordering exists for: **a re-registration consumes no slot,
// so a full parent must not refuse one.** A crashed bus driver is restarted by its
// supervisor and re-registers everything it rediscovers; when the cap was checked
// before the identity match, the restart was refused its own devices and the
// machine degraded a little more on every crash.
// Idempotency still holds, and still matters: a crashed bus driver is restarted and
// re-registers everything it rediscovers, which must return the ids it had before
// rather than duplicate them.
const readmitted = devices_broker.register(parent_id, me, &fits) catch 0;
check("a full parent still re-admits an identical child", readmitted != 0 and readmitted == good);
// ...and the cap is genuinely still in force for anything new.
var novel = childDescriptor("knew", parent_window.start, 0x20);
const still_capped = if (devices_broker.register(parent_id, me, &novel)) |_| false else |err| err == error.TooManyChildren;
check("a full parent still refuses a new child", still_capped);
check("re-registering an identical child still returns its id", readmitted != 0 and readmitted == good);
// The table itself has no ceiling: it grows. The old `maximum_devices = 64` was a
// guess about someone else's computer, and one driver's enumeration starved every