Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v1 net-next] net: phy: Drop #inclusion of <linux/mod_devicetable.h> from <linux/mdio.h>
From: Uwe Kleine-König (The Capable Hub) @ 2026-07-06 20:48 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: Heiner Kallweit, Russell King, netdev, linux-kernel
In-Reply-To: <12ec5246-4124-4edb-a5f8-f10c73b1fcb7@lunn.ch>

[-- Attachment #1: Type: text/plain, Size: 2562 bytes --]

Hello Andrew,

On Mon, Jul 06, 2026 at 04:17:47PM +0200, Andrew Lunn wrote:
> On Mon, Jul 06, 2026 at 11:29:19AM +0200, Uwe Kleine-König (The Capable Hub) wrote:
> > <linux/mdio.h> itself doesn't use any of the device id structures. The
> > files #including <linux/mdio.h> use a variety of them:
> > 
> > 	$ git grep -l '<linux/mdio.h>' | xargs grep --color  -E '\<(acpi_device_id|amba_id|ap_device_id|apr_device_id|auxiliary_device_id|bcma_device_id|ccw_device_id|cdx_device_id|coreboot_device_id|css_device_id|dfl_device_id|dmi_(device|system)_id|eisa_device_id|fsl_mc_device_id|hda_device_id|hid_device_id|hv_vmbus_device_id|i2c_device_id|i3c_device_id|ieee1394_device_id|input_device_id|ipack_device_id|isapnp_device_id|ishtp_device_id|mcb_device_id|mdio_device_id|mei_cl_device_id|mhi_device_id|mips_cdmm_device_id|of_device_id|parisc_device_id|pci_device_id|pci_epf_device_id|pcmcia_device_id|platform_device_id|pnp_(card_)?device_id|rio_device_id|rpmsg_device_id|sdio_device_id|sdw_device_id|serio_device_id|slim_device_id|spi_device_id|spmi_device_id|ssam_device_id|ssb_device_id|tb_service_id|tee_client_device_id|typec_device_id|ulpi_device_id|usb_device_id|vchiq_device_id|virtio_device_id|wmi_device_id|x86_(cpu|device)_id|zorro_device_id|cpu_feature)\>'
> > 	...
> > 
> > but none of them relies on <linux/mdio.h>'s #include.
> > 
> > <linux/mod_devicetable.h> is a bad header mixing many different device
> > id structures and thus is an unfortunate dependency because if e.g.
> > struct wmi_device_id is modified all users of <linux/mdio.h> need to be
> > recompiled despite none of them using struct wmi_device_id.
> > 
> > So drop the unused header.
> 
> While i agree that there are lots of unneeded dependencies here, does
> this change actually solve anything? Pretty much every PHY driver
> needs mdio_device_id, which is part of mod_devicetable.h. So it must
> be getting included somehow.
> 
> I assume the long term plan is to move all the structure definitions
> out of mod_devicetable.h, so a driver can include just the ones it
> needs?

Yes, however not long term, but I intend to get most of this done for
7.3-rc1.

> Is this patch somehow a step along that path? Why not move
> mdio_device_id into mod_devicetable_mdio.h and include that? It seems
> like the more obvious fix.

That's exactly my thought, and there is commit ad428f5811bd
("mod_devicetable.h: Split into per subsystem headers") that is part of
v7.2-rc2 and that does exactly that (apart from the filename).

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH iwl] ice: acquire NVM lock around each flash read
From: Jacob Keller @ 2026-07-06 20:56 UTC (permalink / raw)
  To: Przemek Kitszel, Robert Malz
  Cc: intel-wired-lan, netdev, linux-kernel, Tony Nguyen, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Alexander Lobakin, Jesse Brandeburg
In-Reply-To: <65001d57-9abf-4129-8c7e-e7a5136ba5b3@intel.com>

On 7/3/2026 6:34 AM, Przemek Kitszel wrote:
> On 7/3/26 12:32, Robert Malz wrote:
>> FW caps the NVM read lock at a maximum of 3000ms regardless of the
>> timeout
>> requested via ice_acquire_nvm(). ice_read_flat_nvm() splits a read into
>> multiple ice_aq_read_nvm() commands, one per 4KB sector, all issued
>> under a
>> single lock taken by the caller. Reading a large region can exceed
>> 3000ms,
>> so FW reclaims the lock mid-read and the remaining commands might fail.
>>

Yikes.

>> Move the lock acquire/release into ice_read_flat_nvm() so it brackets
>> each
>> individual ice_aq_read_nvm() command, ensuring the lock is never held
>> across more than one FW read. ice_release_nvm() issues its own AQ command
>> and would overwrite sq_last_status, so the read's AQ error is preserved
>> across the release for callers such as ice_discover_flash_size() that
>> inspect it.
>>
>> Callers that previously took the lock around ice_read_flat_nvm(),
>> ice_read_sr_word() or ice_read_flash_module() now call them without it.
>> The per-block locking in ice_devlink_nvm_snapshot() is now redundant
>> and dropped.
>>

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>


