Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net v2 2/2] 8021q: delete cleared egress QoS mappings
From: Paolo Abeni @ 2026-04-23 10:13 UTC (permalink / raw)
  To: Simon Horman, Ren Wei
  Cc: netdev, edumazet, andrew+netdev, davem, kuba, kees, yuantan098,
	ylong030, yifanwucs, tomapufckgml, bird
In-Reply-To: <20260422162041.GO651125@horms.kernel.org>

On 4/22/26 6:20 PM, Simon Horman wrote:
> On Mon, Apr 20, 2026 at 11:18:46AM +0800, Ren Wei wrote:
>> From: Longxuan Yu <ylong030@ucr.edu>
>>
>> vlan_dev_set_egress_priority() currently keeps cleared egress
>> priority mappings in the hash as tombstones. Repeated set/clear cycles
>> with distinct skb priorities therefore accumulate mapping nodes until
>> device teardown and leak memory.
>>
>> Delete mappings when vlan_prio is cleared instead of keeping tombstones.
>> Now that the egress mapping lists are RCU protected, the node can be
>> unlinked safely and freed after a grace period.
>>
>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>> Cc: stable@kernel.org
>> Reported-by: Yifan Wu <yifanwucs@gmail.com>
>> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
>> Reported-by: Xin Liu <bird@lzu.edu.cn>
>> Co-developed-by: Yuan Tan <yuantan098@gmail.com>
>> Signed-off-by: Yuan Tan <yuantan098@gmail.com>
>> Signed-off-by: Longxuan Yu <ylong030@ucr.edu>
>> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
>> ---
>>  net/8021q/vlan_dev.c     | 20 ++++++++++++++------
>>  net/8021q/vlan_netlink.c |  4 ----
>>  2 files changed, 14 insertions(+), 10 deletions(-)
>>
>> diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
>> index a5340932b657..7aa3af8b10ea 100644
>> --- a/net/8021q/vlan_dev.c
>> +++ b/net/8021q/vlan_dev.c
>> @@ -172,26 +172,34 @@ int vlan_dev_set_egress_priority(const struct net_device *dev,
>>  				 u32 skb_prio, u16 vlan_prio)
>>  {
>>  	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
>> +	struct vlan_priority_tci_mapping __rcu **mpp;
>>  	struct vlan_priority_tci_mapping *mp;
>>  	struct vlan_priority_tci_mapping *np;
>>  	u32 bucket = skb_prio & 0xF;
>>  	u32 vlan_qos = (vlan_prio << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK;
>>  
>>  	/* See if a priority mapping exists.. */
>> -	mp = rtnl_dereference(vlan->egress_priority_map[bucket]);
>> +	mpp = &vlan->egress_priority_map[bucket];
>> +	mp = rtnl_dereference(*mpp);
>>  	while (mp) {
>>  		if (mp->priority == skb_prio) {
>> -			if (mp->vlan_qos && !vlan_qos)
>> +			if (!vlan_qos) {
>> +				rcu_assign_pointer(*mpp, rtnl_dereference(mp->next));
>>  				vlan->nr_egress_mappings--;
>> -			else if (!mp->vlan_qos && vlan_qos)
>> -				vlan->nr_egress_mappings++;
>> -			WRITE_ONCE(mp->vlan_qos, vlan_qos);
>> +				kfree_rcu(mp, rcu);
>> +			} else {
>> +				WRITE_ONCE(mp->vlan_qos, vlan_qos);
>> +			}
>>  			return 0;
>>  		}
>> -		mp = rtnl_dereference(mp->next);
>> +		mpp = &mp->next;
>> +		mp = rtnl_dereference(*mpp);
>>  	}
> 
> Hi Ren,
> 
> Thanks for splitting up the patchset, it is very helpful to me.
> 
> It seems to me that the mpp/mp construct used is a bit complex and
> stems from the use of a hand-rolled list centred the next field of
> struct vlan_priority_tci_mapping.
> 
> I wonder if things can be simplified by moving to use a standardised
> list construct, such as an hlist. And the helpers available for using it.

I agree hlist_rcu usage would simplify the code above, but I fear it
will make this already relatively big change even bigger, as several
list traversing places will need to be updated accordingly.

I think conversion to hlist_rcu could/should be a net-next follow-up.

Thanks,

Paolo


^ permalink raw reply

* Re: [PATCH v2 net 01/11] octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k
From: Simon Horman @ 2026-04-23 10:01 UTC (permalink / raw)
  To: rkannoth
  Cc: Simon Horman, davem, edumazet, linux-kernel, andrew+netdev,
	sumang, netdev, dan.carpenter, sgoutham, error27, kuba, pabeni
In-Reply-To: <20260420023442.3295891-2-rkannoth@marvell.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.

For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k

This patch converts several cn20k NPC functions from void to int return
types to propagate MCAM key-type lookup errors. It adds error checking
for npc_mcam_idx_2_key_type() and threads these errors through the
cn20k MCAM handlers.

Link: https://lore.kernel.org/netdev/adiQJvuKlEhq2ILx@stanley.mountain/

> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> index 7291fdb89b03..18d70f4527d1 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c

[ ... ]

> @@ -1415,9 +1435,9 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu,
>  	else
>  		nix_intf = pfvf->nix_rx_intf;
>
> -	npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
> -				    &req->entry_data, req->enable_entry,
> -				    req->hw_prio, req->req_kw_type);
> +	rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
> +					 &req->entry_data, req->enable_entry,
> +					 req->hw_prio, req->req_kw_type);
>
>  	mutex_unlock(&mcam->lock);
>
>  	rsp->entry = entry_rsp.entry;
>  	return 0;

Does this function need to return rc instead of 0? If
npc_cn20k_config_mcam_entry() fails (for example, due to an invalid key
type), the function still returns success to the caller. The mailbox
response indicates the entry was successfully allocated and configured,
but the hardware entry may be in an undefined state.

[ ... ]

^ permalink raw reply

* Re: [PATCH] selftests: net: add RDMA CM observability and regression scripts
From: Leon Romanovsky @ 2026-04-23 10:19 UTC (permalink / raw)
  To: Chenguang Zhao
  Cc: Shuah Khan, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netdev, linux-kselftest,
	RDMA mailing list
In-Reply-To: <20260416062224.1546388-1-zhaochenguang@kylinos.cn>

On Thu, Apr 16, 2026 at 02:22:24PM +0800, Chenguang Zhao wrote:
> Add a minimal RDMA CM selftest suite that captures observability
> baselines and runs trace, counter-delta, and fault-injection oriented
> checks, plus a review-loop helper for repeated validation rounds.
> 
> Usage (client side):
> - export
>   CM_WORKLOAD_CMD='ib_send_bw -d <dev> -i <port> -R -g <gid> <server_ip>'
>   (User can customize CM_WORKLOAD_CMD)
> - sudo -E make -C tools/testing/selftests
>   TARGETS=drivers/net/rdma run_tests
> 
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
>   The first patch adds a focused RDMA CM selftest suite under
>   kselftest to make CM behavior easier to observe and validate
>   in routine regression runs.
> 
>   It introduces baseline collection, trace-sequence checks,
>   counter-delta checks, and failslab-based recovery checks, plus
>   a review-loop script for one-shot serial execution. It also
>   registers drivers/net/rdma in the top-level selftests TARGETS,
>   so the suite runs through standard kselftest flow
>   (make ... TARGETS=drivers/net/rdma run_tests) instead of requiring
>   manual script-by-script execution.
> ---
>  tools/testing/selftests/Makefile              |   1 +
>  .../selftests/drivers/net/rdma/Makefile       |  13 ++
>  .../selftests/drivers/net/rdma/README.md      | 168 ++++++++++++++++++
>  .../drivers/net/rdma/rdma_cm_baseline.sh      |  58 ++++++
>  .../drivers/net/rdma/rdma_cm_counter_delta.sh |  72 ++++++++
>  .../net/rdma/rdma_cm_fault_injection.sh       |  95 ++++++++++
>  .../drivers/net/rdma/rdma_cm_review_loop.sh   |  35 ++++
>  .../net/rdma/rdma_cm_trace_sequence.sh        |  83 +++++++++
>  .../selftests/drivers/net/rdma/rdma_common.sh | 126 +++++++++++++
>  9 files changed, 651 insertions(+)
>  create mode 100644 tools/testing/selftests/drivers/net/rdma/Makefile
>  create mode 100644 tools/testing/selftests/drivers/net/rdma/README.md
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_cm_baseline.sh
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_cm_counter_delta.sh
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_cm_fault_injection.sh
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_cm_review_loop.sh
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_cm_trace_sequence.sh
>  create mode 100755 tools/testing/selftests/drivers/net/rdma/rdma_common.sh
> 
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 984abb6d42ab..0df7034f46b2 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -22,6 +22,7 @@ TARGETS += drivers/ntsync
>  TARGETS += drivers/s390x/uvdevice
>  TARGETS += drivers/net
>  TARGETS += drivers/net/bonding
> +TARGETS += drivers/net/rdma

It is very wrong place to put RDMA functionality.
We have tools/testing/selftests/rdma folder for that.

Thanks

^ permalink raw reply

* Re: [PATCH net v2 1/2] 8021q: use RCU for egress QoS mappings
From: patchwork-bot+netdevbpf @ 2026-04-23 10:20 UTC (permalink / raw)
  To: Ren Wei
  Cc: netdev, horms, edumazet, andrew+netdev, davem, kuba, pabeni, kees,
	yuantan098, ylong030, yifanwucs, tomapufckgml, bird
In-Reply-To: <9136768189f8c6d3f824f476c62d2fa1111688e8.1776647968.git.yuantan098@gmail.com>

Hello:

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

On Mon, 20 Apr 2026 11:18:45 +0800 you wrote:
> From: Longxuan Yu <ylong030@ucr.edu>
> 
> The TX fast path and reporting paths walk egress QoS mappings without
> RTNL. Convert the mapping lists to RCU-protected pointers, use RCU
> reader annotations in readers, and defer freeing mapping nodes with an
> embedded rcu_head.
> 
> [...]

Here is the summary with links:
  - [net,v2,1/2] 8021q: use RCU for egress QoS mappings
    https://git.kernel.org/netdev/net/c/fc69decc811b
  - [net,v2,2/2] 8021q: delete cleared egress QoS mappings
    https://git.kernel.org/netdev/net/c/7dddc74af369

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



^ permalink raw reply

* Re: [PATCH v2 net 0/11] octeontx2-af: npc: cn20k: MCAM fixes
From: Ratheesh Kannoth @ 2026-04-23 10:20 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: netdev, linux-kernel, sgoutham, davem, edumazet, kuba,
	andrew+netdev, dan.carpenter
In-Reply-To: <a93c34d6-8c9f-417e-9ed2-ff4a9f668d11@redhat.com>

On 2026-04-23 at 15:14:10, Paolo Abeni (pabeni@redhat.com) wrote:
> On 4/20/26 4:34 AM, Ratheesh Kannoth wrote:
> > This series tightens Marvell OcteonTX2 AF NPC support for CN20K
> > silicon around MCAM key typing, optional debugfs setup, defrag
> > allocation rollback, x2 versus x4 KEX profiles and default-rule
> > allocation, logical MCAM clear and configuration, default-rule index
> > bookkeeping and explicit teardown, and NIXLF reserved-slot lookup when
> > default rules are missing.
> >
> > Patches 1 through 3 focus on AF error handling: propagate
> > npc_mcam_idx_2_key_type() failures through cn20k MCAM enable, config,
> > copy, and read paths; treat cn20k NPC debugfs files as optional so
> > probe does not fail when debugfs is unavailable; and fix defrag MCAM
> > allocation rollback so allocation errno is not overwritten by subbank
> > index resolution.
> >
> > Patches 4 and 5 align default-rule and flow-install behaviour with the
> > loaded mkex profile: prefer x4 default entries when the profile is x4,
> > and reject x4 flow keys when the profile is strictly x2.
> >
> > Patches 6 through 8 refine cn20k MCAM programming: clear entries by
> > logical index and resolved key width, fix bank and CFG sequencing in
> > npc_cn20k_config_mcam_entry(), and read action metadata from the
> > correct bank in npc_cn20k_read_mcam_entry().
> >
> > Patches 9 through 11 complete default-rule lifecycle handling:
> > initialize all default-rule index outputs up front, tear down default
> > MCAM rules explicitly (coordinated with npc_mcam_free_all_entries()),
> > and reject USHRT_MAX sentinel indices in npc_get_nixlf_mcam_index()
> > for cn20k.
> >
> > Ratheesh Kannoth (11):
> >   octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k
> >   octeontx2-af: npc: cn20k: Drop debugfs_create_file() error checks in
> >   init
> >   octeontx2-af: npc: cn20k: Propagate errors in defrag MCAM alloc
> >   rollback
> >   octeontx2-af: npc: cn20k: Make default entries as x4.
> >   octeontx2-af: npc: cn20k: Reject request for x4 entries in x2
> >   profile.
> >   octeontx2-af: npc: cn20k: Clear MCAM entries by index and key width
> >   octeontx2-af: npc: cn20k: Fix bank value.
> >   octeontx2-af: npc: cn20k: Fix MCAM actions read
> >   octeontx2-af: npc: cn20k: Initialize default-rule index outputs up
> >   front
> >   octeontx2-af: npc: cn20k: Tear down default MCAM rules explicitly on
> >   free
> >   octeontx2-af: npc: cn20k: Reject missing default-rule MCAM indices
> >
> > Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
> >
> > --
> > v1 -> v2: Addressed simon comments. Added more patch fixes to this series.
> > 	https://lore.kernel.org/netdev/20260418162013.GG280379@horms.kernel.org/
>
> I strongly suggest avoid extending a series to address issues found by
> the AI review, unless such issues are actual regressions/problems
> introduced by the new code. Sashiko tends to find a lot of collateral
> problem and the series could easily expand beyond any hope of merging.
>
> Instead you should reply on the ML, commenting on sashiko's review,
> explain why or why not the outlined problems are valid and/or should be
> handled separately.
>
> Thanks,
>
> Paolo
>

Patch 1: [PATCH v2 net 01/11] octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k

>Is it intentional to discard the return value of npc_cn20k_enable_mcam_entry()
>here?
>If disabling an entry fails during teardown (e.g., in
>rvu_npc_disable_mcam_entries()), this could silently leave stale, active packet
>steering rules in the hardware.

Yes, the return value of npc_cn20k_enable_mcam_entry() is
intentionally discarded in this context.

The primary reason is that the validity of the MCAM index is
already verified prior to this call, so the function is not
expected to fail under normal conditions.

