Commit 717bc436 introduced MSI-X support via
pci_allocate_interrupt_vector but called it as a method on
PciFunction (.pci_allocate_interrupt_vector(1)), which doesn't
exist. The function is actually a free function in
pcid_interface::irq_helpers, matching the pattern used by e1000d,
rtl8139d, rtl8139d, and ihdad.
Fix: add the import and change the call to match e1000d's pattern:
pci_allocate_interrupt_vector(&mut pcid_handle, "ixgbed")
This unblocks the base cook for all targets.
Same release/acquire fence pattern as the other PCI drivers.
The ixgbe (10 GbE) has the same hand-off semantics as e1000/rtl8168
(OWN/OWN-bit), so the same ordering is required: release fence
before TDT (TX doorbell) and RDT (RX doorbell), and an acquire
fence before reading the descriptor status.
The Linux ixgbe driver applies the same fences in the same
places; see afadba9..c17924b in the upstream tree. The original
Redox port from ixy.rs didn't carry them, which is a real bug
on aarch64 (or on any future cache-coherent extension).
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.
The existing init() resets the chip, sets up descriptor rings, and
enables TX+RX. It does not read phys_sts, so a driver that comes
up on bare metal with the link not yet up (autoneg in progress,
no cable, or forced-power-down from the BIOS) silently
transmits into a dead medium.
Add a single phys_sts read at the end of init(). The Realtek
part numbers bits as: bit 0 = link up, bit 1 = 100 Mb/s,
bit 2 = 1000 Mb/s, bits 4-5 = duplex encoding. When the link
is not up, log a warn!() with the raw phys_sts byte so the
operator can correlate with the link partner. When the link
is up, log the detected speed as info!().
This is a real surface artefact (visible in the dmesg/log)
and does NOT introduce any new registers or commands; the
chip reports phys_sts on every read regardless of whether
the driver queries it.
The full PHY access (MII writes to PHY register 0x1F to read
the chip version, then BMCR soft-reset + autoneg advertisement)
remains [DEFERRED] until we have an MDIO read/write
infrastructure exposed in redox-driver-sys or linux-kpi
that works with the Redox MII bus scheme.
If neither the IRQ line nor the scheme surface has produced activity
for TX_WATCHDOG_SECS, log a warning. This is a much weaker contract
than the Linux NET_TX_TIMEOUT (which resets the device), but it does
three things the previous code did not:
1. Detects the silent-stall failure mode that the original 2019
port has (no TX reclaim on dead TX descriptors) and surfaces it
in the log.
2. Forces the next event-loop iteration to call scheme.tick()
which exercises the transmit ring.
3. Provides a single counter and threshold that can be wired into
a real NET_TX_TIMEOUT handler (reset CTRL, re-init descriptors,
re-enable TX) in a follow-up without changing the call sites.
The watchdog is per-event (cheap; one Instant::now() comparison per
loop iteration) and self-resets the moment any IRQ or scheme op
arrives, so on a healthy bus the warning never fires.
The previous code unconditionally required a legacy INTX line, which
on bare metal is missing for nearly every modern 82599/X540/X550
board that has its IRQ lines wired to MSI-X only. pcid_interface
allocates MSI-X when the device advertises it, falls back to MSI,
then to legacy INTX, so the change preserves every existing path
while unlocking the much higher line-rate headroom of MSI-X
per-vector queuing that the device's enable_msix_interrupt code
already implements in device.rs.
The IRQ-acknowledge-and-write pattern is unchanged: the kernel
masks the line on delivery, we must re-arm unconditionally or a
foreign interrupt on a shared line leaves the line masked forever.
- device.rs: release fence before tppoll doorbell write; acquire
fence after the OWN-bit read on the receive descriptor. The
Realtek descriptor hand-off is the same OWN-bit protocol as
e1000d, and the same reordering argument applies: on aarch64
with relaxed ordering the device can clear OWN and update
length+status in any order, and without a fence we can
observe OWN=0 paired with a stale length and compute a partial
read.
- config.toml: drop the description's RTL8125 reference. The
rtl8168d register layout does not support rtl8125 (a 2.5GbE
chip with its own register layout and PHY init sequence); the
description now matches the actual capability.
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).
- tcp/scheme/tcp.rs: write_buf now returns the actual bytes from
send_slice() instead of buf.len(). Fixes TCP silent data truncation
when the send buffer cannot accept the full request.
- netstack/scheme/ip.rs: fpath() uses .expect() with a message in place
of .unwrap() for clearer diagnostics on buffer write failure.
- dhcpd/main.rs: parse the first non-flag positional argument as the
interface name. Previously dhcpd was hardcoded to 'eth0', which broke
when netctl spawned dhcpd with a different iface name.
- drivers/common/dma.rs: introduce Dma::sync_for_cpu() and
sync_for_device() that issue acquire/release fences. Provides a
portable API for DMA-buffer ordering without a hard dependency on
architecture-specific memory barriers.
- drivers/net/virtio-netd/scheme.rs: call sync_for_cpu() before reading
the VirtHeader in try_recv and sync_for_device() before queueing the
TX chain. Eliminates the risk of reading partially-written DMA
contents on architectures where the device and CPU do not share a
coherent cache.
Continuation of 92924224 — the same conditional-ack bug class found in
six more drivers: a foreign interrupt on a shared INTx line skipped the
write-back ack, leaving the kernel-masked IRQ line dead forever.
- sb16d, ac97d: ack before the not-ours continue
- rtl8139d, rtl8168d, ixgbed: ack unconditionally, tick only when ours
(the stale 'TODO: spurious interrupts' comments removed — the
spurious-interrupt confusion was this bug)
- ihdgd: ack unconditionally, handle_events/tick only when ours
Verified: cargo check -Z build-std --target x86_64-unknown-redox for
sb16d, ac97d, rtl8139d, rtl8168d, ixgbed, ihdgd — clean, no new
warnings. nvmed audited: not affected (per-queue MSI pattern, no
conditional ack).
Same bug class as the ahcid fix (ad40fffd): the kernel masks the IRQ
line when it delivers an interrupt and only re-arms it on the userspace
write-back (irq scheme write -> acknowledge() -> pic/ioapic_unmask).
When a driver acks only if the interrupt was its own, one foreign
interrupt on a shared INTx line leaves the line masked forever and all
later interrupts for the device are lost.
- e1000d: ack unconditionally; ICR is read-to-clear, so the ownership
check doubles as the device-level status clear
- ihdad: ack before the not-ours continue
- vboxd: ack unconditionally; device-side ack and event handling stay
conditional on host_events
- xhcid: ack unconditionally in the IRQ reactor; for INTx the ownership
check already clears IMAN.IP (RW1C) and device interrupts are masked
before re-arming, so no storm. MSI/MSI-X vectors are never shared, so
behavior there is unchanged (every delivery is ours by construction)
Verified: cargo check -Z build-std --target x86_64-unknown-redox for
e1000d, ihdad, vboxd, xhcid — clean, no new warnings.
The driver requires MSI-X (virtio-core probe errors out without it) and
binds both virtqueues to MSIX_PRIMARY_VECTOR, but only noted the
transport in a debug log — leaving zero runtime evidence of the MSI-X
path at the default level, which is why test-msix-qemu.sh found no live
MSI-X evidence. Log the delivery method at info where it is set up.
- smolnetd: an empty ip_router (DHCP-managed or deliberately network-less like
the bare target) is a valid 'no default gateway' state, not a malformed config
-> no warning.
- router: a limited/directed IPv4 broadcast (DHCP DISCOVER to 255.255.255.255
before any lease/route exists) legitimately has no routing-table entry; don't
warn 'No route found' for broadcast destinations.
- e1000d: add the 82574L (0x10d3, QEMU '-device e1000e') to the E1000 match
list; it keeps the legacy descriptor/register interface this driver uses.
I219 is intentionally excluded (integrated MDIO PHY, different init).
Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the base
fork was carrying only 38 of 100 patches in local/patches/base/.
The other 62 patches' content was silently missing from the fork
working tree, even though their .patch files were preserved.
This commit re-applies 41 patches that genuinely still apply
cleanly + 17 hunks that partially applied. Recovery covers:
- D-Bus initfs service wiring (P4-initfs-dbus-services)
- USB service wiring (P4-initfs-usb-drm-services)
- netcfg/dhcp/dhcpv6 driver fixes
- acpid shutdown/PM/quirk fixes
- inputd/ps2d hard-fail logging
- pcid driver interface refinements (server cmd channel)
- virtio-core for VirtualBox support
- ixgbed/rtl8139/rtl8168 net drivers
- ahcid NCQ + per-function interrupt coalescing
- logd persistent logging
- bootstrap procmgr race-condition fixes
- cargo version pin to +rb0.3.0 (synchronized release branch)
58 files changed, +1444/-318 lines.
Untracked mnt/ tree and *.orig / *.rej files cleaned up after
patch application (leftover from absolute-path patch headers).