>> Fixes: e94509906d6b ("ice: create function to read a section of the
>> NVM and Shadow RAM")
>> Signed-off-by: Robert Malz <robert.malz@canonical.com>
> 
> thank you for extra effort [1]
> current fix looks elegant!
> Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
> 
> [1] for reference, this is previous attempt for the fix:
> https://lore.kernel.org/intel-wired-lan/CADcc-
> bysA531q2Wh=TD_oFqxivLLdnCRNY5jy7mkZuO0cwJwvg@mail.gmail.com
> 
> [...]
> 
>>   /**
>> - * ice_read_sr_word - Reads Shadow RAM word and acquire NVM if necessary
>> + * ice_read_sr_word - Reads Shadow RAM word
>>    * @hw: pointer to the HW structure
>>    * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
>>    * @data: word read from the Shadow RAM
>>    *
>> - * Reads one 16 bit word from the Shadow RAM using the
>> ice_read_sr_word_aq.
>> + * Reads one 16 bit word from the Shadow RAM using ice_read_sr_word_aq.
>> + *
>> + * The NVM lock is acquired and released internally by
>> ice_read_flat_nvm()
>> + * around the FW read, so this function must be called without the
>> lock held.
>>    */
> 
> for future submissions would be great to "fix" kdoc warnings of touched
> functions, here "Return: " section is missing.
> I do not ask to fix this particular one (given there will be no ask for
> v2 otherwise).
> 
>>   int ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
>>   {
> 


^ permalink raw reply

* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Stanislav Fomichev @ 2026-07-06 21:02 UTC (permalink / raw)
  To: Mahe Tardy
  Cc: bpf, andrew+netdev, andrii, ast, daniel, davem, eddyz87, edumazet,
	john.fastabend, kuba, liamwisehart, martin.lau, pabeni, song,
	netdev
In-Reply-To: <akvlWB6AkP6kQ7xO@gmail.com>

On 07/06, Mahe Tardy wrote:
> On Mon, Jul 06, 2026 at 09:58:09AM -0700, Stanislav Fomichev wrote:
> > On 07/06, Mahe Tardy wrote:
> > > Add BPF kfuncs that allow BPF LSM programs to create and use sockets for
> > > sending data. This provides a mechanism for BPF programs to emit
> > > telemetry. For this first patch set, it's restricted to SOCK_DGRAM
> > > socket types with IPPROTO_UDP protocol but could be easily extended to
> > > SOCK_STREAM and IPPROTO_TCP in the future.
> > > 
> > > The API consists of six kfuncs:
> > > 
> > >   bpf_ksock_create()   - Create a socket (sleepable)
> > 
> > [..]
> > 
> > >   bpf_ksock_bind()     - Bind socket to local address (sleepable)
> > >   bpf_ksock_connect()  - Connect socket to remote address (sleepable)
> > 
> > Since you're doing only UDP for now, maybe you don't need bind/connect? The
> > kernel should autobind (by default) when you sendmsg over UDP socket (IIRC).
> 
> Yep indeed, I kinda overlooked that as I started with UDP & TCP supports
> and mostly added the args checks. Another thing is that send is simpler
> since only used on connected sockets, so you just pass the struct
> bpf_ksock and data. So on one side it would simplify the current
> UDP-only API for now by removing the kfuncs but we might need a more
> complex send kfunc (something like sendto).

Since you were targeting bpf_netpoll_send_udp originally, maybe sendto
is a better fit? You get the payload and the destination and you
bpf_sendto() it? We can later move to stateful bind/connect if needed.

(mostly coming from the pow of minimizing api exposure initially, but
not a strong preference)

> > 
> > >   bpf_ksock_send()     - Send data through the socket (sleepable)
> > >   bpf_ksock_acquire()  - Acquire a reference to a socket context
> > >   bpf_ksock_release()  - Release a reference (cleanup via
> > >                          queue_rcu_work since sock_release sleeps)
> > > 
> > > The setup kfuncs bpf_ksock_create, bpf_ksock_bind, bpf_ksock_connect,
> > > can be called from SYSCALL programs only. While bpf_ksock_acquire,
> > > bpf_ksock_release and bpf_ksock_send can be called from SYSCALL and LSM
> > > programs.
> > > 
> > > The implementation follows the established kfunc lifecycle pattern
> > > (create/acquire/release with refcounting, kptr map storage, dtor
> > > registration). The kernel socket is wrapped in a refcounted bpf_ksock
> > > struct. Cleanup is deferred via queue_rcu_work() because sock_release()
> > > may sleep.
> > > 
> > > The kfuncs are only compiled when CONFIG_INET is enabled, as they
> > > specifically support AF_INET and AF_INET6 sockets.
> > > 
> > > The socket operations go through the expected LSM hooks instead of
> > > by-passing them like many kernel sockets since those are created by BPF
> > > programs and thus system users. Thus bpf_ksock_send() kfunc, which is
> > > exposed to LSM progs, has a re-entering protection to avoid recursion.
> > > Also, because of the LSM checks, we prevent the use of the kfuncs from
> > > asynchronous workqueue as the current value would then be invalid.
> > 
> > [..]
> > 
> > > A bpf_ksock_max sysctl is added to limit the maximum number of BPF
> > > kernel sockets that may exist in each network namespace. Out of
> > > simplicity for now, the settings is host wide but the counters are per
> > > network namespace.
> > 
> > What is this guarding against? Rogue bpf programs creating too many sockets?
> 
> Yes. AI review raised this because users are prevented from creating too
> many sockets by bumping against the max number of fd and this would
> allow them to create way more sockets. I kind of agreed that having "a
> limit" on resource creation would make sense but maybe it doesn't and we
> can simplify this!

I believe even the kernel sockets go via lsm layer, so this enforcement
can be done in an lsm bpf program. Seems like that should be enough?

^ permalink raw reply

* Re: [PATCH net-next] selftests/net/openvswitch: add SCTP flow key test
From: Aaron Conole @ 2026-07-06 21:13 UTC (permalink / raw)
  To: Minxi Hou
  Cc: netdev, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
	horms, shuah, dev, linux-kselftest
In-Reply-To: <20260702090908.1253688-1-houminxi@gmail.com>

Hi Minxi,

Minxi Hou <houminxi@gmail.com> writes:

> Register OVS_KEY_ATTR_SCTP in the flow key parser so that sctp()
> can be used in flow specifications. The ovs_key_sctp class already
> exists (with src/dst fields matching the TCP/UDP siblings) but was
> not wired into the parser, so the token was silently dropped and the
> kernel rejected the flow.
>
> Add test_sctp_connect_v4 exercising the SCTP flow key with
> port-specific matching: sctp(dst=4443) for client-to-server and
> sctp(src=4443) for server-to-client, mirroring the SCTP four-way
> handshake direction. The test verifies connectivity with flows
> installed, confirms failure after flow removal, then reinstalls
> and verifies recovery.
>
> Signed-off-by: Minxi Hou <houminxi@gmail.com>
> ---

One issue I think will happen with this patch is that the other two
in-flight patches will also collide.  I'm thinking it may require
rebasing.

A better strategy going forward would be to bundle the new test
additions in one series.  That's just for going forward, do not resubmit
the existing patches I've acked unless a maintainer asks for it.

>  .../selftests/net/openvswitch/openvswitch.sh  | 92 +++++++++++++++++++
>  .../selftests/net/openvswitch/ovs-dpctl.py    |  5 +
>  2 files changed, 97 insertions(+)
>
> diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
> index 2954245129a2..dd4d0b3bcc23 100755
> --- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
> +++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
> @@ -32,6 +32,7 @@ tests="
>  	dec_ttl					ttl: dec_ttl decrements IP TTL
>  	flow_set				flow-set: Flow modify
>  	action_set				set: SET action rewrites fields
> +	sctp_connect_v4				sctp: SCTP flow key matching
>  	psample					psample: Sampling packets with psample"
>  
>  info() {
> @@ -443,6 +444,97 @@ test_action_set() {
>  	return 0
>  }
>  
> +# sctp_connect_v4 test
> +# - sctp(dst=4443) matches client-to-server INIT
> +# - sctp(src=4443) matches server-to-client INIT-ACK
> +# - remove flows and verify connection fails, reinstall and recover
> +test_sctp_connect_v4() {
> +	local t="test_sctp_connect_v4"
> +	which ncat >/dev/null 2>&1 || return $ksft_skip
> +	modprobe -q sctp 2>/dev/null || return $ksft_skip
> +
> +	sbx_add "$t" || return $?
> +	ovs_add_dp "$t" sctp4 || return 1
> +
> +	info "create namespaces"
> +	for ns in client server; do
> +		ovs_add_netns_and_veths "$t" "sctp4" "$ns" \
> +		    "${ns:0:1}0" "${ns:0:1}1" || return 1
> +	done
> +
> +	ip netns exec client ip addr add 172.31.110.10/24 dev c1
> +	ip netns exec client ip link set c1 up
> +	ip netns exec server ip addr add 172.31.110.20/24 dev s1
> +	ip netns exec server ip link set s1 up
> +
> +	# ARP forwarding
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(1),eth(),eth_type(0x0806),arp()' \
> +	    '2' || return 1
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(2),eth(),eth_type(0x0806),arp()' \
> +	    '1' || return 1
> +
> +	# SCTP port matching: dst for request, src for reply
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
> +	    '2' || return 1
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
> +	    '1' || return 1
> +
> +	echo "server" | \
> +		ovs_netns_spawn_daemon "$t" "server" \
> +				ncat --sctp -l 172.31.110.20 -vn 4443
> +	sleep 0.1

Instead of this pattern, we can probably do an ovs_wait on the Listening
string.  That way we aren't relying only on the sleep, but on the actual
Ncat output.  Something like

   ovs_wait sh -c
     "[ \$(grep 'Ncat: Listening on ....' ${ovs_dir}/stderr) ]" || \
     return 1

Or you could use a count and do '-ge <number>'

That way if the ncat spawn takes time to start up, it can be ready in
time.

> +	info "verify SCTP association with port-keyed flows"
> +	ovs_sbx "$t" ip netns exec client \
> +	    ncat --sctp -i 1 -zv 172.31.110.20 4443 \
> +	    || return 1
> +
> +	ovs_del_flows "$t" sctp4
> +
> +	info "verify connection fails without flows"
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(1),eth(),eth_type(0x0806),arp()' \
> +	    '2' || return 1
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(2),eth(),eth_type(0x0806),arp()' \
> +	    '1' || return 1
> +
> +	echo "server2" | \
> +		ovs_netns_spawn_daemon "$t" "server" \
> +				ncat --sctp -l 172.31.110.20 -vn 4443
> +	sleep 0.1
> +
> +	ovs_sbx "$t" ip netns exec client \
> +	    ncat --sctp -w 2 -zv 172.31.110.20 4443 \
> +	    >/dev/null 2>&1 \
> +	    && { info "FAIL: connection should fail without flows"
> +	         return 1; }
> +
> +	info "reinstall flows and verify recovery"
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(1),eth(),eth_type(0x0800),ipv4(proto=132),sctp(dst=4443)' \
> +	    '2' || return 1
> +	ovs_add_flow "$t" sctp4 \
> +	    'in_port(2),eth(),eth_type(0x0800),ipv4(proto=132),sctp(src=4443)' \
> +	    '1' || return 1
> +
> +	echo "server3" | \
> +		ovs_netns_spawn_daemon "$t" "server" \
> +				ncat --sctp -l 172.31.110.20 -vn 4443
> +	sleep 0.1
> +
> +	ovs_sbx "$t" ip netns exec client \
> +	    ncat --sctp -i 1 -zv 172.31.110.20 4443 \
> +	    || return 1
> +
> +	return 0
> +}
> +
>  # psample test
>  # - use psample to observe packets
>  test_psample() {
> diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> index e1ecfad2c03e..7cfc29ec7e59 100644
> --- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> +++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
> @@ -1982,6 +1982,11 @@ class ovskey(nla):
>                  "icmp",
>                  ovskey.ovs_key_icmp,
>              ),
> +            (
> +                "OVS_KEY_ATTR_SCTP",
> +                "sctp",
> +                ovskey.ovs_key_sctp,
> +            ),
>              (
>                  "OVS_KEY_ATTR_TCP_FLAGS",
>                  "tcp_flags",


^ permalink raw reply

* Re: [PATCH net v4] net: airoha: fix MIB stats collection to be lossless
From: Lorenzo Bianconi @ 2026-07-06 21:23 UTC (permalink / raw)
  To: Aniket Negi
  Cc: netdev, matthias.bgg, angelogioacchino.delregno, aniket.negi,
	kuldeep.malik, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Christian Marangi,
	linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <20260706154730.36949-1-aniket.negi03@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 17239 bytes --]

> REG_FE_GDM_MIB_CLEAR after every read creates a race window where
> packets arriving between read and clear are lost from statistics.
> 
> Switch to a delta-based approach instead:
> 
> - 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute
>   hardware total directly; use a local variable and max(new, old)
>   clamping to prevent intermediate visibility and torn-read regression.
> 
> - 32-bit registers (drops, bc, mc, errors, runt, long): accumulate
>   (u32)(curr - prev) into a 64-bit software counter; unsigned
>   subtraction handles wrap-around transparently.
> 
> - tx/rx_len[0] ([0,64] bucket): combines RUNT_CNT (32-bit, delta via
>   tx_runt/rx_runt) and E64_CNT (64-bit, absolute) into a single
>   assignment using a local accumulator to avoid double-counting.
> 
> Clear MIB counters once in airoha_fe_init() to establish a clean
> baseline, preventing spurious stats from pre-driver activity (kexec,
> driver rebind, warm reboot).
> 
> Merge airoha_dev_get_hw_stats() into airoha_update_hw_stats() and
> move stats_lock inside. Plain spin_lock() is correct: the function
> is only called from ndo_get_stats64() in process context. Each dev
> refreshes only its own MIB counters; sibling devs on a shared GDM3/4
> port are polled when their own netdev is queried.
> 
> Fixes: 8f4695fb67b2 ("net: airoha: better handle MIBs for GDM ports with multiple devs attached")
> Signed-off-by: Aniket Negi <aniket.negi03@gmail.com>
> Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> Changes in v4:
> - Add max(new, old) clamping for 64-bit H+L register pairs to ensure
>   monotonically non-decreasing stats despite torn reads between H and L
> - Use local variable (tmp) for all 64-bit H+L computations to prevent
>   lockless readers from seeing intermediate values during piecewise write
> - Add one-shot MIB counter clear in airoha_fe_init() to establish a
>   clean baseline (kexec, driver rebind, warm reboot)
> - Document sibling dev polling design in commit message
> 
> Changes in v3:
> - Link to V2: https://lore.kernel.org/20260701173941.314795-1-aniket.negi03@gmail.com/
> - Add Acked-by tag from Lorenzo
> - Rename from tx_runt_cnt to tx_runt, tx_long_cnt to tx_long,
>   tx_runt_accum64 to tx_runt64
> - Rename from rx_runt_cnt to rx_runt, rx_long_cnt to rx_long,
>   rx_runt_accum64 to rx_runt64
> - Condense the marked comments in V2, remove new line after comment
> 
> Changes in v2:
> - Store _CNT_L register reads in val before adding to stats, improving
>   readability (suggested by Lorenzo Bianconi)
> - Fix double-counting bug in the RUNT+E64 combined bucket: previously
>   "+=" for E64 re-added the full absolute counter each poll; now a
>   dedicated tx_runt_accum64/rx_runt_accum64 accumulator holds the
>   running RUNT delta, and tx_len[0] is assigned (not accumulated) each
>   poll as runt_accum64 + E64_abs
> - Replace 7-element tx_len[]/rx_len[] shadow arrays in mib_prev with
>   focused tx_runt_cnt/tx_long_cnt and rx_runt_cnt/rx_long_cnt fields;
>   only RUNT and LONG are 32-bit and need wrap-around tracking
> - Rename inner struct hw_prev_stats to mib_prev; rename accumulator
>   fields to tx_runt_accum64/rx_runt_accum64 for clarity
> - Fix comment alignment in mib_prev struct block
> - Rename airoha_dev_get_hw_stats() to airoha_update_hw_stats() and
>   move the port spin_lock inside, removing the separate wrapper
> 
>  drivers/net/ethernet/airoha/airoha_eth.c | 183 +++++++++++++++--------
>  drivers/net/ethernet/airoha/airoha_eth.h |  27 ++++
>  2 files changed, 144 insertions(+), 66 deletions(-)
> 
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 59001fd4b6f7..2d032cc6dfca 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -493,6 +493,8 @@ static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
>  
>  static int airoha_fe_init(struct airoha_eth *eth)
>  {
> +	int i;
> +
>  	airoha_fe_maccr_init(eth);
>  
>  	/* PSE IQ reserve */
> @@ -586,6 +588,14 @@ static int airoha_fe_init(struct airoha_eth *eth)
>  	/* enable 1:N vlan action, init vlan table */
>  	airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
>  
> +	/* Clear MIB counters to establish clean baseline for delta tracking.
> +	 * This prevents spurious statistics from pre-driver activity (e.g.,
> +	 * kexec, driver rebind, warm reboot) on first poll.
> +	 */
> +	for (i = AIROHA_GDM1_IDX; i <= AIROHA_GDM4_IDX; i++)
> +		airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(i),
> +			      FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);

This configuration is not needed since we reset FE at module load.

> +
>  	return airoha_fe_mc_vlan_clear(eth);
>  }
>  
> @@ -1686,11 +1696,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
>  	}
>  }
>  
> -static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
> +static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
>  {
>  	struct airoha_gdm_port *port = dev->port;
>  	struct airoha_eth *eth = dev->eth;
>  	u32 val, i = 0;
> +	u64 tmp, prev;

nit: tmp is not so meaningful, maybe better something like data?

> +
> +	spin_lock(&port->stats_lock);
>  
>  	/* Read relevant MIB for GDM with multiple port attached */
>  	if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
> @@ -1701,152 +1714,190 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
>  
>  	u64_stats_update_begin(&dev->stats.syncp);
>  
> -	/* TX */
> +	/* TX - 64-bit H+L registers: hw accumulates the total, read directly.
> +	 * Use local variable to prevent readers from seeing intermediate values.
> +	 * Clamp to prevent regression from torn reads between H and L.
> +	 */
> +	prev = dev->stats.tx_ok_pkts;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
> -	dev->stats.tx_ok_pkts += ((u64)val << 32);
> +	tmp = ((u64)val << 32);

nit: data = (u64)val << 32;

>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
> -	dev->stats.tx_ok_pkts += val;
> +	tmp |= val;

nit: I would prefer "+" instead of "|"

> +	dev->stats.tx_ok_pkts = max(tmp, prev);

nit: please drop prev and just do:
	dev->stats.tx_ok_pkts = max(data, dev->stats.tx_ok_pkts);

please redo it for all the occurrences.

Regards,
Lorenzo

>  
> +	prev = dev->stats.tx_ok_bytes;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
> -	dev->stats.tx_ok_bytes += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
> -	dev->stats.tx_ok_bytes += val;
> +	tmp |= val;
> +	dev->stats.tx_ok_bytes = max(tmp, prev);
>  
> +	/* TX - 32-bit registers: accumulate delta to handle wrap-around. */
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
> -	dev->stats.tx_drops += val;
> +	dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops);
> +	dev->stats.mib_prev.tx_drops = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
> -	dev->stats.tx_broadcast += val;
> +	dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast);
> +	dev->stats.mib_prev.tx_broadcast = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
> -	dev->stats.tx_multicast += val;
> +	dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast);
> +	dev->stats.mib_prev.tx_multicast = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
> -	dev->stats.tx_len[i] += val;
> +	dev->stats.mib_prev.tx_runt64 +=
> +		(u32)(val - dev->stats.mib_prev.tx_runt);
> +	dev->stats.mib_prev.tx_runt = val;
>  
> +	/* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
> +	tmp = dev->stats.mib_prev.tx_runt64;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp += ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp += val;
> +	dev->stats.tx_len[i++] = tmp;
>  
> +	prev = dev->stats.tx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.tx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.tx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.tx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.tx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.tx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.tx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.tx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.tx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
> -	dev->stats.tx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.tx_len[i++] = max(tmp, prev);
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
> -	dev->stats.tx_len[i++] += val;
> +	dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long);
> +	dev->stats.mib_prev.tx_long = val;
>  
>  	/* RX */
> +	prev = dev->stats.rx_ok_pkts;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
> -	dev->stats.rx_ok_pkts += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
> -	dev->stats.rx_ok_pkts += val;
> +	tmp |= val;
> +	dev->stats.rx_ok_pkts = max(tmp, prev);
>  
> +	prev = dev->stats.rx_ok_bytes;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
> -	dev->stats.rx_ok_bytes += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
> -	dev->stats.rx_ok_bytes += val;
> +	tmp |= val;
> +	dev->stats.rx_ok_bytes = max(tmp, prev);
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
> -	dev->stats.rx_drops += val;
> +	dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops);
> +	dev->stats.mib_prev.rx_drops = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
> -	dev->stats.rx_broadcast += val;
> +	dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast);
> +	dev->stats.mib_prev.rx_broadcast = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
> -	dev->stats.rx_multicast += val;
> +	dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast);
> +	dev->stats.mib_prev.rx_multicast = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
> -	dev->stats.rx_errors += val;
> +	dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors);
> +	dev->stats.mib_prev.rx_errors = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
> -	dev->stats.rx_crc_error += val;
> +	dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error);
> +	dev->stats.mib_prev.rx_crc_error = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
> -	dev->stats.rx_over_errors += val;
> +	dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors);
> +	dev->stats.mib_prev.rx_over_errors = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
> -	dev->stats.rx_fragment += val;
> +	dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment);
> +	dev->stats.mib_prev.rx_fragment = val;
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
> -	dev->stats.rx_jabber += val;
> +	dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber);
> +	dev->stats.mib_prev.rx_jabber = val;
>  
>  	i = 0;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
> -	dev->stats.rx_len[i] += val;
> +	dev->stats.mib_prev.rx_runt64 +=
> +		(u32)(val - dev->stats.mib_prev.rx_runt);
> +	dev->stats.mib_prev.rx_runt = val;
>  
> +	/* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
> +	tmp = dev->stats.mib_prev.rx_runt64;
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp += ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp += val;
> +	dev->stats.rx_len[i++] = tmp;
>  
> +	prev = dev->stats.rx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.rx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.rx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.rx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.rx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.rx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.rx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.rx_len[i++] = max(tmp, prev);
>  
> +	prev = dev->stats.rx_len[i];
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
> -	dev->stats.rx_len[i] += ((u64)val << 32);
> +	tmp = ((u64)val << 32);
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	tmp |= val;
> +	dev->stats.rx_len[i++] = max(tmp, prev);
>  
>  	val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
> -	dev->stats.rx_len[i++] += val;
> +	dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long);
> +	dev->stats.mib_prev.rx_long = val;
>  
>  	u64_stats_update_end(&dev->stats.syncp);
> -}
> -
> -static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
> -{
> -	struct airoha_gdm_port *port = dev->port;
> -	int i;
> -
> -	spin_lock(&port->stats_lock);
> -
> -	for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
> -		if (port->devs[i])
> -			airoha_dev_get_hw_stats(port->devs[i]);
> -	}
> -
> -	/* Reset MIB counters */
> -	airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
> -		      FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
>  
>  	spin_unlock(&port->stats_lock);
>  }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index f6d01a8e8da1..fe934f9ffe8a 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -245,6 +245,33 @@ struct airoha_hw_stats {
>  	u64 rx_fragment;
>  	u64 rx_jabber;
>  	u64 rx_len[7];
> +
> +	struct {
> +		/* Previous HW register values for 32-bit counter delta
> +		 * tracking. Storing the last seen value and accumulating
> +		 * (u32)(curr - prev) into the 64-bit software counter
> +		 * handles wrap-around transparently via unsigned arithmetic.
> +		 * tx_runt64/rx_runt64 hold the running sum of runt deltas.
> +		 * These fields are never reported to userspace.
> +		 */
> +		u32 tx_drops;
> +		u32 tx_broadcast;
> +		u32 tx_multicast;
> +		u32 tx_runt;
> +		u32 tx_long;
> +		u64 tx_runt64;
> +		u32 rx_drops;
> +		u32 rx_broadcast;
> +		u32 rx_multicast;
> +		u32 rx_errors;
> +		u32 rx_crc_error;
> +		u32 rx_over_errors;
> +		u32 rx_fragment;
> +		u32 rx_jabber;
> +		u32 rx_runt;
> +		u32 rx_long;
> +		u64 rx_runt64;
> +	} mib_prev;
>  };
>  
>  enum {
> 
> base-commit: a225f8c20712713406ae47024b8df42deacddd4a
> -- 
> 2.43.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
From: Xiang Mei (Microsoft) @ 2026-07-06 21:25 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Thomas Karlsson, netdev, linux-kernel, AutonomousCodeSecurity,
	tgopinath, kys, Xiang Mei (Microsoft)

The netlink policy for IFLA_MACVLAN_BC_QUEUE_LEN accepts any u32, and the
value becomes port->bc_queue_len_used, the only cap on how many skbs
macvlan_broadcast_enqueue() may queue on port->bc_queue. An unprivileged
user owning a user+net namespace (CAP_NET_ADMIN is checked only against
the target netns) can set it to 0xffffffff, so the backlog check never
trips and every broadcast frame is skb_clone()'d with GFP_ATOMIC and
queued. When RX softirq outpaces the bc_work drain (e.g. many ALLMULTI
macvlans under a broadcast flood), the queue grows unbounded and
OOMs/panics the host.

Bound the value with NLA_POLICY_MAX() at 4096, 4x the default of 1000.
This keeps headroom above the default while capping the backlog; values
above the cap are now rejected with -ERANGE at netlink parse time.

  Out of memory: Killed process 141 (su) UID:0
  Kernel panic - not syncing: System is deadlocked on memory
  Call Trace:
   vpanic (kernel/panic.c:650)
   panic (kernel/panic.c:787)
   out_of_memory (mm/oom_kill.c:1166)
   __alloc_frozen_pages_noprof (mm/page_alloc.c:4914)
   alloc_pages_mpol (mm/mempolicy.c:2490)
   folio_alloc_noprof (mm/mempolicy.c:2591)
   filemap_fault (mm/filemap.c:3565)

Fixes: d4bff72c8401 ("macvlan: Support for high multicast packet rate")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
---
 drivers/net/macvlan.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index c40fa331836b..d4cede6393b0 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -37,6 +37,7 @@
 #define MACVLAN_HASH_BITS	8
 #define MACVLAN_HASH_SIZE	(1<<MACVLAN_HASH_BITS)
 #define MACVLAN_DEFAULT_BC_QUEUE_LEN	1000
+#define MACVLAN_MAX_BC_QUEUE_LEN	4096
 
 #define MACVLAN_F_PASSTHRU	1
 #define MACVLAN_F_ADDRCHANGE	2
@@ -1755,7 +1756,7 @@ static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
 	[IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
 	[IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED },
 	[IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 },
-	[IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NLA_U32 },
+	[IFLA_MACVLAN_BC_QUEUE_LEN] = NLA_POLICY_MAX(NLA_U32, MACVLAN_MAX_BC_QUEUE_LEN),
 	[IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NLA_REJECT },
 	[IFLA_MACVLAN_BC_CUTOFF] = { .type = NLA_S32 },
 };
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v1 net-next] net: phy: Drop #inclusion of <linux/mod_devicetable.h> from <linux/mdio.h>
From: Andrew Lunn @ 2026-07-06 21:28 UTC (permalink / raw)
  To: Uwe Kleine-König (The Capable Hub)
  Cc: Heiner Kallweit, Russell King, netdev, linux-kernel
In-Reply-To: <akwToZyYvHZPtdjV@monoceros>

> That's exactly my thought, and there is commit ad428f5811bd
> ("mod_devicetable.h: Split into per subsystem headers") that is part of
> v7.2-rc2 and that does exactly that (apart from the filename).

Ah, O.K. net-next tends to great dragged along the -rcX versions. So
you can probably submit the networking changes in a weeks time. Just
make sure they apply to

https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git

	Andrew

^ permalink raw reply

* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Amery Hung @ 2026-07-06 21:33 UTC (permalink / raw)
  To: Mahe Tardy
  Cc: bpf, andrew+netdev, andrii, ast, daniel, davem, eddyz87, edumazet,
	john.fastabend, kuba, liamwisehart, martin.lau, pabeni, song,
	netdev
In-Reply-To: <20260706093525.13030-3-mahe.tardy@gmail.com>

On Mon, Jul 6, 2026 at 3:26 AM Mahe Tardy <mahe.tardy@gmail.com> wrote:
>
> Add BPF kfuncs that allow BPF LSM programs to create and use sockets for
> sending data. This provides a mechanism for BPF programs to emit
> telemetry. For this first patch set, it's restricted to SOCK_DGRAM
> socket types with IPPROTO_UDP protocol but could be easily extended to
> SOCK_STREAM and IPPROTO_TCP in the future.
>
> The API consists of six kfuncs:
>
>   bpf_ksock_create()   - Create a socket (sleepable)
>   bpf_ksock_bind()     - Bind socket to local address (sleepable)
>   bpf_ksock_connect()  - Connect socket to remote address (sleepable)
>   bpf_ksock_send()     - Send data through the socket (sleepable)
>   bpf_ksock_acquire()  - Acquire a reference to a socket context
>   bpf_ksock_release()  - Release a reference (cleanup via
>                          queue_rcu_work since sock_release sleeps)
>
> The setup kfuncs bpf_ksock_create, bpf_ksock_bind, bpf_ksock_connect,
> can be called from SYSCALL programs only. While bpf_ksock_acquire,
> bpf_ksock_release and bpf_ksock_send can be called from SYSCALL and LSM
> programs.
>
> The implementation follows the established kfunc lifecycle pattern
> (create/acquire/release with refcounting, kptr map storage, dtor
> registration). The kernel socket is wrapped in a refcounted bpf_ksock
> struct. Cleanup is deferred via queue_rcu_work() because sock_release()
> may sleep.
>
> The kfuncs are only compiled when CONFIG_INET is enabled, as they
> specifically support AF_INET and AF_INET6 sockets.
>
> The socket operations go through the expected LSM hooks instead of
> by-passing them like many kernel sockets since those are created by BPF
> programs and thus system users. Thus bpf_ksock_send() kfunc, which is
> exposed to LSM progs, has a re-entering protection to avoid recursion.
> Also, because of the LSM checks, we prevent the use of the kfuncs from
> asynchronous workqueue as the current value would then be invalid.

Are these the first BPF kfuncs that invoke (attachable) security
hooks? Some fs kfuncs seem to deliberately skip them. Can we avoid the
limiting recursion check by skipping the security hook in
bpf_ksock_send()? There is still security_socket_connect() in
bpf_ksock_connect() to prevent undesired send.

