Package: syslinux
Version: 3:6.04~git20190206.bf6db5b4+dfsg1-3.2
Severity: normal
Tags: patch upstream
X-Debbugs-Cc: [email protected]
undi_transmit() retries PXENV_STATUS_OUT_OF_RESOURCES without blocking.
In polling mode, its higher-priority thread can prevent the polling thread
from running to reclaim transmit buffers. Other transmit failures are
also incorrectly reported as successful.
Reproduced in QEMU by returning OUT_OF_RESOURCES until the next UNDI ISR
call: the old loop stalls. Blocking briefly between bounded retries lets
the poll run and the transfer complete. Persistent failures return ERR_MEM;
other failures return ERR_IF. This cannot interrupt a stuck firmware call.
Patch: 0023-lpxelinux-bound-undi-tx-retries.patch
AI assistance was used for debugging, the patch and this report.
-- System Information:
Debian Release: forky/sid
APT prefers unstable
APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Kernel: Linux 7.2.15+deb14-amd64 (SMP w/12 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_USER, TAINT_OOT_MODULE,
TAINT_UNSIGNED_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8),
LANGUAGE=en_US:en
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
Versions of packages syslinux depends on:
ii libc6 2.43-7
ii mtools 4.0.49-1
Versions of packages syslinux recommends:
ii syslinux-common 3:6.04~git20190206.bf6db5b4+dfsg1-3.2
Versions of packages syslinux suggests:
ii dosfstools 4.2-1.2
-- no debconf information
Description: lpxelinux: yield CPU time and bound UNDI transmit retries
Block briefly on resource exhaustion so the polling thread can reclaim
transmit buffers. Stop after 32 resource failures or 2000 ms between
returned calls, and propagate transmit errors to lwIP.
Origin: other, local debugging with AI assistance
Forwarded: no
Last-Update: 2026-11-03
--- a/core/lwip/src/netif/undiif.c
+++ b/core/lwip/src/netif/undiif.c
@@ -58,6 +58,7 @@
#endif /* UNDIIF_ID_FULL_DEBUG */
#include <core.h>
+#include <thread.h>
#include "lwip/opt.h"
@@ -473,8 +474,12 @@
static __lowmem struct pxe_xmit pxe;
static __lowmem hwaddr_t low_dest;
static __lowmem char pkt_buf[PKTBUF_SIZE];
+ static DECLARE_INIT_SEMAPHORE(tx_retry_wait, 0);
uint32_t now;
static uint32_t first_xmit;
+ mstime_t retry_start = 0;
+ unsigned int retries = 0;
+ int rc;
#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
char *str = malloc(UNIDIF_ID_STRLEN);
int strpos = 0;
@@ -519,7 +524,7 @@
if (dest)
memcpy(low_dest, dest, netif->hwaddr_len);
- do {
+ for (;;) {
memset(&pxe, 0, sizeof pxe);
pxe.xmit.Protocol = undi_protocol;
@@ -529,8 +534,28 @@
pxe.tbd.ImmedLength = pbuf->tot_len;
pxe.tbd.Xmit = FAR_PTR(pkt_buf);
- pxe_call(PXENV_UNDI_TRANSMIT, &pxe.xmit);
- } while (pxe.xmit.Status == PXENV_STATUS_OUT_OF_RESOURCES);
+ rc = pxe_call(PXENV_UNDI_TRANSMIT, &pxe.xmit);
+ if (pxe.xmit.Status != PXENV_STATUS_OUT_OF_RESOURCES)
+ break;
+
+ if (!retries)
+ retry_start = ms_timer();
+ if (++retries >= 32 || ms_timer() - retry_start >= 2000) {
+ printf("UNDI transmit: out of resources after %u attempts\n", retries);
+ LINK_STATS_INC(link.drop);
+ return ERR_MEM;
+ }
+
+ /* A yield would select this higher-priority thread again. Block
+ briefly so the polling thread can service TX completions/RX. */
+ sem_down(&tx_retry_wait, 1);
+ }
+
+ if (rc || pxe.xmit.Status) {
+ printf("UNDI transmit failed: rc=%d status=%04x\n", rc, pxe.xmit.Status);
+ LINK_STATS_INC(link.drop);
+ return ERR_IF;
+ }
LINK_STATS_INC(link.xmit);