Brijesh-Thakkar opened a new pull request, #21303:
URL: https://github.com/apache/nuttx/pull/21303

   ## Summary
   
   `virtio_net_send()` ignored the return value of `virtio_net_addbuffer()`. 
When the TX virtqueue has no room, `virtqueue_add_buffer()` fails with 
`ERROR_VRING_FULL` without queueing anything, but send still returned `OK`.
   
   According to the `transmit()` contract in `netdev_lowerhalf.h`, returning 
`OK` hands the netpkt over to the driver, which has to free it with 
`netpkt_free()` once it is sent. A packet that was never queued is never freed, 
so every failed send leaks the IOB chain and one TX quota. After `bufnum` 
failures the TX quota is exhausted, `netdev_upper_can_tx()` never returns true 
again and the interface stops transmitting for good.
   
   The fix returns a negated errno on failure (`-ENOMEM`, as `e1000`/`igb` do 
when their TX ring is full; the virtqueue error code is not an errno), so the 
upper half recycles the packet and restores the TX quota. Before returning it 
also reclaims the buffers the other side has already consumed and enables the 
TX done callback, so the transmission resumes when the ring drains. This is the 
same sequence the successful path already runs at the end of send.
   
   `virtio_net_addbuffer()` only writes the virtio header into the guard area 
in front of the L2 header (guaranteed by the `static_assert` on 
`CONFIG_NET_LL_GUARDSIZE`), so the packet is left in a state `netpkt_put()` 
handles.
   
   ## Impact
   
   virtio-net only. No API or Kconfig change. The failure path was previously 
unreachable in practice because the upper half limits the in-flight packets 
with the TX quota, so it only matters when the quota is bypassed (see "Allowing 
temporarily exceeding quota" in `netpkt_get()`), but when it does happen the 
effect is a permanent TX stall.
   
   ## Testing
   
   Host: Ubuntu x86_64, QEMU 10.2.1 (`qemu-system-riscv64 -M virt,aclint=on 
-cpu rv32 -smp 1 -bios none -semihosting -netdev user -device 
virtio-net-device`), xPack riscv-none-elf-gcc 15.2.0, `rv-virt:netnsh` built on 
top of current master. `tools/nxstyle` and `./tools/checkpatch.sh -g 
upstream/master..HEAD` pass.
   
   Normal operation with the fix: `ping -c 20 10.0.2.2` gives 20/20 replies, 
free IOBs unchanged (18 before, 18 after).
   
   Fault injection (temporary local change, not part of the PR): 
`virtio_net_addbuffer()` returns an error on every 5th TX call, `ping -c 100 
10.0.2.2` in the background, `/proc/iobinfo` sampled at about 0, 20, 60 and 110 
s:
   
   | | original | with this fix |
   |---|---|---|
   | free IOBs (nfree) | 18, 14, 12, 12 | 18, 18, 18, 18 |
   | ping -c 100 | stalls at the 6th failure (= `bufnum`), never finishes | 
completes, 80/100 received (only the 20 injected failures are lost) |
   
   ```
   # original
   nsh> cat /proc/iobinfo
       ntotal     nfree     nwait nthrottle
           24        12         0        10        <- 6 IOBs leaked, ping stuck 
after icmp_seq=27
   
   # with the fix
   100 packets transmitted, 80 received, 20% packet loss, time 101006 ms
   nsh> cat /proc/iobinfo
       ntotal     nfree     nwait nthrottle
           24        18         0        16
   ```
   
   
   The RX counterpart (`virtio_net_rxfill()` ignoring the 
`virtio_net_addbuffer()` result) was already fixed upstream by #20896, so no 
follow-up is needed for it.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to