>
> A bpf_ksock_max sysctl is added to limit the maximum number of BPF
> kernel sockets that may exist in each network namespace. Out of
> simplicity for now, the settings is host wide but the counters are per
> network namespace.
>
> In bpf_ksock_create(), we copy the arg values to avoid TOCTOU races
> since the kfunc can sleep and the arg values could be stored in a map
> that could be re-written by BPF progs or even userspace programs if the
> map is mmaped.
>
> Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
> ---
>  Documentation/admin-guide/sysctl/net.rst |  12 +
>  include/linux/bpf_ksock.h                |  50 +++
>  kernel/bpf/verifier.c                    |   3 +
>  net/core/Makefile                        |   3 +
>  net/core/bpf_ksock.c                     | 516 +++++++++++++++++++++++
>  net/core/sysctl_net_core.c               |  11 +
>  6 files changed, 595 insertions(+)
>  create mode 100644 include/linux/bpf_ksock.h
>  create mode 100644 net/core/bpf_ksock.c
>
> diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst
> index e586e17fc7a5..8bcdc1bfd711 100644
> --- a/Documentation/admin-guide/sysctl/net.rst
> +++ b/Documentation/admin-guide/sysctl/net.rst
> @@ -128,6 +128,18 @@ compiler in order to reject unprivileged JIT requests once it has
>  been surpassed. bpf_jit_limit contains the value of the global limit
>  in bytes.
>
> +bpf_ksock_max
> +-------------
> +
> +Maximum number of BPF kernel sockets that may exist in each network namespace.
> +This host-wide setting is exposed in the initial network namespace and applies
> +the same limit independently to every network namespace. Sockets awaiting
> +deferred RCU and workqueue cleanup remain counted until the underlying socket
> +has been released. A value of 0 disables creation of BPF kernel sockets in
> +every network namespace.
> +
> +Default: 1024
> +
>  dev_weight
>  ----------
>
> diff --git a/include/linux/bpf_ksock.h b/include/linux/bpf_ksock.h
> new file mode 100644
> index 000000000000..0485b8eddc1f
> --- /dev/null
> +++ b/include/linux/bpf_ksock.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (c) 2026 Isovalent */
> +
> +#ifndef _BPF_KSOCK_H
> +#define _BPF_KSOCK_H
> +
> +#include <linux/types.h>
> +
> +#define BPF_KSOCK_MAX_DEFAULT 1024
> +
> +extern int sysctl_bpf_ksock_max;
> +
> +/**
> + * struct bpf_ksock_create_opts - BPF kernel socket creation parameters
> + * @family:    Address family: AF_INET or AF_INET6.
> + * @type:      Socket type: only SOCK_DGRAM supported for now.
> + * @protocol:  Protocol number (e.g. IPPROTO_UDP), or 0 for the default protocol
> + *             of the given type.
> + * @reserved:  Must be zero. Reserved for future use.
> + */
> +struct bpf_ksock_create_opts {
> +       __u8 family;
> +       __u8 type;
> +       __u8 protocol;
> +       __u8 reserved;
> +};
> +
> +/**
> + * struct bpf_ksock_addr_opts - BPF kernel socket address parameters
> + * @family:    Address family: AF_INET or AF_INET6.
> + * @reserved:  Must be zero. Reserved for future use.
> + * @port:      Port in host byte order.
> + * @scope_id:  IPv6 scope ID for scoped AF_INET6 addresses, or zero.
> + *             Must be zero when family=AF_INET.
> + * @ipv4_addr: IPv4 address in network byte order. Used when family=AF_INET.
> + * @ipv6_addr: IPv6 address (16 bytes, network byte order). Used when family=AF_INET6.
> + */
> +struct bpf_ksock_addr_opts {
> +       __u8 family;
> +       __u8 reserved;
> +       __u16 port;
> +       __u32 scope_id;
> +
> +       union {
> +               __be32 ipv4_addr;
> +               __u32 ipv6_addr[4]; /* in6_addr; network order */
> +       };
> +};
> +
> +#endif /* _BPF_KSOCK_H */
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 25aea4271cd0..52f6a89546e4 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -4424,6 +4424,9 @@ BTF_ID(struct, task_struct)
>  #ifdef CONFIG_CRYPTO
>  BTF_ID(struct, bpf_crypto_ctx)
>  #endif
> +#ifdef CONFIG_INET
> +BTF_ID(struct, bpf_ksock)
> +#endif
>  BTF_SET_END(rcu_protected_types)
>
>  static bool rcu_protected_object(const struct btf *btf, u32 btf_id)
> diff --git a/net/core/Makefile b/net/core/Makefile
> index b3fdcb4e355f..a9295b785901 100644
> --- a/net/core/Makefile
> +++ b/net/core/Makefile
> @@ -44,6 +44,9 @@ obj-$(CONFIG_FAILOVER) += failover.o
>  obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o
>  obj-$(CONFIG_BPF_SYSCALL) += sock_map.o
>  obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o
> +ifneq ($(CONFIG_INET),)
> +obj-$(CONFIG_BPF_SYSCALL) += bpf_ksock.o
> +endif
>  obj-$(CONFIG_OF)       += of_net.o
>  obj-$(CONFIG_NET_TEST) += net_test.o
>  obj-$(CONFIG_NET_DEVMEM) += devmem.o
> diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
> new file mode 100644
> index 000000000000..8bde734bc917
> --- /dev/null
> +++ b/net/core/bpf_ksock.c
> @@ -0,0 +1,516 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2026 Isovalent */
> +
> +#include <linux/bpf.h>
> +#include <linux/bpf_ksock.h>
> +#include <linux/btf.h>
> +#include <linux/btf_ids.h>
> +#include <linux/cache.h>
> +#include <linux/hash.h>
> +#include <linux/in.h>
> +#include <linux/in6.h>
> +#include <linux/list_bl.h>
> +#include <linux/net.h>
> +#include <linux/refcount.h>
> +#include <linux/rcupdate.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/socket.h>
> +#include <linux/unaligned.h>
> +#include <linux/workqueue.h>
> +#include <linux/ip.h>
> +#include <net/ipv6.h>
> +#include <net/net_namespace.h>
> +#include <net/netns/generic.h>
> +#include <net/sock.h>
> +
> +/**
> + * struct bpf_ksock - refcounted BPF kernel socket context
> + * @sock:      The underlying kernel socket.
> + * @usage:     Reference counter.
> + * @rwork:     RCU work for deferred cleanup (sock_release may sleep).
> + */
> +struct bpf_ksock {
> +       struct socket *sock;
> +       refcount_t usage;
> +       struct rcu_work rwork;
> +};
> +
> +struct bpf_ksock_send_guard {
> +       struct hlist_bl_node node;
> +       struct task_struct *task;
> +};
> +
> +#define BPF_KSOCK_SEND_GUARD_HASH_BITS 6
> +
> +struct bpf_ksock_send_bucket {
> +       struct hlist_bl_head head;
> +} ____cacheline_aligned_in_smp;
> +
> +static struct bpf_ksock_send_bucket
> +       bpf_ksock_send_buckets[1 << BPF_KSOCK_SEND_GUARD_HASH_BITS];
> +
> +static struct bpf_ksock_send_bucket *
> +bpf_ksock_send_bucket(const struct task_struct *task)
> +{
> +       u32 bucket_idx = hash_ptr(task, BPF_KSOCK_SEND_GUARD_HASH_BITS);
> +
> +       return &bpf_ksock_send_buckets[bucket_idx];
> +}
> +
> +static bool bpf_ksock_send_enter(struct bpf_ksock_send_guard *guard)
> +{
> +       struct bpf_ksock_send_guard *entry;
> +       struct hlist_bl_node *pos;
> +       struct bpf_ksock_send_bucket *bucket;
> +
> +       bucket = bpf_ksock_send_bucket(current);
> +       hlist_bl_lock(&bucket->head);
> +       hlist_bl_for_each_entry(entry, pos, &bucket->head, node) {
> +               if (entry->task == current) {
> +                       hlist_bl_unlock(&bucket->head);
> +                       return false;
> +               }
> +       }
> +
> +       guard->task = current;
> +       INIT_HLIST_BL_NODE(&guard->node);
> +       hlist_bl_add_head(&guard->node, &bucket->head);
> +       hlist_bl_unlock(&bucket->head);
> +       return true;
> +}
> +
> +static void bpf_ksock_send_exit(struct bpf_ksock_send_guard *guard)
> +{
> +       struct bpf_ksock_send_bucket *bucket;
> +
> +       bucket = bpf_ksock_send_bucket(guard->task);
> +       hlist_bl_lock(&bucket->head);
> +       hlist_bl_del(&guard->node);
> +       hlist_bl_unlock(&bucket->head);
> +}
> +
> +struct bpf_ksock_net {
> +       atomic_t count;
> +};
> +
> +static unsigned int bpf_ksock_net_id;
> +int sysctl_bpf_ksock_max __read_mostly = BPF_KSOCK_MAX_DEFAULT;
> +
> +static struct bpf_ksock_net *bpf_ksock_pernet(const struct net *net)
> +{
> +       return net_generic(net, bpf_ksock_net_id);
> +}
> +
> +static struct pernet_operations bpf_ksock_net_ops = {
> +       .id = &bpf_ksock_net_id,
> +       .size = sizeof(struct bpf_ksock_net),
> +};
> +
> +static bool bpf_ksock_net_try_charge(struct net *net)
> +{
> +       struct bpf_ksock_net *kn = bpf_ksock_pernet(net);
> +       int count = atomic_read(&kn->count);
> +       int max;
> +
> +       do {
> +               max = READ_ONCE(sysctl_bpf_ksock_max);
> +               if (count >= max)
> +                       return false;
> +       } while (!atomic_try_cmpxchg(&kn->count, &count, count + 1));
> +
> +       return true;
> +}
> +
> +static void bpf_ksock_net_uncharge(struct net *net)
> +{
> +       struct bpf_ksock_net *kn = bpf_ksock_pernet(net);
> +
> +       WARN_ON_ONCE(atomic_dec_return(&kn->count) < 0);
> +}
> +
> +static void ksock_release_work_fn(struct work_struct *work)
> +{
> +       struct bpf_ksock *ks =
> +               container_of(to_rcu_work(work), struct bpf_ksock, rwork);
> +       struct net *net = get_net(sock_net(ks->sock->sk));
> +
> +       sock_release(ks->sock);
> +       bpf_ksock_net_uncharge(net);
> +       put_net(net);
> +       kfree(ks);
> +}
> +
> +static int bpf_ksock_get_addr(const struct bpf_ksock_addr_opts *opts,
> +                             u32 opts__sz, struct sockaddr_storage *addr)
> +{
> +       struct bpf_ksock_addr_opts opts_copy;
> +
> +       if (!opts || opts__sz != sizeof(*opts))
> +               return -EINVAL;
> +
> +       /* Kfunc memory arguments are not guaranteed to be naturally aligned. */
> +       memcpy(&opts_copy, opts, sizeof(opts_copy));
> +
> +       if (opts_copy.reserved)
> +               return -EINVAL;
> +
> +       switch (opts_copy.family) {
> +       case AF_INET: {
> +               struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
> +
> +               if (opts_copy.scope_id)
> +                       return -EINVAL;
> +
> +               *addr4 = (struct sockaddr_in){
> +                       .sin_family = AF_INET,
> +                       .sin_port = htons(opts_copy.port),
> +                       .sin_addr.s_addr = opts_copy.ipv4_addr,
> +               };
> +               return sizeof(*addr4);
> +       }
> +#if IS_ENABLED(CONFIG_IPV6)
> +       case AF_INET6: {
> +               struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
> +
> +               *addr6 = (struct sockaddr_in6){
> +                       .sin6_family = AF_INET6,
> +                       .sin6_port = htons(opts_copy.port),
> +                       .sin6_scope_id = opts_copy.scope_id,
> +               };
> +               memcpy(&addr6->sin6_addr, opts_copy.ipv6_addr,
> +                      sizeof(struct in6_addr));
> +               return sizeof(*addr6);
> +       }
> +#endif
> +       default:
> +               return -EAFNOSUPPORT;
> +       }
> +}
> +
> +static bool bpf_ksock_has_user_task_context(void)
> +{
> +       /*
> +        * Task work can run from do_exit() after exit_nsproxy_namespaces()
> +        * cleared current->nsproxy, while current is still not a kthread.
> +        */
> +       return !(current->flags & PF_KTHREAD) && current->nsproxy;
> +}
> +
> +__bpf_kfunc_start_defs();
> +
> +/**
> + * bpf_ksock_create() - Create a BPF kernel socket.
> + *
> + * Allocates and creates a kernel socket.
> + * The socket is charged against the active network namespace's BPF kernel
> + * socket quota.
> + *
> + * The returned context must either be stored in a map as a kptr, or
> + * freed with bpf_ksock_release().
> + *
> + * This function may sleep (sock_create), so it can only be used
> + * in sleepable BPF programs (SYSCALL).
> + * It cannot be called from a BPF workqueue callback because that callback
> + * does not retain the invoking task's namespace or security context.
> + *
> + * @opts:      Pointer to struct bpf_ksock_create_opts with socket parameters.
> + * @opts__sz:  Size of the opts struct.
> + * @err__uninit:       Integer to store error code when NULL is returned.
> + */
> +__bpf_kfunc struct bpf_ksock *
> +bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,
> +                int *err__uninit)
> +{
> +       struct bpf_ksock_create_opts opts_copy;
> +       struct bpf_ksock *ks;
> +       struct net *net;
> +       int err;
> +
> +       /*
> +        * sock_create() derives the network namespace, credentials, and cgroup
> +        * from current. Kernel threads, including BPF workqueue callbacks, do
> +        * not carry the context of the task that invoked the BPF program.
> +        */
> +       if (!bpf_ksock_has_user_task_context()) {
> +               err = -EOPNOTSUPP;
> +               goto err_out;
> +       }
> +
> +       if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
> +               err = -EINVAL;
> +               goto err_out;
> +       }
> +
> +       opts_copy = (struct bpf_ksock_create_opts){
> +               .family = READ_ONCE(opts->family),
> +               .type = READ_ONCE(opts->type),
> +               .protocol = READ_ONCE(opts->protocol),
> +               .reserved = READ_ONCE(opts->reserved),
> +       };
> +
> +       if (opts_copy.reserved) {
> +               err = -EINVAL;
> +               goto err_out;
> +       }
> +
> +       if (opts_copy.family != AF_INET && opts_copy.family != AF_INET6) {
> +               err = -EAFNOSUPPORT;
> +               goto err_out;
> +       }
> +
> +       if (opts_copy.type != SOCK_DGRAM) {
> +               err = -EPROTONOSUPPORT;
> +               goto err_out;
> +       }
> +
> +       if (opts_copy.protocol != IPPROTO_UDP && opts_copy.protocol != 0) {
> +               err = -EPROTONOSUPPORT;
> +               goto err_out;
> +       }
> +
> +       ks = kzalloc_obj(*ks);
> +       if (!ks) {
> +               err = -ENOMEM;
> +               goto err_out;
> +       }
> +
> +       net = current->nsproxy->net_ns;
> +       if (!bpf_ksock_net_try_charge(net)) {
> +               err = -ENOSPC;
> +               goto err_free;
> +       }
> +
> +       /*
> +        * Use the normal current-task socket path so LSM/cgroup policy,
> +        * socket labels, and the active netns reference match a socket(2)
> +        * created by the BPF program's caller.
> +        */
> +       err = sock_create(opts_copy.family, opts_copy.type, opts_copy.protocol,
> +                         &ks->sock);
> +       if (err)
> +               goto err_uncharge;
> +
> +       ks->sock->sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
> +       ks->sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
> +
> +       refcount_set(&ks->usage, 1);
> +       put_unaligned(0, err__uninit);
> +       return ks;
> +
> +err_uncharge:
> +       bpf_ksock_net_uncharge(net);
> +err_free:
> +       kfree(ks);
> +err_out:
> +       put_unaligned(err, err__uninit);
> +       return NULL;
> +}
> +
> +/**
> + * bpf_ksock_bind() - Bind a BPF kernel socket to a local address.
> + * @ks:                The BPF kernel socket context.
> + * @opts:      Pointer to struct bpf_ksock_addr_opts with local address.
> + * @opts__sz:  Size of the opts struct.
> + *
> + * Binds the socket to the specified local address and port.
> + * This is optional; if not called, the kernel will auto-assign.
> + *
> + * This function may sleep while binding the socket, so it can only be used in
> + * sleepable BPF programs (SYSCALL).
> + *
> + * Return: 0 on success, negative errno on error.
> + */
> +__bpf_kfunc int bpf_ksock_bind(struct bpf_ksock *ks,
> +                              const struct bpf_ksock_addr_opts *opts,
> +                              u32 opts__sz)
> +{
> +       struct sockaddr_storage addr = {};
> +       int addrlen;
> +
> +       if (!bpf_ksock_has_user_task_context())
> +               return -EOPNOTSUPP;
> +
> +       addrlen = bpf_ksock_get_addr(opts, opts__sz, &addr);
> +       if (addrlen < 0)
> +               return addrlen;
> +
> +       return __sys_bind_socket(ks->sock, &addr, addrlen);
> +}
> +
> +/**
> + * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.
> + * @ks:                The BPF kernel socket context.
> + * @opts:      Pointer to struct bpf_ksock_addr_opts with remote address.
> + * @opts__sz:  Size of the opts struct.
> + *
> + * Connects the socket to the specified remote address and port.
> + *
> + * This function may sleep while connecting the socket, so it can only be used
> + * in sleepable BPF programs (SYSCALL).
> + *
> + * Return: 0 on success, negative errno on error.
> + */
> +__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,
> +                                 const struct bpf_ksock_addr_opts *opts,
> +                                 u32 opts__sz)
> +{
> +       struct sockaddr_storage addr = {};
> +       int addrlen;
> +
> +       if (!bpf_ksock_has_user_task_context())
> +               return -EOPNOTSUPP;
> +
> +       addrlen = bpf_ksock_get_addr(opts, opts__sz, &addr);
> +       if (addrlen < 0)
> +               return addrlen;
> +
> +       return __sys_connect_socket(ks->sock, &addr, addrlen, 0);
> +}
> +
> +/**
> + * bpf_ksock_acquire() - Acquire a reference to a BPF kernel socket.
> + * @ks:        The BPF kernel socket context to acquire. Must be a
> + *     trusted pointer (e.g. RCU-protected kptr from a map).
> + *
> + * The acquired context must either be stored in a map as a kptr, or
> + * freed with bpf_ksock_release().
> + */
> +__bpf_kfunc struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks)
> +{
> +       if (!refcount_inc_not_zero(&ks->usage))
> +               return NULL;
> +       return ks;
> +}
> +
> +/**
> + * bpf_ksock_release() - Release a BPF kernel socket.
> + * @ks:        The BPF kernel socket context to release.
> + *
> + * When the final reference is released, the socket is cleaned up via
> + * queue_rcu_work() (since sock_release may sleep).
> + */
> +__bpf_kfunc void bpf_ksock_release(struct bpf_ksock *ks)
> +{
> +       if (refcount_dec_and_test(&ks->usage)) {
> +               INIT_RCU_WORK(&ks->rwork, ksock_release_work_fn);
> +               queue_rcu_work(system_dfl_wq, &ks->rwork);
> +       }
> +}
> +
> +__bpf_kfunc void bpf_ksock_release_dtor(void *ks)
> +{
> +       bpf_ksock_release(ks);
> +}
> +CFI_NOSEAL(bpf_ksock_release_dtor);
> +
> +/**
> + * bpf_ksock_send() - Send data through a BPF kernel socket.
> + * @ks:                The BPF kernel socket context. Must be an acquired reference.
> + * @data:      Pointer to the data to send.
> + * @data__sz:  Size of the data to send (max 65535 bytes).
> + *
> + * Sends data on a connected socket, best-effort and nonblocking. This may sleep
> + * (kernel_sendmsg), so it can only be called from sleepable BPF programs.
> + *
> + * Return: Number of bytes sent on success, negative errno on error.
> + */
> +__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,
> +                              u32 data__sz)
> +{
> +       struct bpf_ksock_send_guard guard;
> +       struct msghdr msg = {
> +               .msg_flags = MSG_DONTWAIT,
> +       };
> +       struct kvec iov = {
> +               .iov_base = (void *)data,
> +               .iov_len = data__sz,
> +       };
> +       int ret;
> +
> +       if (!bpf_ksock_has_user_task_context())
> +               return -EOPNOTSUPP;
> +
> +       /* Early check for UDP. Exact limits enforced by kernel_sendmsg(). */
> +       if (data__sz > IP_MAX_MTU)
> +               return -EMSGSIZE;
> +
> +       if (!bpf_ksock_send_enter(&guard))
> +               return -EBUSY;
> +
> +       ret = kernel_sendmsg(ks->sock, &msg, &iov, 1, data__sz);
> +       bpf_ksock_send_exit(&guard);
> +
> +       return ret;
> +}
> +
> +__bpf_kfunc_end_defs();
> +
> +BTF_KFUNCS_START(ksock_init_kfunc_btf_ids)
> +BTF_ID_FLAGS(func, bpf_ksock_create, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_ksock_bind, KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_ksock_connect, KF_SLEEPABLE)
> +BTF_KFUNCS_END(ksock_init_kfunc_btf_ids)
> +
> +static const struct btf_kfunc_id_set ksock_init_kfunc_set = {
> +       .owner = THIS_MODULE,
> +       .set = &ksock_init_kfunc_btf_ids,
> +};
> +
> +BTF_KFUNCS_START(ksock_kfunc_btf_ids)
> +BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)
> +BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
> +BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)
> +BTF_KFUNCS_END(ksock_kfunc_btf_ids)
> +
> +static int bpf_ksock_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
> +{
> +       if (!btf_id_set8_contains(&ksock_kfunc_btf_ids, kfunc_id) ||
> +           prog->type == BPF_PROG_TYPE_SYSCALL ||
> +           prog->type == BPF_PROG_TYPE_LSM)
> +               return 0;
> +
> +       return -EACCES;
> +}
> +
> +static const struct btf_kfunc_id_set ksock_kfunc_set = {
> +       .owner = THIS_MODULE,
> +       .set = &ksock_kfunc_btf_ids,
> +       .filter = bpf_ksock_kfunc_filter,
> +};
> +
> +BTF_ID_LIST(bpf_ksock_dtor_ids)
> +BTF_ID(struct, bpf_ksock)
> +BTF_ID(func, bpf_ksock_release_dtor)
> +
> +static int __init bpf_ksock_kfunc_init(void)
> +{
> +       int ret;
> +       const struct btf_id_dtor_kfunc bpf_ksock_dtors[] = {
> +               {
> +                       .btf_id = bpf_ksock_dtor_ids[0],
> +                       .kfunc_btf_id = bpf_ksock_dtor_ids[1],
> +               },
> +       };
> +
> +       ret = register_pernet_subsys(&bpf_ksock_net_ops);
> +       if (ret)
> +               return ret;
> +
> +       ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
> +                                       &ksock_init_kfunc_set);
> +       if (ret) {
> +               unregister_pernet_subsys(&bpf_ksock_net_ops);
> +               return ret;
> +       }
> +
> +       ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL,
> +                                       &ksock_kfunc_set);
> +       ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
> +                                              &ksock_kfunc_set);
> +       return ret = ret ?: register_btf_id_dtor_kfuncs(bpf_ksock_dtors,
> +                                                ARRAY_SIZE(bpf_ksock_dtors),
> +                                                THIS_MODULE);
> +}
> +
> +late_initcall(bpf_ksock_kfunc_init);
> diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
> index b508618bfc12..d221a180c877 100644
> --- a/net/core/sysctl_net_core.c
> +++ b/net/core/sysctl_net_core.c
> @@ -7,6 +7,7 @@
>   */
>
>  #include <linux/filter.h>
> +#include <linux/bpf_ksock.h>
>  #include <linux/mm.h>
>  #include <linux/sysctl.h>
>  #include <linux/module.h>
> @@ -525,6 +526,16 @@ static struct ctl_table net_core_table[] = {
>                 .extra1         = SYSCTL_LONG_ONE,
>                 .extra2         = &bpf_jit_limit_max,
>         },
> +#endif
> +#if IS_ENABLED(CONFIG_BPF_SYSCALL) && IS_ENABLED(CONFIG_INET)
> +       {
> +               .procname       = "bpf_ksock_max",
> +               .data           = &sysctl_bpf_ksock_max,
> +               .maxlen         = sizeof(int),
> +               .mode           = 0644,
> +               .proc_handler   = proc_dointvec_minmax,
> +               .extra1         = SYSCTL_ZERO,
> +       },
>  #endif
>         {
>                 .procname       = "netdev_tstamp_prequeue",
> --
> 2.34.1
>
>

^ permalink raw reply

* Re: [PATCH v2 ethtool] man: ethtool: link 10000baseCR to SFF-8431, Appendix-E SFP+ DA
From: patchwork-bot+netdevbpf @ 2026-07-06 22:00 UTC (permalink / raw)
  To: D, H, Siddaraju
  Cc: mkubecek, andrew, maxime.chevallier, netdev, Shubham.Das,
	balaji.chintalapalle, vijay.srinivasan, magnus.k.lindberg,
	niklas.damberg, jonas.wirandi, siddarajudh
In-Reply-To: <20260703102004.1111597-1-siddaraju.dh@intel.com>

Hello:

This patch was applied to ethtool/ethtool.git (master)
by Michal Kubecek <mkubecek@suse.cz>:

On Fri,  3 Jul 2026 15:50:04 +0530 you wrote:
> Annotate the 10000baseCR entry in the advertise mask table so users
> understand what physical media this mode it actually represents.
> 
> 10000baseCR does not correspond to any IEEE 802.3 *base-CR PMD.
> It has no autonegotiation, no link training, and no mandatory FEC.
> The industry standard for this media type is SFF-8431 Appendix-E
> Direct Attach cable, also known as 10G_SFI_DA.
> 
> [...]

Here is the summary with links:
  - [v2,ethtool] man: ethtool: link 10000baseCR to SFF-8431, Appendix-E SFP+ DA
    https://git.kernel.org/pub/scm/network/ethtool/ethtool.git/commit/?id=b0d4a0d60471

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



^ permalink raw reply

* Re: [PATCHv2 net] net: emac: mal: replace devm_request_irq with request_irq to fix probe error race
From: Andrew Lunn @ 2026-07-06 22:14 UTC (permalink / raw)
  To: Rosen Penev
  Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, open list
In-Reply-To: <20260704033229.2447410-1-rosenp@gmail.com>

On Fri, Jul 03, 2026 at 08:32:29PM -0700, Rosen Penev wrote:
> devm_request_irq() is a managed resource: the IRQ is not freed until
> devres_release_all() runs after the probe function returns.  In the
> probe error path, free_netdev(mal->dummy_dev) and dcr_unmap() execute
> while the IRQ is still live.  If the shared IRQ fires during cleanup,
> the handler accesses unmapped DCR registers (crash) or the already-
> freed dummy_dev (use-after-free).
> 
> Switch to plain request_irq() with per-IRQ error labels that tear down
> only the IRQs that were successfully registered, and add the matching
> free_irq() calls in mal_remove().
> 
> Tested on Cisco Meraki MX60
> 
> Fixes: 14f59154ff0b ("net: ibm: emac: mal: use devm for request_irq")
> Assisted-by: opencode:big-pickle
> Signed-off-by: Rosen Penev <rosenp@gmail.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [PATCH net-next v14 0/9] tls: Add TLS 1.3 hardware offload support
From: Rishikesh Jethwani @ 2026-07-06 22:14 UTC (permalink / raw)
  To: Nils Juenemann
  Cc: borisp, davem, edumazet, john.fastabend, kuba, leon, mbloch,
	netdev, pabeni, saeedm, sd, tariqt
In-Reply-To: <20260627210635.89769-1-nils.juenemann@gmail.com>

On Sat, Jun 27, 2026 at 2:06 PM Nils Juenemann <nils.juenemann@gmail.com> wrote:
>
> Hi Rishikesh, all,
>
> thanks for picking up the sendfile/EOF fix in tls_device_splice_eof().
>
> Separate issue we hit while testing, looks pre-existing in mlx5e
> rather than v14: a NULL deref on the RX offload path. Trigger is
> ethtool -L <dev> combined 32 (down from 64) under HW-kTLS load,
> then a TLS_RX offload setup:
>
> BUG: unable to handle page fault for address: 00000000000031c0
> RIP: mlx5e_ktls_add_rx+0x268 [mlx5_core]
> Call Trace:
> mlx5e_ktls_add
> tls_device_dev_add_rx
> tls_set_device_offload_rx
> tls_setsockopt
>
> sk_rx_queue_get(sk) returns a stale rxq (62 here) after the
> reduction; mlx5e_ktls_sk_get_rxq() only guards rxq == -1, so
> mlx5e_ktls_add_rx() dereferences priv->channels.c[62], which
> appears to be NULL. The resync paths index
> priv->channels.c[priv_rx->rxq] the same way. This path is not
> TLS 1.3 specific.
>
> I do not have a proposed fix yet. Happy to share the full
> decoded oops / additional kdump details or test patches.
>
> Thanks again,
> Nils Juenemann

Following two patches should fix this issue:

Patch 1: net/mlx5e: kTLS, Reject stale RX queue mapping on offload setup
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
index bca45679e201..232e998a8f24 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
@@ -620,12 +620,15 @@ void mlx5e_ktls_handle_ctx_completion(struct
mlx5e_icosq_wqe_info *wi)
        queue_work(rule->priv->tls->rx_wq, &rule->work);
 }