Additionally, during
rvu_npc_disable_mcam_entries()), failure handling is
best-effort. At this stage, the system is already
transitioning state, and retrying or propagating errors
does not provide meaningful recovery.

Introducing strict error handling here would require
propagating return values through multiple call sites,
which would significantly increase the scope and
complexity of this change.

Any modification to the return type or contract of
npc_enable_mcam_entry() would involve a broader refactor
across the driver and is therefore intentionally deferred
to a separate, dedicated change.

Given these constraints, ignoring the return value here is
a deliberate design decision and does not introduce
functional risk beyond what is already accepted in
teardown scenarios.

>If npc_update_dmac_value() returns an error, its caller in rvu_npc_fs.c seems
>to ignore it:
>			if (rule->vfvlan_cfg)
>				npc_update_dmac_value(rvu, blkaddr, rule, pfvf);
>			if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) {
>				...
>			}
>			npc_enable_mcam_entry(rvu, mcam, blkaddr,
>					      rule->entry, true);
>Could this lead to the code continuing and activating the rule in hardware with
>an invalid or outdated MAC address?

Handling the error returned by npc_update_dmac_value() only
at this call site would not be sufficient.

For correctness, the error would need to be propagated to
higher-level callers, which would require changes across
multiple layers of the call chain. This significantly
increases the scope and complexity of the change.

Such propagation would also introduce additional handling
requirements in several paths, potentially leading to more
issues being flagged by static analysis tools.

Given that this patch is focused on a specific fix,
extending it to include broader error-handling changes is
out of scope.

Therefore, this is being treated as a separate enhancement
and is intentionally not addressed here.

Patch 4: [PATCH v2 net 04/11] octeontx2-af: npc: cn20k: Make default entries as x4.

>Does this in-place modification break the sorting invariant of the mcam_rules
>list?
>In rvu_mcam_add_rule(), the mcam->mcam_rules list appears to be strictly
>maintained in ascending order by entry. Modifying rule->entry in-place
>without removing and correctly re-inserting the node could leave the list
>unsorted.
>Could this cause subsequent rule insertions or debug routines that rely on
>the list order to behave incorrectly?

The mcam_rules list is not required to maintain a strict
sorting invariant for correctness.

Although rvu_mcam_add_rule() inserts entries in ascending
order, there is no functional dependency on this ordering
in subsequent operations.

All lookups and operations on the mcam_rules list iterate
over the entire list rather than relying on ordering.
Therefore, modifying rule->entry in place does not impact
correctness, even if the list becomes unsorted.

As a result, leaving the list temporarily or permanently
unsorted does not lead to incorrect behavior in rule
insertion, lookup, or debug routines.

>While this static structure predates this patch, can this design cause state
>contamination across multiple devices?
>Because pfl_info is static, it is shared globally across all network
>interfaces and physical NICs on the host. However, it is protected by
>mutex_lock(&pfvf->mbox.lock), which is a per-NIC lock.
>If multiple interfaces probe concurrently, they could acquire their own locks
>and race on this global struct. Additionally, if the host has multiple cards
>configured with different hardware profiles (such as one X2 and one X4), the
>first card to query the hardware will permanently cache its profile for all
>subsequent cards.
>Would it be safer to cache this hardware profile data in a device-private
> +			 */
> +			WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n",
> +			     __func__, pcifunc, ptr[i]);
> +			return;
> +		}
AF processes mailbox (mbox) requests from PFs and VFs
serially. This guarantees that initialization of pfl_info
is not performed concurrently by multiple devices.

The profile information is populated once by AF during NPC
initialization and remains constant for the lifetime of the
DUT. There are no subsequent writes or updates to this data
after initialization.

As a result, pfl_info effectively becomes read-only after
being set. All later accesses from different PFs or VFs are
only reading the same stable value.

Even if multiple interfaces probe concurrently, they only
perform reads after initialization is complete. Concurrent
reads of immutable data do not introduce race conditions.

Regarding systems with multiple NICs or different hardware
profiles, the AF provides a consistent hardware view per
system. The profile data exposed through mbox is not
expected to vary dynamically per device after init.

Therefore, using a static structure here does not lead to
state contamination or incorrect behavior, and no
additional per-device storage is required in this context.

Patch 10: [PATCH v2 net 10/11] octeontx2-af: npc: cn20k: Tear down default MCAM rules explicitly on free

>> +		rc = npc_cn20k_idx_free(rvu, &ptr[i], 1);
>> +		if (rc) {
>> +			/* Non recoverable error. Let us WARN and return. Keep system alive to
>> +			 * enable debugging
>> +			 */
>> +			WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n",
>> +			     __func__, pcifunc, ptr[i]);
>> +			return;
>> +		}
>Does this early return on failure skip the software list cleanup and leak
>the rvu_npc_mcam_rule list nodes for both this index and any successfully
>freed indices?
>Since the final list cleanup loop is bypassed, it appears these software
>rules remain permanently. Also, does this skip hardware cleanup for any
>remaining default rules in the array?
Default MCAM rules are allocated upfront during
initialization, and their presence is required for correct
system operation. A failure during allocation itself would
already indicate a critical issue.

If npc_cn20k_idx_free() fails, it indicates that freeing of
previously allocated indices has failed. This suggests a
serious inconsistency in the allocation/free logic rather
than a recoverable runtime error.

In such a scenario, attempting partial cleanup (either
software list or remaining hardware entries) is unsafe, as
it can further corrupt the allocation state and make
debugging significantly harder.

Therefore, the function intentionally emits a WARN and
returns early to preserve the current state for debugging
purposes. This avoids masking the underlying issue.

While this does skip subsequent cleanup of software list
nodes and remaining hardware entries, continuing execution
after such a failure is not considered safe or meaningful.

This condition is treated as a non-recoverable error and
indicates a deeper issue in the allocation/free algorithm
that must be investigated separately.

>+	}
>x_handler_npc_cn20k_mcam_alloc_and_write_entry
>> +
>> +	mutex_lock(&mcam->lock);
>> +	list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) {
>> +		for (int i = 0; i < 4; i++) {
>> +			if (ptr[i] != rule->entry)
>> +				continue;
>> +
>> +			list_del(&rule->list);
>> +			kfree(rule);
>> +			break;
>> +		}
>> +	}
>> +	mutex_unlock(&mcam->lock);
>Can dropping mcam->lock during npc_cn20k_idx_free() lead to a race
>condition? When the hardware index is freed, a concurrent thread could
>immediately allocate it and insert a new rule into mcam->mcam_rules with
>the same entry ID.

>Later, when this thread re-acquires the lock to clean up the software
>rules, the inner loop break only exits the for loop, meaning the list
>traversal continues. Would this cause both the old rule and the newly
>allocated concurrent rule to be deleted and freed, leading to a
>use-after-free for the active thread?

No. There is no concurrent threads;
default indexes are allocated thru mbox messages to AF.
AF process mbox messages processed one by one. So there is no chance of concurrency.

^ permalink raw reply

* [PATCH net v2] ipv6: validate extension header length before copying to cmsg
From: Qi Tang @ 2026-04-23 10:22 UTC (permalink / raw)
  To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: netdev, linux-kernel
In-Reply-To: <20260419150344.624673-1-tpluszz77@gmail.com>

ip6_datagram_recv_specific_ctl() builds IPV6_{HOPOPTS,DSTOPTS,RTHDR}
cmsgs (and their IPV6_2292* legacy counterparts) by trusting the
on-wire hdrlen byte (ptr[1]) when computing the put_cmsg() length.
The length was validated only at parse time (ipv6_parse_hopopts(),
etc.).  An nftables payload-write expression can rewrite hdrlen after
parsing and before the skb reaches recvmsg; the write itself is
in-bounds but put_cmsg() then reads up to ((hdrlen+1) << 3) = 2040
bytes from an 8-byte header.  nftables is reachable from an
unprivileged user namespace, so this is an unprivileged
slab-out-of-bounds read:

  BUG: KASAN: slab-out-of-bounds in put_cmsg+0x3ac/0x540
   put_cmsg+0x3ac/0x540
   udpv6_recvmsg+0xca0/0x1250
   sock_recvmsg+0xdf/0x190
   ____sys_recvmsg+0x1b1/0x620

Add ipv6_get_exthdr_len() which computes the extension header length
and validates it against skb_tail_pointer(skb), returning 0 on
failure.  Extension headers are kept in the linear skb area by
pskb_may_pull() during input, so skb_tail_pointer() is the correct
bound.

Use ipv6_get_exthdr_len() at all non-AH call sites: the five
standalone cmsg blocks (HbH, 2292HbH, 2292DSTOPTS x2, 2292RTHDR)
and the three standard cases in the extension-header walk loop
(DSTOPTS, ROUTING, default).  AH retains an inline bounds check
because its length formula differs ((ptr[1]+2)<<2).

When the walk loop detects a corrupted header, return from the
function instead of continuing to process later socket options.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
---
Changes v1 -> v2 (Paolo Abeni):
  - Factor repeated bounds-check + put_cmsg into ipv6_get_exthdr_len()
  - Return from the function on corrupted walk-loop entry instead of
    goto + empty label

v1: https://lore.kernel.org/netdev/20260419150344.624673-1-tpluszz77@gmail.com/

 net/ipv6/datagram.c | 46 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 8 deletions(-)

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 972bf0426d59..0a7b74d5f402 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -617,6 +617,13 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
 	}
 }
 
+static u16 ipv6_get_exthdr_len(const struct sk_buff *skb, const u8 *ptr)
+{
+	u16 len = (ptr[1] + 1) << 3;
+
+	return (ptr + len <= skb_tail_pointer(skb)) ? len : 0;
+}
+
 void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 				    struct sk_buff *skb)
 {
@@ -643,7 +650,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	/* HbH is allowed only once */
 	if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, len, ptr);
 	}
 
 	if (opt->lastopt &&
@@ -667,23 +677,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 			switch (nexthdr) {
 			case IPPROTO_DSTOPTS:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				if (np->rxopt.bits.dstopts)
 					put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
 				break;
 			case IPPROTO_ROUTING:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				if (np->rxopt.bits.srcrt)
 					put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
 				break;
 			case IPPROTO_AH:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 2) << 2;
+				if (ptr + len > skb_tail_pointer(skb))
+					return;
 				break;
 			default:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				break;
 			}
 
