b3fd5cc682
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).