-static int mlx5e_ktls_sk_get_rxq(struct sock *sk)
+static int mlx5e_ktls_sk_get_rxq(struct mlx5e_priv *priv, struct sock *sk)
 {
        int rxq = sk_rx_queue_get(sk);

        if (unlikely(rxq == -1))
-               rxq = 0;
+               return 0;
+
+       if (unlikely(rxq >= priv->channels.num))
+               return -EINVAL;

        return rxq;
 }
@@ -673,7 +676,11 @@ int mlx5e_ktls_add_rx(struct net_device *netdev,
struct sock *sk,
        INIT_LIST_HEAD(&priv_rx->list);
        spin_lock_init(&priv_rx->lock);

-       rxq = mlx5e_ktls_sk_get_rxq(sk);
+       rxq = mlx5e_ktls_sk_get_rxq(priv, sk);
+       if (unlikely(rxq < 0)) {
+               err = rxq;
+               goto err_create_tir;
+       }
        priv_rx->rxq = rxq;
        priv_rx->sk = sk;


Patch2: net/mlx5e: kTLS, Guard resync paths against stale channel index
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
index 232e998a8f24..c0676148a36e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
@@ -343,6 +343,13 @@ static void resync_handle_work(struct work_struct *work)
                return;
        }

+       if (unlikely(priv_rx->rxq >= resync->priv->channels.num)) {
+               priv_rx->rq_stats->tls_resync_req_skip++;
+               tls_offload_rx_resync_async_request_cancel(&resync->core);
+               mlx5e_ktls_priv_rx_put(priv_rx);
+               return;
+       }
+
        c = resync->priv->channels.c[priv_rx->rxq];
        sq = c->async_icosq;

