Skip to content

Commit b7c57d9

Browse files
committed
network stack improvements, lower latency, more resilience
1 parent 6c842a9 commit b7c57d9

2 files changed

Lines changed: 36 additions & 10 deletions

File tree

src/net.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,9 @@ pub struct NatEngine {
450450
tcp_tw: HashMap<(u32, u16, u16), Instant>, // TIME_WAIT: absorb final ACKs silently
451451
icmp_nat: HashMap<(u32, u16), NatIcmpEntry>, // key: (dst_ip, identifier)
452452
icmp_unavailable: bool, // true after first failed raw socket creation (Windows non-admin)
453+
// Replies generated while draining TX frames are deferred to the next loop iteration
454+
// so they don't race with the TX completion interrupt in IRIX's interrupt handler.
455+
deferred_rx: Vec<Vec<u8>>,
453456
}
454457

455458
impl NatEngine {
@@ -462,7 +465,7 @@ impl NatEngine {
462465
ctl: Arc<NatControl>) -> Self {
463466
Self { config, tx_cons, rx_prod, rx_wake, tx_wake, running, ctl,
464467
udp_nat: HashMap::new(), tcp_nat: HashMap::new(), tcp_tw: HashMap::new(),
465-
icmp_nat: HashMap::new(), icmp_unavailable: false }
468+
icmp_nat: HashMap::new(), icmp_unavailable: false, deferred_rx: Vec::new() }
466469
}
467470

468471
pub fn run(&mut self) {
@@ -472,7 +475,7 @@ impl NatEngine {
472475
{
473476
let (lock, cvar) = &*self.tx_wake;
474477
let mut guard = lock.lock();
475-
let _ = cvar.wait_for(&mut guard, Duration::from_millis(10));
478+
let _ = cvar.wait_for(&mut guard, Duration::from_millis(1));
476479
}
477480

478481
// Machine reset: flush all NAT tables, close all host sockets.
@@ -483,6 +486,26 @@ impl NatEngine {
483486
self.icmp_nat.clear(); // drops all ICMP raw sockets
484487
}
485488

489+
// FIXME: investigate interrupt race between TX completion and RX delivery.
490+
// When a gateway reply (e.g. ICMP echo) is generated synchronously while
491+
// draining TX frames, it can arrive at IRIX while the TX completion interrupt
492+
// handler is still running. IRIX writes CLRINT which clears *all* pending
493+
// interrupts, silently dropping the RX interrupt. Deferring to the next loop
494+
// iteration (after the tx_wake wait) gives IRIX time to exit the TX handler
495+
// before we signal RX. This masks the symptom but the root cause — whether
496+
// IRIX's driver should re-check for new RX after CLRINT, or whether we should
497+
// hold the RX interrupt line asserted until explicitly cleared — is unclear.
498+
// Flush replies deferred from the previous iteration; stop if ring is full.
499+
let pending = std::mem::take(&mut self.deferred_rx);
500+
for frame in pending {
501+
if self.rx_prod.slots() == 0 {
502+
self.deferred_rx.push(frame);
503+
} else {
504+
let _ = self.rx_prod.push(frame);
505+
self.rx_wake.1.notify_one();
506+
}
507+
}
508+
486509
// Drain all pending outbound frames
487510
while let Ok(frame) = self.tx_cons.pop() {
488511
self.process(&frame);
@@ -620,7 +643,7 @@ impl NatEngine {
620643
let c = ip_checksum(&icmp); w16(&mut icmp, 2, c);
621644
let frame = ip_frame(src_mac, &self.config.gateway_mac,
622645
self.config.gateway_ip, src_ip, IP_PROTO_ICMP, &icmp);
623-
self.enqueue_rx(frame);
646+
self.deferred_rx.push(frame);
624647
return;
625648
}
626649

src/seeq8003.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -337,25 +337,28 @@ impl Seeq8003 {
337337
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: in reset, dropping frame");
338338
return RxPumpResult::Nothing;
339339
}
340-
let frame = match rx_cons.pop() {
340+
let frame = match rx_cons.peek() {
341341
Ok(f) => f,
342342
Err(_) => return RxPumpResult::Nothing,
343343
};
344344

345345
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: got frame {} bytes", frame.len());
346346

347-
if !Self::address_filter(rx_cmd_snap, &station_addr_snap, &frame) {
348-
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: address filter dropped {}", eth_summary(&frame));
347+
if !Self::address_filter(rx_cmd_snap, &station_addr_snap, frame) {
348+
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: address filter dropped {}", eth_summary(frame));
349+
let _ = rx_cons.pop(); // discard filtered frame
349350
return RxPumpResult::Nothing;
350351
}
351352

352-
dlog_dev!(LogModule::Seeq, "SEEQ RX {} → guest DMA", eth_summary(&frame));
353+
dlog_dev!(LogModule::Seeq, "SEEQ RX {} → guest DMA", eth_summary(frame));
353354

354355
let (dst, _) = rx_dma.write(0, false); // pad[0]
355356
if dst.refused() {
356-
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: DMA not ready ({:#x}), frame LOST: {}", dst.0, eth_summary(&frame));
357-
return RxPumpResult::Refused;
357+
dlog_dev!(LogModule::Seeq, "SEEQ pump_rx: DMA not ready ({:#x}), will retry: {}", dst.0, eth_summary(frame));
358+
return RxPumpResult::Refused; // frame stays in rx_cons for next iteration
358359
}
360+
// DMA is committed (pad[0] written); pop the frame now and finish writing it.
361+
let frame = rx_cons.pop().expect("peek succeeded so pop must succeed");
359362
let _ = rx_dma.write(0, false); // pad[1]
360363
for b in &frame {
361364
let _ = rx_dma.write(*b as u32, false);
@@ -628,7 +631,7 @@ impl Device for Seeq8003 {
628631
dma_irq |= irq;
629632
}
630633
RxPumpResult::Refused => {
631-
dlog_dev!(LogModule::Seeq, "[ts={}] SEEQ RX DMA refused", st.ts);
634+
dlog_dev!(LogModule::Seeq, "[ts={}] SEEQ RX DMA refused, retrying next tick", st.ts);
632635
}
633636
RxPumpResult::Nothing => {}
634637
}

0 commit comments

Comments
 (0)