Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next 1/6] dt-bindings: net: mediatek-dwmac: add support for MT8189 SoC
From: Louis-Alexis Eyraud @ 2026-07-10 13:01 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Richard Cochran, Matthias Brugger, AngeloGioacchino Del Regno,
	Biao Huang, Maxime Coquelin, Alexandre Torgue, maxime.chevallier,
	rmk+kernel, kernel, netdev, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek, linux-stm32
In-Reply-To: <6ea11726-8d50-411e-afab-da346e09ab9b@lunn.ch>

Hi Andrew,

On Wed, 2026-07-08 at 16:35 +0200, Andrew Lunn wrote:
> > On Tue, 2026-07-07 at 14:42 +0200, Andrew Lunn wrote:
> > > > +  - if:
> > > > +      properties:
> > > > +        compatible:
> > > > +          contains:
> > > > +            enum:
> > > > +              - mediatek,mt8189-gmac
> > > > +    then:
> > > > +      properties:
> > > > +        clocks:
> > > > +          items:
> > > > +            - description: MAC Main clock
> > > > +            - description: PTP clock
> > > > +            - description: RMII reference clock provided by
> > > > MAC
> > > 
> > > Since this is a MAC, it sounds like it is consuming its own
> > > clock?
> > 
> > In the driver ([1]), this clock is described as being only used and
> > needed in RMII when MAC provides the reference clock, and useless
> > otherwise (RGMII/MII or RMII when PHY provides the reference
> > clock).
> 
> So it sounds like this is a clock output, going to the PHY, as its
> reference clock input. So ideally, the PHY should consume this clock,
> not the MAC.
> 
> > Its use and configuration also depends on the "mediatek,rmii-clk-
> > from-
> > mac" vendor property ([2]) presence in devicetree.
> 
> This makes it sounds like it is historically wrong, and the patch is
> just extending this to the new device.
> 
> Do you have a board using RMII? Can you list the clock in the PHY
> node, not the MAC, and see if it still works?
> 

I don't. 
All the MTK Genio boards (510, 520, 720, 1200 EVK) I have are using
RGMII only.

> Ideally, for a new device, we should not repeat past errors.

So, do you want I remove this clock from mt8189 clocks list for v2?

Regards,
Louis-Alexis

> 
> 	 Andrew

^ permalink raw reply

* Re: [PATCH net v2 1/2] net: dsa: mv88e6xxx: write the ATU FID register on 88E6141/88E6341
From: Andrew Lunn @ 2026-07-10 13:01 UTC (permalink / raw)
  To: Luke Howard
  Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Vivien Didelot, Gregory CLEMENT, Richard Cochran,
	Cedric Jehasse, Kieran Tyrrell, Max Holtmann, Max Hunter,
	Christoph Mellauner, Simon Gapp, netdev, linux-kernel
In-Reply-To: <20260710-mv88e6x41-fixes-v2-1-e23654c65aa0@padl.com>

> index e966e7c4cc5de..aca64fd4cb95c 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.h
> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
> @@ -165,16 +165,20 @@ struct mv88e6xxx_info {
>  	 */
>  	bool dual_chip;
>  
> +	/* Has a dedicated ATU FID register. */
> +	bool atu_fid_reg;
> +
> +	/* Supports PTP */
> +	bool ptp_support;
> +

Moving structure members around to make it pack better should not be
mixed with a fix. Add the atu_fid_reg next to other bools. But please
move the ptp_support in another patch, sent to net-next.

> -	const struct mv88e6xxx_ops *ops;
>  
> -	/* Supports PTP */
> -	bool ptp_support;
> +	const struct mv88e6xxx_ops *ops;
>  

The rest of the patch looks fine.

    Andrew

^ permalink raw reply

* RE: [PATCH net v4] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Tung Quang Nguyen @ 2026-07-10 12:57 UTC (permalink / raw)
  To: Cen Zhang (Microsoft)
  Cc: netdev@vger.kernel.org, tipc-discussion@lists.sourceforge.net,
	linux-kernel@vger.kernel.org, vadim.fedorenko@linux.dev,
	AutonomousCodeSecurity@microsoft.com,
	tgopinath@linux.microsoft.com, kys@microsoft.com,
	jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, horms@kernel.org
In-Reply-To: <20260709211649.15623-1-blbllhy@gmail.com>

>Subject: [PATCH net v4] tipc: fix u16 MTU truncation in media and bearer MTU
>validation
>
>Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied
>MTU values but only enforce a minimum bound, not a maximum. When a user
>sets the MTU to a value exceeding U16_MAX (65535), it passes validation but is
>silently truncated when assigned to u16 fields l->mtu and
>l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
>truncate to 0, causing a division by zero in tipc_link_set_queue_limits() which
>computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing values
>(e.g. 65537-131071) produce small incorrect MTU values, resulting in link
>malfunction behaviors.

Yes, this is a bug. I can reproduce the same div-by-zero.

>
>Crash stack (triggered as unprivileged user via user namespace):
>
>  tipc_link_set_queue_limits  net/tipc/link.c:2531
>  tipc_link_create            net/tipc/link.c:520
>  tipc_node_check_dest        net/tipc/node.c:1279
>  tipc_disc_rcv               net/tipc/discover.c:252
>  tipc_rcv                    net/tipc/node.c:2129
>  tipc_udp_recv               net/tipc/udp_media.c:392
>
>Two independent paths lack the upper bound check:
>1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET) 2.
>inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)
>
>Fix both by rejecting MTU values above U16_MAX.
>
>Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
>Reported-by: AutonomousCodeSecurity@microsoft.com
>Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-
>CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
>Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
>---
>v4: Add .min check value
>v3: Use nla_policy check to limit MTU max value as suggested by Vadim
>v2: Solved format issue
>Link: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-
>CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
>
> net/tipc/netlink.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
>diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index
>8336a9664703..811438c44542 100644
>--- a/net/tipc/netlink.c
>+++ b/net/tipc/netlink.c
>@@ -113,12 +113,17 @@ const struct nla_policy
>tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {  };
>
> /* Properties valid for media, bearer and link */
>+static const struct netlink_range_validation tipc_nl_mtu_range = {
>+	.min = TIPC_MIN_BEARER_MTU,

Adding lower threshold checking introduces two issues:
1. Existing function tipc_udp_mtu_bad() can never return "true".
2. Breaking user-space applications looking for error message " Error: MTU value is out-of-range". Look at the error messages before and after your patch:
[Before patch]
node1 ~ # tipc bearer set mtu 15 media udp name UDP0
Error: MTU value is out-of-range.
kernel answers: Invalid argument

[After  patch]
node1 ~ # tipc bearer set mtu 15 media udp name UDP0
kernel answers: Numerical result out of range

>+	.max = U16_MAX,
>+};
>+
> const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
> 	[TIPC_NLA_PROP_UNSPEC]		= { .type = NLA_UNSPEC },
> 	[TIPC_NLA_PROP_PRIO]		= { .type = NLA_U32 },
> 	[TIPC_NLA_PROP_TOL]		= { .type = NLA_U32 },
> 	[TIPC_NLA_PROP_WIN]		= { .type = NLA_U32 },
>-	[TIPC_NLA_PROP_MTU]		= { .type = NLA_U32 },
>+	[TIPC_NLA_PROP_MTU]		=
>NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
> 	[TIPC_NLA_PROP_BROADCAST]	= { .type = NLA_U32 },
> 	[TIPC_NLA_PROP_BROADCAST_RATIO]	= { .type = NLA_U32 }
> };
>--
>2.53.0


^ permalink raw reply

* Re: [PATCH net-next 1/4] dpll: zl3073x: add channel ToD and phase step operations
From: Vadim Fedorenko @ 2026-07-10 12:56 UTC (permalink / raw)
  To: Ivan Vecera, netdev
  Cc: Chris du Quesnay, Arkadiusz Kubalewski, David S. Miller,
	Jakub Kicinski, Jiri Pirko, Michal Schmidt, Paolo Abeni,
	Pasi Vaananen, Petr Oros, Prathosh Satish, Richard Cochran,
	Simon Horman, linux-kernel
In-Reply-To: <20260708170527.916035-2-ivecera@redhat.com>

On 08/07/2026 18:05, Ivan Vecera wrote:
> Add low-level DPLL channel operations for ToD read/write/adjust,
> delta frequency offset write and output phase step. ToD operations
> use a wait-before-write pattern to avoid blocking after each
> operation. tod_adjust additionally waits for completion since callers
> may follow with phase step operations.
> 
> The tod_ready_wait helper selects the poll timeout based on the
> current ToD command - write operations use a longer timeout (1000 ms)
> than reads (30 ms).
> 
> The ToD read captures system timestamps (ptp_system_timestamp) around
> the HW command and completion poll to support cross-timestamping.
> 
> Add output step-time mask invariant to zl3073x_chan and
> zl3073x_chan_is_out_stepped() helper to check if an output
> participates in step-time operations.
> 

[...]

> +/**
> + * zl3073x_chan_tod_read - read ToD registers after issuing a command
> + * @zldev: pointer to zl3073x device
> + * @ch: DPLL channel index
> + * @next_hz: if true, read predicted ToD at next 1 Hz; otherwise read current
> + * @ts: timespec to store the result
> + * @sts: optional system timestamp pair for cross-timestamping
> + *
> + * Context: Caller must serialize all zl3073x_chan_tod_* calls externally.
> + * Return: 0 on success, <0 on error
> + */
> +int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
> +			  bool next_hz, struct timespec64 *ts,
> +			  struct ptp_system_timestamp *sts)
> +{
> +	u32 nsec;
> +	u64 sec;
> +	u8 cmd;
> +	int rc;
> +
> +	if (next_hz)
> +		cmd = ZL_DPLL_TOD_CTRL_CMD_RD_NEXT_1HZ;
> +	else
> +		cmd = ZL_DPLL_TOD_CTRL_CMD_RD_CURRENT;
> +
> +	/* Wait for any previous ToD operation to complete */
> +	rc = zl3073x_chan_tod_ready_wait(zldev, ch);
> +	if (rc)
> +		return rc;
> +
> +	ptp_read_system_prets(sts);
> +	rc = zl3073x_chan_tod_ctrl(zldev, ch, cmd);
> +	if (rc)
> +		return rc;
> +
> +	rc = zl3073x_chan_tod_ready_wait(zldev, ch);
> +	if (rc)
> +		return rc;
> +	ptp_read_system_postts(sts);

AFAIU, this code means that the ToD value was somewhere between tod_ctrl
command and tod_ready read value 0 of the register. How does it work
with "predicted ToD at next 1 Hz"?

> +
> +	rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_TOD_SEC(ch), &sec);
> +	if (rc)
> +		return rc;
> +
> +	/* HW nanoseconds are always in [0, NSEC_PER_SEC) range */
> +	rc = zl3073x_read_u32(zldev, ZL_REG_DPLL_TOD_NS(ch), &nsec);
> +	if (rc)
> +		return rc;
> +
> +	ts->tv_sec = sec;
> +	ts->tv_nsec = nsec;
> +
> +	return 0;
> +}
[...]

^ permalink raw reply

* Re: [PATCH 6.12.y] net: add missing ns_capable check for peer netns
From: Maximilian Heyne @ 2026-07-10 12:49 UTC (permalink / raw)
  To: Greg KH
  Cc: stable, Marc Kleine-Budde, Vincent Mailhol, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Daniel Borkmann, Nikolay Aleksandrov, Eric W. Biederman,
	linux-can, netdev, linux-kernel, bpf
In-Reply-To: <2026062556-residue-anybody-e756@gregkh>

Hi Greg,