@@ -568,6 +575,10 @@ void mlx5e_ktls_rx_resync(struct net_device
*netdev, struct sock *sk,
        resync->seq = seq;

        priv = netdev_priv(netdev);
+       if (unlikely(priv_rx->rxq >= priv->channels.num)) {
+               priv_rx->rq_stats->tls_resync_req_skip++;
+               return;
+       }
        c = priv->channels.c[priv_rx->rxq];

        resync_handle_seq_match(priv_rx, c);

^ permalink raw reply related

* Re: [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
From: Xiang Mei @ 2026-07-06 22:34 UTC (permalink / raw)
  To: Thomas Karlsson
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	AutonomousCodeSecurity@microsoft.com,
	tgopinath@linux.microsoft.com, kys@microsoft.com
In-Reply-To: <OS4P279MB0403BDB88C2B7F4D4939DCC4EAF12@OS4P279MB0403.NORP279.PROD.OUTLOOK.COM>

On Mon, Jul 6, 2026 at 3:31 PM Thomas Karlsson
<thomas.karlsson@paneda.se> wrote:
>
> Dear all,
>
> While restricting a lower max than MAX U32 may be warranted the proposed limit of 4096 is way too small for high throughput multicast traffic.
>
> We currently run many production servers with the BC queue len set to a value of 100 000.
>

Thanks, Thomas, for your feedback. 4096 could be small.
We would like to hear your suggested values, and we would like to do
some tests with the value you suggest.

Xiang


>
> Best regards,
> Thomas Karlsson
>
>
> Sent from Outlook for Android
> ________________________________
> From: Xiang Mei (Microsoft) <xmei5@asu.edu>
> Sent: Monday, 06 July 2026 23:25:56
> To: Andrew Lunn <andrew+netdev@lunn.ch>; David S . Miller <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>
> Cc: Thomas Karlsson <thomas.karlsson@paneda.se>; netdev@vger.kernel.org <netdev@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>; AutonomousCodeSecurity@microsoft.com <AutonomousCodeSecurity@microsoft.com>; tgopinath@linux.microsoft.com <tgopinath@linux.microsoft.com>; kys@microsoft.com <kys@microsoft.com>; Xiang Mei (Microsoft) <xmei5@asu.edu>
> Subject: [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
>
> The netlink policy for IFLA_MACVLAN_BC_QUEUE_LEN accepts any u32, and the
> value becomes port->bc_queue_len_used, the only cap on how many skbs
> macvlan_broadcast_enqueue() may queue on port->bc_queue. An unprivileged
> user owning a user+net namespace (CAP_NET_ADMIN is checked only against
> the target netns) can set it to 0xffffffff, so the backlog check never
> trips and every broadcast frame is skb_clone()'d with GFP_ATOMIC and
> queued. When RX softirq outpaces the bc_work drain (e.g. many ALLMULTI
> macvlans under a broadcast flood), the queue grows unbounded and
> OOMs/panics the host.
>
> Bound the value with NLA_POLICY_MAX() at 4096, 4x the default of 1000.
> This keeps headroom above the default while capping the backlog; values
> above the cap are now rejected with -ERANGE at netlink parse time.
>
>   Out of memory: Killed process 141 (su) UID:0
>   Kernel panic - not syncing: System is deadlocked on memory
>   Call Trace:
>    vpanic (kernel/panic.c:650)
>    panic (kernel/panic.c:787)
>    out_of_memory (mm/oom_kill.c:1166)
>    __alloc_frozen_pages_noprof (mm/page_alloc.c:4914)
>    alloc_pages_mpol (mm/mempolicy.c:2490)
>    folio_alloc_noprof (mm/mempolicy.c:2591)
>    filemap_fault (mm/filemap.c:3565)
>
> Fixes: d4bff72c8401 ("macvlan: Support for high multicast packet rate")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> ---
>  drivers/net/macvlan.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
> index c40fa331836b..d4cede6393b0 100644
> --- a/drivers/net/macvlan.c
> +++ b/drivers/net/macvlan.c
> @@ -37,6 +37,7 @@
>  #define MACVLAN_HASH_BITS       8
>  #define MACVLAN_HASH_SIZE       (1<<MACVLAN_HASH_BITS)
>  #define MACVLAN_DEFAULT_BC_QUEUE_LEN    1000
> +#define MACVLAN_MAX_BC_QUEUE_LEN       4096
>
>  #define MACVLAN_F_PASSTHRU      1
>  #define MACVLAN_F_ADDRCHANGE    2
> @@ -1755,7 +1756,7 @@ static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
>          [IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
>          [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED },
>          [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 },
> -       [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NLA_U32 },
> +       [IFLA_MACVLAN_BC_QUEUE_LEN] = NLA_POLICY_MAX(NLA_U32, MACVLAN_MAX_BC_QUEUE_LEN),
>          [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NLA_REJECT },
>          [IFLA_MACVLAN_BC_CUTOFF] = { .type = NLA_S32 },
>  };
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Kuniyuki Iwashima @ 2026-07-06 22:50 UTC (permalink / raw)
  To: sdf.kernel
  Cc: andrew+netdev, andrii, ast, bpf, daniel, davem, eddyz87, edumazet,
	john.fastabend, kuba, liamwisehart, mahe.tardy, martin.lau,
	netdev, pabeni, song
In-Reply-To: <akwTnnSGJpki9hAn@devvm7509.cco0.facebook.com>

From: Stanislav Fomichev <sdf.kernel@gmail.com>
Date: Mon, 6 Jul 2026 14:02:39 -0700
> On 07/06, Mahe Tardy wrote:
> > On Mon, Jul 06, 2026 at 09:58:09AM -0700, Stanislav Fomichev wrote:
> > > On 07/06, Mahe Tardy wrote:
> > > > Add BPF kfuncs that allow BPF LSM programs to create and use sockets for
> > > > sending data. This provides a mechanism for BPF programs to emit
> > > > telemetry. For this first patch set, it's restricted to SOCK_DGRAM
> > > > socket types with IPPROTO_UDP protocol but could be easily extended to
> > > > SOCK_STREAM and IPPROTO_TCP in the future.
> > > > 
> > > > The API consists of six kfuncs:
> > > > 
> > > >   bpf_ksock_create()   - Create a socket (sleepable)
> > > 
> > > [..]
> > > 
> > > >   bpf_ksock_bind()     - Bind socket to local address (sleepable)
> > > >   bpf_ksock_connect()  - Connect socket to remote address (sleepable)
> > > 
> > > Since you're doing only UDP for now, maybe you don't need bind/connect? The
> > > kernel should autobind (by default) when you sendmsg over UDP socket (IIRC).
> > 
> > Yep indeed, I kinda overlooked that as I started with UDP & TCP supports
> > and mostly added the args checks. Another thing is that send is simpler
> > since only used on connected sockets, so you just pass the struct
> > bpf_ksock and data. So on one side it would simplify the current
> > UDP-only API for now by removing the kfuncs but we might need a more
> > complex send kfunc (something like sendto).
> 
> Since you were targeting bpf_netpoll_send_udp originally, maybe sendto
> is a better fit? You get the payload and the destination and you
> bpf_sendto() it? We can later move to stateful bind/connect if needed.
> 
> (mostly coming from the pow of minimizing api exposure initially, but
> not a strong preference)

+1, small start would be better.


> 
> > > 
> > > >   bpf_ksock_send()     - Send data through the socket (sleepable)
> > > >   bpf_ksock_acquire()  - Acquire a reference to a socket context
> > > >   bpf_ksock_release()  - Release a reference (cleanup via
> > > >                          queue_rcu_work since sock_release sleeps)
> > > > 
> > > > The setup kfuncs bpf_ksock_create, bpf_ksock_bind, bpf_ksock_connect,
> > > > can be called from SYSCALL programs only. While bpf_ksock_acquire,
> > > > bpf_ksock_release and bpf_ksock_send can be called from SYSCALL and LSM
> > > > programs.
> > > > 
> > > > The implementation follows the established kfunc lifecycle pattern
> > > > (create/acquire/release with refcounting, kptr map storage, dtor
> > > > registration). The kernel socket is wrapped in a refcounted bpf_ksock
> > > > struct. Cleanup is deferred via queue_rcu_work() because sock_release()
> > > > may sleep.
> > > > 
> > > > The kfuncs are only compiled when CONFIG_INET is enabled, as they
> > > > specifically support AF_INET and AF_INET6 sockets.
> > > > 
> > > > The socket operations go through the expected LSM hooks instead of
> > > > by-passing them like many kernel sockets since those are created by BPF
> > > > programs and thus system users. Thus bpf_ksock_send() kfunc, which is
> > > > exposed to LSM progs, has a re-entering protection to avoid recursion.
> > > > Also, because of the LSM checks, we prevent the use of the kfuncs from
> > > > asynchronous workqueue as the current value would then be invalid.
> > > 
> > > [..]
> > > 
> > > > A bpf_ksock_max sysctl is added to limit the maximum number of BPF
> > > > kernel sockets that may exist in each network namespace. Out of
> > > > simplicity for now, the settings is host wide but the counters are per
> > > > network namespace.
> > > 
> > > What is this guarding against? Rogue bpf programs creating too many sockets?
> > 
> > Yes. AI review raised this because users are prevented from creating too
> > many sockets by bumping against the max number of fd and this would
> > allow them to create way more sockets. I kind of agreed that having "a
> > limit" on resource creation would make sense but maybe it doesn't and we
> > can simplify this!
> 
> I believe even the kernel sockets go via lsm layer, so this enforcement
> can be done in an lsm bpf program. Seems like that should be enough?

Right, I don't think the per-netns limit is useful for CAP_BPF users.

^ permalink raw reply

* Re: [PATCH bpf-next 2/6] bpf: Add ksock kfuncs
From: Kuniyuki Iwashima @ 2026-07-06 23:01 UTC (permalink / raw)
  To: mahe.tardy
  Cc: andrew+netdev, andrii, ast, bpf, daniel, davem, eddyz87, edumazet,
	john.fastabend, kuba, liamwisehart, martin.lau, netdev, pabeni,
	song
In-Reply-To: <20260706093525.13030-3-mahe.tardy@gmail.com>

From: Mahe Tardy <mahe.tardy@gmail.com>
Date: Mon,  6 Jul 2026 09:35:21 +0000
> diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
[...]
> +static bool bpf_ksock_send_enter(struct bpf_ksock_send_guard *guard)

Can we do this better with assigning a single bit to task_struct ?


> +{
> +	struct bpf_ksock_send_guard *entry;
> +	struct hlist_bl_node *pos;
> +	struct bpf_ksock_send_bucket *bucket;
> +
> +	bucket = bpf_ksock_send_bucket(current);
> +	hlist_bl_lock(&bucket->head);
> +	hlist_bl_for_each_entry(entry, pos, &bucket->head, node) {
> +		if (entry->task == current) {
> +			hlist_bl_unlock(&bucket->head);
> +			return false;
> +		}
> +	}
> +
> +	guard->task = current;
> +	INIT_HLIST_BL_NODE(&guard->node);
> +	hlist_bl_add_head(&guard->node, &bucket->head);
> +	hlist_bl_unlock(&bucket->head);
> +	return true;
> +}
> +
> +static void bpf_ksock_send_exit(struct bpf_ksock_send_guard *guard)
> +{
> +	struct bpf_ksock_send_bucket *bucket;
> +
> +	bucket = bpf_ksock_send_bucket(guard->task);
> +	hlist_bl_lock(&bucket->head);
> +	hlist_bl_del(&guard->node);
> +	hlist_bl_unlock(&bucket->head);
> +}
[...]
> +__bpf_kfunc struct bpf_ksock *
> +bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz,
> +		 int *err__uninit)
> +{
> +	struct bpf_ksock_create_opts opts_copy;
> +	struct bpf_ksock *ks;
> +	struct net *net;
> +	int err;
> +
> +	/*
> +	 * sock_create() derives the network namespace, credentials, and cgroup
> +	 * from current. Kernel threads, including BPF workqueue callbacks, do
> +	 * not carry the context of the task that invoked the BPF program.
> +	 */
> +	if (!bpf_ksock_has_user_task_context()) {
> +		err = -EOPNOTSUPP;
> +		goto err_out;
> +	}
> +
> +	if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
> +		err = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	opts_copy = (struct bpf_ksock_create_opts){
> +		.family = READ_ONCE(opts->family),
> +		.type = READ_ONCE(opts->type),
> +		.protocol = READ_ONCE(opts->protocol),
> +		.reserved = READ_ONCE(opts->reserved),
> +	};
> +
> +	if (opts_copy.reserved) {
> +		err = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	if (opts_copy.family != AF_INET && opts_copy.family != AF_INET6) {
> +		err = -EAFNOSUPPORT;
> +		goto err_out;
> +	}
> +
> +	if (opts_copy.type != SOCK_DGRAM) {
> +		err = -EPROTONOSUPPORT;
> +		goto err_out;
> +	}
> +
> +	if (opts_copy.protocol != IPPROTO_UDP && opts_copy.protocol != 0) {
> +		err = -EPROTONOSUPPORT;
> +		goto err_out;
> +	}
> +
> +	ks = kzalloc_obj(*ks);
> +	if (!ks) {
> +		err = -ENOMEM;
> +		goto err_out;
> +	}
> +
> +	net = current->nsproxy->net_ns;
> +	if (!bpf_ksock_net_try_charge(net)) {
> +		err = -ENOSPC;
> +		goto err_free;
> +	}
> +
> +	/*
> +	 * Use the normal current-task socket path so LSM/cgroup policy,
> +	 * socket labels, and the active netns reference match a socket(2)
> +	 * created by the BPF program's caller.
> +	 */
> +	err = sock_create(opts_copy.family, opts_copy.type, opts_copy.protocol,
> +			  &ks->sock);

This also triggers LSM in __sock_create() so needs the loop
detection, no ?

Same for __sys_bind_socket() and __sys_connect_socket().


> +	if (err)
> +		goto err_uncharge;
> +
> +	ks->sock->sk->sk_rcvbuf = SOCK_MIN_RCVBUF;
> +	ks->sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
> +
> +	refcount_set(&ks->usage, 1);
> +	put_unaligned(0, err__uninit);
> +	return ks;
> +
> +err_uncharge:
> +	bpf_ksock_net_uncharge(net);
> +err_free:
> +	kfree(ks);
> +err_out:
> +	put_unaligned(err, err__uninit);
> +	return NULL;
> +}
> +
> +/**
> + * bpf_ksock_bind() - Bind a BPF kernel socket to a local address.
> + * @ks:		The BPF kernel socket context.
> + * @opts:	Pointer to struct bpf_ksock_addr_opts with local address.
> + * @opts__sz:	Size of the opts struct.
> + *
> + * Binds the socket to the specified local address and port.
> + * This is optional; if not called, the kernel will auto-assign.
> + *
> + * This function may sleep while binding the socket, so it can only be used in
> + * sleepable BPF programs (SYSCALL).
> + *
> + * Return: 0 on success, negative errno on error.
> + */
> +__bpf_kfunc int bpf_ksock_bind(struct bpf_ksock *ks,
> +			       const struct bpf_ksock_addr_opts *opts,
> +			       u32 opts__sz)
> +{
> +	struct sockaddr_storage addr = {};

nit: we don't need to initialise here since bpf_ksock_get_addr() does.


> +	int addrlen;
> +
> +	if (!bpf_ksock_has_user_task_context())
> +		return -EOPNOTSUPP;
> +
> +	addrlen = bpf_ksock_get_addr(opts, opts__sz, &addr);
> +	if (addrlen < 0)
> +		return addrlen;
> +
> +	return __sys_bind_socket(ks->sock, &addr, addrlen);
> +}
> +
> +/**
> + * bpf_ksock_connect() - Connect a BPF kernel socket to a remote address.
> + * @ks:		The BPF kernel socket context.
> + * @opts:	Pointer to struct bpf_ksock_addr_opts with remote address.
> + * @opts__sz:	Size of the opts struct.
> + *
> + * Connects the socket to the specified remote address and port.
> + *
> + * This function may sleep while connecting the socket, so it can only be used
> + * in sleepable BPF programs (SYSCALL).
> + *
> + * Return: 0 on success, negative errno on error.
> + */
> +__bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,
> +				  const struct bpf_ksock_addr_opts *opts,
> +				  u32 opts__sz)
> +{
> +	struct sockaddr_storage addr = {};

Same.

^ permalink raw reply

* [PATCH iwl-net v2 1/2] ice: move ice_vsi_realloc_stat_arrays() up
From: Przemek Kitszel @ 2026-07-06 22:43 UTC (permalink / raw)
  To: intel-wired-lan, Michal Schmidt, Jakub Kicinski
  Cc: netdev, Tony Nguyen, Aleksandr Loktionov, Andrew Lunn,
	David S. Miller, Eric Dumazet, Paolo Abeni, Jedrzej Jagielski,
	Piotr Kwapulinski, Przemek Kitszel, Marcin Szycik

Move ice_vsi_realloc_stat_arrays() up, to allow calling it from
ice_vsi_cfg_def() by the next commit.

Fix kdoc for touched code. One line break removed, "int i" scope
minimized to the loop, no changes otherwise.

Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
---
v2: no changes
---
 drivers/net/ethernet/intel/ice/ice_lib.c | 119 +++++++++++------------
 1 file changed, 59 insertions(+), 60 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 8cdc4fda89e9..e48ee5940f17 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -2303,6 +2303,65 @@ static int ice_vsi_cfg_tc_lan(struct ice_pf *pf, struct ice_vsi *vsi)
 	return 0;
 }
 
+/**
+ * ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones
+ * @vsi: VSI pointer
+ * Return: 0 on success or -ENOMEM on allocation failure.
+ */
+static int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
+{
+	u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq;
+	u16 req_rxq = vsi->req_rxq ? vsi->req_rxq : vsi->alloc_rxq;
+	struct ice_ring_stats **tx_ring_stats;
+	struct ice_ring_stats **rx_ring_stats;
+	struct ice_vsi_stats *vsi_stat;
+	struct ice_pf *pf = vsi->back;
+	u16 prev_txq = vsi->alloc_txq;
+	u16 prev_rxq = vsi->alloc_rxq;
+
+	vsi_stat = pf->vsi_stats[vsi->idx];
+
+	if (req_txq < prev_txq) {
+		for (int i = req_txq; i < prev_txq; i++) {
+			if (vsi_stat->tx_ring_stats[i]) {
+				kfree_rcu(vsi_stat->tx_ring_stats[i], rcu);
+				WRITE_ONCE(vsi_stat->tx_ring_stats[i], NULL);
+			}
+		}
+	}
+
+	tx_ring_stats = vsi_stat->tx_ring_stats;
+	vsi_stat->tx_ring_stats =
+		krealloc_array(vsi_stat->tx_ring_stats, req_txq,
+			       sizeof(*vsi_stat->tx_ring_stats),
+			       GFP_KERNEL | __GFP_ZERO);
+	if (!vsi_stat->tx_ring_stats) {
+		vsi_stat->tx_ring_stats = tx_ring_stats;
+		return -ENOMEM;
+	}
+
+	if (req_rxq < prev_rxq) {
+		for (int i = req_rxq; i < prev_rxq; i++) {
+			if (vsi_stat->rx_ring_stats[i]) {
+				kfree_rcu(vsi_stat->rx_ring_stats[i], rcu);
+				WRITE_ONCE(vsi_stat->rx_ring_stats[i], NULL);
+			}
+		}
+	}
+
+	rx_ring_stats = vsi_stat->rx_ring_stats;
+	vsi_stat->rx_ring_stats =
+		krealloc_array(vsi_stat->rx_ring_stats, req_rxq,
+			       sizeof(*vsi_stat->rx_ring_stats),
+			       GFP_KERNEL | __GFP_ZERO);
+	if (!vsi_stat->rx_ring_stats) {
+		vsi_stat->rx_ring_stats = rx_ring_stats;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
 /**
  * ice_vsi_cfg_def - configure default VSI based on the type
  * @vsi: pointer to VSI
@@ -3011,66 +3070,6 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi,
 	}
 }
 
-/**
- * ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones
- * @vsi: VSI pointer
- */
-static int
-ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
-{
-	u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq;
-	u16 req_rxq = vsi->req_rxq ? vsi->req_rxq : vsi->alloc_rxq;
-	struct ice_ring_stats **tx_ring_stats;
-	struct ice_ring_stats **rx_ring_stats;
-	struct ice_vsi_stats *vsi_stat;
-	struct ice_pf *pf = vsi->back;
-	u16 prev_txq = vsi->alloc_txq;
-	u16 prev_rxq = vsi->alloc_rxq;
-	int i;
-
-	vsi_stat = pf->vsi_stats[vsi->idx];
-
-	if (req_txq < prev_txq) {
-		for (i = req_txq; i < prev_txq; i++) {
-			if (vsi_stat->tx_ring_stats[i]) {
-				kfree_rcu(vsi_stat->tx_ring_stats[i], rcu);
-				WRITE_ONCE(vsi_stat->tx_ring_stats[i], NULL);
-			}
-		}
-	}
-
-	tx_ring_stats = vsi_stat->tx_ring_stats;
-	vsi_stat->tx_ring_stats =
-		krealloc_array(vsi_stat->tx_ring_stats, req_txq,
-			       sizeof(*vsi_stat->tx_ring_stats),
-			       GFP_KERNEL | __GFP_ZERO);
-	if (!vsi_stat->tx_ring_stats) {
-		vsi_stat->tx_ring_stats = tx_ring_stats;
-		return -ENOMEM;
-	}
-
-	if (req_rxq < prev_rxq) {
-		for (i = req_rxq; i < prev_rxq; i++) {
-			if (vsi_stat->rx_ring_stats[i]) {
-				kfree_rcu(vsi_stat->rx_ring_stats[i], rcu);
-				WRITE_ONCE(vsi_stat->rx_ring_stats[i], NULL);
-			}
-		}
-	}
-
-	rx_ring_stats = vsi_stat->rx_ring_stats;
-	vsi_stat->rx_ring_stats =
-		krealloc_array(vsi_stat->rx_ring_stats, req_rxq,
-			       sizeof(*vsi_stat->rx_ring_stats),
-			       GFP_KERNEL | __GFP_ZERO);
-	if (!vsi_stat->rx_ring_stats) {
-		vsi_stat->rx_ring_stats = rx_ring_stats;
-		return -ENOMEM;
-	}
-
-	return 0;
-}
-
 /**
  * ice_vsi_rebuild - Rebuild VSI after reset
  * @vsi: VSI to be rebuild
-- 
2.54.0


^ permalink raw reply related

* [PATCH iwl-net v2 2/2] ice: fix stats array overflow via proper realloc
From: Przemek Kitszel @ 2026-07-06 22:43 UTC (permalink / raw)
  To: intel-wired-lan, Michal Schmidt, Jakub Kicinski
  Cc: netdev, Tony Nguyen, Aleksandr Loktionov, Andrew Lunn,
	David S. Miller, Eric Dumazet, Paolo Abeni, Jedrzej Jagielski,
	Piotr Kwapulinski, Przemek Kitszel, Marcin Szycik
In-Reply-To: <20260706224346.22546-1-przemyslaw.kitszel@intel.com>

Integrate ice_vsi_alloc_stat_arrays() with realloc variant.

Instead of keeping two functions for stat arrays allocation, change the
ice_vsi_realloc_stat_arrays() to handle initial condition (no vsi_stat
entry) and replace ice_vsi_alloc_stat_arrays() by the more generic
ice_vsi_realloc_stat_arrays().

Note that VSIs of ICE_VSI_CHNL type are ignored in realloc variant as they
were in the replaced ice_vsi_alloc_stat_arrays().

This is a fix for stats array overflow that occurs when VF is given more
queues (an operation that will be more frequent, and by bigger increase,
when we will merge my "XLVF" series).

Splat for increasing number of queues thanks to Michal Schmidt:
KASAN detects the bug:
 ==================================================================
 BUG: KASAN: slab-out-of-bounds in ice_vsi_alloc_ring_stats+0x385/0x4a0 [ice]
 Read of size 8 at addr ffff88810affea60 by task kworker/u131:7/221

 CPU: 24 UID: 0 PID: 221 Comm: kworker/u131:7 Not tainted 7.1.0-rc1+ #1 PREEMPT(lazy)
 ...
 Workqueue: ice ice_service_task [ice]
 Call Trace:
  <TASK>
  ...
  kasan_report+0xd7/0x120
  ice_vsi_alloc_ring_stats+0x385/0x4a0 [ice]
  ice_vsi_cfg_def+0x12e2/0x2060 [ice]
  ice_vsi_cfg+0xb5/0x3c0 [ice]
  ice_reset_vf+0x858/0xf80 [ice]
  ice_vc_request_qs_msg+0x1da/0x290 [ice]
  ice_vc_process_vf_msg+0xb15/0x1430 [ice]
  __ice_clean_ctrlq+0x70d/0x9d0 [ice]
  ice_service_task+0x840/0xf20 [ice]
  process_one_work+0x690/0xff0
  worker_thread+0x4d9/0xd20
  kthread+0x322/0x410
  ret_from_fork+0x332/0x660
  ret_from_fork_asm+0x1a/0x30
  </TASK>

 Allocated by task 2439:
  kasan_save_stack+0x1c/0x40
  kasan_save_track+0x10/0x30
  __kasan_kmalloc+0x96/0xb0
  __kmalloc_noprof+0x1d8/0x580
  ice_vsi_cfg_def+0x115c/0x2060 [ice]
  ice_vsi_cfg+0xb5/0x3c0 [ice]
  ice_vsi_setup+0x180/0x320 [ice]
  ice_start_vfs+0x1f3/0x590 [ice]
  ice_ena_vfs+0x66d/0x798 [ice]
  ice_sriov_configure.cold+0xe4/0x121 [ice]
  sriov_numvfs_store+0x279/0x480
  kernfs_fop_write_iter+0x331/0x4f0
  vfs_write+0x4c4/0xe40
  ksys_write+0x10c/0x240
  do_syscall_64+0xd9/0x650
  entry_SYSCALL_64_after_hwframe+0x76/0x7e

 The buggy address belongs to the object at ffff88810affea40
                which belongs to the cache kmalloc-32 of size 32
 The buggy address is located 0 bytes to the right of
                allocated 32-byte region [ffff88810affea40, ffff88810affea60)

Fixes: 2a2cb4c6c181 ("ice: replace ice_vf_recreate_vsi() with ice_vf_reconfig_vsi()")
Closes: https://redhat.atlassian.net/browse/RHEL-164321
Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
Signed-off-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
---
This is an alternative to the fix [1] by Michal Schmidt, which were
blocked due to AI feedback. My fix was already developed before Michal's,
just not public back then. We have agreed to go on with my version.

[1] https://lore.kernel.org/netdev/20260520183501.3360810-3-anthony.l.nguyen@intel.com

v1:
https://lore.kernel.org/intel-wired-lan/20260701104141.9740-2-przemyslaw.kitszel@intel.com

v2: Sashiko:
 * defer pf->vsi_stats[vsi->idx] to be done only after successful Tx and Rx stats arrays
      allocation - this avoids "half initialized" state processing in ice_vsi_free_stats().
    The above was reported by both opus-4.6 and gemini-3.1-pro. All other errors reported by
    just gemini were a mix between false-positives and too-cornercase'y. Gemini report for v1:
    https://sashiko.dev/#/patchset/20260701104141.9740-1-przemyslaw.kitszel%40intel.com
 * store also array lengths in separate variable for better tracking and proper freeing.
---
 drivers/net/ethernet/intel/ice/ice.h     |  2 +
 drivers/net/ethernet/intel/ice/ice_lib.c | 80 ++++++++----------------
 2 files changed, 29 insertions(+), 53 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index f72bb1aa4067..b63b59f2d203 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -328,6 +328,8 @@ enum ice_vsi_state {
 struct ice_vsi_stats {
 	struct ice_ring_stats **tx_ring_stats;  /* Tx ring stats array */
 	struct ice_ring_stats **rx_ring_stats;  /* Rx ring stats array */
+	u16 tx_ring_stats_len;
+	u16 rx_ring_stats_len;
 };
 
 /* struct that defines a VSI, associated with a dev */
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index e48ee5940f17..c71849119d04 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -338,7 +338,6 @@ static void ice_vsi_free_stats(struct ice_vsi *vsi)
 {
 	struct ice_vsi_stats *vsi_stat;
 	struct ice_pf *pf = vsi->back;
-	int i;
 
 	if (vsi->type == ICE_VSI_CHNL)
 		return;
@@ -349,14 +348,14 @@ static void ice_vsi_free_stats(struct ice_vsi *vsi)
 	if (!vsi_stat)
 		return;
 
-	ice_for_each_alloc_txq(vsi, i) {
+	for (int i = 0; i < vsi_stat->tx_ring_stats_len; i++) {
 		if (vsi_stat->tx_ring_stats[i]) {
 			kfree_rcu(vsi_stat->tx_ring_stats[i], rcu);
 			WRITE_ONCE(vsi_stat->tx_ring_stats[i], NULL);
 		}
 	}
 
-	ice_for_each_alloc_rxq(vsi, i) {
+	for (int i = 0; i < vsi_stat->rx_ring_stats_len; i++) {
 		if (vsi_stat->rx_ring_stats[i]) {
 			kfree_rcu(vsi_stat->rx_ring_stats[i], rcu);
 			WRITE_ONCE(vsi_stat->rx_ring_stats[i], NULL);
@@ -513,51 +512,6 @@ static irqreturn_t ice_msix_clean_rings(int __always_unused irq, void *data)
 	return IRQ_HANDLED;
 }
 
-/**
- * ice_vsi_alloc_stat_arrays - Allocate statistics arrays
- * @vsi: VSI pointer
- */
-static int ice_vsi_alloc_stat_arrays(struct ice_vsi *vsi)
-{
-	struct ice_vsi_stats *vsi_stat;
-	struct ice_pf *pf = vsi->back;
-
-	if (vsi->type == ICE_VSI_CHNL)
-		return 0;
-	if (!pf->vsi_stats)
-		return -ENOENT;
-
-	if (pf->vsi_stats[vsi->idx])
-	/* realloc will happen in rebuild path */
-		return 0;
-
-	vsi_stat = kzalloc_obj(*vsi_stat);
-	if (!vsi_stat)
-		return -ENOMEM;
-
-	vsi_stat->tx_ring_stats =
-		kzalloc_objs(*vsi_stat->tx_ring_stats, vsi->alloc_txq);
-	if (!vsi_stat->tx_ring_stats)
-		goto err_alloc_tx;
-
-	vsi_stat->rx_ring_stats =
-		kzalloc_objs(*vsi_stat->rx_ring_stats, vsi->alloc_rxq);
-	if (!vsi_stat->rx_ring_stats)
-		goto err_alloc_rx;
-
-	pf->vsi_stats[vsi->idx] = vsi_stat;
-
-	return 0;
-
-err_alloc_rx:
-	kfree(vsi_stat->rx_ring_stats);
-err_alloc_tx:
-	kfree(vsi_stat->tx_ring_stats);
-	kfree(vsi_stat);
-	pf->vsi_stats[vsi->idx] = NULL;
-	return -ENOMEM;
-}
-
 /**
  * ice_vsi_alloc_def - set default values for already allocated VSI
  * @vsi: ptr to VSI
@@ -2316,11 +2270,19 @@ static int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
 	struct ice_ring_stats **rx_ring_stats;
 	struct ice_vsi_stats *vsi_stat;
 	struct ice_pf *pf = vsi->back;
-	u16 prev_txq = vsi->alloc_txq;
-	u16 prev_rxq = vsi->alloc_rxq;
+	u16 prev_txq, prev_rxq;
+
+	if (vsi->type == ICE_VSI_CHNL)
+		return 0;
 
 	vsi_stat = pf->vsi_stats[vsi->idx];
+	if (!vsi_stat) {
+		vsi_stat = kzalloc_obj(*vsi_stat);
+		if (!vsi_stat)
+			return -ENOMEM;
+	}
 
+	prev_txq = vsi_stat->tx_ring_stats_len;
 	if (req_txq < prev_txq) {
 		for (int i = req_txq; i < prev_txq; i++) {
 			if (vsi_stat->tx_ring_stats[i]) {
@@ -2337,9 +2299,11 @@ static int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
 			       GFP_KERNEL | __GFP_ZERO);
 	if (!vsi_stat->tx_ring_stats) {
 		vsi_stat->tx_ring_stats = tx_ring_stats;
-		return -ENOMEM;
+		goto err_free_partial_vsi_stat;
 	}
+	vsi_stat->tx_ring_stats_len = req_txq;
 
+	prev_rxq = vsi_stat->rx_ring_stats_len;
 	if (req_rxq < prev_rxq) {
 		for (int i = req_rxq; i < prev_rxq; i++) {
 			if (vsi_stat->rx_ring_stats[i]) {
@@ -2356,10 +2320,20 @@ static int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
 			       GFP_KERNEL | __GFP_ZERO);
 	if (!vsi_stat->rx_ring_stats) {
 		vsi_stat->rx_ring_stats = rx_ring_stats;
-		return -ENOMEM;
+		goto err_free_partial_vsi_stat;
 	}
+	vsi_stat->rx_ring_stats_len = req_rxq;
 
+	pf->vsi_stats[vsi->idx] = vsi_stat;
 	return 0;
+
+err_free_partial_vsi_stat:
+	if (!pf->vsi_stats[vsi->idx]) {
+		/* vsi_stat was not visible before current alloc attempt */
+		kfree(vsi_stat->tx_ring_stats);
+		kfree(vsi_stat);
+	}
+	return -ENOMEM;
 }
 
 /**
@@ -2379,7 +2353,7 @@ static int ice_vsi_cfg_def(struct ice_vsi *vsi)
 		return ret;
 
 	/* allocate memory for Tx/Rx ring stat pointers */
-	ret = ice_vsi_alloc_stat_arrays(vsi);
+	ret = ice_vsi_realloc_stat_arrays(vsi);
 	if (ret)
 		goto unroll_vsi_alloc;
 
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH bpf-next v3 11/15] bpf: tcp: Support selected sock_ops callbacks as struct_ops
From: Amery Hung @ 2026-07-06 23:11 UTC (permalink / raw)
  To: bot+bpf-ci
  Cc: bpf, netdev, alexei.starovoitov, andrii, daniel, eddyz87, memxor,
	martin.lau, shakeel.butt, roman.gushchin, kuniyu, kerneljasonxing,
	kernel-team, ast, yonghong.song, clm, ihor.solodrai
In-Reply-To: <53de193473737a3692df075a179e186e996dc26a592b697f2f335d9d4039c943@mail.kernel.org>

On Mon, Jul 6, 2026 at 11:28 AM <bot+bpf-ci@kernel.org> wrote:
>
> > diff --git a/net/ipv4/bpf_tcp_ops.c b/net/ipv4/bpf_tcp_ops.c
> > new file mode 100644
> > index 000000000000..3febbc8dd1a0
> > --- /dev/null
> > +++ b/net/ipv4/bpf_tcp_ops.c
> > @@ -0,0 +1,188 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> > +
> > +#include <linux/bpf.h>
> > +#include <linux/btf_ids.h>
> > +#include <linux/bpf_verifier.h>
> > +#include <net/bpf_sk_storage.h>
> > +#include <net/tcp.h>
> > +
> > +static int timeout_init_stub(struct sock *sk, struct request_sock *req__nullable)
> > +{
> > +     struct bpf_tramp_run_ctx *ctx =
> > +             container_of(current->bpf_ctx, struct bpf_tramp_run_ctx, run_ctx);
> > +
> > +     return ctx->retval;
> > +}
> > +
> > +static int rwnd_init_stub(struct sock *sk, struct request_sock *req__nullable)
> > +{
> > +     struct bpf_tramp_run_ctx *ctx =
> > +             container_of(current->bpf_ctx, struct bpf_tramp_run_ctx, run_ctx);
> > +
> > +     return ctx->retval;
> > +}
>
> This isn't a bug, but could timeout_init_stub() and rwnd_init_stub()
> simply return 0 like the analogous int-returning stubs in
> net/ipv4/bpf_tcp_ca.c?

The stub function returning int here must return ctx->retval to allow
the next program in the array to inspect the return value. The stub
function are the struct_ops counterpart of dummy bpf program. It
replaces a normal struct_ops program when update_effective_progs()
fails and can be executed.

>
> These stubs are only used for CFI/trampoline generation and never
> executed. The sibling file's int stubs (bpf_tcp_ca_undo_cwnd,
> bpf_tcp_ca_sndbuf_expand, bpf_tcp_ca_ssthresh) all return 0. The
> container_of() plus ctx->retval logic here duplicates the actual
> bpf_tcp_ops_get_retval() helper below.
>
> > +static void active_established_stub(struct sock *sk, struct sk_buff *skb__nullable)
> > +{
> > +}
>
> [ ... ]
>
> > +BPF_CALL_0(bpf_tcp_ops_get_retval)
> > +{
> > +     struct bpf_tramp_run_ctx *ctx =
> > +             container_of(current->bpf_ctx, struct bpf_tramp_run_ctx, run_ctx);
> > +
> > +     /* bpf_get_retval() is only exposed to timeout_init/rwnd_init, which
> > +      * always run via bpf_tcp_ops_call_int(). Its run_ctx carries the int
> > +      * return value chained across the bpf_tcp_ops attached to the cgroup
> > +      * and is this program's saved_run_ctx.
> > +      */
> > +     if (WARN_ON_ONCE(!ctx->saved_run_ctx))
> > +             return 0;
> > +
> > +     return container_of(ctx->saved_run_ctx, struct bpf_tramp_run_ctx,
> > +                         run_ctx)->retval;
> > +}
>
> [ ... ]
>
>
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28810680939

^ permalink raw reply

* Re: [PATCH net-next v4 3/3] selftest: Add tests for useful handling of LSM denials on SCM_RIGHTS
From: Kuniyuki Iwashima @ 2026-07-06 23:17 UTC (permalink / raw)
  To: Jori Koolstra
  Cc: Christian Brauner, Aleksa Sarai, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, netdev, linux-fsdevel,
	linux-kernel
In-Reply-To: <20260705123826.3818443-4-jkoolstra@xs4all.nl>

On Sun, Jul 5, 2026 at 5:37 AM Jori Koolstra <jkoolstra@xs4all.nl> wrote:
>
> Tests SCM_RIGHTS fd passing on a socket with the new socket option
> SO_RIGHTS_NOTRUNC turned on. To hook into the security_file_receive()
> call, BPF is used. The BPF program shares a hashmap with userspace that
> lists the inos to be blocked (of the receiver tgid).
>
> Signed-off-by: Jori Koolstra <jkoolstra@xs4all.nl>
> Assisted-by: LLM # used LLM to get skeleton BPF and libbpf code
> ---
>  .../testing/selftests/net/af_unix/.gitignore  |   2 +
>  tools/testing/selftests/net/af_unix/Makefile  |   8 +
>  .../net/af_unix/scm_rights_denial_lsm.bpf.c   |  36 +++
>  .../net/af_unix/scm_rights_denial_lsm.c       | 263 ++++++++++++++++++
>  4 files changed, 309 insertions(+)
>  create mode 100644 tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.bpf.c
>  create mode 100644 tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.c
>
> diff --git a/tools/testing/selftests/net/af_unix/.gitignore b/tools/testing/selftests/net/af_unix/.gitignore
> index 240b26740c9e..5034482f8864 100644
> --- a/tools/testing/selftests/net/af_unix/.gitignore
> +++ b/tools/testing/selftests/net/af_unix/.gitignore
> @@ -3,6 +3,8 @@ msg_oob
>  scm_inq
>  scm_pidfd
>  scm_rights
> +scm_rights_denial_lsm
> +scm_rights_denial_lsm.bpf.o
>  so_peek_off
>  unix_connect
>  unix_connreset
> diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
> index 4c0375e28bbe..594cd26ec398 100644
> --- a/tools/testing/selftests/net/af_unix/Makefile
> +++ b/tools/testing/selftests/net/af_unix/Makefile
> @@ -11,9 +11,17 @@ TEST_GEN_PROGS := \
>         scm_inq \
>         scm_pidfd \
>         scm_rights \
> +       scm_rights_denial_lsm \
>         so_peek_off \
>         unix_connect \
>         unix_connreset \
>  # end of TEST_GEN_PROGS
>
> +TEST_GEN_FILES := scm_rights_denial_lsm.bpf.o
> +
>  include ../../lib.mk
> +include ../bpf.mk
> +
> +$(OUTPUT)/scm_rights_denial_lsm: $(BPFOBJ)
> +$(OUTPUT)/scm_rights_denial_lsm: CFLAGS += -I$(SCRATCH_DIR)/include
> +$(OUTPUT)/scm_rights_denial_lsm: LDLIBS += -lelf -lz
> diff --git a/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.bpf.c b/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.bpf.c
> new file mode 100644
> index 000000000000..4f2414465bfd
> --- /dev/null
> +++ b/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.bpf.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/bpf.h>
> +#include <linux/errno.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +
> +char _license[] SEC("license") = "GPL";
> +
> +struct inode {
> +       unsigned long i_ino;
> +} __attribute__((preserve_access_index));
> +
> +struct file {
> +       struct inode *f_inode;
> +} __attribute__((preserve_access_index));
> +
> +struct {
> +       __uint(type, BPF_MAP_TYPE_HASH);
> +       __uint(max_entries, 16);
> +       __type(key, __u64);     /* inode number */
> +       __type(value, __u32);   /* tgid of the receiver being tested */
> +} denied_inodes SEC(".maps");
> +
> +SEC("lsm/file_receive")
> +int BPF_PROG(scm_rights_deny, struct file *file)
> +{
> +       __u32 tgid = bpf_get_current_pid_tgid() >> 32;
> +       __u64 ino = file->f_inode->i_ino;
> +       __u32 *owner;
> +
> +       owner = bpf_map_lookup_elem(&denied_inodes, &ino);
> +       if (owner && *owner == tgid)
> +               return -EPERM;
> +
> +       return 0;
> +}
> diff --git a/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.c b/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.c
> new file mode 100644
> index 000000000000..b8656de86efe
> --- /dev/null
> +++ b/tools/testing/selftests/net/af_unix/scm_rights_denial_lsm.c
> @@ -0,0 +1,263 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#define _GNU_SOURCE
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/socket.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +
> +#include <bpf/bpf.h>
> +#include <bpf/libbpf.h>
> +
> +#include "kselftest_harness.h"
> +
> +#ifndef SO_RIGHTS_NOTRUNC
> +#define SO_RIGHTS_NOTRUNC 85
> +#endif
> +
> +#define NR_FILES 2
> +
> +/* Per-file content, so a received fd can be matched to the file sent */
> +#define SECRET(n) "secret %d", (n)
> +
> +/* Indices into the socketpair */
> +#define SK_SENDER 0
> +#define SK_RECEIVER 1
> +
> +FIXTURE(scm_rights_denial_bpf)
> +{
> +       struct bpf_object *obj;
> +       struct bpf_link *link;
> +       int map_fd;
> +       int sk[2];
> +       int files[NR_FILES];
> +       __u64 inos[NR_FILES];
> +       char paths[NR_FILES][64];
> +};

Could you add FIXTURE_VARIANT() and cover all types ?
SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET


> +
> +FIXTURE_SETUP(scm_rights_denial_bpf)
> +{
> +       struct bpf_program *prog;
> +       char lsms[256] = {};
> +       int i, fd;
> +
> +       if (geteuid() != 0)
> +               SKIP(return, "requires root");
> +
> +       fd = open("/sys/kernel/security/lsm", O_RDONLY);
> +       ASSERT_LE(0, fd);
> +       ASSERT_LT(0, read(fd, lsms, sizeof(lsms) - 1));

This style is easier to follow the logic for me.

err = func();
ASSERT_LT(val, err);


> +       close(fd);
> +
> +       if (!strstr(lsms, "bpf"))
> +               SKIP(return, "BPF LSM not active (boot with lsm=...,bpf)");

Could you add minimal relevant configs (CONFIG_BPF_LSM etc)
to tools/testing/selftests/net/af_unix/config ?

NIPA (netdev CI) uses it as the base config.


> +
> +       self->obj = bpf_object__open_file("scm_rights_denial_lsm.bpf.o", NULL);
> +       ASSERT_NE(NULL, self->obj);
> +       ASSERT_EQ(0, bpf_object__load(self->obj));
> +
> +       prog = bpf_object__find_program_by_name(self->obj, "scm_rights_deny");
> +       ASSERT_NE(NULL, prog);
> +
> +       self->link = bpf_program__attach_lsm(prog);
> +       ASSERT_NE(NULL, self->link);
> +
> +       self->map_fd = bpf_object__find_map_fd_by_name(self->obj,
> +                                                      "denied_inodes");
> +       ASSERT_LE(0, self->map_fd);
> +
> +       ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, self->sk));
> +
> +       for (i = 0; i < NR_FILES; i++) {
> +               struct stat st;
> +
> +               snprintf(self->paths[i], sizeof(self->paths[i]),
> +                        "/tmp/scm_rights_denial_bpf.%d.XXXXXX", i);
> +               self->files[i] = mkstemp(self->paths[i]);
> +               ASSERT_LE(0, self->files[i]);
> +
> +               ASSERT_LT(0, dprintf(self->files[i], SECRET(i)));
> +
> +               ASSERT_EQ(0, fstat(self->files[i], &st));
> +               self->inos[i] = st.st_ino;
> +       }
> +}
> +
> +FIXTURE_TEARDOWN(scm_rights_denial_bpf)
> +{
> +       bpf_link__destroy(self->link);
> +       bpf_object__close(self->obj);
> +
> +       for (int i = 0; i < NR_FILES; i++) {
> +               if (self->files[i] >= 0) {
> +                       close(self->files[i]);
> +                       unlink(self->paths[i]);
> +               }
> +       }
> +
> +       close(self->sk[SK_SENDER]);
> +       close(self->sk[SK_RECEIVER]);
> +}
> +
> +static int deny_inode(int map_fd, __u64 ino)
> +{
> +       __u32 tgid = getpid();

nit: newline here

> +       return bpf_map_update_elem(map_fd, &ino, &tgid, BPF_ANY);
> +}
> +
> +static int set_notrunc(int sk)
> +{
> +       int one = 1;

nit: ditto.

> +       return setsockopt(sk, SOL_SOCKET, SO_RIGHTS_NOTRUNC,
> +                         &one, sizeof(one));
> +}
> +
> +static int send_fds(int sk, int *fds, int n)
> +{
> +       char ctrl[CMSG_SPACE(NR_FILES * sizeof(int))] = {};
> +       char data = 'x';
> +       struct iovec iov = {
> +               .iov_base = &data,
> +               .iov_len = sizeof(data),
> +       };
> +       struct msghdr msg = {
> +               .msg_iov = &iov,
> +               .msg_iovlen = 1,
> +               .msg_control = ctrl,
> +               .msg_controllen = CMSG_SPACE(n * sizeof(int)),
> +       };
> +       struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
> +
> +       cmsg->cmsg_level = SOL_SOCKET;
> +       cmsg->cmsg_type = SCM_RIGHTS;
> +       cmsg->cmsg_len = CMSG_LEN(n * sizeof(int));
> +       memcpy(CMSG_DATA(cmsg), fds, n * sizeof(int));
> +
> +       return sendmsg(sk, &msg, 0);
> +}
> +
> +static int recv_fd_slots(int sk, int *slots, int *msg_flags)
> +{
> +       int nr_slots;
> +       char ctrl[CMSG_SPACE(NR_FILES * sizeof(int))];
> +       char data;
> +       struct iovec iov = {
> +               .iov_base = &data,
> +               .iov_len = sizeof(data),
> +       };
> +       struct msghdr msg = {
> +               .msg_iov = &iov,
> +               .msg_iovlen = 1,
> +               .msg_control = ctrl,
> +               .msg_controllen = sizeof(ctrl),
> +       };
> +       struct cmsghdr *cmsg;
> +
> +       if (recvmsg(sk, &msg, 0) < 0)
> +               return -1;
> +
> +       *msg_flags = msg.msg_flags;
> +
> +       cmsg = CMSG_FIRSTHDR(&msg);
> +       if (!cmsg)
> +               return 0;
> +
> +       nr_slots = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
> +       memcpy(slots, CMSG_DATA(cmsg), nr_slots * sizeof(int));
> +
> +       return nr_slots;
> +}
> +
> +/* Prove a received fd works by reading back the file's content. */
> +static int check_secret(int fd, int idx)
> +{
> +       char want[32], got[32] = {};
> +
> +       snprintf(want, sizeof(want), SECRET(idx));
> +       if (pread(fd, got, sizeof(got) - 1, 0) < 0)
> +               return -1;
> +
> +       return strcmp(want, got);
> +}
> +
> +TEST_F(scm_rights_denial_bpf, all_allowed)
> +{
> +       int slots[NR_FILES], nr_slots, flags, i;
> +
> +       ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
> +       ASSERT_NE(-1, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
> +       nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
> +
> +       ASSERT_EQ(NR_FILES, nr_slots);
> +       EXPECT_EQ(0, flags & MSG_CTRUNC);
> +
> +       for (i = 0; i < NR_FILES; i++) {
> +               ASSERT_LE(0, slots[i]);
> +               EXPECT_EQ(0, check_secret(slots[i], i));
> +               close(slots[i]);
> +       }
> +}
> +
> +TEST_F(scm_rights_denial_bpf, first_denied)
> +{
> +       int slots[NR_FILES], nr_slots, flags;
> +
> +       ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[0]));
> +
> +       ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
> +       ASSERT_NE(-1, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
> +       nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
> +
> +       ASSERT_EQ(NR_FILES, nr_slots);
> +       EXPECT_EQ(0, flags & MSG_CTRUNC);
> +       EXPECT_EQ(-EPERM, slots[0]);
> +
> +       ASSERT_LE(0, slots[1]);
> +       EXPECT_EQ(0, check_secret(slots[1], 1));
> +       close(slots[1]);
> +}
> +
> +TEST_F(scm_rights_denial_bpf, all_denied)
> +{
> +       int slots[NR_FILES], nr_slots, flags, i;
> +
> +       for (i = 0; i < NR_FILES; i++)
> +               ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[i]));
> +
> +       ASSERT_EQ(0, set_notrunc(self->sk[SK_RECEIVER]));
> +       ASSERT_NE(-1, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
> +       nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
> +
> +       ASSERT_EQ(NR_FILES, nr_slots);
> +       EXPECT_EQ(0, flags & MSG_CTRUNC);
> +
> +       for (i = 0; i < NR_FILES; i++)
> +               EXPECT_EQ(-EPERM, slots[i]);
> +}
> +
> +TEST_F(scm_rights_denial_bpf, denied_without_notrunc)
> +{
> +       int slots[NR_FILES], nr_slots, flags;
> +
> +       /*
> +        * Baseline behaviour without SO_RIGHTS_NOTRUNC: the fd array is
> +        * truncated at the first denied fd and MSG_CTRUNC is set.
> +        */
> +       ASSERT_EQ(0, deny_inode(self->map_fd, self->inos[1]));
> +
> +       ASSERT_NE(-1, send_fds(self->sk[SK_SENDER], self->files, NR_FILES));
> +       nr_slots = recv_fd_slots(self->sk[SK_RECEIVER], slots, &flags);
> +
> +       ASSERT_EQ(1, nr_slots);
> +       EXPECT_NE(0, flags & MSG_CTRUNC);
> +
> +       ASSERT_LE(0, slots[0]);
> +       EXPECT_EQ(0, check_secret(slots[0], 0));
> +       close(slots[0]);
> +}
> +
> +TEST_HARNESS_MAIN
> --
> 2.55.0
>