@@ -705,19 +723,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	}
 	if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst0) {
 		u8 *ptr = nh + opt->dst0;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.osrcrt && opt->srcrt) {
 		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+		u16 len = ipv6_get_exthdr_len(skb, (u8 *)rthdr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, len, rthdr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst1) {
 		u8 *ptr = nh + opt->dst1;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.rxorigdstaddr) {
 		struct sockaddr_in6 sin6;
-- 
2.47.3


^ permalink raw reply related

* [PATCH net 0/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (series)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet

cake_dump_stats() runs without qdisc spinlock being held.

This mini series adds missing READ_ONCE()/WRITE_ONCE() annotations.

Original patch was too big, splitting it eases code review.

Eric Dumazet (5):
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (I)
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (II)
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (III)
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (IV)
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (V)

 net/sched/sch_cake.c | 217 ++++++++++++++++++++++---------------------
 1 file changed, 113 insertions(+), 104 deletions(-)

-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply

* [PATCH net 1/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (I)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet, Toke Høiland-Jørgensen
In-Reply-To: <20260423102324.3172448-1-edumazet@google.com>

cake_dump_stats() runs without qdisc spinlock being held.

In this first patch, I add READ_ONCE()/WRITE_ONCE() annotations
for the following fields:

- way_hits
- way_misses
- way_collisions
- sparse_flow_count
- decaying_flow_count

Other annotations are added in following patches, to ease code review.

Fixes: 046f6fd5daef ("sched: Add Common Applications Kept Enhanced (cake) qdisc")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: "Toke Høiland-Jørgensen" <toke@toke.dk>
---
 net/sched/sch_cake.c | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 02e1fa4577ae6089646e79b901b0d21945891855..bcc601fc486b13ab1609134f1ee29ebdcb9e8a06 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -813,7 +813,7 @@ static u32 cake_hash(struct cake_tin_data *q, const struct sk_buff *skb,
 		     i++, k = (k + 1) % CAKE_SET_WAYS) {
 			if (q->tags[outer_hash + k] == flow_hash) {
 				if (i)
-					q->way_hits++;
+					WRITE_ONCE(q->way_hits, q->way_hits + 1);
 
 				if (!q->flows[outer_hash + k].set) {
 					/* need to increment host refcnts */
@@ -831,7 +831,7 @@ static u32 cake_hash(struct cake_tin_data *q, const struct sk_buff *skb,
 		for (i = 0; i < CAKE_SET_WAYS;
 			 i++, k = (k + 1) % CAKE_SET_WAYS) {
 			if (!q->flows[outer_hash + k].set) {
-				q->way_misses++;
+				WRITE_ONCE(q->way_misses, q->way_misses + 1);
 				allocate_src = cake_dsrc(flow_mode);
 				allocate_dst = cake_ddst(flow_mode);
 				goto found;
@@ -841,7 +841,7 @@ static u32 cake_hash(struct cake_tin_data *q, const struct sk_buff *skb,
 		/* With no empty queues, default to the original
 		 * queue, accept the collision, update the host tags.
 		 */
-		q->way_collisions++;
+		WRITE_ONCE(q->way_collisions, q->way_collisions + 1);
 		allocate_src = cake_dsrc(flow_mode);
 		allocate_dst = cake_ddst(flow_mode);
 
@@ -1917,11 +1917,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		if (!flow->set) {
 			list_add_tail(&flow->flowchain, &b->new_flows);
 		} else {
-			b->decaying_flow_count--;
+			WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count - 1);
 			list_move_tail(&flow->flowchain, &b->new_flows);
 		}
 		flow->set = CAKE_SET_SPARSE;
-		b->sparse_flow_count++;
+		WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count + 1);
 
 		flow->deficit = cake_get_flow_quantum(b, flow, q->config->flow_mode);
 	} else if (flow->set == CAKE_SET_SPARSE_WAIT) {
@@ -1929,7 +1929,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		 * in the bulk rotation.
 		 */
 		flow->set = CAKE_SET_BULK;
-		b->sparse_flow_count--;
+		WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
 		b->bulk_flow_count++;
 
 		cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2149,7 +2149,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 		 */
 		if (flow->set == CAKE_SET_SPARSE) {
 			if (flow->head) {
-				b->sparse_flow_count--;
+				WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
 				b->bulk_flow_count++;
 
 				cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2192,27 +2192,27 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 					cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 					cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
 
-					b->decaying_flow_count++;
+					WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count + 1);
 				} else if (flow->set == CAKE_SET_SPARSE ||
 					   flow->set == CAKE_SET_SPARSE_WAIT) {
-					b->sparse_flow_count--;
-					b->decaying_flow_count++;
+					WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
+					WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count + 1);
 				}
 				flow->set = CAKE_SET_DECAYING;
 			} else {
 				/* remove empty queue from the flowchain */
 				list_del_init(&flow->flowchain);
 				if (flow->set == CAKE_SET_SPARSE ||
-				    flow->set == CAKE_SET_SPARSE_WAIT)
-					b->sparse_flow_count--;
-				else if (flow->set == CAKE_SET_BULK) {
+				    flow->set == CAKE_SET_SPARSE_WAIT) {
+					WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
+				} else if (flow->set == CAKE_SET_BULK) {
 					b->bulk_flow_count--;
 
 					cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 					cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
-				} else
-					b->decaying_flow_count--;
-
+				} else {
+					WRITE_ONCE(b->decaying_flow_count, b->decaying_flow_count - 1);
+				}
 				flow->set = CAKE_SET_NONE;
 			}
 			goto begin;
@@ -3050,12 +3050,12 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 		PUT_TSTAT_U32(BASE_DELAY_US,
 			      ktime_to_us(ns_to_ktime(b->base_delay)));
 
-		PUT_TSTAT_U32(WAY_INDIRECT_HITS, b->way_hits);
-		PUT_TSTAT_U32(WAY_MISSES, b->way_misses);
-		PUT_TSTAT_U32(WAY_COLLISIONS, b->way_collisions);
+		PUT_TSTAT_U32(WAY_INDIRECT_HITS, READ_ONCE(b->way_hits));
+		PUT_TSTAT_U32(WAY_MISSES, READ_ONCE(b->way_misses));
+		PUT_TSTAT_U32(WAY_COLLISIONS, READ_ONCE(b->way_collisions));
 
-		PUT_TSTAT_U32(SPARSE_FLOWS, b->sparse_flow_count +
-					    b->decaying_flow_count);
+		PUT_TSTAT_U32(SPARSE_FLOWS, READ_ONCE(b->sparse_flow_count) +
+					    READ_ONCE(b->decaying_flow_count));
 		PUT_TSTAT_U32(BULK_FLOWS, b->bulk_flow_count);
 		PUT_TSTAT_U32(UNRESPONSIVE_FLOWS, b->unresponsive_flow_count);
 		PUT_TSTAT_U32(MAX_SKBLEN, b->max_skblen);
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* [PATCH net 2/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (II)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet, Toke Høiland-Jørgensen
In-Reply-To: <20260423102324.3172448-1-edumazet@google.com>

cake_dump_stats() runs without qdisc spinlock being held.

In this second patch, I add READ_ONCE()/WRITE_ONCE() annotations
for the following fields:

- bulk_flow_count
- unresponsive_flow_count
- max_skblen
- flow_quantum

Other annotations are added in following patches, to ease code review.

Fixes: 046f6fd5daef ("sched: Add Common Applications Kept Enhanced (cake) qdisc")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: "Toke Høiland-Jørgensen" <toke@toke.dk>
---
 net/sched/sch_cake.c | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index bcc601fc486b13ab1609134f1ee29ebdcb9e8a06..d7465ee4c5507467effd5aaaf8cfd05bcafde2cf 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1590,7 +1590,8 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)
 	}
 
 	if (cobalt_queue_full(&flow->cvars, &b->cparams, now))
-		b->unresponsive_flow_count++;
+		WRITE_ONCE(b->unresponsive_flow_count,
+			   b->unresponsive_flow_count + 1);
 
 	len = qdisc_pkt_len(skb);
 	q->buffer_used      -= skb->truesize;
@@ -1795,7 +1796,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	}
 
 	if (unlikely(len > b->max_skblen))
-		b->max_skblen = len;
+		WRITE_ONCE(b->max_skblen, len);
 
 	if (qdisc_pkt_segs(skb) > 1 && q->config->rate_flags & CAKE_FLAG_SPLIT_GSO) {
 		struct sk_buff *segs, *nskb;
@@ -1930,7 +1931,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		 */
 		flow->set = CAKE_SET_BULK;
 		WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
-		b->bulk_flow_count++;
+		WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count + 1);
 
 		cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 		cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2150,7 +2151,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 		if (flow->set == CAKE_SET_SPARSE) {
 			if (flow->head) {
 				WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
-				b->bulk_flow_count++;
+				WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count + 1);
 
 				cake_inc_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 				cake_inc_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2177,7 +2178,8 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 		if (!skb) {
 			/* this queue was actually empty */
 			if (cobalt_queue_empty(&flow->cvars, &b->cparams, now))
-				b->unresponsive_flow_count--;
+				WRITE_ONCE(b->unresponsive_flow_count,
+					   b->unresponsive_flow_count - 1);
 
 			if (flow->cvars.p_drop || flow->cvars.count ||
 			    ktime_before(now, flow->cvars.drop_next)) {
@@ -2187,7 +2189,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 				list_move_tail(&flow->flowchain,
 					       &b->decaying_flows);
 				if (flow->set == CAKE_SET_BULK) {
-					b->bulk_flow_count--;
+					WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count - 1);
 
 					cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 					cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2206,7 +2208,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 				    flow->set == CAKE_SET_SPARSE_WAIT) {
 					WRITE_ONCE(b->sparse_flow_count, b->sparse_flow_count - 1);
 				} else if (flow->set == CAKE_SET_BULK) {
-					b->bulk_flow_count--;
+					WRITE_ONCE(b->bulk_flow_count, b->bulk_flow_count - 1);
 
 					cake_dec_srchost_bulk_flow_count(b, flow, q->config->flow_mode);
 					cake_dec_dsthost_bulk_flow_count(b, flow, q->config->flow_mode);
@@ -2329,9 +2331,9 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
 	u8  rate_shft = 0;
 	u64 rate_ns = 0;
 
-	b->flow_quantum = 1514;
 	if (rate) {
-		b->flow_quantum = max(min(rate >> 12, 1514ULL), 300ULL);
+		WRITE_ONCE(b->flow_quantum,
+			   max(min(rate >> 12, 1514ULL), 300ULL));
 		rate_shft = 34;
 		rate_ns = ((u64)NSEC_PER_SEC) << rate_shft;
 		rate_ns = div64_u64(rate_ns, max(MIN_RATE, rate));
@@ -2339,8 +2341,10 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
 			rate_ns >>= 1;
 			rate_shft--;
 		}
-	} /* else unlimited, ie. zero delay */
-
+	} else {
+		/* else unlimited, ie. zero delay */
+		WRITE_ONCE(b->flow_quantum, 1514);
+	}
 	b->tin_rate_bps  = rate;
 	b->tin_rate_ns   = rate_ns;
 	b->tin_rate_shft = rate_shft;
@@ -3056,11 +3060,11 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 
 		PUT_TSTAT_U32(SPARSE_FLOWS, READ_ONCE(b->sparse_flow_count) +
 					    READ_ONCE(b->decaying_flow_count));
-		PUT_TSTAT_U32(BULK_FLOWS, b->bulk_flow_count);
-		PUT_TSTAT_U32(UNRESPONSIVE_FLOWS, b->unresponsive_flow_count);
-		PUT_TSTAT_U32(MAX_SKBLEN, b->max_skblen);
+		PUT_TSTAT_U32(BULK_FLOWS, READ_ONCE(b->bulk_flow_count));
+		PUT_TSTAT_U32(UNRESPONSIVE_FLOWS, READ_ONCE(b->unresponsive_flow_count));
+		PUT_TSTAT_U32(MAX_SKBLEN, READ_ONCE(b->max_skblen));
 
-		PUT_TSTAT_U32(FLOW_QUANTUM, b->flow_quantum);
+		PUT_TSTAT_U32(FLOW_QUANTUM, READ_ONCE(b->flow_quantum));
 		nla_nest_end(d->skb, ts);
 	}
 
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* [PATCH net 3/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (III)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet, Toke Høiland-Jørgensen
In-Reply-To: <20260423102324.3172448-1-edumazet@google.com>

cake_dump_stats() runs without qdisc spinlock being held.

In this third patch, I add READ_ONCE()/WRITE_ONCE() annotations
for the following fields:

- packets
- tin_dropped
- tin_ecn_mark
- ack_drops
- peak_delay
- avge_delay
- base_delay

Other annotations are added in following patches, to ease code review.

Fixes: 046f6fd5daef ("sched: Add Common Applications Kept Enhanced (cake) qdisc")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: "Toke Høiland-Jørgensen" <toke@toke.dk>
---
 net/sched/sch_cake.c | 38 ++++++++++++++++++++------------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index d7465ee4c5507467effd5aaaf8cfd05bcafde2cf..c5aae31565e984e40937b55201b498174a37180e 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1600,7 +1600,7 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)
 	sch->qstats.backlog -= len;
 
 	flow->dropped++;
-	b->tin_dropped++;
+	WRITE_ONCE(b->tin_dropped, b->tin_dropped + 1);
 
 	if (q->config->rate_flags & CAKE_FLAG_INGRESS)
 		cake_advance_shaper(q, b, skb, now, true);
@@ -1820,7 +1820,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			numsegs++;
 			slen += segs->len;
 			q->buffer_used += segs->truesize;
-			b->packets++;
+			WRITE_ONCE(b->packets, b->packets + 1);
 		}
 
 		/* stats */
@@ -1844,7 +1844,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			ack = cake_ack_filter(q, flow);
 
 		if (ack) {
-			b->ack_drops++;
+			WRITE_ONCE(b->ack_drops, b->ack_drops + 1);
 			sch->qstats.drops++;
 			ack_pkt_len = qdisc_pkt_len(ack);
 			b->bytes += ack_pkt_len;
@@ -1860,7 +1860,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		}
 
 		/* stats */
-		b->packets++;
+		WRITE_ONCE(b->packets, b->packets + 1);
 		b->bytes	    += len - ack_pkt_len;
 		b->backlogs[idx]    += len - ack_pkt_len;
 		b->tin_backlog      += len - ack_pkt_len;
@@ -2236,7 +2236,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 			b->tin_deficit -= len;
 		}
 		flow->dropped++;
-		b->tin_dropped++;
+		WRITE_ONCE(b->tin_dropped, b->tin_dropped + 1);
 		qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb));
 		qdisc_qstats_drop(sch);
 		qdisc_dequeue_drop(sch, skb, reason);
@@ -2244,17 +2244,19 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 			goto retry;
 	}
 
-	b->tin_ecn_mark += !!flow->cvars.ecn_marked;
+	WRITE_ONCE(b->tin_ecn_mark, b->tin_ecn_mark + !!flow->cvars.ecn_marked);
 	qdisc_bstats_update(sch, skb);
 	WRITE_ONCE(q->last_active, now);
 
 	/* collect delay stats */
 	delay = ktime_to_ns(ktime_sub(now, cobalt_get_enqueue_time(skb)));
-	b->avge_delay = cake_ewma(b->avge_delay, delay, 8);
-	b->peak_delay = cake_ewma(b->peak_delay, delay,
-				  delay > b->peak_delay ? 2 : 8);
-	b->base_delay = cake_ewma(b->base_delay, delay,
-				  delay < b->base_delay ? 2 : 8);
+	WRITE_ONCE(b->avge_delay, cake_ewma(b->avge_delay, delay, 8));
+	WRITE_ONCE(b->peak_delay,
+		   cake_ewma(b->peak_delay, delay,
+			     delay > b->peak_delay ? 2 : 8));
+	WRITE_ONCE(b->base_delay,
+		   cake_ewma(b->base_delay, delay,
+			     delay < b->base_delay ? 2 : 8));
 
 	len = cake_advance_shaper(q, b, skb, now, false);
 	flow->deficit -= len;
@@ -3042,17 +3044,17 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 		PUT_TSTAT_U32(INTERVAL_US,
 			      ktime_to_us(ns_to_ktime(b->cparams.interval)));
 
-		PUT_TSTAT_U32(SENT_PACKETS, b->packets);
-		PUT_TSTAT_U32(DROPPED_PACKETS, b->tin_dropped);
-		PUT_TSTAT_U32(ECN_MARKED_PACKETS, b->tin_ecn_mark);
-		PUT_TSTAT_U32(ACKS_DROPPED_PACKETS, b->ack_drops);
+		PUT_TSTAT_U32(SENT_PACKETS, READ_ONCE(b->packets));
+		PUT_TSTAT_U32(DROPPED_PACKETS, READ_ONCE(b->tin_dropped));
+		PUT_TSTAT_U32(ECN_MARKED_PACKETS, READ_ONCE(b->tin_ecn_mark));
+		PUT_TSTAT_U32(ACKS_DROPPED_PACKETS, READ_ONCE(b->ack_drops));
 
 		PUT_TSTAT_U32(PEAK_DELAY_US,
-			      ktime_to_us(ns_to_ktime(b->peak_delay)));
+			      ktime_to_us(ns_to_ktime(READ_ONCE(b->peak_delay))));
 		PUT_TSTAT_U32(AVG_DELAY_US,
-			      ktime_to_us(ns_to_ktime(b->avge_delay)));
+			      ktime_to_us(ns_to_ktime(READ_ONCE(b->avge_delay))));
 		PUT_TSTAT_U32(BASE_DELAY_US,
-			      ktime_to_us(ns_to_ktime(b->base_delay)));
+			      ktime_to_us(ns_to_ktime(READ_ONCE(b->base_delay))));
 
 		PUT_TSTAT_U32(WAY_INDIRECT_HITS, READ_ONCE(b->way_hits));
 		PUT_TSTAT_U32(WAY_MISSES, READ_ONCE(b->way_misses));
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* [PATCH net 4/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (IV)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet, Toke Høiland-Jørgensen
In-Reply-To: <20260423102324.3172448-1-edumazet@google.com>

cake_dump_stats() runs without qdisc spinlock being held.

In this fourth patch, I add READ_ONCE()/WRITE_ONCE() annotations
for the following fields:

- avg_peak_bandwidth
- buffer_limit
- buffer_max_used
- avg_netoff
- max_netlen
- max_adjlen
- min_netlen
- min_adjlen
- active_queues
- tin_rate_bps
- bytes
- tin_backlog

Other annotations are added in following patch, to ease code review.

Fixes: 046f6fd5daef ("sched: Add Common Applications Kept Enhanced (cake) qdisc")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: "Toke Høiland-Jørgensen" <toke@toke.dk>
---
 net/sched/sch_cake.c | 90 ++++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 44 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index c5aae31565e984e40937b55201b498174a37180e..c3b09f67f0fdbc51d23b3d22df9ab89a716c7e2b 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1379,9 +1379,9 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off)
 		len -= off;
 
 	if (qd->max_netlen < len)
