Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions issues/netstacks-i219-drops-a-transmit-burst-past-its-ring.md

This file was deleted.

This file was deleted.

103 changes: 103 additions & 0 deletions issues/no-machine-has-read-an-intel-nic-across-a-link-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
status: open
kind: tooling
opened: 2026-10-09
---

# No machine has read an Intel NIC across a link change

A link that changes while the transmit ring holds unsent descriptors leaves
them the part's, on the 82574 and on the I219 alike
(`toyos-i219/src/lib.rs`, "A frame is offered only to a ring with room for
it"): they are counted `descriptors.stranded`, the part is owed their
write-back within `STRANDED_DEADLINE_NANOS` (12.98 s) of the link being read
up, and a part that has not written them back by then is refused by name, so
netstack ends with "cannot be driven on". Every reading of that is the
driver's own model.

**Both parts' documents say they keep the frame for the link that returns.**
The 82574 datasheet's Defer Count (317694 §10.2.7) and the I219 datasheet's
own (612523 §9.5.4.6) count a transmit deferred because "the link is not up".
Both give the same sixteen attempts the bound is built from: the 82574's
`TCTL.CT`, and the I219's Power Management Control register (PHY address 01,
page 769, register 21, bits 8:1, "Number of retries for a collided packet",
default 0x0F). No machine has read either part do it: no 82574 is in reach,
QEMU's `e1000e` is not in the harness, and no row can drop the T14's link.

**Whether a reset over a live ring hangs the I219 is unknown.** Nothing Intel
publishes that was found says it is safe or that it is not, which is why
neither part is reset to take a ring back. Read, and silent on it: the I219
datasheet (612523 rev 2.02), the 500 Series on-package PCH datasheet volumes
1 and 2 (631119-007 §18, 631120-002 §8), the 100 Series PCH datasheet volume
2 (332691) and specification update (332692-015), the 400 Series PCH
datasheet volume 2 (620855-002) and specification update (620856-009), the
300 Series PCH specification update (337349-012), the 7th generation U/Y
platform I/O specification update (334660-009), the I218 specification
update (rev 1.0), and the 82574 datasheet (317694 rev 3.4) and specification
update. intel.com's search finds no I219 specification update, sighting or
application note, and no specification update for the 500 Series on-package
PCH. The one piece of evidence the tree has is
`issues/the-t14-hung-after-rebooting-with-its-i219-faulted.md`: a T14 that
froze at reboot with that function left holding live rings, cause not
established.

**`open`'s own reset has the same exposure.** Every start of netstack resets
the function before it programs it. On a netstack started again over a
function that still holds published descriptors, `toyos_i219::quiesce` stops
the receiver and the transmitter and the reset then goes out over that ring.
Nothing guards it, because nothing found says what the guard is.

## What the rule costs

Three weaknesses of leaving the ring to the part, each still true and each
read by the row the exit names:

- **Stale frames leave on the link that returns**, up to
`toyos_i219::TX_RING - 1` of them and as old as the outage. Or they are
lost: the 82574 datasheet's §3.2 also has a transmission that "completes
successfully even if the PHY fails to indicate CRS", as in "a link down
situation", counted `TNCRS`, and a part that behaves so writes the
descriptors back with nothing sent. A TCP segment, a DHCP message with a
dead transaction id and an ARP request are each absorbed by the protocol
that receives them. The one with a cost is an ARP frame carrying the old
address onto a different network, which can displace a neighbour's entry
there until its owner speaks.
- **The deadline bounds collisions and not deference.** 12.98 s is a full
ring, sixteen attempts a frame, every attempt behind the largest back-off,
at 10 Mb/s. Nothing bounds how long a station defers to others on a busy
half-duplex segment, and no document read says how long after `STATUS.LU`
a part resumes. A part still deferring at the deadline is refused, and that
ends the machine's network.
- **The refusal is safe only while netstack's row has no `restart`.**
`[programs.netstack]` in `system.toml` carries none, so a refused part is
next reset by a `swap netstack` or a reboot. With a `restart` the refusal
becomes an automatic `open` over the ring the part kept, which is the reset
this issue does not know to be safe.

## Owner

Stage 2 of `issues/the-lan-is-not-yet-production-grade.md`.

## Exit