^ permalink raw reply

* Re: [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
From: Thomas Karlsson @ 2026-07-06 23:25 UTC (permalink / raw)
  To: Xiang Mei
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	AutonomousCodeSecurity@microsoft.com,
	tgopinath@linux.microsoft.com, kys@microsoft.com
In-Reply-To: <CAPpSM+QjiZxUOqK6Aor6gLehVj=xGQ0uAcuPm089EuSNRFeynA@mail.gmail.com>

Hello Xiang,

Let me elaborate on our use-case with a little example calculation. Lets say medium latency real-time audio sent at 1ms per packet. This would amount to 1000 packets per second per stream. Scaling that to 600-800 unique audio streams would result in 6-800k pps. This would be a typical use case for our servers. The macvlan BC queue len need adjustment to be able to swallow short scheduling/processing delays in these workloads or packet loss start to show. 

Switching to even lower latency (although not recommended) or increasing the number of unique streams could of course increase the load even further.

As for a recommended upper limit, I'm not sure. Like I said, we use 100k today and are currently running our workload extremely stable with that. Do we have some margin in that number? Probably, but I'm not able to quantify exactly how much.

To account for someone using it even more aggressively, 128 or 256k could perhaps be a resonable upper limit? 

Best regards,
Thomas

________________________________________
From: Xiang Mei <xmei5@asu.edu>
Sent: Tuesday, 7 July 2026 00:34
To: Thomas Karlsson <thomas.karlsson@paneda.se>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>; David S . Miller <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>; netdev@vger.kernel.org <netdev@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>; AutonomousCodeSecurity@microsoft.com <AutonomousCodeSecurity@microsoft.com>; tgopinath@linux.microsoft.com <tgopinath@linux.microsoft.com>; kys@microsoft.com <kys@microsoft.com>
Subject: Re: [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
 
On Mon, Jul 6, 2026 at 3:31 PM Thomas Karlsson
<thomas.karlsson@paneda.se> wrote:
>
> Dear all,
>
> While restricting a lower max than MAX U32 may be warranted the proposed limit of 4096 is way too small for high throughput multicast traffic.
>
> We currently run many production servers with the BC queue len set to a value of 100 000.
>

Thanks, Thomas, for your feedback. 4096 could be small.
We would like to hear your suggested values, and we would like to do
some tests with the value you suggest.

Xiang


>
> Best regards,
> Thomas Karlsson
>
>
> Sent from Outlook for Android
> ________________________________
> From: Xiang Mei (Microsoft) <xmei5@asu.edu>
> Sent: Monday, 06 July 2026 23:25:56
> To: Andrew Lunn <andrew+netdev@lunn.ch>; David S . Miller <davem@davemloft.net>; Eric Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni <pabeni@redhat.com>
> Cc: Thomas Karlsson <thomas.karlsson@paneda.se>; netdev@vger.kernel.org <netdev@vger.kernel.org>; linux-kernel@vger.kernel.org <linux-kernel@vger.kernel.org>; AutonomousCodeSecurity@microsoft.com <AutonomousCodeSecurity@microsoft.com>; tgopinath@linux.microsoft.com <tgopinath@linux.microsoft.com>; kys@microsoft.com <kys@microsoft.com>; Xiang Mei (Microsoft) <xmei5@asu.edu>
> Subject: [PATCH net] macvlan: cap IFLA_MACVLAN_BC_QUEUE_LEN to bound broadcast backlog
>
> The netlink policy for IFLA_MACVLAN_BC_QUEUE_LEN accepts any u32, and the
> value becomes port->bc_queue_len_used, the only cap on how many skbs
> macvlan_broadcast_enqueue() may queue on port->bc_queue. An unprivileged
> user owning a user+net namespace (CAP_NET_ADMIN is checked only against
> the target netns) can set it to 0xffffffff, so the backlog check never
> trips and every broadcast frame is skb_clone()'d with GFP_ATOMIC and
> queued. When RX softirq outpaces the bc_work drain (e.g. many ALLMULTI
> macvlans under a broadcast flood), the queue grows unbounded and
> OOMs/panics the host.
>
> Bound the value with NLA_POLICY_MAX() at 4096, 4x the default of 1000.
> This keeps headroom above the default while capping the backlog; values
> above the cap are now rejected with -ERANGE at netlink parse time.
>
>   Out of memory: Killed process 141 (su) UID:0
>   Kernel panic - not syncing: System is deadlocked on memory
>   Call Trace:
>    vpanic (kernel/panic.c:650)
>    panic (kernel/panic.c:787)
>    out_of_memory (mm/oom_kill.c:1166)
>    __alloc_frozen_pages_noprof (mm/page_alloc.c:4914)
>    alloc_pages_mpol (mm/mempolicy.c:2490)
>    folio_alloc_noprof (mm/mempolicy.c:2591)
>    filemap_fault (mm/filemap.c:3565)
>
> Fixes: d4bff72c8401 ("macvlan: Support for high multicast packet rate")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> ---
>  drivers/net/macvlan.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
> index c40fa331836b..d4cede6393b0 100644
> --- a/drivers/net/macvlan.c
> +++ b/drivers/net/macvlan.c
> @@ -37,6 +37,7 @@
>  #define MACVLAN_HASH_BITS       8
>  #define MACVLAN_HASH_SIZE       (1<<MACVLAN_HASH_BITS)
>  #define MACVLAN_DEFAULT_BC_QUEUE_LEN    1000
> +#define MACVLAN_MAX_BC_QUEUE_LEN       4096
>
>  #define MACVLAN_F_PASSTHRU      1
>  #define MACVLAN_F_ADDRCHANGE    2
> @@ -1755,7 +1756,7 @@ static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
>          [IFLA_MACVLAN_MACADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
>          [IFLA_MACVLAN_MACADDR_DATA] = { .type = NLA_NESTED },
>          [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NLA_U32 },
> -       [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NLA_U32 },
> +       [IFLA_MACVLAN_BC_QUEUE_LEN] = NLA_POLICY_MAX(NLA_U32, MACVLAN_MAX_BC_QUEUE_LEN),
>          [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NLA_REJECT },
>          [IFLA_MACVLAN_BC_CUTOFF] = { .type = NLA_S32 },
>  };
> --
> 2.43.0
>

^ permalink raw reply

* Re: [PATCH net-next] net: dpaa_eth: convert to napi_gro_receive
From: Rosen Penev @ 2026-07-06 23:25 UTC (permalink / raw)
  To: Vladimir Oltean
  Cc: netdev, Madalin Bucur, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, open list
In-Reply-To: <20260706083906.5oxmr47vbnaztfu2@skbuf>

On Mon, Jul 6, 2026 at 1:39 AM Vladimir Oltean <olteanv@gmail.com> wrote:
>
> Hi Rosen,
>
> On Sun, Jul 05, 2026 at 08:06:32PM -0700, Rosen Penev wrote:
> > Replace netif_receive_skb() with napi_gro_receive() to improve receive
> > performance for this driver. It has rx checksum support so routing speed
> > shouldn't suffer.
> >
> > Tested on a WatchGuard Firebox M300.
> >
> > iperf3 bidir speed test:
> >
> > [ ID][Role]   Interval         Transfer      Bitrate        Retr
> > [  5][TX-C]   0.00-60.01  sec  5.35 GBytes   766 Mbits/sec  184    sender
> > [  5][TX-C]   0.00-60.02  sec  5.35 GBytes   766 Mbits/sec         receiver
> > [  7][RX-C]   0.00-60.01  sec  5.50 GBytes   787 Mbits/sec  124    sender
> > [  7][RX-C]   0.00-60.02  sec  5.49 GBytes   786 Mbits/sec         receiver
> >
> > After:
> >
> > [ ID][Role]   Interval         Transfer      Bitrate        Retr
> > [  5][TX-C]   0.00-60.01  sec  6.49 GBytes   929 Mbits/sec    0    sender
> > [  5][TX-C]   0.00-60.02  sec  6.49 GBytes   928 Mbits/sec         receiver
> > [  7][RX-C]   0.00-60.01  sec  6.55 GBytes   938 Mbits/sec    0    sender
> > [  7][RX-C]   0.00-60.02  sec  6.55 GBytes   937 Mbits/sec         receiver
> >
> > Assisted-by: Opencode:big-pickle
> > Signed-off-by: Rosen Penev <rosenp@gmail.com>
> > ---
> >  drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 5 +----
> >  1 file changed, 1 insertion(+), 4 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > index ad2d8256eb8d..83191f636ec7 100644
> > --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> > @@ -2824,10 +2824,7 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> >
> >       skb_len = skb->len;
> >
> > -     if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) {
> > -             percpu_stats->rx_dropped++;
> > -             return qman_cb_dqrr_consume;
> > -     }
> > +     napi_gro_receive(&np->napi, skb);
> >
> >       percpu_stats->rx_packets++;
> >       percpu_stats->rx_bytes += skb_len;
> > --
> > 2.55.0
> >
> >
>
> (thanks to Madalin for pointing out this change to me)
>
> I do not have time this week to test this patch, but in premise, you are
> creating exactly the conditions for this bug to occur:
> https://github.com/nxp-qoriq/linux/commit/d0ebec2092d6c5fe327513cb66e02d8c2c1a8f87
>
> In sdk_dpaa we already do GRO for TCP flows and it has the problems
> pointed out in the above commit. With your change to use GRO for all
> capable flows, I currently have no reason to believe that without
> similar countermeasures as those taken by sdk_dpaa (napi_gro_receive()
> global to the entire QMan portal) the outcome would be different.
>
> Have you tested traffic in mixed scenarios, where flows from multiple
> interfaces land on the same CPU?
Just iperf3 --bidir.
>
> Until further evidence comes in:
Maybe netif_receive_skb_list is a better fit here.
>
> pw-bot: cr

^ permalink raw reply

* [PATCH net] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment()
From: Xiang Mei (Microsoft) @ 2026-07-06 23:28 UTC (permalink / raw)
  To: Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman
  Cc: netfilter-devel, coreteam, netdev, linux-kernel,
	AutonomousCodeSecurity, tgopinath, kys, Xiang Mei (Microsoft)

br_ip6_fragment() gets prevhdr, a pointer into the skb head, from
ip6_find_1stfragopt(), then calls skb_checksum_help().  For a cloned skb
skb_checksum_help() reallocates the head via pskb_expand_head(), leaving
prevhdr dangling.  It is later dereferenced in ip6_frag_next(), causing a
use-after-free write.

Re-find prevhdr after skb_checksum_help() so it points into the current
head.

  BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857)
  Write of size 1 at addr ffff888013ff5016 by task exploit/141
  Call Trace:
   ...
   kasan_report (mm/kasan/report.c:595)
   ip6_frag_next (net/ipv6/ip6_output.c:857)
   br_ip6_fragment (net/ipv6/netfilter.c:212)
   nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407)
   nf_hook_slow (net/netfilter/core.c:619)
   br_forward_finish (net/bridge/br_forward.c:66)
   __br_forward (net/bridge/br_forward.c:115)
   maybe_deliver (net/bridge/br_forward.c:191)
   br_flood (net/bridge/br_forward.c:245)
   br_handle_frame_finish (net/bridge/br_input.c:229)
   br_handle_frame (net/bridge/br_input.c:442)
   ...
   packet_sendmsg (net/packet/af_packet.c:3114)
   ...
   do_syscall_64 (arch/x86/entry/syscall_64.c:94)
   entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
  Kernel panic - not syncing: Fatal exception in interrupt

Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
---
 net/ipv6/netfilter.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 6d80f85e55fa..547879da9532 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -147,6 +147,10 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
 	    (err = skb_checksum_help(skb)))
 		goto blackhole;
 
