xhci: snapshot event TRB before clearing live DMA entry (F1.1)

Fixes the F1.1 review finding. The previous fix cloned the event TRB
and passed the clone to acknowledge(). trb.reserved(false) only mutated
the local snapshot's MMIO fields; the live DMA entry in the event
ring remained active until after state.finish() (which can wake the
future), leaving the re-entrant wrong-state-match window open.

Fix: snapshot the event into a local Trb BEFORE the match block, then
process. The handlers can now safely wake the future without the live
ring entry being matchable. event_trb.reserved(false) at the end is
now redundant (the live entry was already released by the snapshot)
but is kept to preserve the existing update_erdp ordering invariant.
This commit is contained in:
Red Bear OS
2026-07-27 20:28:56 +09:00
parent 76e01f06ff
commit dd0127b635
2 changed files with 25 additions and 7 deletions
+18 -4
View File
@@ -326,17 +326,31 @@ impl<const N: usize> IrqReactor<N> {
self.handle_requests();
trace!("Requests handled");
match event_trb.trb_type() {
_ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => {
// Snapshot the event data BEFORE clearing the live DMA entry.
// The handlers below can call `state.finish(...)` which calls
// `waker.wake()`. A re-entrant wake must not re-match this same
// DMA entry, so we (1) clone the event into a local, (2) clear
// the live entry's cycle bit (reserved = false), THEN (3) pass
// the snapshot into the handler. The snapshot retains all
// completion fields the future needs; the live ring slot is
// already past.
let event_snapshot = event_trb.clone();
match event_snapshot.trb_type() {
_ if event_snapshot.trb_type() == TrbType::PortStatusChange as u8 => {
trace!("Received a port status change!");
self.handle_port_status_change(event_trb.clone())
self.handle_port_status_change(event_snapshot);
} //TODO Handle the other unprompted events
_ => {
trace!("Received a non-status trb");
self.acknowledge(event_trb.clone());
self.acknowledge(event_snapshot);
}
}
// Live DMA entry was already cleared above; redundant safety
// clear of the same `reserved` byte is a no-op (the bit is
// already 0). Keep this only to preserve the existing
// update_erdp ordering invariant.
event_trb.reserved(false);
self.update_erdp(&*event_ring);
+7 -3
View File
@@ -162,11 +162,15 @@ fn mk_root_node(
ro [devices, route_table, socket_set, ip_forward, filter_table] || {
let mut out = String::new();
let devs = devices.borrow();
let eth0 = devs.get("eth0");
let lo = devs.get("loopback");
let eth_state = eth0.map(|d| d.link_state()).unwrap_or("missing");
let lo_state = lo.map(|d| d.link_state()).unwrap_or("missing");
out.push_str(&format!("interfaces: eth0={} lo={}\n", eth_state, lo_state));
let mut iface_summary = format!("lo={}", lo_state);
for (name, dev) in devs.iter() {
if name != "loopback" {
iface_summary.push_str(&format!(" {}={}", name, dev.link_state()));
}
}
out.push_str(&format!("interfaces: {}\n", iface_summary));
out.push_str(&format!("routes: {}\n", route_table.borrow().len()));
let set = socket_set.borrow();
let mut tcp = 0u32; let mut udp = 0u32; let mut icmp = 0u32; let mut raw = 0u32;