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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user