On Thu, Jun 25, 2026 at 12:37:31PM +0100, Greg KH wrote:
> On Wed, Jun 17, 2026 at 08:25:31AM +0000, Maximilian Heyne wrote:
> > The upstream commit 7b735ef81286 ("rtnetlink: add missing
> > netlink_ns_capable() check for peer netns") doesn't apply on older
> > stable kernels due to refactoring. Therefore, this patch is an attempt
> > to implement the same capability check just directly in the respective
> > interface types.
> 
> Why can't we take the full series of patches instead?  Otherwise this is
> going to be a pain over time for any other fixes/updates in this area,
> right?

Agree that this would be a pain. The issue is that this requires to
backport >10 patches. I think for 6.12 it would be like 15 patches so
that each patch doesn't need to be reworked too much.

The reason for me submitting this was that it's easily backports to all
stable kernels. I haven't tested for 6.6 or earlier how many patches
would need to be backported.

I can try to post the series for 6.12 after some more testing (after my
vacation) but I'm think I won't succeed backporting the refactoring
patches back to, say, 5.10.

> 
> And if not, then we need acks from the maintainers here...

So for the backports to older stable kernels we might need this.

Links:
- 6.6.y backport: https://lore.kernel.org/all/20260617-sprain-dye-86c242ac@mheyne-amazon/
- 6.1.y backport: https://lore.kernel.org/all/20260617-keyed-dude-3493dbdb@mheyne-amazon/
- 5.15.y backport: https://lore.kernel.org/all/20260617-forgot-manic-27dda774@mheyne-amazon/
- 5.10.y backport: https://lore.kernel.org/all/20260617-thaws-enid-af4ad67d@mheyne-amazon/

Regards,

Maximilian

^ permalink raw reply

* [PATCH RFC iproute2-next 2/2] seg6: add support for the End.MAP behavior under seg6mobile encap
From: Yuya Kusakabe @ 2026-07-10 12:18 UTC (permalink / raw)
  To: David Ahern, netdev; +Cc: Andrea Mayer, Andrea Mayer, Yuya Kusakabe
In-Reply-To: <20260710-seg6-mobile-end-map-v1-0-970275f3d80b@gmail.com>

Wire up a new "seg6mobile" lightweight tunnel encap type that mirrors
the kernel's LWTUNNEL_ENCAP_SEG6_MOBILE namespace, and add parse and
print support for the first RFC 9433 behavior it carries: End.MAP.

    $ ip -6 route add 2001:db8:f::/64 \
        encap seg6mobile action End.MAP nh6 2001:db8:2::e dev eth0

    $ ip -6 route show 2001:db8:f::/64
    2001:db8:f::/64  encap seg6mobile action End.MAP nh6 2001:db8:2::e dev eth0 metric 1024 pref medium

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 ip/iproute.c           |   6 +-
 ip/iproute_lwtunnel.c  | 173 +++++++++++++++++++++++++++++++++++++++++++++++++
 man/man8/ip-route.8.in |  24 +++++++
 3 files changed, 201 insertions(+), 2 deletions(-)

diff --git a/ip/iproute.c b/ip/iproute.c
index 5b9e7ac1134a..659ebfa1b113 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -98,8 +98,8 @@ static void usage(void)
 		"TIME := NUMBER[s|ms]\n"
 		"BOOL := [1|0]\n"
 		"FEATURES := ecn\n"
-		"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl | ioam6 | xfrm ]\n"
-		"ENCAPHDR := [ MPLSLABEL | SEG6HDR | SEG6LOCAL | IOAM6HDR | XFRMINFO ]\n"
+		"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | seg6mobile | rpl | ioam6 | xfrm ]\n"
+		"ENCAPHDR := [ MPLSLABEL | SEG6HDR | SEG6LOCAL | SEG6MOBILE | IOAM6HDR | XFRMINFO ]\n"
 		"SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
 		"SEGMODE := [ encap | encap.red | inline | l2encap | l2encap.red ]\n"
 		"SEG6LOCAL := action ACTION [ OPTIONS ] [ count ]\n"
@@ -111,6 +111,8 @@ static void usage(void)
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME }\n"
 		"FLAVORS := { FLAVOR[,FLAVOR] }\n"
 		"FLAVOR := { psp | usp | usd | next-csid }\n"
+		"SEG6MOBILE := action MOBILE_ACTION nh6 ADDR [ count ]\n"
+		"MOBILE_ACTION := { End.MAP }\n"
 		"IOAM6HDR := trace prealloc type IOAM6_TRACE_TYPE ns IOAM6_NAMESPACE size IOAM6_TRACE_SIZE\n"
 		"XFRMINFO := if_id IF_ID [ link_dev LINK ]\n"
 		"ROUTE_GET_FLAGS := ROUTE_GET_FLAG [ ROUTE_GET_FLAGS ]\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 00b4f7565be6..53df6e2d7610 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -28,6 +28,7 @@
 #include <linux/rpl_iptunnel.h>
 #include <linux/seg6_hmac.h>
 #include <linux/seg6_local.h>
+#include <linux/seg6_mobile.h>
 #include <linux/if_tunnel.h>
 #include <linux/ioam6.h>
 #include <linux/ioam6_iptunnel.h>
@@ -55,6 +56,8 @@ static const char *format_encap_type(uint16_t type)
 		return "ioam6";
 	case LWTUNNEL_ENCAP_XFRM:
 		return "xfrm";
+	case LWTUNNEL_ENCAP_SEG6_MOBILE:
+		return "seg6mobile";
 	default:
 		return "unknown";
 	}
@@ -95,6 +98,8 @@ static uint16_t read_encap_type(const char *name)
 		return LWTUNNEL_ENCAP_IOAM6;
 	else if (strcmp(name, "xfrm") == 0)
 		return LWTUNNEL_ENCAP_XFRM;
+	else if (strcmp(name, "seg6mobile") == 0)
+		return LWTUNNEL_ENCAP_SEG6_MOBILE;
 	else if (strcmp(name, "help") == 0)
 		encap_type_usage();
 
@@ -578,6 +583,91 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
 		print_seg6_local_flavors(fp, tb[SEG6_LOCAL_FLAVORS]);
 }
 
+static const char *seg6_mobile_action_names[SEG6_MOBILE_ACTION_MAX + 1] = {
+	[SEG6_MOBILE_ACTION_END_MAP]	= "End.MAP",
+};
+
+static const char *format_seg6_mobile_action(int action)
+{
+	if (action < 0 || action > SEG6_MOBILE_ACTION_MAX)
+		return "<invalid>";
+
+	return seg6_mobile_action_names[action] ?: "<unknown>";
+}
+
+static int read_seg6_mobile_action(const char *name)
+{
+	int i;
+
+	for (i = 0; i < SEG6_MOBILE_ACTION_MAX + 1; i++) {
+		if (!seg6_mobile_action_names[i])
+			continue;
+
+		if (strcmp(seg6_mobile_action_names[i], name) == 0)
+			return i;
+	}
+
+	return SEG6_MOBILE_ACTION_UNSPEC;
+}
+
+static void print_seg6_mobile_counters(FILE *fp, struct rtattr *encap)
+{
+	struct rtattr *tb[SEG6_MOBILE_CNT_MAX + 1];
+	__u64 packets = 0, bytes = 0, errors = 0;
+
+	parse_rtattr_nested(tb, SEG6_MOBILE_CNT_MAX, encap);
+
+	if (tb[SEG6_MOBILE_CNT_PACKETS])
+		packets = rta_getattr_u64(tb[SEG6_MOBILE_CNT_PACKETS]);
+
+	if (tb[SEG6_MOBILE_CNT_BYTES])
+		bytes = rta_getattr_u64(tb[SEG6_MOBILE_CNT_BYTES]);
+
+	if (tb[SEG6_MOBILE_CNT_ERRORS])
+		errors = rta_getattr_u64(tb[SEG6_MOBILE_CNT_ERRORS]);
+
+	if (is_json_context()) {
+		open_json_object("stats64");
+
+		print_u64(PRINT_JSON, "packets", NULL, packets);
+		print_u64(PRINT_JSON, "bytes", NULL, bytes);
+		print_u64(PRINT_JSON, "errors", NULL, errors);
+
+		close_json_object();
+	} else {
+		print_string(PRINT_FP, NULL, "%s ", "packets");
+		print_num(fp, 1, packets);
+
+		print_string(PRINT_FP, NULL, "%s ", "bytes");
+		print_num(fp, 1, bytes);
+
+		print_string(PRINT_FP, NULL, "%s ", "errors");
+		print_num(fp, 1, errors);
+	}
+}
+
+static void print_encap_seg6mobile(FILE *fp, struct rtattr *encap)
+{
+	struct rtattr *tb[SEG6_MOBILE_MAX + 1];
+	int action;
+
+	parse_rtattr_nested(tb, SEG6_MOBILE_MAX, encap);
+
+	if (!tb[SEG6_MOBILE_ACTION])
+		return;
+
+	action = rta_getattr_u32(tb[SEG6_MOBILE_ACTION]);
+	print_string(PRINT_ANY, "action",
+		     "action %s ", format_seg6_mobile_action(action));
+
+	if (tb[SEG6_MOBILE_NH6])
+		print_string(PRINT_ANY, "nh6", "nh6 %s ",
+			     rt_addr_n2a_rta(AF_INET6, tb[SEG6_MOBILE_NH6]));
+
+	if (tb[SEG6_MOBILE_COUNTERS] && show_stats)
+		print_seg6_mobile_counters(fp, tb[SEG6_MOBILE_COUNTERS]);
+}
+
 static void print_encap_mpls(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[MPLS_IPTUNNEL_MAX+1];
@@ -893,6 +983,9 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
 	case LWTUNNEL_ENCAP_XFRM:
 		print_encap_xfrm(fp, encap);
 		break;
+	case LWTUNNEL_ENCAP_SEG6_MOBILE:
+		print_encap_seg6mobile(fp, encap);
+		break;
 	}
 	close_json_object();
 }
@@ -1587,6 +1680,83 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	return ret;
 }
 
