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:
Red Bear OS
2026-07-26 20:19:29 +09:00
parent cca052338d
commit c71e518391
+14
View File
@@ -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;