-		qd->max_netlen = len;
+		WRITE_ONCE(qd->max_netlen, len);
 	if (qd->min_netlen > len)
-		qd->min_netlen = len;
+		WRITE_ONCE(qd->min_netlen, len);
 
 	len += q->rate_overhead;
 
@@ -1401,9 +1401,9 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off)
 	}
 
 	if (qd->max_adjlen < len)
-		qd->max_adjlen = len;
+		WRITE_ONCE(qd->max_adjlen, len);
 	if (qd->min_adjlen > len)
-		qd->min_adjlen = len;
+		WRITE_ONCE(qd->min_adjlen, len);
 
 	return len;
 }
@@ -1416,7 +1416,7 @@ static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
 	u16 segs = qdisc_pkt_segs(skb);
 	u32 len = qdisc_pkt_len(skb);
 
-	q->avg_netoff = cake_ewma(q->avg_netoff, off << 16, 8);
+	WRITE_ONCE(q->avg_netoff, cake_ewma(q->avg_netoff, off << 16, 8));
 
 	if (segs == 1)
 		return cake_calc_overhead(q, len, off);
@@ -1596,7 +1596,7 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)
 	len = qdisc_pkt_len(skb);
 	q->buffer_used      -= skb->truesize;
 	b->backlogs[idx]    -= len;
-	b->tin_backlog      -= len;
+	WRITE_ONCE(b->tin_backlog, b->tin_backlog - len);
 	sch->qstats.backlog -= len;
 
 	flow->dropped++;
@@ -1824,9 +1824,9 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		}
 
 		/* stats */
-		b->bytes	    += slen;
+		WRITE_ONCE(b->bytes, b->bytes + slen);
 		b->backlogs[idx]    += slen;
-		b->tin_backlog      += slen;
+		WRITE_ONCE(b->tin_backlog, b->tin_backlog + slen);
 		sch->qstats.backlog += slen;
 		q->avg_window_bytes += slen;
 
@@ -1847,7 +1847,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			WRITE_ONCE(b->ack_drops, b->ack_drops + 1);
 			sch->qstats.drops++;
 			ack_pkt_len = qdisc_pkt_len(ack);
-			b->bytes += ack_pkt_len;
+			WRITE_ONCE(b->bytes, b->bytes + ack_pkt_len);
 			q->buffer_used += skb->truesize - ack->truesize;
 			if (q->config->rate_flags & CAKE_FLAG_INGRESS)
 				cake_advance_shaper(q, b, ack, now, true);
@@ -1861,9 +1861,9 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 
 		/* stats */
 		WRITE_ONCE(b->packets, b->packets + 1);
-		b->bytes	    += len - ack_pkt_len;
+		WRITE_ONCE(b->bytes, b->bytes + len - ack_pkt_len);
 		b->backlogs[idx]    += len - ack_pkt_len;
-		b->tin_backlog      += len - ack_pkt_len;
+		WRITE_ONCE(b->tin_backlog, b->tin_backlog + len - ack_pkt_len);
 		sch->qstats.backlog += len - ack_pkt_len;
 		q->avg_window_bytes += len - ack_pkt_len;
 	}
@@ -1895,9 +1895,9 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			u64 b = q->avg_window_bytes * (u64)NSEC_PER_SEC;
 
 			b = div64_u64(b, window_interval);
-			q->avg_peak_bandwidth =
-				cake_ewma(q->avg_peak_bandwidth, b,
-					  b > q->avg_peak_bandwidth ? 2 : 8);
+			WRITE_ONCE(q->avg_peak_bandwidth,
+				   cake_ewma(q->avg_peak_bandwidth, b,
+					     b > q->avg_peak_bandwidth ? 2 : 8));
 			q->avg_window_bytes = 0;
 			q->avg_window_begin = now;
 
@@ -1938,7 +1938,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	}
 
 	if (q->buffer_used > q->buffer_max_used)
-		q->buffer_max_used = q->buffer_used;
+		WRITE_ONCE(q->buffer_max_used, q->buffer_used);
 
 	if (q->buffer_used <= q->buffer_limit)
 		return NET_XMIT_SUCCESS;
@@ -1978,7 +1978,7 @@ static struct sk_buff *cake_dequeue_one(struct Qdisc *sch)
 		skb = dequeue_head(flow);
 		len = qdisc_pkt_len(skb);
 		b->backlogs[q->cur_flow] -= len;
-		b->tin_backlog		 -= len;
+		WRITE_ONCE(b->tin_backlog, b->tin_backlog - len);
 		sch->qstats.backlog      -= len;
 		q->buffer_used		 -= skb->truesize;
 		sch->q.qlen--;
@@ -2043,7 +2043,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 
 		cake_configure_rates(sch, new_rate, true);
 		q->last_checked_active = now;
-		q->active_queues = num_active_qs;
+		WRITE_ONCE(q->active_queues, num_active_qs);
 	}
 
 begin:
@@ -2347,7 +2347,7 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
 		/* else unlimited, ie. zero delay */
 		WRITE_ONCE(b->flow_quantum, 1514);
 	}
-	b->tin_rate_bps  = rate;
+	WRITE_ONCE(b->tin_rate_bps, rate);
 	b->tin_rate_ns   = rate_ns;
 	b->tin_rate_shft = rate_shft;
 
@@ -2617,25 +2617,27 @@ static void cake_reconfigure(struct Qdisc *sch)
 {
 	struct cake_sched_data *qd = qdisc_priv(sch);
 	struct cake_sched_config *q = qd->config;
+	u32 buffer_limit;
 
 	cake_configure_rates(sch, qd->config->rate_bps, false);
 
 	if (q->buffer_config_limit) {
-		qd->buffer_limit = q->buffer_config_limit;
+		buffer_limit = q->buffer_config_limit;
 	} else if (q->rate_bps) {
 		u64 t = q->rate_bps * q->interval;
 
 		do_div(t, USEC_PER_SEC / 4);
-		qd->buffer_limit = max_t(u32, t, 4U << 20);
+		buffer_limit = max_t(u32, t, 4U << 20);
 	} else {
-		qd->buffer_limit = ~0;
+		buffer_limit = ~0;
 	}
 
 	sch->flags &= ~TCQ_F_CAN_BYPASS;
 
-	qd->buffer_limit = min(qd->buffer_limit,
-			       max(sch->limit * psched_mtu(qdisc_dev(sch)),
-				   q->buffer_config_limit));
+	WRITE_ONCE(qd->buffer_limit,
+		   min(buffer_limit,
+		       max(sch->limit * psched_mtu(qdisc_dev(sch)),
+			   q->buffer_config_limit)));
 }
 
 static int cake_config_change(struct cake_sched_config *q, struct nlattr *opt,
@@ -2780,10 +2782,10 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt,
 		return ret;
 
 	if (overhead_changed) {
-		qd->max_netlen = 0;
-		qd->max_adjlen = 0;
-		qd->min_netlen = ~0;
-		qd->min_adjlen = ~0;
+		WRITE_ONCE(qd->max_netlen, 0);
+		WRITE_ONCE(qd->max_adjlen, 0);
+		WRITE_ONCE(qd->min_netlen, ~0);
+		WRITE_ONCE(qd->min_adjlen, ~0);
 	}
 
 	if (qd->tins) {
@@ -3001,15 +3003,15 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 			goto nla_put_failure;			       \
 	} while (0)
 
-	PUT_STAT_U64(CAPACITY_ESTIMATE64, q->avg_peak_bandwidth);
-	PUT_STAT_U32(MEMORY_LIMIT, q->buffer_limit);
-	PUT_STAT_U32(MEMORY_USED, q->buffer_max_used);
-	PUT_STAT_U32(AVG_NETOFF, ((q->avg_netoff + 0x8000) >> 16));
-	PUT_STAT_U32(MAX_NETLEN, q->max_netlen);
-	PUT_STAT_U32(MAX_ADJLEN, q->max_adjlen);
-	PUT_STAT_U32(MIN_NETLEN, q->min_netlen);
-	PUT_STAT_U32(MIN_ADJLEN, q->min_adjlen);
-	PUT_STAT_U32(ACTIVE_QUEUES, q->active_queues);
+	PUT_STAT_U64(CAPACITY_ESTIMATE64, READ_ONCE(q->avg_peak_bandwidth));
+	PUT_STAT_U32(MEMORY_LIMIT, READ_ONCE(q->buffer_limit));
+	PUT_STAT_U32(MEMORY_USED, READ_ONCE(q->buffer_max_used));
+	PUT_STAT_U32(AVG_NETOFF, ((READ_ONCE(q->avg_netoff) + 0x8000) >> 16));
+	PUT_STAT_U32(MAX_NETLEN, READ_ONCE(q->max_netlen));
+	PUT_STAT_U32(MAX_ADJLEN, READ_ONCE(q->max_adjlen));
+	PUT_STAT_U32(MIN_NETLEN, READ_ONCE(q->min_netlen));
+	PUT_STAT_U32(MIN_ADJLEN, READ_ONCE(q->min_adjlen));
+	PUT_STAT_U32(ACTIVE_QUEUES, READ_ONCE(q->active_queues));
 
 #undef PUT_STAT_U32
 #undef PUT_STAT_U64
@@ -3035,9 +3037,9 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 		if (!ts)
 			goto nla_put_failure;
 
-		PUT_TSTAT_U64(THRESHOLD_RATE64, b->tin_rate_bps);
-		PUT_TSTAT_U64(SENT_BYTES64, b->bytes);
-		PUT_TSTAT_U32(BACKLOG_BYTES, b->tin_backlog);
+		PUT_TSTAT_U64(THRESHOLD_RATE64, READ_ONCE(b->tin_rate_bps));
+		PUT_TSTAT_U64(SENT_BYTES64, READ_ONCE(b->bytes));
+		PUT_TSTAT_U32(BACKLOG_BYTES, READ_ONCE(b->tin_backlog));
 
 		PUT_TSTAT_U32(TARGET_US,
 			      ktime_to_us(ns_to_ktime(b->cparams.target)));
@@ -3304,10 +3306,10 @@ static int cake_mq_change(struct Qdisc *sch, struct nlattr *opt,
 		struct cake_sched_data *qd = qdisc_priv(chld);
 
 		if (overhead_changed) {
-			qd->max_netlen = 0;
-			qd->max_adjlen = 0;
-			qd->min_netlen = ~0;
-			qd->min_adjlen = ~0;
+			WRITE_ONCE(qd->max_netlen, 0);
+			WRITE_ONCE(qd->max_adjlen, 0);
+			WRITE_ONCE(qd->min_netlen, ~0);
+			WRITE_ONCE(qd->min_adjlen, ~0);
 		}
 
 		if (qd->tins) {
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* [PATCH net 5/5] net/sched: sch_cake: annotate data-races in cake_dump_stats() (V)
From: Eric Dumazet @ 2026-04-23 10:23 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko,
	Toke Høiland-Jørgensen, netdev, eric.dumazet,
	Eric Dumazet, Toke Høiland-Jørgensen
In-Reply-To: <20260423102324.3172448-1-edumazet@google.com>

cake_dump_stats() runs without qdisc spinlock being held.

In this final patch, I add READ_ONCE()/WRITE_ONCE() annotations
for cparams.target and cparams.interval.

Fixes: 046f6fd5daef ("sched: Add Common Applications Kept Enhanced (cake) qdisc")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: "Toke Høiland-Jørgensen" <toke@toke.dk>
---
 net/sched/sch_cake.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index c3b09f67f0fdbc51d23b3d22df9ab89a716c7e2b..53d90062c32fcf647e4837416e335f4562ab97d9 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -2356,10 +2356,11 @@ static void cake_set_rate(struct cake_tin_data *b, u64 rate, u32 mtu,
 
 	byte_target_ns = (byte_target * rate_ns) >> rate_shft;
 
-	b->cparams.target = max((byte_target_ns * 3) / 2, target_ns);
-	b->cparams.interval = max(rtt_est_ns +
-				     b->cparams.target - target_ns,
-				     b->cparams.target * 2);
+	WRITE_ONCE(b->cparams.target,
+		   max((byte_target_ns * 3) / 2, target_ns));
+	WRITE_ONCE(b->cparams.interval,
+		   max(rtt_est_ns + b->cparams.target - target_ns,
+		       b->cparams.target * 2));
 	b->cparams.mtu_time = byte_target_ns;
 	b->cparams.p_inc = 1 << 24; /* 1/256 */
 	b->cparams.p_dec = 1 << 20; /* 1/4096 */
@@ -3042,9 +3043,9 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 		PUT_TSTAT_U32(BACKLOG_BYTES, READ_ONCE(b->tin_backlog));
 
 		PUT_TSTAT_U32(TARGET_US,
-			      ktime_to_us(ns_to_ktime(b->cparams.target)));
+			      ktime_to_us(ns_to_ktime(READ_ONCE(b->cparams.target))));
 		PUT_TSTAT_U32(INTERVAL_US,
-			      ktime_to_us(ns_to_ktime(b->cparams.interval)));
+			      ktime_to_us(ns_to_ktime(READ_ONCE(b->cparams.interval))));
 
 		PUT_TSTAT_U32(SENT_PACKETS, READ_ONCE(b->packets));
 		PUT_TSTAT_U32(DROPPED_PACKETS, READ_ONCE(b->tin_dropped));
-- 
2.54.0.rc2.544.gc7ae2d5bb8-goog


^ permalink raw reply related

* [PATCH net] net/smc: fix NULL pointer dereference in smc_clc_wait_msg()
From: Weiming Shi @ 2026-04-23 10:02 UTC (permalink / raw)
  To: D . Wythe, Dust Li, Sidraya Jayagond, Wenjia Zhang,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Mahanta Jambigi, Tony Lu, Wen Gu, Simon Horman, Ursula Braun,
	linux-rdma, linux-s390, netdev, Xiang Mei, Weiming Shi

In smc_listen_work(), smc_clc_wait_msg() is called to wait for a CLC
PROPOSAL message before any link group has been created, so
smc->conn.lgr is still NULL at this point. smc_clc_wait_msg() also
accepts CLC DECLINE messages regardless of the expected type. When a
DECLINE with SMC_FIRST_CONTACT_MASK set in hdr.typev2 arrives, the code
unconditionally dereferences smc->conn.lgr to set sync_err, causing a
NULL pointer dereference.

KASAN reported a null-ptr-deref in smc_clc_wait_msg():

 Oops: general protection fault, 0000 [#1] SMP KASAN NOPTI
 KASAN: null-ptr-deref in range [0x0000000000000310-0x0000000000000317]
 RIP: 0010:smc_clc_wait_msg (net/smc/smc_clc.c:793)
 Call Trace:
  <TASK>
  smc_listen_work (net/smc/af_smc.c:2491)
  process_one_work (kernel/workqueue.c:3281)
  worker_thread (kernel/workqueue.c:3440)
  kthread (kernel/kthread.c:436)
  ret_from_fork (arch/x86/kernel/process.c:164)
  ret_from_fork_asm (arch/x86/entry/entry_64.S:257)
  </TASK>
 Kernel panic - not syncing: Fatal exception

Add a NULL check for smc->conn.lgr before dereferencing it. 

Fixes: 0cfdd8f92cac ("smc: connection and link group creation")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
 net/smc/smc_clc.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c
index c38fc7bf0a7e..d22c9417d239 100644
--- a/net/smc/smc_clc.c
+++ b/net/smc/smc_clc.c
@@ -790,8 +790,10 @@ int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen,
 		smc->peer_diagnosis = ntohl(dclc->peer_diagnosis);
 		if (((struct smc_clc_msg_decline *)buf)->hdr.typev2 &
 						SMC_FIRST_CONTACT_MASK) {
-			smc->conn.lgr->sync_err = 1;
-			smc_lgr_terminate_sched(smc->conn.lgr);
+			if (smc->conn.lgr) {
+				smc->conn.lgr->sync_err = 1;
+				smc_lgr_terminate_sched(smc->conn.lgr);
+			}
 		}
 	}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH net v3] ipv6: validate extension header length before copying to cmsg
From: Qi Tang @ 2026-04-23 10:32 UTC (permalink / raw)
  To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman
  Cc: netdev, linux-kernel, Qi Tang

ip6_datagram_recv_specific_ctl() builds IPV6_{HOPOPTS,DSTOPTS,RTHDR}
cmsgs (and their IPV6_2292* legacy counterparts) by trusting the
on-wire hdrlen byte (ptr[1]) when computing the put_cmsg() length.
The length was validated only at parse time (ipv6_parse_hopopts(),
etc.).  An nftables payload-write expression can rewrite hdrlen after
parsing and before the skb reaches recvmsg; the write itself is
in-bounds but put_cmsg() then reads up to ((hdrlen+1) << 3) = 2040
bytes from an 8-byte header.  nftables is reachable from an
unprivileged user namespace, so this is an unprivileged
slab-out-of-bounds read:

  BUG: KASAN: slab-out-of-bounds in put_cmsg+0x3ac/0x540
   put_cmsg+0x3ac/0x540
   udpv6_recvmsg+0xca0/0x1250
   sock_recvmsg+0xdf/0x190
   ____sys_recvmsg+0x1b1/0x620

Add ipv6_get_exthdr_len() which computes the extension header length
and validates it against skb_tail_pointer(skb), returning 0 on
failure.  Extension headers are kept in the linear skb area by
pskb_may_pull() during input, so skb_tail_pointer() is the correct
bound.

Use ipv6_get_exthdr_len() at all non-AH call sites: the five
standalone cmsg blocks (HbH, 2292HbH, 2292DSTOPTS x2, 2292RTHDR)
and the three standard cases in the extension-header walk loop
(DSTOPTS, ROUTING, default).  AH retains an inline bounds check
because its length formula differs ((ptr[1]+2)<<2).

When the walk loop detects a corrupted header, return from the
function instead of continuing to process later socket options.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
---
Changes v2 -> v3:
  - Resend as new thread (v2 was incorrectly sent as reply to v1)

Changes v1 -> v2 (Paolo Abeni):
  - Factor repeated bounds-check + put_cmsg into ipv6_get_exthdr_len()
  - Return from the function on corrupted walk-loop entry instead of
    goto + empty label

v2: https://lore.kernel.org/netdev/20260423102255.3752004-1-tpluszz77@gmail.com/
v1: https://lore.kernel.org/netdev/20260419150344.624673-1-tpluszz77@gmail.com/

 net/ipv6/datagram.c | 46 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 8 deletions(-)

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 972bf0426d59..0a7b74d5f402 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -617,6 +617,13 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
 	}
 }
 
