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).
This commit is contained in:
@@ -135,6 +135,11 @@ impl NetworkAdapter for Intel8254x {
|
|||||||
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>> {
|
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||||
let desc = unsafe { &mut *(self.receive_ring.as_ptr().add(self.receive_index) as *mut Rd) };
|
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 {
|
if desc.status & RD_DD == RD_DD {
|
||||||
desc.status = 0;
|
desc.status = 0;
|
||||||
|
|
||||||
@@ -143,6 +148,7 @@ impl NetworkAdapter for Intel8254x {
|
|||||||
let i = cmp::min(buf.len(), data.len());
|
let i = cmp::min(buf.len(), data.len());
|
||||||
buf[..i].copy_from_slice(&data[..i]);
|
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) };
|
unsafe { self.write_reg(RDT, self.receive_index as u32) };
|
||||||
self.receive_index = wrap_ring(self.receive_index, self.receive_ring.len());
|
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_index = wrap_ring(self.transmit_index, self.transmit_ring.len());
|
||||||
self.transmit_ring_free -= 1;
|
self.transmit_ring_free -= 1;
|
||||||
|
|
||||||
|
self.transmit_buffer[self.transmit_index].sync_for_device();
|
||||||
unsafe { self.write_reg(TDT, self.transmit_index as u32) };
|
unsafe { self.write_reg(TDT, self.transmit_index as u32) };
|
||||||
|
|
||||||
Ok(i)
|
Ok(i)
|
||||||
|
|||||||
Reference in New Issue
Block a user