None can be read yet without a hand on the cable: a link change under
traffic on the T14 is its cable pulled or its switch port cycled, and no row
can do either. The instrument is a link partner a row commands, owed by stage
2 of `issues/the-lan-is-not-yet-production-grade.md`, whose own exit ("link
survives unplug and replug") needs the same one. That row fills the ring,
drops the link and raises it, and reads:

- `descriptors.stranded` above 0 with netstack still serving, and on the
partner whether the stranded frames arrived (stale frames) or `wire.sent`
stood still while the descriptors came back (lost by the `TNCRS` reading);
or netstack's "cannot be driven on" line naming the ring, where the part
wrote nothing back;
- the same at 10 Mb/s half duplex against a partner that keeps the segment
busy, for the deadline's false refusal;
- with netstack started again under traffic and the machine then rebooted,
whether it comes back: the reset over a live ring. Until that is read,
netstack's row takes no `restart`.

An Intel document that states the I219's sequence for a reset over published
descriptors closes the second unknown without a machine: `open` then does
exactly that.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
status: open
kind: defect
opened: 2026-10-08
---

# The Intel driver writes `TXDCTL` and `TIDV` against the 82574 datasheet's newer revision

`toyos-i219`'s header cites revision 2.7 of the *Intel 82574 GbE Controller
Family Datasheet*. The copy intel.com serves is revision 3.4 (June 2014), and
it reads differently in two places the driver's bring-up writes:

- `TXDCTL` bit 22: "Reserved. Must be set to 1b for proper operation." The
revision history gives it to revision 3.1 ("changed bit 22 value to 1b").
`regs::txdctl::SUGGESTED` is `GRAN` and a `WTHRESH` of 1, written whole, so
the bit is cleared.
- `TIDV`: "Counts in units of 1.024 microseconds. A value of 0 is not allowed."
`open` writes 0, which is also the register's reset value; the same section
says a descriptor with `IDE` clear, which is every one the driver publishes,
is not delayed by it.

Revision 3.4 also numbers `TXDCTL` §10.2.6.11 where `regs.rs` cites §10.2.6.10;
every other section the tree cites that was compared sits at the same number.

Neither has been seen to matter: the T14 has sent frames with both. Whether
either register means the same on the PCH's MAC no document says (631120 §8.2
publishes neither). The transmit wake rests on a write-back per descriptor
with bit 22 clear, and on the I219 the reading of that is the T14 boot
`issues/the-intel-nics-room-and-wake-are-unread-on-hardware.md` exits on.

## Owner

Stage 2 of `issues/the-lan-is-not-yet-production-grade.md`.

## Exit

`toyos-i219`'s header names the revision its citations were last read in, the
bring-up writes `TXDCTL` with bit 22 set on the 82574 and leaves `TIDV` alone,
and the stub refuses a `TXDCTL` write that clears the bit.
52 changes: 52 additions & 0 deletions issues/the-intel-nics-room-and-wake-are-unread-on-hardware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
status: open
kind: tooling
opened: 2026-10-08
---

# The Intel NIC's transmit room and its wake have been read on no hardware

netstack offers its card a frame only when the driver counts room for it, and
where there is none it sleeps on the claim until `toyos_i219::I219::wake_on_room`'s
cause arrives. On the I219 and the 82574 every reading of that path is the
driver's own model (`toyos-i219/src/stub.rs`), which is Intel's documents
written down: no image gives netstack `pci:8086:15fc`, and the harness has no
Intel card, so neither the T14 nor QEMU's `e1000e` has run it.

What the documents do not say of the T14's part, and the model therefore takes
on trust: that the PCH's MAC raises a message for `TXDW` once it is unmasked.
Its function has an MSI capability and no MSI-X one (631120 §8.1.15), so the
82574's §7.4.1 is the mapping the driver assumes; 631120 publishes no
interrupt register of that MAC at all.

A wake that never comes is no wedge and no lost frame: room is counted from
the descriptors in memory, so the next pass any other event begins finds it.
It is a stall until that pass, and it reads as `transmit.wake_armed` ahead of
`transmit.wake_taken`: the driver counts a wake only on a pass a message
began whose one unmasked cause was the transmit one, so a cause the part
recorded and raised nothing for is never counted.

The same boot is the reading of `TXDCTL` as the bring-up writes it, with bit 22
clear and a write-back per descriptor
(`issues/the-intel-driver-writes-txdctl-and-tidv-against-the-82574-datasheets-newer-revision.md`):
`descriptors.sent` short of `wire.sent`, or armed ahead of taken, makes that
bit this path's.

What either part does with its ring across a link change is not in this
exit: `issues/no-machine-has-read-an-intel-nic-across-a-link-change.md`.

## Owner

The T14's outbound rows of `issues/toyos-has-its-own-network-stack.md`: the
first boot that gives netstack the wired card.

## Exit

A T14 boot whose netstack drives `8086:15fc` queues more than
`toyos_i219::TX_RING - 1` frames in one pass, from more sockets than one and
to a destination that answers none of them, and `inspect` then answers
`transmit.full`, `transmit.wake_armed` and `transmit.wake_taken` each at least
1, `descriptors.stranded` 0, and `descriptors.sent`
equal to `wire.sent`; the boot's log carries no "cannot be driven on" line
from netstack. A boot whose `transmit.full` reads 0 never filled the ring and
has read nothing.
3 changes: 3 additions & 0 deletions issues/the-lan-is-not-yet-production-grade.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ why, and the boot still leases.
- **1000 Mb/s.** Done: bench run 124 linked at 1000 full.
- **Link events** arrive by interrupt and are recovered from without a restart:
cable pulled and replugged, the partner rebooting, a flapping link.
Owed for it: a link partner a T14 row commands, so a row can drop the link
and raise it under traffic. Nothing has one, and
`issues/no-machine-has-read-an-intel-nic-across-a-link-change.md` waits on it.
- **Throughput** is measured against Ubuntu on the same machine and cable, with
line rate (about 940 Mb/s of TCP) as the target: interrupt moderation (off
today), ring sizes, checksum and segmentation offload.
Expand Down
Loading
Loading