+	err = ip6_find_1stfragopt(skb, &prevhdr);
+	if (err < 0)
+		goto blackhole;
+
 	hroom = LL_RESERVED_SPACE(skb->dev);
 	if (skb_has_frag_list(skb)) {
 		unsigned int first_len = skb_pagelen(skb);
-- 
2.43.0


^ permalink raw reply related

* [PATCH iwl-net v2] ice: add missing xa_destroy for sched_node_ids
From: Jacob Keller @ 2026-07-06 23:31 UTC (permalink / raw)
  To: Tony Nguyen, Przemek Kitszel; +Cc: intel-wired-lan, netdev, Jacob Keller

Commit 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node")
added a sched_node_ids xarray to the port info structure, but never called
xa_destroy on it.

Since xarrays can allocate internal memory, this can result in a memory
leak even if every element in the xarray has been removed.

The xarray is currently embedded in the port_info structure. This appears
to have been done because its use is within functions that take the
port_info as a primary argument.

However, this complicates managing the lifecycle of the field. The
port_info structure is allocated in ice_init_hw() using devm, and it is
not released until the devm cleanup when the driver is unloaded.

The ice_init_hw() function is called in many places, including devlink
reload, and possibly during DDP load after updating the Tx scheduler
layout.

Adding a call of xa_destroy to the ice_deinit_hw() causes Sashiko to raise
multiple concerns due to potential ordering issues and possible ways that
port_info could be a dangling reference.

To handle this, move the sched_node_ids out of port_info and into the hw
structure. All users of the array already have a pointer to hw anyways, and
there is only one sched_node_ids per adapter. While here, remove the overly
verbose comment explaining the nature of the sched_node_ids xarray.

Add the missing xa_destroy to the cleanup path and to ice_deinit_hw(),
ensuring that we properly release the xarray memory.

This was caught by Sashiko during development of unrelated code.

Fixes: 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node")
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Changes in v2:
- Move sched_node_ids out of port_into into hw.
- Link to v1: https://patch.msgid.link/20260514-jk-fix-missing-xa-destroy-v1-1-de437bf52347@intel.com
---
 drivers/net/ethernet/intel/ice/ice_type.h   | 2 +-
 drivers/net/ethernet/intel/ice/ice_common.c | 9 ++++++---
 drivers/net/ethernet/intel/ice/ice_sched.c  | 4 ++--
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index d9a5c1aae7c2..cf147a212707 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -765,7 +765,6 @@ struct ice_port_info {
 	/* List contain profile ID(s) and other params per layer */
 	struct list_head rl_prof_list[ICE_AQC_TOPO_MAX_LEVEL_NUM];
 	struct ice_qos_cfg qos_cfg;
-	struct xarray sched_node_ids;
 	u8 is_vf:1;
 	u8 is_custom_tx_enabled:1;
 };
@@ -930,6 +929,7 @@ struct ice_hw {
 	u8 sw_entry_point_layer;
 	u16 max_children[ICE_AQC_TOPO_MAX_LEVEL_NUM];
 	struct list_head agg_list;	/* lists all aggregator */
+	struct xarray sched_node_ids;
 
 	struct ice_vsi_ctx *vsi_ctx[ICE_MAX_VSI];
 	u8 evb_veb;		/* true for VEB, false for VEPA */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index ef1ce106f81b..04633103e3e6 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -1051,14 +1051,13 @@ int ice_init_hw(struct ice_hw *hw)
 
 	hw->evb_veb = true;
 
-	/* init xarray for identifying scheduling nodes uniquely */
-	xa_init_flags(&hw->port_info->sched_node_ids, XA_FLAGS_ALLOC);
+	xa_init_flags(&hw->sched_node_ids, XA_FLAGS_ALLOC);
 
 	/* Query the allocated resources for Tx scheduler */
 	status = ice_sched_query_res_alloc(hw);
 	if (status) {
 		ice_debug(hw, ICE_DBG_SCHED, "Failed to get scheduler allocated resources\n");
-		goto err_unroll_alloc;
+		goto err_unroll_xarray;
 	}
 	ice_sched_get_psm_clk_freq(hw);
 
@@ -1146,6 +1145,8 @@ int ice_init_hw(struct ice_hw *hw)
 	ice_cleanup_fltr_mgmt_struct(hw);
 err_unroll_sched:
 	ice_sched_cleanup_all(hw);
+err_unroll_xarray:
+	xa_destroy(&hw->sched_node_ids);
 err_unroll_alloc:
 	devm_kfree(ice_hw_to_dev(hw), hw->port_info);
 err_unroll_cqinit:
@@ -1186,6 +1187,8 @@ void ice_deinit_hw(struct ice_hw *hw)
 
 	/* Clear VSI contexts if not already cleared */
 	ice_clear_all_vsi_ctx(hw);
+
+	xa_destroy(&hw->sched_node_ids);
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/ice/ice_sched.c b/drivers/net/ethernet/intel/ice/ice_sched.c
index fff0c1afdb41..ffa18d86729a 100644
--- a/drivers/net/ethernet/intel/ice/ice_sched.c
+++ b/drivers/net/ethernet/intel/ice/ice_sched.c
@@ -371,7 +371,7 @@ void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node)
 
 	devm_kfree(ice_hw_to_dev(hw), node->children);
 	kfree(node->name);
-	xa_erase(&pi->sched_node_ids, node->id);
+	xa_erase(&hw->sched_node_ids, node->id);
 	devm_kfree(ice_hw_to_dev(hw), node);
 }
 