+static u16 ipv6_get_exthdr_len(const struct sk_buff *skb, const u8 *ptr)
+{
+	u16 len = (ptr[1] + 1) << 3;
+
+	return (ptr + len <= skb_tail_pointer(skb)) ? len : 0;
+}
+
 void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 				    struct sk_buff *skb)
 {
@@ -643,7 +650,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	/* HbH is allowed only once */
 	if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, len, ptr);
 	}
 
 	if (opt->lastopt &&
@@ -667,23 +677,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 			switch (nexthdr) {
 			case IPPROTO_DSTOPTS:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				if (np->rxopt.bits.dstopts)
 					put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr);
 				break;
 			case IPPROTO_ROUTING:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				if (np->rxopt.bits.srcrt)
 					put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr);
 				break;
 			case IPPROTO_AH:
 				nexthdr = ptr[0];
 				len = (ptr[1] + 2) << 2;
+				if (ptr + len > skb_tail_pointer(skb))
+					return;
 				break;
 			default:
 				nexthdr = ptr[0];
-				len = (ptr[1] + 1) << 3;
+				len = ipv6_get_exthdr_len(skb, ptr);
+				if (!len)
+					return;
 				break;
 			}
 
@@ -705,19 +723,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
 	}
 	if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) {
 		u8 *ptr = nh + sizeof(struct ipv6hdr);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst0) {
 		u8 *ptr = nh + opt->dst0;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.osrcrt && opt->srcrt) {
 		struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt);
