From b3fd5cc682477f5fadf49c9f0a6e9819f0786c43 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sun, 26 Jul 2026 17:12:18 +0900 Subject: [PATCH] e1000d: DMA barriers on RX and TX descriptor paths Without explicit memory ordering, the Rust compiler is free to reorder loads of the device-written descriptor (status, length) relative to the writeback of the descriptor ownership flag. On x86 the resulting tearing is usually invisible (the CPU serializes implicitly), but on aarch64 with relaxed ordering, or on any architecture with a future writeback-cache or snoop filter, the RX path can read a length that does not yet match the status it just observed, and the TX path can ring the doorbell before the descriptor's length is visible to the NIC. Dma::sync_for_cpu() is the acquire barrier called before reading the completion status; Dma::sync_for_device() is the release barrier called before ringing TDT or RDT. These are the same fences the Linux e1000e driver places around doorbell writes and descriptor status reads, and they are cheap (single compiler fence, no real CPU cost). --- drivers/net/e1000d/src/device.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/e1000d/src/device.rs b/drivers/net/e1000d/src/device.rs index 0e42d72b2e..840cc3a9fa 100644 --- a/drivers/net/e1000d/src/device.rs +++ b/drivers/net/e1000d/src/device.rs @@ -135,6 +135,11 @@ impl NetworkAdapter for Intel8254x { fn read_packet(&mut self, buf: &mut [u8]) -> Result> { let desc = unsafe { &mut *(self.receive_ring.as_ptr().add(self.receive_index) as *mut Rd) }; + // Acquire fence pairs the device's store to desc.status with our + // load of desc.length; without it the compiler can reorder + // and we read a stale length on weakly-ordered architectures. + self.receive_buffer[self.receive_index].sync_for_cpu(); + if desc.status & RD_DD == RD_DD { desc.status = 0; @@ -143,6 +148,7 @@ impl NetworkAdapter for Intel8254x { let i = cmp::min(buf.len(), data.len()); buf[..i].copy_from_slice(&data[..i]); + self.receive_buffer[self.receive_index].sync_for_device(); unsafe { self.write_reg(RDT, self.receive_index as u32) }; self.receive_index = wrap_ring(self.receive_index, self.receive_ring.len()); @@ -200,6 +206,7 @@ impl NetworkAdapter for Intel8254x { self.transmit_index = wrap_ring(self.transmit_index, self.transmit_ring.len()); self.transmit_ring_free -= 1; + self.transmit_buffer[self.transmit_index].sync_for_device(); unsafe { self.write_reg(TDT, self.transmit_index as u32) }; Ok(i)