@@ -977,7 +977,7 @@ ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node,
 		if (!new_node->name)
 			return -ENOMEM;
 
-		status = xa_alloc(&pi->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX),
+		status = xa_alloc(&hw->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX),
 				  GFP_KERNEL);
 		if (status) {
 			ice_debug(hw, ICE_DBG_SCHED, "xa_alloc failed for sched node status =%d\n",

---
base-commit: 9e05e91a9a847ed57926414bd7c2c5e54d6c56c6
change-id: 20260514-jk-fix-missing-xa-destroy-d3f90f3711be

Best regards,
--  
Jacob Keller <jacob.e.keller@intel.com>


^ permalink raw reply related

* [PATCH net] vxlan: add missing NULL check for vxlan_sock in GRO path
From: Xiang Mei (Microsoft) @ 2026-07-06 23:33 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: netdev, linux-kernel, Tom Herbert, AutonomousCodeSecurity,
	tgopinath, kys, Xiang Mei (Microsoft)

vxlan_gro_prepare_receive() reads vs->flags from the vxlan_sock returned
by rcu_dereference_sk_user_data(sk) without a NULL check, unlike the rest
of the driver (e.g. vxlan_rcv() does "if (!vs) goto drop;").

udp_tunnel_sock_release() clears sk_user_data to NULL and waits an RCU
grace period before the socket leaves the hash, so any callback running on
the tunnel socket during teardown must tolerate a NULL sk_user_data. If a
VXLAN device is torn down while GRO runs on its UDP tunnel socket,
sk_user_data can be NULL when a packet carrying VXLAN_HF_RCO arrives,
faulting in NAPI/softirq context.

The GRO path was the only sk_user_data reader on the socket missing this
guard (vxlan_rcv and vxlan_err_lookup already have it; the gro_complete
callbacks do not touch sk_user_data). Add the same NULL check so GRO
flushes instead of dereferencing a NULL socket pointer.

  Oops: general protection fault ... SMP KASAN NOPTI
  KASAN: probably user-memory-access in range [0x2018-0x201f]
  RIP: 0010:vxlan_gro_prepare_receive (drivers/net/vxlan/vxlan_core.c:678)
   vxlan_gro_receive (drivers/net/vxlan/vxlan_core.c:713)
   udp_gro_receive (net/ipv4/udp_offload.c:841)
   ...
   dev_gro_receive (net/core/gro.c:522)
   gro_receive_skb (net/core/gro.c:640)
   tun_napi_poll (drivers/net/tun.c:259)
   ...
  Kernel panic - not syncing: Fatal exception in interrupt

Fixes: 5602c48cf875 ("vxlan: change vxlan to use UDP socket GRO")
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
---
 drivers/net/vxlan/vxlan_core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index b5b1253ac08b..f2235bae9bfe 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -663,6 +663,9 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
 	struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
 	__be32 flags;
 
+	if (!vs)
+		return NULL;
+
 	skb_gro_remcsum_init(grc);
 
 	off_vx = skb_gro_offset(skb);
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH iwl-net] ice: add missing xa_destroy for sched_node_ids
From: Jacob Keller @ 2026-07-06 23:36 UTC (permalink / raw)
  To: Simon Horman, Loktionov, Aleksandr
  Cc: Tony Nguyen, Przemek Kitszel, Michal Wilczynski, intel-wired-lan,
	netdev
In-Reply-To: <20260519194602.GK98116@horms.kernel.org>

On 5/19/2026 12:46 PM, Simon Horman wrote:
> On Thu, May 14, 2026 at 09:55:21AM -0700, Jacob Keller wrote:
>> Commit 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node")
>> added a sched_node_ids xarray to the port info structure, but never called
>> xa_destroy on it.
>>
>> Since xarrays can allocate internal memory, this can result in a memory
>> leak even if every element in the xarray has been removed.
>>
>> Add a call to xa_destroy the structure during ice_deinit_hw(), and one to
>> the unrolling cleanup path during errors in ice_init_hw(). While here,
>> remove the overly verbose comment explaining the nature of the
>> sched_node_ids xarray.
>>
>> This was caught by Sashiko during development of unrelated code.
>>
>> Fixes: 16dfa49406bc ("ice: Introduce new parameters in ice_sched_node")
>> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> 
> Reviewed-by: Simon Horman <horms@kernel.org>
> 

Sashiko pointed out the following concerns:

> Is it possible for hw->port_info to be freed but not set to NULL before we
> reach this point?
> In ice_init_hw(), if an error occurs after port_info is allocated, the
> err_unroll_alloc path calls devm_kfree() on hw->port_info but leaves the
> pointer dangling:
> ice_common.c:ice_init_hw() {
> ...
> err_unroll_alloc:
>         devm_kfree(ice_hw_to_dev(hw), hw->port_info);
> ...
> }
> If the device falls back to safe mode after this failure and later calls
> ice_deinit_hw(), does dereferencing hw->port_info here result in a
> use-after-free regression?
> Additionally, if the device boots in firmware recovery mode, ice_init_hw()
> might be skipped entirely, leaving hw->port_info as NULL.
> If a devlink reload later triggers ice_deinit_hw(), will taking the address
> of sched_node_ids from a NULL port_info pointer cause a null pointer
> dereference?
> Would it make sense to guard this call by checking if hw->port_info is
> valid?
This is a valid concern because ice_init_hw and ice_deinit_hw() can be
called from devlink reload, which doesn't appear to be blocked during
recovery mode. As a result, we might potentially access a NULL pointer.

Its concern about a dangling port_info is invalid is harder to trace,
because at least ice_probe() exits immediately if it fails ice_init_hw.
However, we also call it from other places that was less obvious.

Regardless, I submitted a v2 which moves the sched_node_ids out of
port_info and into hw, which makes the life cycle management much simpler.

As a result, I did *not* include either your or Aleksandrs reviewd-by
tags, because the patch is substantially different from v1.

Thanks,
Jake

^ permalink raw reply

* Re: [PATCH net] vxlan: add missing NULL check for vxlan_sock in GRO path
From: Kuniyuki Iwashima @ 2026-07-06 23:56 UTC (permalink / raw)
  To: xmei5
  Cc: AutonomousCodeSecurity, andrew+netdev, davem, edumazet, kuba, kys,
	linux-kernel, netdev, pabeni, tgopinath, tom
In-Reply-To: <20260706233344.3334648-1-xmei5@asu.edu>

From: "Xiang Mei (Microsoft)" <xmei5@asu.edu>
Date: Mon,  6 Jul 2026 23:33:44 +0000
> vxlan_gro_prepare_receive() reads vs->flags from the vxlan_sock returned
> by rcu_dereference_sk_user_data(sk) without a NULL check, unlike the rest
> of the driver (e.g. vxlan_rcv() does "if (!vs) goto drop;").
> 
> udp_tunnel_sock_release() clears sk_user_data to NULL and waits an RCU
> grace period before the socket leaves the hash, so any callback running on
> the tunnel socket during teardown must tolerate a NULL sk_user_data. If a
> VXLAN device is torn down while GRO runs on its UDP tunnel socket,
> sk_user_data can be NULL when a packet carrying VXLAN_HF_RCO arrives,
> faulting in NAPI/softirq context.
> 
> The GRO path was the only sk_user_data reader on the socket missing this
> guard (vxlan_rcv and vxlan_err_lookup already have it; the gro_complete
> callbacks do not touch sk_user_data). Add the same NULL check so GRO
> flushes instead of dereferencing a NULL socket pointer.
> 
>   Oops: general protection fault ... SMP KASAN NOPTI
>   KASAN: probably user-memory-access in range [0x2018-0x201f]
>   RIP: 0010:vxlan_gro_prepare_receive (drivers/net/vxlan/vxlan_core.c:678)
>    vxlan_gro_receive (drivers/net/vxlan/vxlan_core.c:713)
>    udp_gro_receive (net/ipv4/udp_offload.c:841)
>    ...
>    dev_gro_receive (net/core/gro.c:522)
>    gro_receive_skb (net/core/gro.c:640)
>    tun_napi_poll (drivers/net/tun.c:259)
>    ...
>   Kernel panic - not syncing: Fatal exception in interrupt
> 
> Fixes: 5602c48cf875 ("vxlan: change vxlan to use UDP socket GRO")
> Reported-by: AutonomousCodeSecurity@microsoft.com
> Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> ---
>  drivers/net/vxlan/vxlan_core.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> index b5b1253ac08b..f2235bae9bfe 100644
> --- a/drivers/net/vxlan/vxlan_core.c
> +++ b/drivers/net/vxlan/vxlan_core.c
> @@ -663,6 +663,9 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
>  	struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
>  	__be32 flags;
>  
> +	if (!vs)
> +		return NULL;

Please work on the latest tree, it's fixed 2 months ago.

commit 30a45c0bffdd62350261e2f2689fdba426a33578
Author: Kuniyuki Iwashima <kuniyu@google.com>
Date:   Sat May 2 03:12:59 2026

    vxlan: Fix potential null-ptr-deref in vxlan_gro_prepare_receive().

^ 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