-		put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+		u16 len = ipv6_get_exthdr_len(skb, (u8 *)rthdr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, len, rthdr);
 	}
 	if (np->rxopt.bits.odstopts && opt->dst1) {
 		u8 *ptr = nh + opt->dst1;
-		put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr);
+		u16 len = ipv6_get_exthdr_len(skb, ptr);
+
+		if (len)
+			put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr);
 	}
 	if (np->rxopt.bits.rxorigdstaddr) {
 		struct sockaddr_in6 sin6;
-- 
2.47.3


^ permalink raw reply related

* Re: [PATCH net v2] ipv6: validate extension header length before copying to cmsg
From: Qi Tang @ 2026-04-23 10:33 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
	Simon Horman, netdev, linux-kernel, Qi Tang
In-Reply-To: <20260423102255.3752004-1-tpluszz77@gmail.com>

Please drop v2 — it was incorrectly sent as a reply to v1 instead
of a new thread.  Resent as v3:
https://lore.kernel.org/netdev/20260423103238.3987364-1-tpluszz77@gmail.com/

Sorry for the noise.

Qi

^ permalink raw reply

* [PATCH] ntfs: Avoid NULL pointer dereference in ntfs_iomap_submit_read()
From: Eric Naim @ 2026-04-23 10:41 UTC (permalink / raw)
  To: Namjae Jeon, Hyunchul Lee, Richard Cochran, Nathan Chancellor,
	Nick Desaulniers, Bill Wendling, Justin Stitt
  Cc: Eric Naim, linux-fsdevel, linux-kernel, netdev, llvm

ctx->read_ctx can be NULL when ntfs_iomap_submit_read is called, leading
to below trace:

[   44.977614] BUG: kernel NULL pointer dereference, address: 0000000000000040
[   44.977617] #PF: supervisor write access in kernel mode
[   44.977618] #PF: error_code(0x0002) - not-present page
[   44.977619] PGD 0 P4D 0
[   44.977621] Oops: Oops: 0002 [#1] SMP
[   44.977623] CPU: 0 UID: 1000 PID: 5010 Comm: pool-4 Kdump: loaded Tainted: G     U     OE       7.0.1-1-cachyos-bmq-hakuu-tlto-gdc7bc3c05102 #7 PREEMPT(full)  c202625180654aea7fdad2184acc19b9c28ed6ee
[   44.977626] Tainted: [U]=USER, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
[   44.977626] Hardware name: ASUSTeK COMPUTER INC. ASUS TUF Gaming F16 FX607JV_FX607JV/FX607JV, BIOS FX607JV.316 10/13/2025
[   44.977627] RIP: 0010:ntfs_swap_activate.llvm.1224280209124021557+0x2d/0x3c0 [ntfs]
[   44.977631] Code: fa 0f 1f 44 00 00 48 c7 c1 78 76 93 a2 e9 2b 2d cc de cc cc cc cc cc cc cc cc cc cc cc f3 0f 1e fa 0f 1f 44 00 00 48 8b 7f 18 <48> c7 47 40 60 04 aa a2 e9 56 c9 fd de cc cc cc cc cc cc f3 0f 1e
[   44.977632] RSP: 0018:ffffc90023c27648 EFLAGS: 00010282
[   44.977633] RAX: ffffffffa2aa0440 RBX: ffffc90023c27758 RCX: 0000000000001000
[   44.977634] RDX: 0000000000001000 RSI: 0000000006f20000 RDI: 0000000000000000
[   44.977635] RBP: 0000000006f20000 R08: 0000000000000000 R09: ffffc90023c27680
[   44.977636] R10: 0000000000000009 R11: 0000000006f203ff R12: ffffc90023c27650
[   44.977637] R13: ffffea000810e0c0 R14: ffffffffa2937678 R15: ffffc90023c27658
[   44.977638] FS:  00007fffa7fff6c0(0000) GS:ffff888d29052000(0000) knlGS:0000000000000000
[   44.977639] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   44.977640] CR2: 0000000000000040 CR3: 0000000160320004 CR4: 0000000000f72ef0
[   44.977641] PKRU: 55555554
[   44.977641] Call Trace:
[   44.977642]  <TASK>
[   44.977643]  iomap_read_folio+0xe2/0x180
[   44.977647]  ntfs_read_folio.llvm.1224280209124021557+0x69/0xe0 [ntfs df169bf55ac22e619ebd511d6378b3aa21a54f15]
[   44.977650]  do_read_cache_folio.llvm.11351189850855672942+0x1a9/0x310
[   44.977652]  ? cleanup_module+0x1f0/0x1f0 [fat f3f47899f717abaf282870f380e376f623b66fa1]
[   44.977654]  ntfs_mft_record_alloc+0x8df/0x2bd0 [ntfs df169bf55ac22e619ebd511d6378b3aa21a54f15]
[   44.977657]  ntfs_get_parent.llvm.15803940035981701475+0x569/0x1780 [ntfs df169bf55ac22e619ebd511d6378b3aa21a54f15]
[   44.977659]  ? kmem_cache_alloc_noprof+0x187/0x420
[   44.977660]  ntfs_create.llvm.15803940035981701475+0x106/0x170 [ntfs df169bf55ac22e619ebd511d6378b3aa21a54f15]
[   44.977662]  path_openat+0x541/0xdb0
[   44.977664]  do_file_open+0xd7/0x190
[   44.977666]  do_sys_openat2+0x76/0xe0
[   44.977668]  __x64_sys_openat+0x80/0xa0
[   44.977669]  do_syscall_64+0xf8/0x350
[   44.977671]  ? do_statx_fd+0x100/0x140
[   44.977672]  ? ext4_listxattr+0x1d9/0x200
[   44.977674]  ? listxattr+0xfe/0x150
[   44.977675]  ? __x64_sys_flistxattr+0x7a/0xa0
[   44.977677]  ? do_syscall_64+0x133/0x350
[   44.977678]  ? __x64_sys_flistxattr+0x7a/0xa0
[   44.977679]  ? do_syscall_64+0x133/0x350
[   44.977681]  ? rcu_report_qs_rdp+0xca/0x180
[   44.977683]  ? sched_clock+0x10/0x20
[   44.977684]  ? sched_clock_cpu+0x10/0x190
[   44.977685]  ? irqtime_account_irq+0x28/0xa0
[   44.977687]  ? do_syscall_64+0x133/0x350
[   44.977688]  entry_SYSCALL_64_after_hwframe+0x4b/0x53
[   44.977689] RIP: 0033:0x7ffff58b00e2
[   44.977713] Code: 08 0f 85 b1 3d ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66
[   44.977714] RSP: 002b:00007fffa7ffd8c8 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
[   44.977715] RAX: ffffffffffffffda RBX: 00007fffa00db6a0 RCX: 00007ffff58b00e2
[   44.977716] RDX: 00000000000800c1 RSI: 00007fffa00db940 RDI: ffffffffffffff9c
[   44.977716] RBP: 00007fffa0069970 R08: 0000000000000000 R09: 0000000000000000
[   44.977717] R10: 00000000000001a4 R11: 0000000000000246 R12: 00007fffa0068f10
[   44.977717] R13: 0000000000000000 R14: 00007fffa7ffdb90 R15: 00005555580ea620
[   44.977719]  </TASK>
[   44.977719] Modules linked in: uinput(E) ccm(E) rfcomm(E) snd_seq_dummy(E) snd_hrtimer(E) snd_seq(E) nft_masq(E) nft_ct(E) veth(E) nft_reject_ipv4(E) nf_reject_ipv4(E) nft_reject(E) act_csum(E) cls_u32(E) sch_htb(E) nf_conntrack_netlink(E) xt_nat(E) xt_tcpudp(E) xt_conntrack(E) xt_MASQUERADE(E) bridge(E) stp(E) llc(E) xfrm_user(E) xfrm_algo(E) tun(E) xt_set(E) ip_set(E) nft_chain_nat(E) nf_nat(E) nf_conntrack(E) nf_defrag_ipv6(E) nf_defrag_ipv4(E) xt_addrtype(E) nft_compat(E) x_tables(E) nf_tables(E) overlay(E) cdc_ncm(E) cdc_ether(E) usbnet(E) mii(E) ipheth(E) vmnet(OE) cmac(E) algif_hash(E) algif_skcipher(E) af_alg(E) bnep(E) nls_utf8(E) vfat(E) ntfs(E) fat(E) hid_logitech_hidpp(E) uvcvideo(E) uvc(E) btusb(E) videobuf2_vmalloc(E) btmtk(E) videobuf2_memops(E) btrtl(E) videobuf2_v4l2(E) btbcm(E) videobuf2_common(E) btintel(E) apple_mfi_fastcharge(E) videodev(E) bluetooth(E) snd_hda_codec_intelhdmi(E) snd_sof_pci_intel_tgl(E) snd_sof_pci_intel_cnl(E) snd_sof_intel_hda_generic(E) soundwire_intel(E)
[   44.977739]  snd_sof_intel_hda_sdw_bpt(E) snd_sof_intel_hda_common(E) snd_soc_hdac_hda(E) intel_uncore_frequency(E) snd_sof_intel_hda_mlink(E) intel_uncore_frequency_common(E) intel_tcc_cooling(E) snd_sof_intel_hda(E) soundwire_cadence(E) x86_pkg_temp_thermal(E) snd_sof_pci(E) intel_powerclamp(E) snd_sof_xtensa_dsp(E) coretemp(E) snd_sof(E) iwlmld(E) snd_sof_utils(E) snd_soc_acpi_intel_match(E) snd_soc_acpi_intel_sdca_quirks(E) snd_hda_codec_alc269(E) ucsi_acpi(E) soundwire_generic_allocation(E) mac80211(E) snd_hda_codec_realtek_lib(E) snd_soc_sdw_utils(E) typec_ucsi(E) snd_hda_scodec_component(E) kvm_intel(E) snd_soc_acpi(E) ptp(E) typec(E) snd_hda_codec_generic(E) soundwire_bus(E) pps_core(E) roles(E) spd5118(E) mei_hdcp(E) mei_pxp(E) intel_rapl_msr(E) asus_nb_wmi(E) libarc4(E) snd_hda_codec_nvhdmi(E) snd_soc_sdca(E) snd_hda_codec_hdmi(E) kvm(E) crc8(E) irqbypass(E) snd_soc_avs(E) ghash_clmulni_intel(E) aesni_intel(E) snd_soc_hda_codec(E) processor_thermal_device_pci(E) gf128mul(E) asus_armoury(E)
[   44.977755]  snd_hda_ext_core(E) snd_hda_intel(E) rapl(E) processor_thermal_device(E) snd_hda_codec(E) snd_usb_audio(E) intel_cstate(E) processor_thermal_power_floor(E) iwlwifi(E) firmware_attributes_class(E) r8169(E) snd_soc_core(E) snd_usbmidi_lib(E) snd_hda_core(E) processor_thermal_wt_hint(E) spi_nor(E) processor_thermal_wt_req(E) asus_wmi(E) snd_ump(E) snd_hda_scodec_cs35l41_spi(E) realtek(E) snd_intel_dspcfg(E) ac97_bus(E) intel_uncore(E) mousedev(E) joydev(E) sparse_keymap(E) wmi_bmof(E) pcspkr(E) mtd(E) nvidia_wmi_ec_backlight(E) snd_rawmidi(E) processor_thermal_rfim(E) snd_hda_scodec_cs35l41_i2c(E) hid_logitech_dj(E) snd_pcm_dmaengine(E) mdio_devres(E) snd_intel_sdw_acpi(E) cfg80211(E) processor_thermal_mbox(E) i2c_i801(E) snd_seq_device(E) snd_hda_scodec_cs35l41(E) snd_compress(E) snd_hwdep(E) libphy(E) platform_temperature_control(E) snd_soc_cs_amp_lib(E) i2c_smbus(E) snd_pcm(E) processor_thermal_rapl(E) i2c_mux(E) snd_soc_cs35l41_lib(E) mdio_bus(E) uas(E) snd_timer(E) cs_dsp(E) rfkill(E)
[   44.977771]  intel_rapl_common(E) mei_me(E) snd(E) processor_thermal_soc_slider(E) mei(E) hid_cmedia(E) usb_storage(E) mc(E) soundcore(E) platform_profile(E) serial_multi_instantiate(E) intel_pmc_core(E) intel_pmc_ssram_telemetry(E) pmt_telemetry(E) int3400_thermal(E) pmt_discovery(E) int3403_thermal(E) thunderbolt(E) pmt_class(E) pinctrl_alderlake(E) int340x_thermal_zone(E) acpi_thermal_rel(E) acpi_pad(E) acpi_tad(E) mac_hid(E) tcp_bbr(E) sch_cake(E) vmmon(OE) sg(E) vmw_vmci(E) ntsync(E) dm_mod(E) i2c_dev(E) pkcs8_key_parser(E) crypto_user(E) nfnetlink(E) zram(E) 842_decompress(E) 842_compress(E) lz4hc_compress(E) lz4_compress(E) xe(E) nvme(E) nvme_core(E) nvidia_drm(OE) intel_vsec(E) nvme_keyring(E) drm_gpusvm_helper(E) nvme_auth(E) nvidia_uvm(OE) drm_suballoc_helper(E) hkdf(E) gpu_sched(E) nvidia_modeset(OE) drm_gpuvm(E) drm_exec(E) i2c_algo_bit(E) drm_display_helper(E) intel_lpss_pci(E) spi_intel_pci(E) cec(E) intel_lpss(E) spi_intel(E) idma64(E) drm_buddy(E) serio_raw(E) nvidia(OE) drm_ttm_helper(E) video(E)
[   44.977790]  wmi(E) ttm(E)
[   44.977791] Unloaded tainted modules: acpi_cpufreq(E):1 fjes(E):2 ie31200_edac(E):1
[   44.977793] CR2: 0000000000000040
[   44.977795] ---[ end trace 0000000000000000 ]---

Return early if it is NULL.

Fixes: 8b4064e6146e ("ntfs: zero out stale data in straddle block beyond initialized_size")
Signed-off-by: Eric Naim <dnaim@cachyos.org>
---
 fs/ntfs/aops.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index 1fbf832ad165..f39b6eda251e 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -41,6 +41,9 @@ static void ntfs_iomap_bio_submit_read(const struct iomap_iter *iter,
 	struct iomap_read_folio_ctx *ctx)
 {
 	struct bio *bio = ctx->read_ctx;
+	if (!bio)
+		return;
+
 	bio->bi_end_io = ntfs_iomap_read_end_io;
 	submit_bio(bio);
 }
-- 
2.54.0


^ permalink raw reply related

* [PATCH v3 net 0/11] octeontx2-af: npc: cn20k: MCAM fixes
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth

This series tightens Marvell OcteonTX2 AF NPC support for CN20K
silicon around MCAM key typing, optional debugfs setup, defrag
allocation rollback, x2 versus x4 KEX profiles and default-rule
allocation, logical MCAM clear and configuration, default-rule index
bookkeeping and explicit teardown, and NIXLF reserved-slot lookup when
default rules are missing.

Patches 1 through 3 focus on AF error handling: propagate
npc_mcam_idx_2_key_type() failures through cn20k MCAM enable, config,
copy, and read paths; treat cn20k NPC debugfs files as optional so
probe does not fail when debugfs is unavailable; and fix defrag MCAM
allocation rollback so allocation errno is not overwritten by subbank
index resolution.

Patches 4 and 5 align default-rule and flow-install behaviour with the
loaded mkex profile: prefer x4 default entries when the profile is x4,
and reject x4 flow keys when the profile is strictly x2.

Patches 6 through 8 refine cn20k MCAM programming: clear entries by
logical index and resolved key width, fix bank and CFG sequencing in
npc_cn20k_config_mcam_entry(), and read action metadata from the
correct bank in npc_cn20k_read_mcam_entry().

Patches 9 through 11 complete default-rule lifecycle handling:
initialize all default-rule index outputs up front, tear down default
MCAM rules explicitly (coordinated with npc_mcam_free_all_entries()),
and reject USHRT_MAX sentinel indices in npc_get_nixlf_mcam_index()
for cn20k.

Ratheesh Kannoth (11):
  octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k
  octeontx2-af: npc: cn20k: Drop debugfs_create_file() error checks in
  init
  octeontx2-af: npc: cn20k: Propagate errors in defrag MCAM alloc
  rollback
  octeontx2-af: npc: cn20k: Make default entries as x4.
  octeontx2-af: npc: cn20k: Reject request for x4 entries in x2
  profile.
  octeontx2-af: npc: cn20k: Clear MCAM entries by index and key width
  octeontx2-af: npc: cn20k: Fix bank value.
  octeontx2-af: npc: cn20k: Fix MCAM actions read
  octeontx2-af: npc: cn20k: Initialize default-rule index outputs up
  front
  octeontx2-af: npc: cn20k: Tear down default MCAM rules explicitly on
  free
  octeontx2-af: npc: cn20k: Reject missing default-rule MCAM indices

Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>

--

v2 -> v3:  Addresses simon, paolo comments.
	https://lore.kernel.org/netdev/20260420023442.3295891-1-rkannoth@marvell.com/

v1 -> v2: Addressed simon comments. Added more patch fixes to this series.
	https://lore.kernel.org/netdev/20260418162013.GG280379@horms.kernel.org/

2.43.0

^ permalink raw reply

* [PATCH v3 net 03/11] octeontx2-af: npc: cn20k: Propagate errors in defrag MCAM alloc rollback
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Dan Carpenter
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

npc_defrag_alloc_free_slots() allocates MCAM indexes in up to two passes
on bank0 then bank1.  On failure it rolls back by freeing entries already
placed in save[].

__npc_subbank_alloc() can return a negative errno while only part of the
indexes are valid.  The rollback loop used rc for
npc_mcam_idx_2_subbank_idx() as well, so a successful lookup stored zero
in rc and a later __npc_subbank_free() failure could still end with
return 0 when the allocation path had also left rc at zero
(for example shortfall after zero return values from the alloc helpers).

Jump to the rollback path immediately when either __npc_subbank_alloc()
call fails, preserving its errno.  If both calls succeed but the total
allocated count is still less than cnt, set rc to -ENOSPC before rollback.
Use a separate err variable for npc_mcam_idx_2_subbank_idx() so a
successful lookup no longer clears a non-zero rc from the allocation
phase.

Cc: Dan Carpenter <error27@gmail.com>
Fixes: 645c6e3c1999 ("octeontx2-af: npc: cn20k: virtual index support")
Link: https://lore.kernel.org/netdev/adjNJEpILRZATB2N@stanley.mountain/
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../net/ethernet/marvell/octeontx2/af/cn20k/npc.c    | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 49b5cd2838b5..8630a6a73387 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -2325,6 +2325,7 @@ static int __npc_subbank_alloc(struct rvu *rvu, struct npc_subbank *sb,
 		__npc_subbank_mark_free(rvu, sb);
 err1:
 	kfree(save);
+	*alloc_cnt = 0;
 	return rc;
 }
 
@@ -3502,7 +3503,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
 {
 	int alloc_cnt1, alloc_cnt2;
 	struct npc_subbank *sb;
-	int rc, sb_off, i;
+	int rc, sb_off, i, err;
 	bool deleted;
 
 	sb = &npc_priv.sb[f->idx];
@@ -3516,6 +3517,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
 				 NPC_MCAM_LOWER_PRIO,
 				 false, cnt, save, cnt, true,
 				 &alloc_cnt1);
+
 	if (alloc_cnt1 < cnt) {
 		rc = __npc_subbank_alloc(rvu, sb,
 					 NPC_MCAM_KEY_X2, sb->b1b,
@@ -3531,15 +3533,17 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
 		dev_err(rvu->dev,
 			"%s: Failed to alloc cnt=%u alloc_cnt1=%u alloc_cnt2=%u\n",
 			__func__, cnt, alloc_cnt1, alloc_cnt2);
+		rc = -ENOSPC;
 		goto fail_free_alloc;
 	}
+
 	return 0;
 
 fail_free_alloc:
 	for (i = 0; i < alloc_cnt1 + alloc_cnt2; i++) {
-		rc =  npc_mcam_idx_2_subbank_idx(rvu, save[i],
-						 &sb, &sb_off);
-		if (rc) {
+		err =  npc_mcam_idx_2_subbank_idx(rvu, save[i],
+						  &sb, &sb_off);
+		if (err) {
 			dev_err(rvu->dev,
 				"%s: Error to find subbank for mcam idx=%u\n",
 				__func__, save[i]);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 01/11] octeontx2-af: npc: cn20k: Propagate MCAM key-type errors on cn20k
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Suman Ghosh, Dan Carpenter
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

npc_mcam_idx_2_key_type() can fail; callers used to ignore it and still
used kw_type when enabling, configuring, copying, and reading MCAM
entries. That could program or decode hardware with an undefined key
type.

Return -EINVAL when key-type lookup fails. Return -EINVAL from
npc_cn20k_copy_mcam_entry() when src and dest key types differ instead
of failing silently.

Change npc_cn20k_{enable,config,copy,read}_mcam_entry() to return int on
success or error. Thread those errors through the cn20k MCAM write and
read mbox handlers, the cn20k baseline steer read path, NPC defrag
move (disable/copy/enable with dev_err and -EFAULT), and the DMAC
update path in rvu_npc_fs.c.

Make npc_copy_mcam_entry() return int so the cn20k branch can return
npc_cn20k_copy_mcam_entry() without a void/int mismatch, and fail
NPC_MCAM_SHIFT_ENTRY when copy fails.

Cc: Suman Ghosh <sumang@marvell.com>
Cc: Dan Carpenter <error27@gmail.com>
Fixes: 6d1e70282f76 ("octeontx2-af: npc: cn20k: Use common APIs")
Link: https://lore.kernel.org/netdev/adiQJvuKlEhq2ILx@stanley.mountain/
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../ethernet/marvell/octeontx2/af/cn20k/npc.c | 109 ++++++++++++------
 .../ethernet/marvell/octeontx2/af/cn20k/npc.h |  20 ++--
 .../ethernet/marvell/octeontx2/af/rvu_npc.c   |  18 ++-
 .../marvell/octeontx2/af/rvu_npc_fs.c         |  20 ++--
 4 files changed, 111 insertions(+), 56 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 7291fdb89b03..49b5cd2838b5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -798,7 +798,7 @@ void npc_cn20k_load_mkex_profile(struct rvu *rvu, int blkaddr,
 		iounmap(mkex_prfl_addr);
 }
 
-void
+int
 npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
 			    int index, bool enable)
 {
@@ -808,7 +808,9 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
 	u64 cfg, hw_prio;
 	u8 kw_type;
 
-	npc_mcam_idx_2_key_type(rvu, index, &kw_type);
+	if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+		return -EINVAL;
+
 	if (kw_type == NPC_MCAM_KEY_X2) {
 		cfg = rvu_read64(rvu, blkaddr,
 				 NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx,
@@ -819,7 +821,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
 		rvu_write64(rvu, blkaddr,
 			    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
 			    cfg);
-		return;
+		return 0;
 	}
 
 	/* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks
@@ -836,6 +838,8 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
 			    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
 			    cfg);
 	}
+
+	return 0;
 }
 
 void
@@ -1042,9 +1046,9 @@ npc_cn20k_set_mcam_bank_cfg(struct rvu *rvu, int blkaddr, int mcam_idx,
 	}
 }
 
-void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
-				 u8 intf, struct cn20k_mcam_entry *entry,
-				 bool enable, u8 hw_prio, u8 req_kw_type)
+int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
+				u8 intf, struct cn20k_mcam_entry *entry,
+				bool enable, u8 hw_prio, u8 req_kw_type)
 {
 	struct npc_mcam *mcam = &rvu->hw->mcam;
 	int mcam_idx = index % mcam->banksize;
@@ -1052,10 +1056,13 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 	int kw = 0;
 	u8 kw_type;
 
+	if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+		return -EINVAL;
+
 	/* Disable before mcam entry update */
-	npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false);
+	if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false))
+		return -EINVAL;
 
-	npc_mcam_idx_2_key_type(rvu, index, &kw_type);
 	/* CAM1 takes the comparison value and
 	 * CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'.
 	 * CAM1<n> = 0 & CAM0<n> = 1 => match if key<n> = 0
@@ -1120,9 +1127,11 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 	/* PF installing VF rule */
 	npc_cn20k_set_mcam_bank_cfg(rvu, blkaddr, mcam_idx, bank,
 				    kw_type, enable, hw_prio);
+
+	return 0;
 }
 
-void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
+int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
 {
 	struct npc_mcam *mcam = &rvu->hw->mcam;
 	u64 cfg, sreg, dreg, soff, doff;
@@ -1132,10 +1141,15 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
 
 	dbank = npc_get_bank(mcam, dest);
 	sbank = npc_get_bank(mcam, src);
-	npc_mcam_idx_2_key_type(rvu, src, &src_kwtype);
-	npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype);
+
+	if (npc_mcam_idx_2_key_type(rvu, src, &src_kwtype))
+		return -EINVAL;
+
+	if (npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype))
+		return -EINVAL;
+
 	if (src_kwtype != dest_kwtype)
