usb/fat: transfer events matched by slot+endpoint; storage failures heal
B2 — the 1-in-3 boot-time READ CAPACITY failure, root-caused: the xHCI library's awaitTransfer claimed ANY unclaimed transfer event as its own completion. An interrupt-endpoint event whose TRB pointer no longer matched the armed subscription (an error or stale completion from the keyboard/mouse polling concurrently with storage bring-up) fell through and was misread as the bulk transfer's completion — desynchronizing the mass-storage bulk protocol in controller state that SURVIVED driver restarts, so every retry failed too. Awaited transfers now match the event's slot id and endpoint DCI; foreign events are dropped and named. Twelve consecutive runs of the previously-flaky cases pass; the full suite is green with none of its old intermittents. B1 — and when storage does fail transiently, the system now heals instead of giving up forever: a nonzero exit maps to ExitReason.aborted (a deliberate FAILURE exit — supervisors restart those with backoff, unlike a clean .exited), usb-storage exits nonzero when a PRESENT device fails bring-up, and the fat service no longer blocks its harness polling for a block device and then dies — it serves immediately (requests fail politely), retries on a 500 ms timer, and mounts whenever storage appears, including after a driver restart.
This commit is contained in:
@@ -682,16 +682,29 @@ pub const Controller = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn awaitTransfer(self: *Controller, requested_length: u32) ?u8 {
|
||||
/// Await the completion of OUR transfer — identified by the event's slot id
|
||||
/// (control[31:24]) and endpoint DCI (control[20:16]). Any other transfer
|
||||
/// event is either a subscription's report (serviced) or foreign noise (an
|
||||
/// interrupt endpoint's error/stale completion whose TRB pointer no longer
|
||||
/// matches the armed one) — DROPPED, never misattributed: claiming a foreign
|
||||
/// event as our completion desynchronized the mass-storage bulk protocol in
|
||||
/// a way that survived every driver restart (the 1-in-3 READ CAPACITY
|
||||
/// failure at boot, with a USB keyboard and mouse polling concurrently).
|
||||
fn awaitTransfer(self: *Controller, slot_id: u8, dci: u32, requested_length: u32) ?u8 {
|
||||
const deadline = system.clock() + 1_000_000_000;
|
||||
while (true) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
if (trbType(event.control) == @intFromEnum(TrbType.transfer_event)) {
|
||||
if (self.serviceInterruptEvent(event)) continue; // a subscription's report
|
||||
const residual = event.status & 0xFFFFFF;
|
||||
self.last_transfer_length = if (residual >= requested_length) 0 else requested_length - residual;
|
||||
return completionCode(event.status); // our transfer's completion (or error)
|
||||
if (trbType(event.control) != @intFromEnum(TrbType.transfer_event)) continue;
|
||||
if (self.serviceInterruptEvent(event)) continue; // a subscription's report
|
||||
const event_slot: u8 = @truncate(event.control >> 24);
|
||||
const event_dci: u32 = (event.control >> 16) & 0x1F;
|
||||
if (event_slot != slot_id or event_dci != dci) {
|
||||
std.log.info("dropped foreign transfer event (slot {d} dci {d}, code {d})", .{ event_slot, event_dci, completionCode(event.status) });
|
||||
continue;
|
||||
}
|
||||
const residual = event.status & 0xFFFFFF;
|
||||
self.last_transfer_length = if (residual >= requested_length) 0 else requested_length - residual;
|
||||
return completionCode(event.status); // our transfer's completion (or error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,7 +745,7 @@ pub const Controller = struct {
|
||||
|
||||
mmio.wmb();
|
||||
self.ringDoorbell(device.slot_id, 1); // DCI 1 = EP0
|
||||
const code = self.awaitTransfer(@intCast(data.len)) orelse return false;
|
||||
const code = self.awaitTransfer(device.slot_id, 1, @intCast(data.len)) orelse return false;
|
||||
if (code != @intFromEnum(CompletionCode.success) and code != @intFromEnum(CompletionCode.short_packet)) return false;
|
||||
|
||||
if (has_data and direction_in) {
|
||||
@@ -925,8 +938,9 @@ pub const Controller = struct {
|
||||
mmio.wmb();
|
||||
const number: u8 = endpoint.address & 0x0F;
|
||||
const direction_in = endpoint.address & 0x80 != 0;
|
||||
self.ringDoorbell(device.slot_id, doorbellContextIndex(number, direction_in));
|
||||
const code = self.awaitTransfer(length) orelse return null;
|
||||
const dci = doorbellContextIndex(number, direction_in);
|
||||
self.ringDoorbell(device.slot_id, dci);
|
||||
const code = self.awaitTransfer(device.slot_id, dci, length) orelse return null;
|
||||
if (code != @intFromEnum(CompletionCode.success) and code != @intFromEnum(CompletionCode.short_packet)) return null;
|
||||
return self.last_transfer_length;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user