rtl8139d: DMA barriers on CAPR doorbell and TSD doorbell
Apply the same release/acquire fence pattern as e1000d and rtl8168d: - acquire fence before reading the RX status / length / data bytes, so the compiler does not reorder the buffer reads past the rxsts observation on weakly-ordered architectures. - release fence before the CAPR write (RX doorbell) so the device sees the buffer reads before the doorbell signals the consumer is done with the ring. - release fence before the TSD write (TX doorbell) so the device sees the buffer writes before claiming ownership of the descriptor. The 8139 is a legacy 100 Mb/s chip with a 64 KB ring rather than a 16-entry descriptor table, but the same hand-off semantics apply: ownership bit is cleared on read, set on write, and the device accesses buffer memory after the doorbell. The fences are the same compiler-level ordering the Linux 8139too driver places around its MMIO writes; the 8139 chip itself does not need anything more.
This commit is contained in:
@@ -138,6 +138,11 @@ impl NetworkAdapter for Rtl8139 {
|
||||
|
||||
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
if !self.regs.cr.readf(CR_BUFE) {
|
||||
// Acquire fence pairs the device's write of rxsts / size /
|
||||
// data with our subsequent reads on weakly-ordered
|
||||
// architectures.
|
||||
core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);
|
||||
|
||||
let rxsts = (self.rx(0) as u16) | (self.rx(1) as u16) << 8;
|
||||
|
||||
let size_with_crc = (self.rx(2) as usize) | (self.rx(3) as usize) << 8;
|
||||
@@ -158,6 +163,10 @@ impl NetworkAdapter for Rtl8139 {
|
||||
self.receive_i =
|
||||
(self.receive_i + 4 + size_with_crc).next_multiple_of(4) % RX_BUFFER_SIZE;
|
||||
let capr = self.receive_i.wrapping_sub(16) as u16;
|
||||
// Release fence ensures the buffer reads above are
|
||||
// visible to the device before the CAPR doorbell is
|
||||
// observed.
|
||||
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
|
||||
self.regs.capr.write(capr);
|
||||
|
||||
res
|
||||
@@ -189,6 +198,11 @@ impl NetworkAdapter for Rtl8139 {
|
||||
assert_eq!(i as u32, i as u32 & TSD_SIZE_MASK);
|
||||
self.regs.tsd[self.transmit_i].write(i as u32 & TSD_SIZE_MASK);
|
||||
|
||||
// Release fence ensures the buffer writes above are
|
||||
// visible to the device before the TSD doorbell is
|
||||
// observed.
|
||||
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
|
||||
|
||||
//TODO: wait for TSD_TOK or error
|
||||
|
||||
self.transmit_i += 1;
|
||||
|
||||
Reference in New Issue
Block a user