+/* counters are always initialized to zero by the kernel, so no value
+ * is parsed from the command line -- "count" is a bare keyword.
+ */
+static int seg6mobile_fill_counters(struct rtattr *rta, size_t len, int attr)
+{
+	struct rtattr *nest;
+	int ret;
+
+	nest = rta_nest(rta, len, attr);
+
+	ret = rta_addattr64(rta, len, SEG6_MOBILE_CNT_PACKETS, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = rta_addattr64(rta, len, SEG6_MOBILE_CNT_BYTES, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = rta_addattr64(rta, len, SEG6_MOBILE_CNT_ERRORS, 0);
+	if (ret < 0)
+		return ret;
+
+	rta_nest_end(rta, nest);
+	return 0;
+}
+
+static int parse_encap_seg6mobile(struct rtattr *rta, size_t len, int *argcp,
+				  char ***argvp)
+{
+	int action_ok = 0, nh6_ok = 0, counters_ok = 0;
+	char **argv = *argvp;
+	int argc = *argcp;
+	inet_prefix addr;
+	__u32 action = 0;
+	int ret = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "action") == 0) {
+			NEXT_ARG();
+			if (action_ok++)
+				duparg2("action", *argv);
+			action = read_seg6_mobile_action(*argv);
+			if (!action)
+				invarg("\"action\" value is invalid\n", *argv);
+			ret = rta_addattr32(rta, len, SEG6_MOBILE_ACTION,
+					    action);
+		} else if (strcmp(*argv, "nh6") == 0) {
+			NEXT_ARG();
+			if (nh6_ok++)
+				duparg2("nh6", *argv);
+			get_addr(&addr, *argv, AF_INET6);
+			ret = rta_addattr_l(rta, len, SEG6_MOBILE_NH6,
+					    &addr.data, addr.bytelen);
+		} else if (strcmp(*argv, "count") == 0) {
+			if (counters_ok++)
+				duparg2("count", *argv);
+			ret = seg6mobile_fill_counters(rta, len,
+						       SEG6_MOBILE_COUNTERS);
+		} else {
+			break;
+		}
+		if (ret)
+			return ret;
+		argc--; argv++;
+	}
+
+	if (!action) {
+		fprintf(stderr, "Missing action type\n");
+		exit(-1);
+	}
+
+	*argcp = argc + 1;
+	*argvp = argv - 1;
+
+	return ret;
+}
+
 static int parse_encap_mpls(struct rtattr *rta, size_t len,
 			    int *argcp, char ***argvp)
 {
@@ -2326,6 +2496,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
 	case LWTUNNEL_ENCAP_XFRM:
 		ret = parse_encap_xfrm(rta, len, &argc, &argv);
 		break;
+	case LWTUNNEL_ENCAP_SEG6_MOBILE:
+		ret = parse_encap_seg6mobile(rta, len, &argc, &argv);
+		break;
 	default:
 		fprintf(stderr, "Error: unsupported encap type\n");
 		break;
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index 9f29fd436f59..396a1dae9118 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -762,6 +762,9 @@ is a string specifying the supported encapsulation type. Namely:
 .BI seg6local
 - local SRv6 segment processing
 .sp
+.BI seg6mobile
+- SRv6 Mobile User Plane processing (RFC 9433)
+.sp
 .BI ioam6
 - encapsulation type IPv6 IOAM
 .sp
@@ -1083,6 +1086,27 @@ flavors. The Locator-Node Function length must be greater than 0 and evenly
 divisible by 8. This attribute can be used only with NEXT-C-SID flavor.
 .in -4
 
+.B seg6mobile
+.in +2
+.IR SEG6_MOBILE_ACTION " [ "
+.IR SEG6_MOBILE_PARAM " ]"
+- SRv6 Mobile User Plane operation (RFC 9433) to perform on matching
+packets. Currently only the End.MAP behavior is supported.
+.in +2
+
+.B End.MAP nh6
+.I ADDRESS
+- Replace the IPv6 destination address with the configured SID
+.RI ( ADDRESS )
+and forward via the IPv6 FIB without consuming the SRH.
+.in -2
+
+.B count
+- Enable per-CPU packet, byte and error counters for this route.
+The aggregated values are printed by
+.RI \(dq ip "\ -s\ route\ show\(dq."
+.in -2
+
 .B ioam6
 .in +2
 .B freq K/N

-- 
2.50.1


^ permalink raw reply related

* [PATCH RFC iproute2-next 1/2] uapi: sync lwtunnel.h and add seg6_mobile.h
From: Yuya Kusakabe @ 2026-07-10 12:18 UTC (permalink / raw)
  To: David Ahern, netdev; +Cc: Andrea Mayer, Andrea Mayer, Yuya Kusakabe
In-Reply-To: <20260710-seg6-mobile-end-map-v1-0-970275f3d80b@gmail.com>

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/lwtunnel.h    |  1 +
 include/uapi/linux/seg6_mobile.h | 58 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h
index 9d22961be619..b5adca10a9f1 100644
--- a/include/uapi/linux/lwtunnel.h
+++ b/include/uapi/linux/lwtunnel.h
@@ -16,6 +16,7 @@ enum lwtunnel_encap_types {
 	LWTUNNEL_ENCAP_RPL,
 	LWTUNNEL_ENCAP_IOAM6,
 	LWTUNNEL_ENCAP_XFRM,
+	LWTUNNEL_ENCAP_SEG6_MOBILE,
 	__LWTUNNEL_ENCAP_MAX,
 };
 
diff --git a/include/uapi/linux/seg6_mobile.h b/include/uapi/linux/seg6_mobile.h
new file mode 100644
index 000000000000..b99eabe9a2a1
--- /dev/null
+++ b/include/uapi/linux/seg6_mobile.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ *  SRv6 Mobile User Plane implementation
+ */
+#ifndef _LINUX_SEG6_MOBILE_H
+#define _LINUX_SEG6_MOBILE_H
+
+enum {
+	SEG6_MOBILE_UNSPEC,
+	SEG6_MOBILE_ACTION,
+	SEG6_MOBILE_NH6,
+	SEG6_MOBILE_COUNTERS,
+	__SEG6_MOBILE_MAX,
+};
+
+#define SEG6_MOBILE_MAX	(__SEG6_MOBILE_MAX - 1)
+
+enum {
+	SEG6_MOBILE_ACTION_UNSPEC	= 0,
+	/* swap IPv6 DA with the next SID, leave SRH untouched */
+	SEG6_MOBILE_ACTION_END_MAP	= 1,
+
+	__SEG6_MOBILE_ACTION_MAX,
+};
+
+#define SEG6_MOBILE_ACTION_MAX	(__SEG6_MOBILE_ACTION_MAX - 1)
+
+/* SRv6 Mobile Behavior counters are encoded as netlink attributes
+ * guaranteeing the correct alignment.
+ * Each counter is identified by a different attribute type (i.e.
+ * SEG6_MOBILE_CNT_PACKETS).
+ *
+ * - SEG6_MOBILE_CNT_PACKETS: identifies a counter that counts the number
+ *   of packets that have been CORRECTLY processed by an SRv6 Behavior
+ *   instance (i.e., packets that generate errors or are dropped are NOT
+ *   counted).
+ *
+ * - SEG6_MOBILE_CNT_BYTES: identifies a counter that counts the total
+ *   amount of traffic in bytes of all packets that have been CORRECTLY
+ *   processed by an SRv6 Behavior instance (i.e., packets that generate
+ *   errors or are dropped are NOT counted).
+ *
+ * - SEG6_MOBILE_CNT_ERRORS: identifies a counter that counts the number
+ *   of packets that have NOT been properly processed by an SRv6 Behavior
+ *   instance (i.e., packets that generate errors or are dropped).
+ */
+enum {
+	SEG6_MOBILE_CNT_UNSPEC,
+	SEG6_MOBILE_CNT_PACKETS,
+	SEG6_MOBILE_CNT_BYTES,
+	SEG6_MOBILE_CNT_ERRORS,
+	SEG6_MOBILE_CNT_PAD,		/* pad for 64 bits values */
+	__SEG6_MOBILE_CNT_MAX,
+};
+
+#define SEG6_MOBILE_CNT_MAX	(__SEG6_MOBILE_CNT_MAX - 1)
+
+#endif /* _LINUX_SEG6_MOBILE_H */

-- 
2.50.1


^ permalink raw reply related

* [PATCH RFC iproute2-next 0/2] seg6: add support for the SRv6 End.MAP behavior
From: Yuya Kusakabe @ 2026-07-10 12:18 UTC (permalink / raw)
  To: David Ahern, netdev; +Cc: Andrea Mayer, Andrea Mayer, Yuya Kusakabe

This series adds support for the "seg6mobile" lightweight tunnel, the
iproute2 counterpart of the SRv6 Mobile User Plane (RFC 9433) kernel
RFC series [1], starting with the End.MAP behavior.

Patch 1 imports the new UAPI headers as a standalone patch so it can
be dropped once the matching kernel header lands.  The selftest in
the kernel series needs the "seg6mobile" keyword to run.

[1] https://lore.kernel.org/netdev/20260710-seg6-mobile-end-map-v1-0-99be02d68143@gmail.com/

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
Yuya Kusakabe (2):
      uapi: sync lwtunnel.h and add seg6_mobile.h
      seg6: add support for the End.MAP behavior under seg6mobile encap

 include/uapi/linux/lwtunnel.h    |   1 +
 include/uapi/linux/seg6_mobile.h |  58 +++++++++++++
 ip/iproute.c                     |   6 +-
 ip/iproute_lwtunnel.c            | 173 +++++++++++++++++++++++++++++++++++++++
 man/man8/ip-route.8.in           |  24 ++++++
 5 files changed, 260 insertions(+), 2 deletions(-)
---
base-commit: 37c491d82cebef070173878e167076a1956dcd92
change-id: 20260523-seg6-mobile-end-map-6f5560bfb68e

Best regards,
--  
Yuya Kusakabe <yuya.kusakabe@gmail.com>


^ permalink raw reply

* Re: [PATCH net v4] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Vadim Fedorenko @ 2026-07-10 12:12 UTC (permalink / raw)
  To: Cen Zhang (Microsoft), jmaloy, davem, edumazet, kuba, pabeni,
	horms
  Cc: netdev, tipc-discussion, linux-kernel, tung.quang.nguyen,
	AutonomousCodeSecurity, tgopinath, kys
In-Reply-To: <20260709211649.15623-1-blbllhy@gmail.com>

On 09/07/2026 22:16, Cen Zhang (Microsoft) wrote:
> Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied
> MTU values but only enforce a minimum bound, not a maximum. When a user
> sets the MTU to a value exceeding U16_MAX (65535), it passes validation
> but is silently truncated when assigned to u16 fields l->mtu and
> l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
> truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
> which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
> values (e.g. 65537-131071) produce small incorrect MTU values, resulting
> in link malfunction behaviors.
> 
> Crash stack (triggered as unprivileged user via user namespace):
> 
>    tipc_link_set_queue_limits  net/tipc/link.c:2531
>    tipc_link_create            net/tipc/link.c:520
>    tipc_node_check_dest        net/tipc/node.c:1279
>    tipc_disc_rcv               net/tipc/discover.c:252
>    tipc_rcv                    net/tipc/node.c:2129
>    tipc_udp_recv               net/tipc/udp_media.c:392
> 
> Two independent paths lack the upper bound check:
> 1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
> 2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)
> 
> Fix both by rejecting MTU values above U16_MAX.
> 
> Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
> Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
> ---
> v4: Add .min check value
> v3: Use nla_policy check to limit MTU max value as suggested by Vadim
> v2: Solved format issue
> Link: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
> 
>   net/tipc/netlink.c | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
> index 8336a9664703..811438c44542 100644
> --- a/net/tipc/netlink.c
> +++ b/net/tipc/netlink.c
> @@ -113,12 +113,17 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
>   };
>   
>   /* Properties valid for media, bearer and link */
> +static const struct netlink_range_validation tipc_nl_mtu_range = {
> +	.min = TIPC_MIN_BEARER_MTU,
> +	.max = U16_MAX,
> +};
> +
>   const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
>   	[TIPC_NLA_PROP_UNSPEC]		= { .type = NLA_UNSPEC },
>   	[TIPC_NLA_PROP_PRIO]		= { .type = NLA_U32 },
>   	[TIPC_NLA_PROP_TOL]		= { .type = NLA_U32 },
>   	[TIPC_NLA_PROP_WIN]		= { .type = NLA_U32 },
> -	[TIPC_NLA_PROP_MTU]		= { .type = NLA_U32 },
> +	[TIPC_NLA_PROP_MTU]		= NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
>   	[TIPC_NLA_PROP_BROADCAST]	= { .type = NLA_U32 },
>   	[TIPC_NLA_PROP_BROADCAST_RATIO]	= { .type = NLA_U32 }
>   };

Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>

^ permalink raw reply

* Re: (subset) [PATCH v9 00/14] firmware: qcom: Add OP-TEE PAS service support
From: Sumit Garg @ 2026-07-10 12:12 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: konradybcio, linux-arm-msm, devicetree, dri-devel, freedreno,
	linux-media, netdev, linux-wireless, ath12k, linux-remoteproc,
	robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
	abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
	vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
	edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
	trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
	tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
	jenswi, op-tee, apurupa, skare, linux-kernel, Sumit Garg
In-Reply-To: <178362521364.2422497.1305957434056184382.b4-ty@kernel.org>

Hi Bjorn,

On Thu, Jul 09, 2026 at 02:32:39PM -0500, Bjorn Andersson wrote:
> 
> On Thu, 02 Jul 2026 17:28:16 +0530, Sumit Garg wrote:
> > From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > 
> > Qcom platforms has the legacy of using non-standard SCM calls
> > splintered over the various kernel drivers. These SCM calls aren't
> > compliant with the standard SMC calling conventions which is a
> > prerequisite to enable migration to the FF-A specifications from Arm.
> > 
> > [...]
> 
> Applied, thanks!
> 
> [01/14] firmware: qcom: Add a generic PAS service
>         commit: 08314e7c2c38b9ae6a5e01c58ed10a950859404d
> [02/14] firmware: qcom_scm: Migrate to generic PAS service
>         commit: 5c1a2975d23c51c01aca51945d0f10a4ee4c9020
> [03/14] firmware: qcom: Add a PAS TEE service
>         commit: b6f7978da0c4d26fe465aa6634f5a0b48f900de0
> [14/14] MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service
>         commit: 6701259025d49139131a0eb2257659a066dcca22
> 
> This is available as an immutable branch, for other subsystems to pull at:
>   https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git 20260702115835.167602-2-sumit.garg@kernel.org
> 
> 
> [04/14] remoteproc: qcom_q6v5_pas: Switch over to generic PAS TZ APIs
>         commit: 254030af0d81b12b7624d9ce85c6bdd3171629c6
> [05/14] remoteproc: qcom_q6v5_mss: Switch to generic PAS TZ APIs
>         commit: f3b1357673ddb37ae8b9a8fe44df73cbd2a519c5
> [06/14] remoteproc: qcom_wcnss: Switch to generic PAS TZ APIs
>         commit: ea3b5245f5deba916320b32a8e6510a74c034c17
> [07/14] remoteproc: qcom: Select QCOM_PAS generic service
>         commit: c4383254ac7a529736577e304176a10371c2ee0b
> 

Thanks for picking the partial set although I expected for you to pick
the entire set given acks from all the other subsystem maintainers. Let
me know how we should proceed further.

-Sumit

^ permalink raw reply

* [PATCH net] net: stmmac: enable the MAC on link up at 10G on 10GBASE-R
From: vadik likholetov @ 2026-07-10 12:09 UTC (permalink / raw)
  To: netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Thierry Reding, Jonathan Hunter, Bhadram Varka,
	linux-tegra, linux-kernel

stmmac_mac_link_down() clears the MAC's transmit and receive enable bits.
stmmac_mac_link_up() is expected to set them again through
stmmac_mac_set(..., true), but it first switches on the negotiated speed
and returns early for a speed it does not recognise.

The generic branch of that switch -- taken for every interface that is
neither USXGMII nor XLGMII, which includes PHY_INTERFACE_MODE_10GBASER --
handles only SPEED_2500, SPEED_1000, SPEED_100 and SPEED_10.

MGBE on Tegra234 runs 10GBASE-R into an Aquantia AQR113C. That PHY does
rate matching, so phylink_link_up() replaces the media speed with the
MAC-side interface speed before calling into the MAC:

	case RATE_MATCH_PAUSE:
		speed = phylink_interface_max_speed(link_state.interface);
		duplex = DUPLEX_FULL;

The driver is therefore called as

	stmmac_mac_link_up(interface=10GBASER, speed=10000, duplex=1)

which falls through to "default: return;". The MAC is never re-enabled,
and the interface stops passing traffic after the first link flap.

The failure is easy to misread. The link still comes up, because the PHY
is polled over MDIO and needs no MAC, so the interface reports carrier 1
at the media speed. The DMA is untouched, so its start bits stay set and
descriptors are still consumed. Only the MAC itself is gated off: the
receiver counts nothing (mmc_rx_framecount_gb stops advancing, RE is 0)
and nothing reaches the wire (TE is 0). The interface survives boot only
because stmmac_hw_setup(), called from ndo_open, enables the MAC
unconditionally -- so the problem appears only once the cable has been
unplugged and plugged back in, and "ip link set dev <ethX> down && ip
link set dev <ethX> up" appears to fix it.

Handle SPEED_10000 in the generic branch, as the USXGMII and XLGMII
branches already do. For dwxgmac2, link.xgmii.speed10000 is
XGMAC_CONFIG_SS_10000, which is 0 and is the correct speed selection for
a 10GBASE-R MAC: ctrl then equals old_ctrl, the register write is
skipped, and execution reaches stmmac_mac_set(..., true).

Fixes: d8ca113724e7 ("net: stmmac: tegra: Add MGBE support")
Signed-off-by: vadik likholetov <vadikas@gmail.com>
---
Verified on an AGX Orin devkit (Tegra234 MGBE0 + AQR113C), before and
after, on the same board and cable. MAC registers read with
`ethtool -d`, after a physical unplug and replug:

  stock		MAC_TX_CONFIG 0x00010000 (TE=0)
		MAC_RX_CONFIG 0x3ff022c0 (RE=0)
		rx_packets frozen, DHCP lease lost

  patched	MAC_TX_CONFIG 0x00010001 (TE=1)
		MAC_RX_CONFIG 0x3ff022c1 (RE=1)
		rx_packets keeps climbing, DHCP lease retained

A kprobe trace of the stock kernel across a link flap shows the
mechanism directly -- dwxgmac2_set_mac(enable=1) never follows the
link-up:

  linkdown: (stmmac_mac_link_down+0x0/0xc8) iface=27
  setmac:   (dwxgmac2_set_mac+0x0/0x80) enable=0
  linkup:   (stmmac_mac_link_up+0x0/0x350) iface=27 speed=10000 duplex=1

(iface 27 is PHY_INTERFACE_MODE_10GBASER.)

Note on the Fixes tag: the missing SPEED_10000 case in the generic branch
predates the commit cited above. I picked d8ca113724e7 because MGBE is
the first in-tree user to reach it -- it needs a 10GBASE-R interface
driven by a rate-matching PHY, so that phylink hands the MAC a 10G speed
on a link that negotiated slower. Happy to re-target the tag if you would
rather it point at the stmmac commit that introduced the switch.