-		return;
+		return -EINVAL;
 
 	src &= (mcam->banksize - 1);
 	dest &= (mcam->banksize - 1);
@@ -1170,6 +1184,8 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
 		if (src_kwtype == NPC_MCAM_KEY_X2)
 			break;
 	}
+
+	return 0;
 }
 
 static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx,
@@ -1179,16 +1195,17 @@ static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx,
 	entry->kw_mask[idx] = cam1 ^ cam0;
 }
 
-void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
-			       struct cn20k_mcam_entry *entry,
-			       u8 *intf, u8 *ena, u8 *hw_prio)
+int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
+			      struct cn20k_mcam_entry *entry,
+			      u8 *intf, u8 *ena, u8 *hw_prio)
 {
 	struct npc_mcam *mcam = &rvu->hw->mcam;
 	u64 cam0, cam1, bank_cfg, cfg;
 	int kw = 0, bank;
 	u8 kw_type;
 
-	npc_mcam_idx_2_key_type(rvu, index, &kw_type);
+	if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+		return -EINVAL;
 
 	bank = npc_get_bank(mcam, index);
 	index &= (mcam->banksize - 1);
@@ -1298,6 +1315,8 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
 	cfg = rvu_read64(rvu, blkaddr,
 			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 1));
 	entry->vtag_action = cfg;
+
+	return 0;
 }
 
 int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu,
@@ -1335,11 +1354,10 @@ int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu,
 	if (is_pffunc_af(req->hdr.pcifunc))
 		nix_intf = req->intf;
 
-	npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf,
-				    &req->entry_data, req->enable_entry,
-				    req->hw_prio, req->req_kw_type);
+	rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf,
+					 &req->entry_data, req->enable_entry,
+					 req->hw_prio, req->req_kw_type);
 
-	rc = 0;
 exit:
 	mutex_unlock(&mcam->lock);
 	return rc;
@@ -1361,11 +1379,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_read_entry(struct rvu *rvu,
 
 	mutex_lock(&mcam->lock);
 	rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
-	if (!rc)
-		npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry,
-					  &rsp->entry_data, &rsp->intf,
-					  &rsp->enable, &rsp->hw_prio);
+	if (rc)
+		goto fail;
 
+	rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry,
+				       &rsp->entry_data, &rsp->intf,
+				       &rsp->enable, &rsp->hw_prio);
+fail:
 	mutex_unlock(&mcam->lock);
 	return rc;
 }
@@ -1415,14 +1435,14 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu,
 	else
 		nix_intf = pfvf->nix_rx_intf;
 
-	npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
-				    &req->entry_data, req->enable_entry,
-				    req->hw_prio, req->req_kw_type);
+	rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
+					 &req->entry_data, req->enable_entry,
+					 req->hw_prio, req->req_kw_type);
 
 	mutex_unlock(&mcam->lock);
 
 	rsp->entry = entry_rsp.entry;
-	return 0;
+	return rc;
 }
 
 static int rvu_npc_get_base_steer_rule_type(struct rvu *rvu, u16 pcifunc)
@@ -1480,9 +1500,9 @@ int rvu_mbox_handler_npc_cn20k_read_base_steer_rule(struct rvu *rvu,
 
 read_entry:
 	/* Read the mcam entry */
-	npc_cn20k_read_mcam_entry(rvu, blkaddr, index,
-				  &rsp->entry, &intf,
-				  &enable, &hw_prio);
+	rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, index,
+				       &rsp->entry, &intf,
+				       &enable, &hw_prio);
 	mutex_unlock(&mcam->lock);
 out:
 	return rc;
@@ -3607,9 +3627,30 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu,
 				   NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx,
 								      bank));
 
-		npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false);
-		npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx);
-		npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true);
+		/* If bug happened during copy/enable mcam, then there is a bug in allocation
+		 * algorithm itself. There is no point in rewinding and returning, as it
+		 * will face further issue. Return error after printing error
+		 */
+		if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false)) {
+			dev_err(rvu->dev,
+				"%s: Error happened while disabling old_mid=%u\n",
+				__func__, old_midx);
+			return -EFAULT;
+		}
+
+		if (npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx)) {
+			dev_err(rvu->dev,
+				"%s: Error happened to while copying old_midx=%u new_midx=%u\n",
+				__func__, old_midx, new_midx);
+			return -EFAULT;
+		}
+
+		if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true)) {
+			dev_err(rvu->dev,
+				"%s: Error happened while enabling new_mid=%u\n",
+				__func__, new_midx);
+			return -EFAULT;
+		}
 
 		midx = new_midx % mcam->banksize;
 		bank = new_midx / mcam->banksize;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
index 815d0b257a7e..8f3eea9cfb1d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
@@ -320,16 +320,16 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc);
 int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
 				u16 *mcast, u16 *promisc, u16 *ucast);
 
-void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
-				 u8 intf, struct cn20k_mcam_entry *entry,
-				 bool enable, u8 hw_prio, u8 req_kw_type);
-void npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
-				 int index, bool enable);
-void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr,
-			       u16 src, u16 dest);
-void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
-			       struct cn20k_mcam_entry *entry, u8 *intf,
-			       u8 *ena, u8 *hw_prio);
+int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
+				u8 intf, struct cn20k_mcam_entry *entry,
+				bool enable, u8 hw_prio, u8 req_kw_type);
+int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
+				int index, bool enable);
+int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr,
+			      u16 src, u16 dest);
+int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
+			      struct cn20k_mcam_entry *entry, u8 *intf,
+			      u8 *ena, u8 *hw_prio);
 void npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr,
 				int bank, int index);
 int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index c2ca5ed1d028..ecaf0946b852 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -241,7 +241,10 @@ void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
 		if (index < 0 || index >= mcam->banksize * mcam->banks)
 			return;
 
-		return npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable);
+		if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable))
+			dev_err(rvu->dev, "Error to %s mcam %u entry\n",
+				enable ? "enable" : "disable", index);
+		return;
 	}
 
 	index &= (mcam->banksize - 1);
@@ -589,8 +592,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
 			  NPC_AF_MCAMEX_BANKX_CFG(src, sbank)) & 1;
 }
 
-static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
-				int blkaddr, u16 src, u16 dest)
+static int npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
+			       int blkaddr, u16 src, u16 dest)
 {
 	int dbank = npc_get_bank(mcam, dest);
 	int sbank = npc_get_bank(mcam, src);
@@ -630,6 +633,7 @@ static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
 			 NPC_AF_MCAMEX_BANKX_CFG(src, sbank));
 	rvu_write64(rvu, blkaddr,
 		    NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg);
+	return 0;
 }
 
 u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
@@ -3266,7 +3270,10 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
 		npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false);
 
 		/* Copy rule from old entry to new entry */
-		npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry);
+		if (npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry)) {
+			rc = NPC_MCAM_INVALID_REQ;
+			break;
+		}
 
 		/* Copy counter mapping, if any */
 		cntr = mcam->entry2cntr_map[old_entry];
@@ -3284,7 +3291,8 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
 
 	/* If shift has failed then report the failed index */
 	if (index != req->shift_count) {
-		rc = NPC_MCAM_PERM_DENIED;
+		if (!rc)
+			rc = NPC_MCAM_PERM_DENIED;
 		rsp->failed_entry_idx = index;
 	}
 
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index b45798d9fdab..fe10554b1f0e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1980,13 +1980,15 @@ static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr,
 
 	ether_addr_copy(rule->packet.dmac, pfvf->mac_addr);
 
-	if (is_cn20k(rvu->pdev))
-		npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry,
-					  cn20k_entry, &intf,
-					  &enable, &hw_prio);
-	else
+	if (is_cn20k(rvu->pdev)) {
+		if (npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry,
+					      cn20k_entry, &intf,
+					      &enable, &hw_prio))
+			return -EINVAL;
+	} else {
 		npc_read_mcam_entry(rvu, mcam, npcblkaddr, rule->entry,
 				    entry, &intf, &enable);
+	}
 
 	npc_update_entry(rvu, NPC_DMAC, &mdata,
 			 ether_addr_to_u64(pfvf->mac_addr), 0,
@@ -2038,8 +2040,12 @@ void npc_mcam_enable_flows(struct rvu *rvu, u16 target)
 				continue;
 			}
 
-			if (rule->vfvlan_cfg)
-				npc_update_dmac_value(rvu, blkaddr, rule, pfvf);
+			if (rule->vfvlan_cfg) {
+				if (npc_update_dmac_value(rvu, blkaddr, rule, pfvf))
+					dev_err(rvu->dev,
+						"Update dmac failed for %u, target=%#x\n",
+						rule->entry, target);
+			}
 
 			if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) {
 				if (!def_ucast_rule)
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 05/11] octeontx2-af: npc: cn20k: Align MCAM X2/X4 use with KEX profile
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

Flow install derives whether an NPC MCAM line must be X2 or X4 from the
effective key width (>256 bits needs X4).  That is only valid when the
global KEX profile allows X4 keys; if the profile is X2-only, fail the
install with -EOPNOTSUPP instead of allocating a mismatched entry.
Default L2 rules allocation always requested X2 keys.  When npc_priv.kw
is X4, request X4 MCAM entries and wrap the reference index to the MCAM
bank depth so default rules match the active key mode.

Fixes: 9000cada7aa9 ("octeontx2-af: npc: cn20k: Allocate MCAM entry for flow installation")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../net/ethernet/marvell/octeontx2/af/cn20k/npc.c    | 11 +++++++++--
 .../net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c   | 12 +++++++++++-
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 97acba77ff88..74dad164b6a9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -4328,11 +4328,18 @@ int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc)
 	 * as NPC_DFT_RULE_PRIO - 1 (higher hw priority)
 	 */
 	req.contig = false;
-	req.kw_type = NPC_MCAM_KEY_X2;
 	req.count = cnt;
 	req.hdr.pcifunc = pcifunc;
 	req.ref_prio = NPC_MCAM_LOWER_PRIO;
-	req.ref_entry = eidx + 1;
+
+	if (npc_priv.kw == NPC_MCAM_KEY_X4) {
+		req.kw_type = NPC_MCAM_KEY_X4;
+		req.ref_entry = eidx & (npc_priv.bank_depth - 1);
+	} else {
+		req.kw_type = NPC_MCAM_KEY_X2;
+		req.ref_entry = eidx;
+	}
+
 	ret = rvu_mbox_handler_npc_mcam_alloc_entry(rvu, &req, &rsp);
 	if (ret) {
 		dev_err(rvu->dev,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index fe10554b1f0e..dd5d50d52964 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1663,9 +1663,11 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu,
 {
 	struct npc_mcam_alloc_entry_req entry_req;
 	struct npc_mcam_alloc_entry_rsp entry_rsp;
+	struct npc_get_pfl_info_rsp rsp = { 0 };
 	struct npc_get_num_kws_req kws_req;
 	struct npc_get_num_kws_rsp kws_rsp;
 	int off, kw_bits, rc;
+	struct msg_req req;
 	u8 *src, *dst;
 
 	if (!is_cn20k(rvu->pdev)) {
@@ -1689,8 +1691,16 @@ rvu_npc_alloc_entry_for_flow_install(struct rvu *rvu,
 	kw_bits = kws_rsp.kws * 64;
 
 	*kw_type = NPC_MCAM_KEY_X2;
-	if (kw_bits > 256)
+	if (kw_bits > 256) {
+		rvu_mbox_handler_npc_get_pfl_info(rvu, &req, &rsp);
+		if (rsp.kw_type == NPC_MCAM_KEY_X2) {
+			dev_err(rvu->dev,
+				"Only X2 entries are supported in X2 profile\n");
+			return -EOPNOTSUPP;
+		}
+
 		*kw_type = NPC_MCAM_KEY_X4;
+	}
 
 	memset(&entry_req, 0, sizeof(entry_req));
 	memset(&entry_rsp, 0, sizeof(entry_rsp));
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 02/11] octeontx2-af: npc: cn20k: Drop debugfs_create_file() error checks in init
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Dan Carpenter, Simon Horman
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

debugfs is not intended to be checked for allocation failures the way
other kernel APIs are: callers should not fail probe or subsystem init
because a debugfs node could not be created, including when debugfs is
disabled in Kconfig.  Replacing NULL checks with IS_ERR() checks is
similarly wrong for optional debugfs.

Remove dentry checks and -EFAULT returns from npc_cn20k_debugfs_init().
https://staticthinking.wordpress.com/2023/07/24/debugfs-functions-are-not-supposed-to-be-checked/

Cc: Dan Carpenter <error27@gmail.com>
Fixes: 528530dff56b ("octeontx2-af: npc: cn20k: add debugfs support")
Link: https://lore.kernel.org/netdev/adjNGPWKMOk3KgWL@stanley.mountain/
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../marvell/octeontx2/af/cn20k/debugfs.c      | 33 ++++++-------------
 1 file changed, 10 insertions(+), 23 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
index 3debf2fae1a4..6f13296303cb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
@@ -249,34 +249,21 @@ DEFINE_SHOW_ATTRIBUTE(npc_defrag);
 int npc_cn20k_debugfs_init(struct rvu *rvu)
 {
 	struct npc_priv_t *npc_priv = npc_priv_get();
-	struct dentry *npc_dentry;
 
-	npc_dentry = debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc,
-					 npc_priv, &npc_mcam_layout_fops);
+	debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc,
+			    npc_priv, &npc_mcam_layout_fops);
 
-	if (!npc_dentry)
-		return -EFAULT;
+	debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc,
+			    rvu, &npc_mcam_default_fops);
 
-	npc_dentry = debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc,
-					 rvu, &npc_mcam_default_fops);
+	debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
+			    npc_priv, &npc_vidx2idx_map_fops);
 
