[PATCH net 2/3] netconsole: do not dequeue pooled skbs that cannot satisfy len

From: Breno Leitao

Date: Fri May 29 2026 - 03:50:12 EST


find_skb() falls back to np->skb_pool when the GFP_ATOMIC alloc_skb()
fails. The pool is refilled by refill_skbs(), which always allocates
buffers of MAX_SKB_SIZE (ethhdr + iphdr + udphdr + MAX_UDP_CHUNK ==
1502 bytes).

netconsole, however, computes the requested length dynamically as

total_len + np->dev->needed_tailroom

If the egress device declares a non-zero needed_tailroom (e.g. some
tunnel or hardware accelerator devices), the required length can exceed
MAX_SKB_SIZE. The pooled skb is then handed back to the caller, which
immediately performs skb_put(skb, len), trips the tail > end check, and
triggers skb_over_panic().

Leave the normal alloc_skb(len, GFP_ATOMIC) path untouched -- the slab
allocator can still satisfy oversized requests when memory is available,
so senders to devices with non-zero needed_tailroom keep working in the
common case. Only the pool fallback is gated: when alloc_skb() failed
and len exceeds the pool buffer size, skip the skb_dequeue() instead of
burning a pre-allocated skb on a request that would later trip
skb_over_panic(). Reserving pool entries for requests they can actually
satisfy also keeps the panic path, which depends on the pool being
primed, intact.

When that drop happens, emit a rate-limited net_warn() so the user
notices that netconsole is unable to push messages on the egress device.
The warn is skipped under in_nmi() for the same reason schedule_work()
is: printk machinery taken by net_warn_ratelimited() is not NMI-safe and
would risk recursing into the same nbcon console we are servicing.

MAX_SKB_SIZE / MAX_UDP_CHUNK were private to net/core/netpoll.c. Move
them to include/linux/netpoll.h so netconsole can reference the same
definition that refill_skbs() uses, keeping the two in sync by
construction. The header now pulls in <linux/ip.h> and <linux/udp.h>
explicitly so MAX_SKB_SIZE remains self-contained for any future user.

Fixes: 954fba027405 ("netpoll: fix netpoll_send_udp() bugs")
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
drivers/net/netconsole.c | 7 ++++++-
include/linux/netpoll.h | 16 ++++++++++++++++
net/core/netpoll.c | 7 -------
3 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 699bdfa1fb45..a3dcbe713a0b 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -1680,8 +1680,13 @@ static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
repeat:

skb = alloc_skb(len, GFP_ATOMIC);
- if (!skb)
+ if (!skb) {
+ /* The pool is refilled with MAX_SKB_SIZE buffers */
+ if (WARN_ON_ONCE(len > MAX_SKB_SIZE))
+ return NULL;
+
skb = netcons_skb_pop(np);
+ }

if (!skb) {
if (++count < 10) {
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index e4b8f1f91e54..88f7daa8560e 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -13,12 +13,28 @@
#include <linux/rcupdate.h>
#include <linux/list.h>
#include <linux/refcount.h>
+#include <linux/ip.h>
+#include <linux/udp.h>

union inet_addr {
__be32 ip;
struct in6_addr in6;
};

+/*
+ * Maximum payload netpoll's preallocated skb pool can carry. Keep this in
+ * sync with the buffer size used by refill_skbs() in net/core/netpoll.c;
+ * callers (e.g. netconsole) use it to detect requests the pool can never
+ * satisfy and avoid dequeuing a pooled skb that would later trip
+ * skb_over_panic() in skb_put().
+ */
+#define MAX_UDP_CHUNK 1460
+#define MAX_SKB_SIZE \
+ (sizeof(struct ethhdr) + \
+ sizeof(struct iphdr) + \
+ sizeof(struct udphdr) + \
+ MAX_UDP_CHUNK)
+
struct netpoll {
struct net_device *dev;
netdevice_tracker dev_tracker;
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index b3fe59445f2d..229dde818ab3 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -41,16 +41,9 @@
* message gets out even in extreme OOM situations.
*/

-#define MAX_UDP_CHUNK 1460
#define MAX_SKBS 32
#define USEC_PER_POLL 50

-#define MAX_SKB_SIZE \
- (sizeof(struct ethhdr) + \
- sizeof(struct iphdr) + \
- sizeof(struct udphdr) + \
- MAX_UDP_CHUNK)
-
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);


--
2.51.0