Testing was done with MGBE0 handed to a VM via vfio-platform, so the
driver ran in a guest; the MAC register evidence above is read from the
device itself and the code path is not virtualisation-specific. I do not
have a bare-metal AGX Orin running mainline dwmac-tegra to confirm on,
but nothing in the analysis depends on the passthrough.

 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1125,6 +1125,9 @@ static void stmmac_mac_link_up(struct phylink_config *config,
 		}
 	} else {
 		switch (speed) {
+		case SPEED_10000:
+			ctrl |= priv->hw->link.xgmii.speed10000;
+			break;
 		case SPEED_2500:
 			ctrl |= priv->hw->link.speed2500;
 			break;
--
2.51.0

^ permalink raw reply

* Re: [Intel-wired-lan] [ANN] E830 NIC join intel's netdev-ci
From: Paul Menzel @ 2026-07-10 12:06 UTC (permalink / raw)
  To: Adrian Pielech
  Cc: kuba, netdev, Przemyslaw Kitszel, intel-wired-lan,
	Anthony L Nguyen, pabeni, davem, edumazet, horms, andrew+netdev
In-Reply-To: <2c5c3c03-7a4d-4020-84eb-825bd6ed1e78@intel.com>

Dear Adrian,


Am 10.07.26 um 13:20 schrieb Pielech, Adrian:

> I'm pleased to announce that E830-XXVDA2 NIC from Intel Ethernet
> E830 product line joins the suite that tests ice driver against net-
> next-hw branch.

Great news! Thank you for sharing this on the list.

I am curious: Is there an overview of the test lab?


Kind regards,

Paul

^ permalink raw reply

* Re: [PATCH net v2] cxgb4: flower: fix 802.1ad VLAN TPID matching in tc flower filters
From: Harsha Mahadeva @ 2026-07-10 12:03 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: netdev@vger.kernel.org, davem@davemloft.net, kuba@kernel.org,
	edumazet@google.com, andrew+netdev@lunn.ch, Potnuri Bharat Teja
In-Reply-To: <20260709094816.114720-1-pabeni@redhat.com>

On Thursday, July 07/09/26, 2026 at 15:18:16 +0530, Paolo Abeni wrote:
> From: AI Reviewer <ai@example.com>
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://netdev-ai.bots.linux.dev/sashiko/
> ---
> cxgb4: flower: fix 802.1ad VLAN TPID matching in tc flower filters
> 
> This patch adds an 802.1ad branch to cxgb4_process_flow_match() that
> programs a port-specific OVLAN register with ETH_P_8021AD, sets bit 0
> of the RX control register, clears bit 9 of TP_INGRESS_CONFIG, and
> populates the filter's outer-VLAN match fields.
> 
> A few questions about the new code follow inline.

Hi Paolo,

Thank you for taking the time to review the patch. Please find my responses inline below.

> 
> > diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> > index 3307e5042681..8c5cfa6982e7 100644
> > --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> > +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
> > @@ -266,24 +267,48 @@ static void cxgb4_process_flow_match(struct net_device *dev,
> >  					       VLAN_PRIO_SHIFT);
> >  		vlan_tci_mask = match.mask->vlan_id | (match.mask->vlan_priority <<
> >  						     VLAN_PRIO_SHIFT);
> > -		fs->val.ivlan = vlan_tci;
> > -		fs->mask.ivlan = vlan_tci_mask;
> > -
> > -		fs->val.ivlan_vld = 1;
> > -		fs->mask.ivlan_vld = 1;
> > -
> > -		/* Chelsio adapters use ivlan_vld bit to match vlan packets
> > [ ... ]
> > -		if (fs->val.ethtype == ETH_P_8021Q) {
> > -			fs->val.ethtype = 0;
> > -			fs->mask.ethtype = 0;
> > +
> > +		if (match.key->vlan_tpid == cpu_to_be16(ETH_P_8021AD)) {
> 
> [Medium]
> The gating condition consults only match.key->vlan_tpid and does not
> look at match.mask->vlan_tpid.  In tc-flower semantics a key value is
> only significant to the extent the mask covers it.
> 
> If a rule sets key=0x88a8 with mask=0 (don't care about TPID), this
> branch is still entered and the adapter registers are still
> reprogrammed.  Should this test be gated on the mask as well, similar
> to how mlx5 and nfp consult the mask before honoring the key?
> 
This programming is per port configuration rather than per-filter state. 
The TPID value is used to configure the port's OVLAN parsing behavior, 
and the corresponding hardware register uses a fixed mask of `0xffff` 
regardless of the mask supplied in the tc-flower rule. As a result, the intent 
here is to select the port's outer VLAN TPID based on the requested TPID value,
rather than to honor per-rule TPID masking semantics.
> > +			struct adapter *adap = netdev2adap(dev);
> > +			u32 ovlan_reg, ctl_reg, val, port_id;
> > +
> > +			if (!adap) {
> > +				netdev_err(dev, "%s: adap not found\n", __func__);
> > +				return;
> > +			}
> 
> [Low]
> netdev2adap() expands to netdev2pinfo(dev)->adapter and is not expected
> to return NULL for a netdev that tc-flower can bind to.  If the check
> ever did fire, cxgb4_process_flow_match() is void, so this early return
> also skips the trailing:
> 
>     fs->val.iport = netdev2pinfo(dev)->port_id;
>     fs->mask.iport = ~0;
> 
> and cxgb4_flow_rule_replace() would proceed to install a filter with
> iport=0 / iport_mask=0.  Should the check either be removed, or the
> function refactored to propagate an error?

Yes , `netdev2adap()` is not expected to return `NULL` for a valid cxgb4 netdev. 
I'll remove this check in the next revision
> 
> > +
> > +			val = (0xffff << 16) | ETH_P_8021AD;
> 
> [Low]
> The literal 0xffff has type int (signed), so 0xffff << 16 evaluates to
> a value that is not representable in a signed int.  Per C11 6.5.7 this
> is undefined behavior and UBSAN's shift sanitizer flags it.  Would
> 0xffffU << 16 (or a symbolic _V()/_M() macro) be preferable?
> 
> The layout of MPS_PORT_RX_OVLAN0_A ([31:16]=mask, [15:0]=value) is also
> not expressed through the driver's usual _S/_V/_F/_M helpers used by
> neighboring registers.  Could symbolic field macros be added?

I'll switch to using an unsigned value and add symbolic field macros in the next 
revision to match the existing register definition style.
> 
> > +			port_id = netdev2pinfo(dev)->port_id;
> > +			fs->val.ovlan = vlan_tci;
> > +			fs->mask.ovlan = vlan_tci_mask;
> > +			fs->val.ovlan_vld = 1;
> > +			fs->mask.ovlan_vld = 1;
> 
> [High]
> Does this ovlan programming survive on adapters that have VNIC_F set in
> the cached ingress_config?  In cxgb4_filter.c, both __cxgb4_set_filter()
> and cxgb4_set_hash_filter() unconditionally do:
> 
>     if (adap->params.tp.ingress_config & VNIC_F) {
>         fs->val.ovlan = (fs->val.pf << 13) | fs->val.vf;
>         fs->val.ovlan_vld = fs->val.pfvf_vld;
>         ...
>     }
> 
> which overwrites the fs->val.ovlan / fs->val.ovlan_vld set here.
> validate_filter() also rejects with -EOPNOTSUPP when ovlan_vld is set
> together with VNIC_F, or when the filter mode does not include
> VNIC_ID_F.
> 
> On adapters where VNIC_F is enabled, does this branch actually restore
> matching, or does it silently lose the VID (and, per the next comment,
> still mutate hardware before failing validation)?

I'll address this issue by moving the register configuration to happen 
after validate_filter() and install succeeds, rather than at cxgb4_process_flow_match() time 
as it does now. That way, if validate_filter() rejects the filter including the 
-EOPNOTSUPP case you mentioned,  the hardware registers are never touched 
in the first place, since we bail out before we get anywhere near programming them
> 
> > +			ovlan_reg = PORT_REG(port_id, MPS_PORT_RX_OVLAN0_A);
> > +			ctl_reg = PORT_REG(port_id, MPS_PORT_RX_CTL_A);
> > +			t4_write_reg(adap, ovlan_reg, val);
> 
> [High]
> cxgb4_process_flow_match() is called from cxgb4_flow_rule_replace()
> before __cxgb4_set_filter() runs validate_filter() and the mailbox/L2T
> install.  Do these three writes need to be deferred until after
> validation and successful install, so that a filter rejected by
> validate_filter(), or one that fails allocation/mailbox/L2T setup,
> does not leave the port and adapter in a reconfigured state with no
> installed filter to justify it?

will move the register writes to run after validate_filter() and successful install, so 
a filter that's rejected or fails allocation doesn't leave the port/adapter reconfigured 
with nothing installed to justify it
> 
> Additionally, clear_filter() (cxgb4_filter.c) only tears down L2T/SMT/
> CLIP and memsets the filter entry.  It does not undo MPS_PORT_RX_OVLAN0,
> MPS_PORT_RX_CTL bit 0, or TP_INGRESS_CONFIG bit 9.  Is a corresponding
> teardown expected on filter delete/replace so that 'tc filter add' +
> 'tc filter del' does not leave OVLAN matching permanently enabled and
> TP_INGRESS_CONFIG bit 9 permanently cleared?

Yes, this needs teardown handling. Since these are per-port registers rather than 
per-filter state, we can't simply undo them on every individual filter delete another 802.1ad 
filter on the same port may still depend on that configuration. I'll add a per-port reference count 
of installed 802.1ad/ovlan filters, incremented when one is installed and decremented in clear_filter() 
when one is removed, and only restore MPS_PORT_RX_OVLAN0, MPS_PORT_RX_CTL bit 0 and 
TP_INGRESS_CONFIG bit 9 to their original state once that count reaches zero
> 
> MPS_PORT_RX_OVLAN0_A is also hardcoded to slot 0 with ETH_P_8021AD as
> the TPID.  How is a second concurrent flow rule requesting a different
> outer TPID intended to be represented, given that installing it would
> clobber slot 0?

Currently we only support ETH_P_8021AD as the outer TPID there's no intent
right now to support multiple outer TPIDs concurrently on the same port, since 
only a single slot (MPS_PORT_RX_OVLAN0_A) is used. I'll add an explicit check 
so that if a second flow rule requests a different outer TPID while one is already 
active on the port, it's rejected  instead of silently clobbering slot 0
> 
> > +			val = t4_read_reg(adap, ctl_reg);
> > +			t4_write_reg(adap, ctl_reg, val | 1);
> 
> [High]
> This RMW on MPS_PORT_RX_CTL_A is unserialized.  If another context
> modifies other bits of the same register between the read and the
> write, that update is lost.  Is there a lock that should be held here
> (win0_lock or similar)?
> 
> > +			t4_tp_wr_bits_indirect(adap, TP_INGRESS_CONFIG_A, 1U << 9, 0);
> 
> [High, Medium, Medium]
> t4_tp_wr_bits_indirect() drives the TP_PIO_ADDR_A / TP_PIO_DATA_A
> indirect-register pair:
> 
>     void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr,
>                                 unsigned int mask, unsigned int val)
>     {
>         t4_write_reg(adap, TP_PIO_ADDR_A, addr);
>         val |= t4_read_reg(adap, TP_PIO_DATA_A) & ~mask;
>         t4_write_reg(adap, TP_PIO_DATA_A, val);
>     }
> 
> Before this patch the only in-tree caller was probe-time
> adap_init0_tweaks().  This patch adds a runtime, user-triggerable
> caller.
> 
> Can a concurrent TP_PIO user (t4_tp_pio_read/t4_tp_pio_write when LDST
> is unavailable, register-dump code, or another tc-flower install on a
> different netdev of the same adapter) interleave between the ADDR
> write and the DATA read/write and cause the wrong TP register to be
> read or written?  What serializes the TP_PIO_ADDR_A/TP_PIO_DATA_A pair
> here?

I will fix above in next revision .Will take locking around the MPS_PORT_RX_CTL_A RMW and 
TP_PIO_ADDR_A/TP_PIO_DATA_A sequence in t4_tp_wr_bits_indirect(), so both are 
serialized against concurrent access
> 
> Separately, the hardware bit is toggled but adap->params.tp.ingress_config
> is not updated.  That shadow is consulted throughout cxgb4_filter.c
> (validate_filter, __cxgb4_set_filter, cxgb4_set_hash_filter) and l2t.c
> to make VNIC_F / USE_ENC_IDX_F / ovlan-vs-pfvf decisions.  Should the
> shadow be kept in sync with the hardware write?
> 
> Also, bit 9 of TP_INGRESS_CONFIG_A is written as the bare literal
> 1U << 9, while neighboring bits of the same register are exposed as
> _S/_V/_F macros in t4_regs.h (CSUM_HAS_PSEUDO_HDR_S=10, VNIC_S=11,
> USE_ENC_IDX_S=13).  What feature does bit 9 control, and could a
> symbolic name be introduced?  The commit message enumerates three
> actions (OVLAN register, RX control enable, filter fields) but does
> not mention this fourth adapter-wide write.

Bit 9 is RM_OVLAN and when its unset, it stops hardware from stripping the outer 
VLAN tag. I will add a symbolic _S/_F macro for it (replacing the bare 1U << 9) and 
call out this fourth write in the commit message, and will also update 
adap->params.tp.ingress_config alongside the hardware write 
> 
> > +		} else {
> > +			fs->val.ivlan = vlan_tci;
> > +			fs->mask.ivlan = vlan_tci_mask;
> > +			fs->val.ivlan_vld = 1;
> > +			fs->mask.ivlan_vld = 1;
> > +
> > +			/* Chelsio adapters use ivlan_vld bit to match vlan packets
> > +			 * as 802.1Q. Also, when vlan tag is present in packets,
> > +			 * ethtype match is used then to match on ethtype of inner
> > +			 * header ie. the header following the vlan header.
> > +			 * So, set the ivlan_vld based on ethtype info supplied by
> > +			 * TC for vlan packets if its 802.1Q. And then reset the
> > +			 * ethtype value else, hw will try to match the supplied
> > +			 * ethtype value with ethtype of inner header.
> > +			 */
> > +
> > +			if (fs->val.ethtype == ETH_P_8021Q) {
> > +				fs->val.ethtype = 0;
> > +				fs->mask.ethtype = 0;
> > +			}
> 
> [High]
> The preserved comment states that the hardware ethtype field matches
> the ethtype after the outer VLAN tag (the inner header), and the
> 802.1Q branch resets fs->val.ethtype/fs->mask.ethtype to avoid a
> mismatch.
> 
> The new 802.1ad branch above does not perform the equivalent reset.
> For a common rule such as:
> 
>     tc filter add ... flower \
>         vlan_ethtype 0x88a8 vlan_id X ...
> 
> FLOW_DISSECTOR_KEY_BASIC processing earlier in the same function has
> already set fs->val.ethtype = 0x88a8, but the ethtype field is matched
> against the inner header (which for real 802.1ad traffic is 0x8100 or
> an L3 protocol, never 0x88a8).  Should the 802.1ad branch clear
> fs->val.ethtype / fs->mask.ethtype the same way the 802.1Q branch
> does?

The preserved comment applies to a single-tagged VLAN packet. I'll rework the logic 
to handle ethtype reset correctly for both the single-tagged and QinQ (802.1ad) cases
> 
> >  		}
> >  	}
> >  
> > diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> > index 695916ba0405..38c585f3b1ad 100644
> > --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> > +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
> > @@ -1921,6 +1921,9 @@
> >  #define MAC_PORT_PTP_SUM_LO_A 0x990
> >  #define MAC_PORT_PTP_SUM_HI_A 0x994
> >  
> > +#define MPS_PORT_RX_OVLAN0_A 0x120
> > +#define MPS_PORT_RX_CTL_A    0X100
> 
> [Low]
> This isn't a bug, but the hex prefix on 0X100 is uppercase while the
> line above (and every neighboring macro) uses lowercase 0x.  Was that
> intentional?