-	if (!npc_dentry)
-		return -EFAULT;
+	debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
+			    npc_priv, &npc_idx2vidx_map_fops);
 
-	npc_dentry = debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
-					 npc_priv, &npc_vidx2idx_map_fops);
-	if (!npc_dentry)
-		return -EFAULT;
-
-	npc_dentry = debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
-					 npc_priv, &npc_idx2vidx_map_fops);
-	if (!npc_dentry)
-		return -EFAULT;
-
-	npc_dentry = debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc,
-					 npc_priv, &npc_defrag_fops);
-	if (!npc_dentry)
-		return -EFAULT;
+	debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc,
+			    npc_priv, &npc_defrag_fops);
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 07/11] octeontx2-af: npc: cn20k: Fix bank value.
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Suman Ghosh
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

or X4 keys its loop reused the bank parameter as the loop counter,
so bank no longer reflected the caller's bank after the loop and
the control flow was hard to follow.
Program NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT directly in
npc_cn20k_config_mcam_entry(): one CFG write for X2 using the computed
bank, and one CFG write per bank inside the X4 action loop. Enable the
entry at the end with npc_cn20k_enable_mcam_entry(..., true) instead of
embedding the enable bit in bank_cfg via the removed helper.

Cc: Suman Ghosh <sumang@marvell.com>
Fixes: 4e527f1e5c15 ("octeontx2-af: npc: cn20k: Add new mailboxes for CN20K silicon")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../ethernet/marvell/octeontx2/af/cn20k/npc.c | 92 ++++++++-----------
 1 file changed, 37 insertions(+), 55 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 9ded0763ad54..66d0d56f3045 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -1045,34 +1045,6 @@ static void npc_cn20k_config_kw_x4(struct rvu *rvu, struct npc_mcam *mcam,
 				       kw, req_kw_type);
 }
 
-static void
-npc_cn20k_set_mcam_bank_cfg(struct rvu *rvu, int blkaddr, int mcam_idx,
-			    int bank, u8 kw_type, bool enable, u8 hw_prio)
-{
-	struct npc_mcam *mcam = &rvu->hw->mcam;
-	u64 bank_cfg;
-
-	bank_cfg = (u64)hw_prio << 24;
-	if (enable)
-		bank_cfg |= 0x1;
-
-	if (kw_type == NPC_MCAM_KEY_X2) {
-		rvu_write64(rvu, blkaddr,
-			    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
-			    bank_cfg);
-		return;
-	}
-
-	/* For NPC_MCAM_KEY_X4 keys, both the banks
-	 * need to be programmed with the same value.
-	 */
-	for (bank = 0; bank < mcam->banks_per_entry; bank++) {
-		rvu_write64(rvu, blkaddr,
-			    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
-			    bank_cfg);
-	}
-}
-
 int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 				u8 intf, struct cn20k_mcam_entry *entry,
 				bool enable, u8 hw_prio, u8 req_kw_type)
@@ -1080,6 +1052,7 @@ int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 	struct npc_mcam *mcam = &rvu->hw->mcam;
 	int mcam_idx = index % mcam->banksize;
 	int bank = index / mcam->banksize;
+	u64 bank_cfg = (u64)hw_prio << 24;
 	int kw = 0;
 	u8 kw_type;
 
@@ -1119,41 +1092,50 @@ int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 			    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
 								  bank, 1),
 			    entry->vtag_action);
-		goto set_cfg;
-	}
 
-	/* Clear mcam entry to avoid writes being suppressed by NPC */
-	npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx);
-	npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx);
-
-	npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
-			       mcam_idx, intf, entry,
-			       kw_type, req_kw_type);
-	for (bank = 0; bank < mcam->banks_per_entry; bank++) {
-		/* Set 'action' */
+		/* Set HW priority */
 		rvu_write64(rvu, blkaddr,
-			    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
-								  bank, 0),
-			    entry->action);
+			    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
+			    bank_cfg);
 
-		/* Set TAG 'action' */
-		rvu_write64(rvu, blkaddr,
-			    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
-								  bank, 1),
-			    entry->vtag_action);
+	} else {
+		/* Clear mcam entry to avoid writes being suppressed by NPC */
+		npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx);
+		npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx);
 
-		/* Set 'action2' for inline receive */
-		rvu_write64(rvu, blkaddr,
-			    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
-								  bank, 2),
-			    entry->action2);
+		npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
+				       mcam_idx, intf, entry,
+				       kw_type, req_kw_type);
+		for (bank = 0; bank < mcam->banks_per_entry; bank++) {
+			/* Set 'action' */
+			rvu_write64(rvu, blkaddr,
+				    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+									  bank, 0),
+				    entry->action);
+
+			/* Set TAG 'action' */
+			rvu_write64(rvu, blkaddr,
+				    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+									  bank, 1),
+				    entry->vtag_action);
+
+			/* Set 'action2' for inline receive */
+			rvu_write64(rvu, blkaddr,
+				    NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+									  bank, 2),
+				    entry->action2);
+
+			/* Set HW priority */
+			rvu_write64(rvu, blkaddr,
+				    NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
+				    bank_cfg);
+		}
 	}
 
-set_cfg:
 	/* TODO: */
 	/* PF installing VF rule */
-	npc_cn20k_set_mcam_bank_cfg(rvu, blkaddr, mcam_idx, bank,
-				    kw_type, enable, hw_prio);
+	if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable))
+		return -EINVAL;
 
 	return 0;
 }
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 06/11] octeontx2-af: npc: cn20k: Clear MCAM entries by index and key width
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Suman Ghosh
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

Replace the old four-argument CN20K MCAM clear with a per-bank static
helper and npc_cn20k_clear_mcam_entry() that takes a logical MCAM index,
resolves the key width via npc_mcam_idx_2_key_type(), and clears either
one bank (X2) or every bank (X4).
Call it from npc_clear_mcam_entry() on cn20k and log when key-type lookup
fails. Use the per-bank helper from npc_cn20k_config_mcam_entry() for
pre-program clears.
For loopback VFs, use the promisc MCAM index as ucast_idx when copying
RSS action for promisc, matching cn20k default-rule layout.

Cc: Suman Ghosh <sumang@marvell.com>
Fixes: 6d1e70282f76 ("octeontx2-af: npc: cn20k: Use common APIs")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../ethernet/marvell/octeontx2/af/cn20k/npc.c | 37 ++++++++++++++++---
 .../ethernet/marvell/octeontx2/af/cn20k/npc.h |  3 +-
 .../ethernet/marvell/octeontx2/af/rvu_npc.c   | 17 ++++++++-
 3 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 74dad164b6a9..9ded0763ad54 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -842,8 +842,8 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
 	return 0;
 }
 
-void
-npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index)
+static void
+npc_clear_x2_entry(struct rvu *rvu, int blkaddr, int bank, int index)
 {
 	rvu_write64(rvu, blkaddr,
 		    NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1),
@@ -877,6 +877,33 @@ npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index)
 		    NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, bank), 0);
 }
 
+int
+npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int mcam_idx)
+{
+	struct npc_mcam *mcam = &rvu->hw->mcam;
+	int bank = npc_get_bank(mcam, mcam_idx);
+	u8 kw_type;
+	int index;
+
+	if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &kw_type))
+		return -EINVAL;
+
+	index = mcam_idx & (mcam->banksize - 1);
+
+	if (kw_type == NPC_MCAM_KEY_X2) {
+		npc_clear_x2_entry(rvu, blkaddr, bank, index);
+		return 0;
+	}
+
+	/* For NPC_MCAM_KEY_X4 keys, both the banks
+	 * need to be programmed with the same value.
+	 */
+	for (bank = 0; bank < mcam->banks_per_entry; bank++)
+		npc_clear_x2_entry(rvu, blkaddr, bank, index);
+
+	return 0;
+}
+
 static void npc_cn20k_get_keyword(struct cn20k_mcam_entry *entry, int idx,
 				  u64 *cam0, u64 *cam1)
 {
@@ -1071,7 +1098,7 @@ int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 	 */
 	if (kw_type == NPC_MCAM_KEY_X2) {
 		/* Clear mcam entry to avoid writes being suppressed by NPC */
-		npc_cn20k_clear_mcam_entry(rvu, blkaddr, bank, mcam_idx);
+		npc_clear_x2_entry(rvu, blkaddr, bank, mcam_idx);
 		npc_cn20k_config_kw_x2(rvu, mcam, blkaddr,
 				       mcam_idx, intf, entry,
 				       bank, kw_type, kw, req_kw_type);
@@ -1096,8 +1123,8 @@ int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
 	}
 
 	/* Clear mcam entry to avoid writes being suppressed by NPC */
-	npc_cn20k_clear_mcam_entry(rvu, blkaddr, 0, mcam_idx);
-	npc_cn20k_clear_mcam_entry(rvu, blkaddr, 1, mcam_idx);
+	npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx);
+	npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx);
 
 	npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
 			       mcam_idx, intf, entry,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
index 8f3eea9cfb1d..2f761b97f91b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
@@ -330,8 +330,7 @@ int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr,
 int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
 			      struct cn20k_mcam_entry *entry, u8 *intf,
 			      u8 *ena, u8 *hw_prio);
-void npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr,
-				int bank, int index);
+int npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int index);
 int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type);
 u16 npc_cn20k_vidx2idx(u16 index);
 u16 npc_cn20k_idx2vidx(u16 idx);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index ecaf0946b852..44ca65efc80f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -261,6 +261,13 @@ static void npc_clear_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
 	int bank = npc_get_bank(mcam, index);
 	int actbank = bank;
 
+	if (is_cn20k(rvu->pdev)) {
+		if (npc_cn20k_clear_mcam_entry(rvu, blkaddr, index))
+			dev_err(rvu->dev, "%s Failed to clear mcam %u\n",
+				__func__, index);
+		return;
+	}
+
 	index &= (mcam->banksize - 1);
 	for (; bank < (actbank + mcam->banks_per_entry); bank++) {
 		rvu_write64(rvu, blkaddr,
@@ -755,9 +762,15 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
 
 	/* If the corresponding PF's ucast action is RSS,
 	 * use the same action for promisc also
+	 * Please note that for lbk(s) "index" and "ucast_idx"
+	 * will be same.
 	 */
-	ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
-					     nixlf, NIXLF_UCAST_ENTRY);
+	if (is_lbk_vf(rvu, pcifunc))
+		ucast_idx = index;
+	else
+		ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
+						     nixlf, NIXLF_UCAST_ENTRY);
+
 	if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx))
 		*(u64 *)&action = npc_get_mcam_action(rvu, mcam,
 						      blkaddr, ucast_idx);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 09/11] octeontx2-af: npc: cn20k: Initialize default-rule index outputs up front
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

npc_cn20k_dft_rules_idx_get() wrote USHRT_MAX into individual outputs
only on some error paths (lbk promisc lookup, VF ucast lookup, and the
PF rule walk), which could leave other caller slots stale across
retries.

Set every non-NULL bcast/mcast/promisc/ucast pointer to USHRT_MAX once
at entry, then drop the duplicate assignments on failure. Successful
lookups still overwrite the relevant slot before returning.

Fixes: 09d3b7a1403f ("octeontx2-af: npc: cn20k: Allocate default MCAM indexes")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 0bcfd1243f12..6d3166960a3f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -4004,6 +4004,13 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
 	void *val;
 	int i, j;
 
+	for (i = 0; i < ARRAY_SIZE(ptr); i++) {
+		if (!ptr[i])
+			continue;
+
+		*ptr[i] = USHRT_MAX;
+	}
+
 	if (!npc_priv.init_done)
 		return 0;
 
@@ -4019,7 +4026,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
 				 npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID],
 				 pcifunc);
 
-			*ptr[0] = USHRT_MAX;
 			return -ESRCH;
 		}
 
@@ -4039,7 +4045,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
 				 npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID],
 				 pcifunc);
 
-			*ptr[3] = USHRT_MAX;
 			return -ESRCH;
 		}
 
@@ -4059,7 +4064,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
 				 __func__,
 				 npc_dft_rule_name[i], pcifunc);
 
-			*ptr[j] = USHRT_MAX;
 			continue;
 		}
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 net 08/11] octeontx2-af: npc: cn20k: Fix MCAM actions read
From: Ratheesh Kannoth @ 2026-04-23 10:43 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: sgoutham, davem, edumazet, kuba, pabeni, andrew+netdev,
	Ratheesh Kannoth, Suman Ghosh
In-Reply-To: <20260423104317.2707923-1-rkannoth@marvell.com>

npc_cn20k_read_mcam_entry() always reloaded action and vtag_action from
bank 0 after programming the CAM words. Use the bank returned by
npc_get_bank() for the ACTION reads as well, and read those registers
once up front so both X2 and X4 paths share the same metadata.

Return directly from the X2 keyword path now that the action fields are
already populated.

Cc: Suman Ghosh <sumang@marvell.com>
Fixes: 6d1e70282f76 ("octeontx2-af: npc: cn20k: Use common APIs")
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 .../ethernet/marvell/octeontx2/af/cn20k/npc.c | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 66d0d56f3045..0bcfd1243f12 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -1219,6 +1219,18 @@ int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
 	bank = npc_get_bank(mcam, index);
 	index &= (mcam->banksize - 1);
 
+	cfg = rvu_read64(rvu, blkaddr,
+			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 0));
+	entry->action = cfg;
+
+	cfg = rvu_read64(rvu, blkaddr,
+			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 1));
+	entry->vtag_action = cfg;
+
+	cfg = rvu_read64(rvu, blkaddr,
+			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 2));
+	entry->action2 = cfg;
+
 	cfg = rvu_read64(rvu, blkaddr,
 			 NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index,
 								 bank, 1)) & 3;
@@ -1268,7 +1280,7 @@ int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
 									bank,
 									0));
 		npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
-		goto read_action;
+		return 0;
 	}
 
 	for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4) {
@@ -1313,18 +1325,6 @@ int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
 		npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
 	}
 
-read_action:
-	/* 'action' is set to same value for both bank '0' and '1'.
-	 * Hence, reading bank '0' should be enough.
-	 */
-	cfg = rvu_read64(rvu, blkaddr,
-			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 0));
-	entry->action = cfg;
-
-	cfg = rvu_read64(rvu, blkaddr,
-			 NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 1));
-	entry->vtag_action = cfg;
-
 	return 0;
 }
 
-- 
2.43.0


^ permalink raw reply related


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