Its not intentional ,I will fix this in next revision.Thanks for flagging it
> 
> > +
> >  #define MPS_CMN_CTL_A	0x9000
> -- 
> This is an AI-generated review.
> 

^ permalink raw reply

* RE: [Intel-wired-lan] [PATCH iwl-next v1 5/6] ixgbe: E610: add MAC address runtime refresh
From: Jagielski, Jedrzej @ 2026-07-10 11:59 UTC (permalink / raw)
  To: Fijalkowski, Maciej
  Cc: Simon Horman, intel-wired-lan@lists.osuosl.org, Nguyen, Anthony L,
	netdev@vger.kernel.org
In-Reply-To: <alDCHrOYHvS1EdHE@boxer>

From: Fijalkowski, Maciej <maciej.fijalkowski@intel.com> 
Sent: Friday, July 10, 2026 11:58 AM

>On Thu, Jul 09, 2026 at 09:37:27AM +0000, Jagielski, Jedrzej wrote:
>> From: Simon Horman <horms@kernel.org> 
>> Sent: Wednesday, July 8, 2026 7:02 PM
>> 
>> >This is an AI-generated review of your patch. The human sending this
>> >email has considered the AI review valid, or at least plausible.
>> >Full review at: https://netdev-ai.bots.linux.dev/sashiko/
>> >---
>> >ixgbe: E610: add MAC address runtime refresh
>> >
>> >This change polls RAR0 each service-task cycle on E610 and updates
>> >netdev->dev_addr, hw->mac.addr and mac_table[0].addr when firmware has
>> >altered the MAC.  A refresh is also invoked from ixgbe_reset() so the
>> >address survives RAR0 clearing during init_hw().
>
>[...]
>
>> >[Medium]
>> >Would it make sense to hoist the E610 gating out of
>> >ixgbe_mac_addr_refresh() and skip the rtnl_lock()/rtnl_unlock()
>> >entirely for non-E610 devices?
>> >
>> >The type check lives inside the helper:
>> >
>> >    static void ixgbe_mac_addr_refresh(struct ixgbe_adapter *adapter)
>> >    {
>> >        ...
>> >        if (hw->mac.type != ixgbe_mac_e610)
>> >            return;
>> >        ...
>> >    }
>> >
>> >so 82598, 82599, X540 and X550 adapters (which comprise most of the
>> >ixgbe device family) acquire the global rtnl_mutex every service-task
>> >cycle only to return immediately.  Something like:
>> >
>> >    if (adapter->hw.mac.type == ixgbe_mac_e610) {
>> >        rtnl_lock();
>> >        ixgbe_mac_addr_refresh(adapter);
>> >        rtnl_unlock();
>> >    }
>> 
>> Cannot introduce rtnl locking into ixgbe_mac_addr_refresh() as this
>> can be already called within rtnl-locked path
>
>i40e does a lot of dancing for cases like this. rtnl lock is handled via
>boolean provided as function arg:
>
>static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>...
>	/* Release the RTNL lock before we start resetting VFs */
>	if (!lock_acquired)
>		rtnl_unlock();
>
>...
>
>end_unlock:
>	if (!lock_acquired)
>		rtnl_unlock();

yeah, not the prettiest solution but, agree, should work :)
will redesign it, thanks!

>> 
>> >
>> >would avoid the rtnl_mutex contention on non-E610 hardware.
>> >
>> >>  	ixgbe_reset_subtask(adapter);
>> >>  	ixgbe_phy_interrupt_subtask(adapter);
>> >>  	ixgbe_sfp_detection_subtask(adapter)

^ permalink raw reply

* [PATCH net-next v3 9/9] netconsole: move remote_mac from struct netpoll to netconsole_target
From: Breno Leitao @ 2026-07-10 11:39 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

The destination ethernet address is netconsole configuration: no other
netpoll user (bonding, team, vlan, bridge, macvlan, dsa) references
np->remote_mac, only netconsole's ethernet framing and its
configfs/cmdline interface do.

Move it into struct netconsole_target and convert push_eth() to take the
netconsole_target; netconsole_print_banner() and
netconsole_parser_cmdline() already take it. The configfs show/store
handlers and alloc_and_init() reach the field directly.

No functional change; the remote_mac sysfs attribute is unchanged.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 20 +++++++++++---------
 include/linux/netpoll.h  |  1 -
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index d0739b45f66e1..49b2243a20b2d 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -178,9 +178,9 @@ enum target_state {
  *		local_ip	(read-write)
  *		remote_ip	(read-write)
  *		local_mac	(read-only)
- *		remote_mac	(read-write)
  * @local_port:	Source UDP port of the target (read-write).
  * @remote_port: Destination UDP port of the target (read-write).
+ * @remote_mac:	Destination ethernet address of the target (read-write).
  * @buf:	The buffer used to send the full msg to the network stack
  * @resume_wq:	Workqueue to resume deactivated target
  * @skb_pool:	Per-target fallback skb pool consulted by find_skb() when
@@ -209,6 +209,7 @@ struct netconsole_target {
 	bool			release;
 	struct netpoll		np;
 	u16			local_port, remote_port;
+	u8			remote_mac[ETH_ALEN];
 	/* protected by target_list_lock; +1 gives scnprintf() room for its
 	 * NUL terminator so a full MAX_PRINT_CHUNK payload is not truncated
 	 */
@@ -462,7 +463,7 @@ static struct netconsole_target *alloc_and_init(void)
 	strscpy(nt->np.dev_name, "eth0", IFNAMSIZ);
 	nt->local_port = 6665;
 	nt->remote_port = 6666;
-	eth_broadcast_addr(nt->np.remote_mac);
+	eth_broadcast_addr(nt->remote_mac);
 	nt->state = STATE_DISABLED;
 	INIT_WORK(&nt->resume_wq, process_resume_target);
 	/* Set up the skb pool primitives once; enabling only refills it. */
@@ -516,7 +517,7 @@ static void netconsole_print_banner(struct netconsole_target *nt)
 		np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6);
 	else
 		np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip);
-	np_info(np, "remote ethernet address %pM\n", np->remote_mac);
+	np_info(np, "remote ethernet address %pM\n", nt->remote_mac);
 }
 
 /* Parse the string and populate the `inet_addr` union. Return 0 if IPv4 is
@@ -672,7 +673,7 @@ static ssize_t local_mac_show(struct config_item *item, char *buf)
 
 static ssize_t remote_mac_show(struct config_item *item, char *buf)
 {
-	return sysfs_emit(buf, "%pM\n", to_target(item)->np.remote_mac);
+	return sysfs_emit(buf, "%pM\n", to_target(item)->remote_mac);
 }
 
 static ssize_t transmit_errors_show(struct config_item *item, char *buf)
@@ -1077,7 +1078,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,
 		goto out_unlock;
 	if (buf[MAC_ADDR_STR_LEN] && buf[MAC_ADDR_STR_LEN] != '\n')
 		goto out_unlock;
-	memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN);
+	memcpy(nt->remote_mac, remote_mac, ETH_ALEN);
 
 	ret = count;
 out_unlock:
@@ -1885,14 +1886,15 @@ static void push_udp(struct netconsole_target *nt, struct sk_buff *skb, int len)
 	netpoll_udp_checksum(np, skb, len);
 }
 
-static void push_eth(struct netpoll *np, struct sk_buff *skb)
+static void push_eth(struct netconsole_target *nt, struct sk_buff *skb)
 {
+	struct netpoll *np = &nt->np;
 	struct ethhdr *eth;
 
 	eth = skb_push(skb, ETH_HLEN);
 	skb_reset_mac_header(skb);
 	ether_addr_copy(eth->h_source, np->dev->dev_addr);
-	ether_addr_copy(eth->h_dest, np->remote_mac);
+	ether_addr_copy(eth->h_dest, nt->remote_mac);
 	if (np->ipv6)
 		eth->h_proto = htons(ETH_P_IPV6);
 	else
@@ -1980,7 +1982,7 @@ static int netpoll_send_udp(struct netconsole_target *nt, const char *msg,
 		push_ipv6(np, skb, len);
 	else
 		push_ipv4(np, skb, len);
-	push_eth(np, skb);
+	push_eth(nt, skb);
 	skb->dev = np->dev;
 
 	return (int)netpoll_send_skb(np, skb);
@@ -2375,7 +2377,7 @@ static int netconsole_parser_cmdline(struct netconsole_target *nt, char *opt)
 
 	if (*cur != 0) {
 		/* MAC address */
-		if (!mac_pton(cur, np->remote_mac))
+		if (!mac_pton(cur, nt->remote_mac))
 			goto parse_failed;
 	}
 
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 5ca79fa7d9431..79315461a7b1e 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -35,7 +35,6 @@ struct netpoll {
 
 	union inet_addr local_ip, remote_ip;
 	bool ipv6;
-	u8 remote_mac[ETH_ALEN];
 };
 
 #define np_info(np, fmt, ...)				\

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 8/9] netconsole: move local_port / remote_port from struct netpoll to netconsole_target
From: Breno Leitao @ 2026-07-10 11:39 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

The source and destination UDP ports live in struct netpoll but are
netconsole configuration. No other netpoll user (bonding, team, vlan,
bridge, macvlan, dsa) touches np->local_port or np->remote_port; they
only use the netpoll TX/forwarding path. Only netconsole's UDP framing
and its configfs/cmdline interface read these fields.

Move both into struct netconsole_target and convert the three helpers
that read them - push_udp(), netconsole_print_banner() and
netconsole_parser_cmdline() - to take the netconsole_target. The
configfs show/store handlers already have the target in hand.

No functional change; the local_port / remote_port sysfs attributes are
unchanged.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 47 ++++++++++++++++++++++++++---------------------
 include/linux/netpoll.h  |  1 -
 2 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index cf591ae667362..d0739b45f66e1 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -175,12 +175,12 @@ enum target_state {
  * @np:		The netpoll structure for this target.
  *		Contains the other userspace visible parameters:
  *		dev_name	(read-write)
- *		local_port	(read-write)
- *		remote_port	(read-write)
  *		local_ip	(read-write)
  *		remote_ip	(read-write)
  *		local_mac	(read-only)
  *		remote_mac	(read-write)
+ * @local_port:	Source UDP port of the target (read-write).
+ * @remote_port: Destination UDP port of the target (read-write).
  * @buf:	The buffer used to send the full msg to the network stack
  * @resume_wq:	Workqueue to resume deactivated target
  * @skb_pool:	Per-target fallback skb pool consulted by find_skb() when
@@ -208,6 +208,7 @@ struct netconsole_target {
 	bool			extended;
 	bool			release;
 	struct netpoll		np;
+	u16			local_port, remote_port;
 	/* protected by target_list_lock; +1 gives scnprintf() room for its
 	 * NUL terminator so a full MAX_PRINT_CHUNK payload is not truncated
 	 */
@@ -459,8 +460,8 @@ static struct netconsole_target *alloc_and_init(void)
 
 	nt->np.name = "netconsole";
 	strscpy(nt->np.dev_name, "eth0", IFNAMSIZ);
-	nt->np.local_port = 6665;
-	nt->np.remote_port = 6666;
+	nt->local_port = 6665;
+	nt->remote_port = 6666;
 	eth_broadcast_addr(nt->np.remote_mac);
 	nt->state = STATE_DISABLED;
 	INIT_WORK(&nt->resume_wq, process_resume_target);
@@ -499,16 +500,18 @@ static void netconsole_process_cleanups_core(void)
 	mutex_unlock(&target_cleanup_list_lock);
 }
 
-static void netconsole_print_banner(struct netpoll *np)
+static void netconsole_print_banner(struct netconsole_target *nt)
 {
-	np_info(np, "local port %d\n", np->local_port);
+	struct netpoll *np = &nt->np;
+
+	np_info(np, "local port %d\n", nt->local_port);
 	if (np->ipv6)
 		np_info(np, "local IPv6 address %pI6c\n", &np->local_ip.in6);
 	else
 		np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip);
 	np_info(np, "interface name '%s'\n", np->dev_name);
 	np_info(np, "local ethernet address '%pM'\n", np->dev_mac);
-	np_info(np, "remote port %d\n", np->remote_port);
+	np_info(np, "remote port %d\n", nt->remote_port);
 	if (np->ipv6)
 		np_info(np, "remote IPv6 address %pI6c\n", &np->remote_ip.in6);
 	else
@@ -631,12 +634,12 @@ static ssize_t dev_name_show(struct config_item *item, char *buf)
 
 static ssize_t local_port_show(struct config_item *item, char *buf)
 {
-	return sysfs_emit(buf, "%d\n", to_target(item)->np.local_port);
+	return sysfs_emit(buf, "%d\n", to_target(item)->local_port);
 }
 
 static ssize_t remote_port_show(struct config_item *item, char *buf)
 {
-	return sysfs_emit(buf, "%d\n", to_target(item)->np.remote_port);
+	return sysfs_emit(buf, "%d\n", to_target(item)->remote_port);
 }
 
 static ssize_t local_ip_show(struct config_item *item, char *buf)
@@ -826,7 +829,7 @@ static ssize_t enabled_store(struct config_item *item,
 		 * Skip netconsole_parser_cmdline() -- all the attributes are
 		 * already configured via configfs. Just print them out.
 		 */
-		netconsole_print_banner(&nt->np);
+		netconsole_print_banner(nt);
 
 		/* Initialise the skb pool before netpoll_setup() so the pool
 		 * is valid as soon as nt->np.dev becomes visible to
@@ -965,7 +968,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf,
 		goto out_unlock;
 	}
 
-	ret = kstrtou16(buf, 10, &nt->np.local_port);
+	ret = kstrtou16(buf, 10, &nt->local_port);
 	if (ret < 0)
 		goto out_unlock;
 	ret = count;
@@ -987,7 +990,7 @@ static ssize_t remote_port_store(struct config_item *item,
 		goto out_unlock;
 	}
 
-	ret = kstrtou16(buf, 10, &nt->np.remote_port);
+	ret = kstrtou16(buf, 10, &nt->remote_port);
 	if (ret < 0)
 		goto out_unlock;
 	ret = count;
@@ -1863,8 +1866,9 @@ static void netpoll_udp_checksum(struct netpoll *np, struct sk_buff *skb,
 		udph->check = CSUM_MANGLED_0;
 }
 
-static void push_udp(struct netpoll *np, struct sk_buff *skb, int len)
+static void push_udp(struct netconsole_target *nt, struct sk_buff *skb, int len)
 {
+	struct netpoll *np = &nt->np;
 	struct udphdr *udph;
 	int udp_len;
 
@@ -1874,8 +1878,8 @@ static void push_udp(struct netpoll *np, struct sk_buff *skb, int len)
 	skb_reset_transport_header(skb);
 
 	udph = udp_hdr(skb);
-	udph->source = htons(np->local_port);
-	udph->dest = htons(np->remote_port);
+	udph->source = htons(nt->local_port);
+	udph->dest = htons(nt->remote_port);
 	udph->len = htons(udp_len);
 
 	netpoll_udp_checksum(np, skb, len);
@@ -1971,7 +1975,7 @@ static int netpoll_send_udp(struct netconsole_target *nt, const char *msg,
 	skb_copy_to_linear_data(skb, msg, len);
 	skb_put(skb, len);
 
-	push_udp(np, skb, len);
+	push_udp(nt, skb, len);
 	if (np->ipv6)
 		push_ipv6(np, skb, len);
 	else
@@ -2289,8 +2293,9 @@ __releases(&target_list_lock)
 	spin_unlock_irqrestore(&target_list_lock, flags);
 }
 
-static int netconsole_parser_cmdline(struct netpoll *np, char *opt)
+static int netconsole_parser_cmdline(struct netconsole_target *nt, char *opt)
 {
+	struct netpoll *np = &nt->np;
 	bool ipversion_set = false;
 	char *cur = opt;
 	char *delim;
@@ -2301,7 +2306,7 @@ static int netconsole_parser_cmdline(struct netpoll *np, char *opt)
 		if (!delim)
 			goto parse_failed;
 		*delim = 0;
-		if (kstrtou16(cur, 10, &np->local_port))
+		if (kstrtou16(cur, 10, &nt->local_port))
 			goto parse_failed;
 		cur = delim;
 	}
@@ -2348,7 +2353,7 @@ static int netconsole_parser_cmdline(struct netpoll *np, char *opt)
 		*delim = 0;
 		if (*cur == ' ' || *cur == '\t')
 			np_info(np, "warning: whitespace is not allowed\n");
-		if (kstrtou16(cur, 10, &np->remote_port))
+		if (kstrtou16(cur, 10, &nt->remote_port))
 			goto parse_failed;
 		cur = delim;
 	}
@@ -2374,7 +2379,7 @@ static int netconsole_parser_cmdline(struct netpoll *np, char *opt)
 			goto parse_failed;
 	}
 
-	netconsole_print_banner(np);
+	netconsole_print_banner(nt);
 
 	return 0;
 
@@ -2412,7 +2417,7 @@ static struct netconsole_target *alloc_param_target(char *target_config,
 	}
 
 	/* Parse parameters and setup netpoll */
-	err = netconsole_parser_cmdline(&nt->np, target_config);
+	err = netconsole_parser_cmdline(nt, target_config);
 	if (err)
 		goto fail;
 
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index f377fdf7839ca..5ca79fa7d9431 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -35,7 +35,6 @@ struct netpoll {
 
 	union inet_addr local_ip, remote_ip;
 	bool ipv6;
-	u16 local_port, remote_port;
 	u8 remote_mac[ETH_ALEN];
 };
 

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 7/9] netconsole: move skb_pool / refill_wq from struct netpoll to netconsole_target
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

These two fields back the fallback skb pool that find_skb() uses.
Every helper that touches them lives in netconsole now (refill_skbs,
refill_skbs_work_handler, netconsole_skb_pool_init,
netconsole_skb_pool_flush, find_skb, netcons_skb_pop), so the data
can move alongside its only consumer.

Add skb_pool and refill_wq to struct netconsole_target, drop them
from struct netpoll.

This will save 48-bytes for every netpoll user instance (except
netconsole that will have it in netconsole target struct).

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 53 +++++++++++++++++++++++++++---------------------
 include/linux/netpoll.h  |  2 --
 2 files changed, 30 insertions(+), 25 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 742723c5a7d46..cf591ae667362 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -183,6 +183,11 @@ enum target_state {
  *		remote_mac	(read-write)
  * @buf:	The buffer used to send the full msg to the network stack
  * @resume_wq:	Workqueue to resume deactivated target
+ * @skb_pool:	Per-target fallback skb pool consulted by find_skb() when
+ *		its GFP_ATOMIC allocation fails. Lifetime brackets a
+ *		successful netpoll_setup() / netpoll_cleanup() pair on @np.
+ * @refill_wq:	Work item that asynchronously tops @skb_pool back up to
+ *		MAX_SKBS after find_skb() drains an entry.
  */
 struct netconsole_target {
 	struct list_head	list;
@@ -208,6 +213,8 @@ struct netconsole_target {
 	 */
 	char			buf[MAX_PRINT_CHUNK + 1];
 	struct work_struct	resume_wq;
+	struct sk_buff_head	skb_pool;
+	struct work_struct	refill_wq;
 };
 
 #ifdef	CONFIG_NETCONSOLE_DYNAMIC
@@ -305,13 +312,11 @@ static void netcons_release_dev(struct netconsole_target *nt)
 		memset(&nt->np.dev_name, 0, IFNAMSIZ);
 }
 
-static void refill_skbs(struct netpoll *np)
+static void refill_skbs(struct netconsole_target *nt)
 {
-	struct sk_buff_head *skb_pool;
+	struct sk_buff_head *skb_pool = &nt->skb_pool;
 	struct sk_buff *skb;
 
-	skb_pool = &np->skb_pool;
-
 	while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
 		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
 		if (!skb)
@@ -323,10 +328,10 @@ static void refill_skbs(struct netpoll *np)
 
 static void refill_skbs_work_handler(struct work_struct *work)
 {
-	struct netpoll *np =
-		container_of(work, struct netpoll, refill_wq);
+	struct netconsole_target *nt =
+		container_of(work, struct netconsole_target, refill_wq);
 
-	refill_skbs(np);
+	refill_skbs(nt);
 }
 
 /* Seed the per-target skb pool that find_skb() falls back to. The queue
@@ -335,15 +340,13 @@ static void refill_skbs_work_handler(struct work_struct *work)
  */
 static void netconsole_skb_pool_init(struct netconsole_target *nt)
 {
-	refill_skbs(&nt->np);
+	refill_skbs(nt);
 }
 
 static void netconsole_skb_pool_flush(struct netconsole_target *nt)
 {
-	struct netpoll *np = &nt->np;
-
-	cancel_work_sync(&np->refill_wq);
-	skb_queue_purge_reason(&np->skb_pool, SKB_CONSUMED);
+	cancel_work_sync(&nt->refill_wq);
+	skb_queue_purge_reason(&nt->skb_pool, SKB_CONSUMED);
 }
 
 /* Attempts to resume logging to a deactivated target. */
@@ -462,8 +465,8 @@ static struct netconsole_target *alloc_and_init(void)
 	nt->state = STATE_DISABLED;
 	INIT_WORK(&nt->resume_wq, process_resume_target);
 	/* Set up the skb pool primitives once; enabling only refills it. */
-	skb_queue_head_init(&nt->np.skb_pool);
-	INIT_WORK(&nt->np.refill_wq, refill_skbs_work_handler);
+	skb_queue_head_init(&nt->skb_pool);
+	INIT_WORK(&nt->refill_wq, refill_skbs_work_handler);
 
 	return nt;
 }
@@ -1785,7 +1788,7 @@ static struct notifier_block netconsole_netdev_notifier = {
  * pool locks and is therefore not NMI-safe. Skip the refill when called
  * from NMI context; the next non-NMI caller will top the pool back up.
  */
-static struct sk_buff *netcons_skb_pop(struct netpoll *np, int len)
+static struct sk_buff *netcons_skb_pop(struct netconsole_target *nt, int len)
 {
 	struct sk_buff *skb;
 
@@ -1797,19 +1800,21 @@ static struct sk_buff *netcons_skb_pop(struct netpoll *np, int len)
 		if (!in_nmi())
 			net_warn_ratelimited("netconsole: dropping message, requested skb len %d exceeds pool buffer size %zu on %s\n",
 					     len, (size_t)MAX_SKB_SIZE,
-					     np->dev->name);
+					     nt->np.dev->name);
 		return NULL;
 	}
 
-	skb = skb_dequeue(&np->skb_pool);
+	skb = skb_dequeue(&nt->skb_pool);
 	if (!in_nmi())
-		schedule_work(&np->refill_wq);
+		schedule_work(&nt->refill_wq);
 
 	return skb;
 }
 
-static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
+static struct sk_buff *find_skb(struct netconsole_target *nt, int len,
+				int reserve)
 {
+	struct netpoll *np = &nt->np;
 	int count = 0;
 	struct sk_buff *skb;
 
@@ -1818,7 +1823,7 @@ static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
 
 	skb = alloc_skb(len, GFP_ATOMIC | __GFP_NOWARN);
 	if (!skb)
-		skb = netcons_skb_pop(np, len);
+		skb = netcons_skb_pop(nt, len);
 
 	if (!skb) {
 		if (++count < 10) {
@@ -1940,8 +1945,10 @@ static void push_ipv6(struct netpoll *np, struct sk_buff *skb, int len)
 	skb->protocol = htons(ETH_P_IPV6);
 }
 
-static int netpoll_send_udp(struct netpoll *np, const char *msg, int len)
+static int netpoll_send_udp(struct netconsole_target *nt, const char *msg,
+			    int len)
 {
+	struct netpoll *np = &nt->np;
 	int total_len, ip_len, udp_len;
 	struct sk_buff *skb;
 
@@ -1956,7 +1963,7 @@ static int netpoll_send_udp(struct netpoll *np, const char *msg, int len)
 
 	total_len = ip_len + LL_RESERVED_SPACE(np->dev);
 
-	skb = find_skb(np, total_len + np->dev->needed_tailroom,
+	skb = find_skb(nt, total_len + np->dev->needed_tailroom,
 		       total_len - len);
 	if (!skb)
 		return -ENOMEM;
@@ -1987,7 +1994,7 @@ static int netpoll_send_udp(struct netpoll *np, const char *msg, int len)
  */
 static void send_udp(struct netconsole_target *nt, const char *msg, int len)
 {
-	int result = netpoll_send_udp(&nt->np, msg, len);
+	int result = netpoll_send_udp(nt, msg, len);
 
 	if (IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC)) {
 		if (result == NET_XMIT_DROP) {
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 1216b5c237ce4..f377fdf7839ca 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -37,8 +37,6 @@ struct netpoll {
 	bool ipv6;
 	u16 local_port, remote_port;
 	u8 remote_mac[ETH_ALEN];
-	struct sk_buff_head skb_pool;
-	struct work_struct refill_wq;
 };
 
 #define np_info(np, fmt, ...)				\

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 6/9] netconsole: move skb_pool_flush() from netpoll
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

skb_pool_flush() has no callers left in net/core/netpoll.c after
netconsole took over the pool lifecycle. Inline its body into
netconsole_skb_pool_flush() (the only caller) and drop the function
and its export from netpoll. The prototype goes from
<linux/netpoll.h>.

Pure code motion: cancel_work_sync() + skb_queue_purge_reason()
semantics are unchanged.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c |  5 ++++-
 include/linux/netpoll.h  |  1 -
 net/core/netpoll.c       | 10 ----------
 3 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index efeada762536f..742723c5a7d46 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -340,7 +340,10 @@ static void netconsole_skb_pool_init(struct netconsole_target *nt)
 
 static void netconsole_skb_pool_flush(struct netconsole_target *nt)
 {
-	skb_pool_flush(&nt->np);
+	struct netpoll *np = &nt->np;
+
+	cancel_work_sync(&np->refill_wq);
+	skb_queue_purge_reason(&np->skb_pool, SKB_CONSUMED);
 }
 
 /* Attempts to resume logging to a deactivated target. */
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 7e2fbce863e9b..1216b5c237ce4 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -76,7 +76,6 @@ void netpoll_cleanup(struct netpoll *np);
 void do_netpoll_cleanup(struct netpoll *np);
 netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
 void netpoll_zap_completion_queue(void);
-void skb_pool_flush(struct netpoll *np);
 
 #ifdef CONFIG_NETPOLL
 static inline void *netpoll_poll_lock(struct napi_struct *napi)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 9ca695f64210c..f8da1048ea3ab 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -329,16 +329,6 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(netpoll_send_skb);
 
-void skb_pool_flush(struct netpoll *np)
-{
-	struct sk_buff_head *skb_pool;
-
-	cancel_work_sync(&np->refill_wq);
-	skb_pool = &np->skb_pool;
-	skb_queue_purge_reason(skb_pool, SKB_CONSUMED);
-}
-EXPORT_SYMBOL_GPL(skb_pool_flush);
-
 int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
 {
 	struct netpoll_info *npinfo;

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 5/9] netconsole: move refill_skbs() and skb-pool sizing macros from netpoll
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

refill_skbs() is now only called from netconsole (directly via
netconsole_skb_pool_init() and indirectly via the just-moved
refill_skbs_work_handler()), and the MAX_UDP_CHUNK / MAX_SKBS /
MAX_SKB_SIZE macros are private to it. Move them all into
drivers/net/netconsole.c.

MAX_UDP_CHUNK and MAX_SKB_SIZE were promoted to <linux/netpoll.h>
by commit 6c537b845c99 ("netconsole: do not dequeue pooled skbs that
cannot satisfy len") so find_skb() could detect oversized requests
against the same value refill_skbs() used. With both functions now
local to netconsole, the shared definition no longer needs to live
in the header.

Pure code motion: bodies and pool sizing semantics are unchanged.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 29 +++++++++++++++++++++++++++++
 include/linux/netpoll.h  | 15 ---------------
 net/core/netpoll.c       | 23 -----------------------
 3 files changed, 29 insertions(+), 38 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 96d9a47312cd7..efeada762536f 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -61,6 +61,19 @@ MODULE_IMPORT_NS("NETDEV_INTERNAL");
 #define MAX_USERDATA_ITEMS		256
 #define MAX_PRINT_CHUNK			1000
 
+/*
+ * Sizing for the per-target fallback skb pool consulted by find_skb()
+ * when its GFP_ATOMIC allocation fails so messages still get out under
+ * memory pressure.
+ */
+#define MAX_UDP_CHUNK			1460
+#define MAX_SKBS			32
+#define MAX_SKB_SIZE							\
+	(sizeof(struct ethhdr) +					\
+	 sizeof(struct iphdr) +						\
+	 sizeof(struct udphdr) +					\
+	 MAX_UDP_CHUNK)
+
 static char config[MAX_PARAM_LENGTH];
 module_param_string(netconsole, config, MAX_PARAM_LENGTH, 0);
 MODULE_PARM_DESC(netconsole, " netconsole=[src-port]@[src-ip]/[dev],[tgt-port]@<tgt-ip>/[tgt-macaddr]");
@@ -292,6 +305,22 @@ static void netcons_release_dev(struct netconsole_target *nt)
 		memset(&nt->np.dev_name, 0, IFNAMSIZ);
 }
 
+static void refill_skbs(struct netpoll *np)
+{
+	struct sk_buff_head *skb_pool;
+	struct sk_buff *skb;
+
+	skb_pool = &np->skb_pool;
+
+	while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
+		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
+		if (!skb)
+			break;
+
+		skb_queue_tail(skb_pool, skb);
+	}
+}
+
 static void refill_skbs_work_handler(struct work_struct *work)
 {
 	struct netpoll *np =
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 51e5863d8e678..7e2fbce863e9b 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -21,20 +21,6 @@ union inet_addr {
 	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;
@@ -90,7 +76,6 @@ void netpoll_cleanup(struct netpoll *np);
 void do_netpoll_cleanup(struct netpoll *np);
 netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
 void netpoll_zap_completion_queue(void);
-void refill_skbs(struct netpoll *np);
 void skb_pool_flush(struct netpoll *np);
 
 #ifdef CONFIG_NETPOLL
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 93a16faf808c8..9ca695f64210c 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -36,12 +36,6 @@
 #include <trace/events/napi.h>
 #include <linux/kconfig.h>
 
-/*
- * We maintain a small pool of fully-sized skbs, to make sure the
- * message gets out even in extreme OOM situations.
- */
-
-#define MAX_SKBS 32
 #define USEC_PER_POLL	50
 
 static unsigned int carrier_timeout = 4;
@@ -213,23 +207,6 @@ void netpoll_poll_enable(struct net_device *dev)
 		up(&ni->dev_lock);
 }
 
-void refill_skbs(struct netpoll *np)
-{
-	struct sk_buff_head *skb_pool;
-	struct sk_buff *skb;
-
-	skb_pool = &np->skb_pool;
-
-	while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
-		skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
-		if (!skb)
-			break;
-
-		skb_queue_tail(skb_pool, skb);
-	}
-}
-EXPORT_SYMBOL_GPL(refill_skbs);
-
 void netpoll_zap_completion_queue(void)
 {
 	unsigned long flags;

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 4/9] netconsole: move refill_skbs_work_handler() from netpoll
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

The work handler is wired via INIT_WORK() in
netconsole_skb_pool_init() and has no other callers since the
previous patch took the skb pool lifecycle out of __netpoll_setup().
Move the function body into drivers/net/netconsole.c as a
file-static helper, drop EXPORT_SYMBOL_GPL() and remove the
prototype from <linux/netpoll.h>.

Pure code motion: the body is unchanged and still calls the
exported refill_skbs() in net/core/netpoll.c.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 8 ++++++++
 include/linux/netpoll.h  | 1 -
 net/core/netpoll.c       | 9 ---------
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 1f75c4bbea8b6..96d9a47312cd7 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -292,6 +292,14 @@ static void netcons_release_dev(struct netconsole_target *nt)
 		memset(&nt->np.dev_name, 0, IFNAMSIZ);
 }
 
+static void refill_skbs_work_handler(struct work_struct *work)
+{
+	struct netpoll *np =
+		container_of(work, struct netpoll, refill_wq);
+
+	refill_skbs(np);
+}
+
 /* Seed the per-target skb pool that find_skb() falls back to. The queue
  * head and refill work are set up once in alloc_and_init(); this only
  * (re)fills the pool. Pair with netconsole_skb_pool_flush().
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index a7b96e1792207..51e5863d8e678 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -91,7 +91,6 @@ void do_netpoll_cleanup(struct netpoll *np);
 netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
 void netpoll_zap_completion_queue(void);
 void refill_skbs(struct netpoll *np);
-void refill_skbs_work_handler(struct work_struct *work);
 void skb_pool_flush(struct netpoll *np);
 
 #ifdef CONFIG_NETPOLL
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 58f30a4d5eb0f..93a16faf808c8 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -362,15 +362,6 @@ void skb_pool_flush(struct netpoll *np)
 }
 EXPORT_SYMBOL_GPL(skb_pool_flush);
 
-void refill_skbs_work_handler(struct work_struct *work)
-{
-	struct netpoll *np =
-		container_of(work, struct netpoll, refill_wq);
-
-	refill_skbs(np);
-}
-EXPORT_SYMBOL_GPL(refill_skbs_work_handler);
-
 int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
 {
 	struct netpoll_info *npinfo;

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 3/9] netconsole: take over skb pool lifecycle from netpoll
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

The fallback skb pool fronted by find_skb() is netconsole's only client:
every other netpoll goes through __netpoll_setup() / netpoll_send_skb()
without ever touching np->skb_pool.

Today __netpoll_setup() and __netpoll_cleanup() create and destroy the
pool for everyone, paying ~48 KB of pre-allocated skbs per netpoll
instance that only netconsole uses, what a waste!

Move the responsibility to netconsole. __netpoll_setup() did this under
the RTNL, but netconsole enables targets from enabled_store() /
alloc_param_target() without it, while the teardown path flushes the pool
(cancel_work_sync() + skb_queue_purge()) under the RTNL from
netconsole_process_cleanups_core(). Initialising the queue head and the
refill work on every enable would therefore race that flush.

They only need initialising once: after a flush the queue head is left
valid and empty and cancel_work_sync() leaves the work re-armable. Set
them up in alloc_and_init(), while the target is not yet reachable, and
let the enable paths only refill the pool via refill_skbs(), which
serialises with the flush through skb_pool.lock. See discussions in [1]

Link: https://lore.kernel.org/all/alDMvD5S7TZnoD_V@gmail.com/ [1]
Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++--
 net/core/netpoll.c       | 12 +----------
 2 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index a939daa07cf9d..1f75c4bbea8b6 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -292,11 +292,33 @@ static void netcons_release_dev(struct netconsole_target *nt)
 		memset(&nt->np.dev_name, 0, IFNAMSIZ);
 }
 
+/* Seed the per-target skb pool that find_skb() falls back to. The queue
+ * head and refill work are set up once in alloc_and_init(); this only
+ * (re)fills the pool. Pair with netconsole_skb_pool_flush().
+ */
+static void netconsole_skb_pool_init(struct netconsole_target *nt)
+{
+	refill_skbs(&nt->np);
+}
+
+static void netconsole_skb_pool_flush(struct netconsole_target *nt)
+{
+	skb_pool_flush(&nt->np);
+}
+
 /* Attempts to resume logging to a deactivated target. */
 static void resume_target(struct netconsole_target *nt)
 {
+	/* Initialise the skb pool before netpoll_setup() makes nt->np.dev
+	 * visible to target_list walkers (e.g. netconsole_netdev_event),
+	 * which otherwise may move the target to the cleanup list and
+	 * call netconsole_skb_pool_flush() on uninitialised state.
+	 */
+	netconsole_skb_pool_init(nt);
+
 	if (netpoll_setup(&nt->np)) {
 		/* netpoll fails setup once, do not try again. */
+		netconsole_skb_pool_flush(nt);
 		nt->state = STATE_DISABLED;
 		return;
 	}
@@ -358,6 +380,7 @@ static void process_resume_target(struct work_struct *work)
 	rtnl_lock();
 	if (nt->state == STATE_ENABLED && nt->np.dev &&
 	    nt->np.dev->reg_state != NETREG_REGISTERED) {
+		netconsole_skb_pool_flush(nt);
 		netcons_release_dev(nt);
 		nt->state = STATE_DISABLED;
 	}
@@ -398,6 +421,9 @@ static struct netconsole_target *alloc_and_init(void)
 	eth_broadcast_addr(nt->np.remote_mac);
 	nt->state = STATE_DISABLED;
 	INIT_WORK(&nt->resume_wq, process_resume_target);
+	/* Set up the skb pool primitives once; enabling only refills it. */
+	skb_queue_head_init(&nt->np.skb_pool);
+	INIT_WORK(&nt->np.refill_wq, refill_skbs_work_handler);
 
 	return nt;
 }
@@ -417,6 +443,7 @@ static void netconsole_process_cleanups_core(void)
 	list_for_each_entry_safe(nt, tmp, &target_cleanup_list, list) {
 		/* all entries in the cleanup_list needs to be disabled */
 		WARN_ON_ONCE(nt->state == STATE_ENABLED);
+		netconsole_skb_pool_flush(nt);
 		netcons_release_dev(nt);
 		/* moved the cleaned target to target_list. Need to hold both
 		 * locks
@@ -758,9 +785,19 @@ static ssize_t enabled_store(struct config_item *item,
 		 */
 		netconsole_print_banner(&nt->np);
 
+		/* Initialise the skb pool before netpoll_setup() so the pool
+		 * is valid as soon as nt->np.dev becomes visible to
+		 * target_list walkers (netconsole_netdev_event), which would
+		 * otherwise call netconsole_skb_pool_flush() on uninitialised
+		 * state.
+		 */
+		netconsole_skb_pool_init(nt);
+
 		ret = netpoll_setup(&nt->np);
-		if (ret)
+		if (ret) {
+			netconsole_skb_pool_flush(nt);
 			goto out_unlock;
+		}
 
 		nt->state = STATE_ENABLED;
 		pr_info("network logging started\n");
@@ -1514,8 +1551,10 @@ static void drop_netconsole_target(struct config_group *group,
 	 * netpoll_cleanup() is idempotent (it skips when np->dev is NULL), so
 	 * it is safe even if the cleanup worker already tore the netpoll down.
 	 */
-	if (needs_cleanup)
+	if (needs_cleanup) {
+		netconsole_skb_pool_flush(nt);
 		netpoll_cleanup(&nt->np);
+	}
 
 	config_item_put(&nt->group.cg_item);
 }
@@ -2330,10 +2369,18 @@ static struct netconsole_target *alloc_param_target(char *target_config,
 	if (err)
 		goto fail;
 
+	/* Initialise the skb pool before netpoll_setup() so the pool is
+	 * valid as soon as nt->np.dev becomes visible. The target is not
+	 * yet on target_list, so a netdev event cannot reach it here, but
+	 * mirror the configfs path for symmetry.
+	 */
+	netconsole_skb_pool_init(nt);
+
 	err = netpoll_setup(&nt->np);
 	if (err) {
 		pr_err("Not enabling netconsole for %s%d. Netpoll setup failed\n",
 		       NETCONSOLE_PARAM_TARGET_PREFIX, cmdline_count);
+		netconsole_skb_pool_flush(nt);
 		if (!IS_ENABLED(CONFIG_NETCONSOLE_DYNAMIC))
 			/* only fail if dynamic reconfiguration is set,
 			 * otherwise, keep the target in the list, but disabled.
@@ -2355,6 +2402,8 @@ static struct netconsole_target *alloc_param_target(char *target_config,
 static void free_param_target(struct netconsole_target *nt)
 {
 	cancel_work_sync(&nt->resume_wq);
+	if (nt->state == STATE_ENABLED)
+		netconsole_skb_pool_flush(nt);
 	netpoll_cleanup(&nt->np);
 #ifdef	CONFIG_NETCONSOLE_DYNAMIC
 	kfree(nt->userdata);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index e062d88d10a37..58f30a4d5eb0f 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -377,9 +377,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
 	const struct net_device_ops *ops;
 	int err;
 
-	skb_queue_head_init(&np->skb_pool);
-	INIT_WORK(&np->refill_wq, refill_skbs_work_handler);
-
 	if (ndev->priv_flags & IFF_DISABLE_NETPOLL) {
 		np_err(np, "%s doesn't support polling, aborting\n",
 		       ndev->name);
@@ -414,9 +411,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
 	np->dev = ndev;
 	strscpy(np->dev_name, ndev->name, IFNAMSIZ);
 
-	/* fill up the skb queue */
-	refill_skbs(np);
-
 	/* last thing to do is link it to the net device structure */
 	rcu_assign_pointer(ndev->npinfo, npinfo);
 
@@ -606,7 +600,7 @@ int netpoll_setup(struct netpoll *np)
 
 	err = __netpoll_setup(np, ndev);
 	if (err)
-		goto flush;
+		goto put;
 	rtnl_unlock();
 
 	/* Make sure all NAPI polls which started before dev->npinfo
@@ -617,8 +611,6 @@ int netpoll_setup(struct netpoll *np)
 
 	return 0;
 
-flush:
-	skb_pool_flush(np);
 put:
 	DEBUG_NET_WARN_ON_ONCE(np->dev);
 	if (ip_overwritten)
@@ -662,8 +654,6 @@ static void __netpoll_cleanup(struct netpoll *np)
 		disable_delayed_work_sync(&npinfo->tx_work);
 		call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info);
 	}
-
-	skb_pool_flush(np);
 }
 
 void __netpoll_free(struct netpoll *np)

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 2/9] netpoll: export refill_skbs(), refill_skbs_work_handler(), skb_pool_flush()
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

These three helpers manage the per-netpoll fallback skb pool. They
are file-static today because all of their callers live in
net/core/netpoll.c. Subsequent patches relocate the pool's owner
from struct netpoll to the only consumer that actually uses it
(netconsole), and that work needs netconsole to drive the helpers
directly while the function bodies still live here.

Drop static, add prototypes in <linux/netpoll.h>, and
EXPORT_SYMBOL_GPL() each. No behaviour change.

The exports are transitional. Each helper is moved into
drivers/net/netconsole.c later in this series, and at that point
its EXPORT_SYMBOL_GPL() and prototype are dropped. By the end of
the series no symbol introduced here remains exported.

The goal of this patch is to make the subsequente patches easy to
review.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 include/linux/netpoll.h | 3 +++
 net/core/netpoll.c      | 9 ++++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 88f7daa8560e5..a7b96e1792207 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -90,6 +90,9 @@ void netpoll_cleanup(struct netpoll *np);
 void do_netpoll_cleanup(struct netpoll *np);
 netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb);
 void netpoll_zap_completion_queue(void);
+void refill_skbs(struct netpoll *np);
+void refill_skbs_work_handler(struct work_struct *work);
+void skb_pool_flush(struct netpoll *np);
 
 #ifdef CONFIG_NETPOLL
 static inline void *netpoll_poll_lock(struct napi_struct *napi)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index aed415d3cd74f..e062d88d10a37 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -213,7 +213,7 @@ void netpoll_poll_enable(struct net_device *dev)
 		up(&ni->dev_lock);
 }
 
-static void refill_skbs(struct netpoll *np)
+void refill_skbs(struct netpoll *np)
 {
 	struct sk_buff_head *skb_pool;
 	struct sk_buff *skb;
@@ -228,6 +228,7 @@ static void refill_skbs(struct netpoll *np)
 		skb_queue_tail(skb_pool, skb);
 	}
 }
+EXPORT_SYMBOL_GPL(refill_skbs);
 
 void netpoll_zap_completion_queue(void)
 {
@@ -351,7 +352,7 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(netpoll_send_skb);
 
-static void skb_pool_flush(struct netpoll *np)
+void skb_pool_flush(struct netpoll *np)
 {
 	struct sk_buff_head *skb_pool;
 
@@ -359,14 +360,16 @@ static void skb_pool_flush(struct netpoll *np)
 	skb_pool = &np->skb_pool;
 	skb_queue_purge_reason(skb_pool, SKB_CONSUMED);
 }
+EXPORT_SYMBOL_GPL(skb_pool_flush);
 
-static void refill_skbs_work_handler(struct work_struct *work)
+void refill_skbs_work_handler(struct work_struct *work)
 {
 	struct netpoll *np =
 		container_of(work, struct netpoll, refill_wq);
 
 	refill_skbs(np);
 }
+EXPORT_SYMBOL_GPL(refill_skbs_work_handler);
 
 int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
 {

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 1/9] netconsole: clean up released targets dropped before the cleanup worker
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team
In-Reply-To: <20260710-netconsole_move_more-v3-0-6f63f76b28bc@debian.org>

drop_netconsole_target() might eventually tear down a target that
netconsole_netdev_event() had moved to target_cleanup_list but that
netconsole_process_cleanups_core() had not processed yet.

Always cleanup devices that eventually have a device attached to the
target, independent of the state.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
 drivers/net/netconsole.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index c1812a98365b7..a939daa07cf9d 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -1481,15 +1481,15 @@ static void drop_netconsole_target(struct config_group *group,
 
 	mutex_lock(&target_cleanup_list_lock);
 	spin_lock_irqsave(&target_list_lock, flags);
-	/* A STATE_DEACTIVATED target may have been moved to
-	 * target_cleanup_list by netconsole_netdev_event() but not yet
-	 * processed by netconsole_process_cleanups_core(). Unlinking it below
-	 * hides it from the cleanup worker, so this path has to clean it up
-	 * itself. Record that the target still owns a netpoll before the
-	 * state is downgraded.
+	/* A target moved to target_cleanup_list by netconsole_netdev_event()
+	 * but not yet processed still owns a netpoll; unlinking it below hides
+	 * it from the cleanup worker, so this path must tear it down itself.
+	 * This covers NETDEV_UNREGISTER (STATE_DEACTIVATED) and
+	 * NETDEV_RELEASE / NETDEV_JOIN (STATE_DISABLED); key off nt->np.dev,
+	 * which stays set until the netpoll is cleaned up.
 	 */
 	needs_cleanup = nt->state == STATE_ENABLED ||
-			nt->state == STATE_DEACTIVATED;
+			nt->state == STATE_DEACTIVATED || nt->np.dev;
 	/* Disable deactivated target to prevent races between resume attempt
 	 * and target removal.
 	 */

-- 
2.53.0-Meta


^ permalink raw reply related

* [PATCH net-next v3 0/9] netconsole: stop charging netpoll users for netconsole-only data
From: Breno Leitao @ 2026-07-10 11:38 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Andrew Lunn
  Cc: netdev, asantostc, gustavold, linux-kernel, Breno Leitao,
	kernel-team

This work continue to untangle netconsole and netpoll, improving
memory usage for netpoll users that are not netconsole.

struct netpoll is shared by every netpoll consumer in the tree:
bonding, bridge, team, vlan, macvlan, dsa and netconsole. The non-
netconsole users only need a per-port handle that lets them transmit
SKBs.

Yet struct netpoll has accumulated a pile of fields that exist only
to serve netconsole's printk path:

  - skb_pool / refill_wq: the fallback skb pool find_skb() falls back
    on when alloc_skb() returns NULL, plus the workqueue that refills
    it.
  - local_port / remote_port / remote_mac: the source and destination
    UDP ports and the destination ethernet address used to build
    outgoing log packets.

Every netpoll user that allocates a struct netpoll pays for all of
these fields, even though it never queues a single skb on the pool,
never sends a UDP frame and never looks at any of the addressing.

Move this netconsole-only state out of the shared struct into struct
netconsole_target, where its only consumer already lives.

After the series every passive netpoll consumer drops
sizeof(sk_buff_head) + sizeof(work_struct) + 2 * sizeof(u16) +
ETH_ALEN bytes per port (plus padding), and struct netpoll keeps only
what belongs to the generic poll/transmit abstraction or what netpoll
core still reads itself.

Follow-ups
----------
local_ip, remote_ip and ipv6 are still in struct netpoll. Moving the
addressing additionally requires relocating netpoll_setup() -- whose
only caller is netconsole -- and its IPv4/IPv6 helpers, so it is left
for a follow-up to keep this series focused on a single responsibility
transfer.

This is pure code motion with no functional change. The pre-existing
target/device teardown races reported against v1 have since been fixed
independently (as suggested by Jakub) and are already in the tree, so,
re-posting this one.

--

Changes in v3:
- Added a fix for a pre-existing cleanup-skip leak (released/joined targets).
- Added an skb-pool init-once patch closing an init/teardown race; rebased.
- Link to v2: https://lore.kernel.org/r/20260702-netconsole_move_more-v2-0-1ebedd921dcb@debian.org

Changes in v2:
- Rebase on net-next, now that the prerequisite netconsole
  teardown-race fixes are merged.
- Squash the local_port and remote_port moves into a single patch.
- v1: https://lore.kernel.org/r/20260524-netconsole_move_more-v1-0-909d1ab398b4@debian.org

To: David S. Miller <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Simon Horman <horms@kernel.org>
To: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: netdev@vger.kernel.org
Cc: asantostc@gmail.com
Cc: gustavold@gmail.com
Cc: linux-kernel@vger.kernel.org

Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Simon Horman <horms@kernel.org>
---
Breno Leitao (9):
      netconsole: clean up released targets dropped before the cleanup worker
      netpoll: export refill_skbs(), refill_skbs_work_handler(), skb_pool_flush()
      netconsole: take over skb pool lifecycle from netpoll
      netconsole: move refill_skbs_work_handler() from netpoll
      netconsole: move refill_skbs() and skb-pool sizing macros from netpoll
      netconsole: move skb_pool_flush() from netpoll
      netconsole: move skb_pool / refill_wq from struct netpoll to netconsole_target
      netconsole: move local_port / remote_port from struct netpoll to netconsole_target
      netconsole: move remote_mac from struct netpoll to netconsole_target

 drivers/net/netconsole.c | 199 +++++++++++++++++++++++++++++++++++------------
 include/linux/netpoll.h  |  18 -----
 net/core/netpoll.c       |  51 +-----------
 3 files changed, 152 insertions(+), 116 deletions(-)
---
base-commit: 34cf6dafc47441dfb6b356a095b89c3585a93714
change-id: 20260514-netconsole_move_more-888fde03dfc7

Best regards,
--  
Breno Leitao <leitao@debian.org>


^ permalink raw reply

* Re: [PATCH net v3] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
From: patchwork-bot+netdevbpf @ 2026-07-10 11:40 UTC (permalink / raw)
  To: Norbert Szetei
  Cc: netdev, andrew+netdev, davem, edumazet, kuba, pabeni,
	qingfang.deng, yuehaibing, gnault, kees, hataegu0826, linux-ppp,
	linux-kernel
In-Reply-To: <E793FCF2-58DE-4387-A983-C7B4BC3158BD@doyensec.com>

Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Mon, 6 Jul 2026 11:01:59 +0200 you wrote:
> pppol2tp_recv() runs in the L2TP UDP-encap softirq RX path:
> 
>  l2tp_udp_encap_recv() -> l2tp_recv_common() -> pppol2tp_recv()
>    -> ppp_input(&po->chan)
> 
> It runs under rcu_read_lock() holding only an l2tp_session reference and
> takes NO reference on the internal PPP channel (struct channel,
> chan->ppp) that ppp_input() dereferences.
> 
> [...]

Here is the summary with links:
  - [net,v3] ppp: defer channel free to an RCU grace period to fix pppol2tp RX UAF
    https://git.kernel.org/netdev/net/c/ec4215683e47

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox