* Re: [PATCH net-next] net: phylink: allow PHYs to be attached in 802.3z inband mode
From: Andrew Lunn @ 2026-03-30 12:26 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Maxime Chevallier, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni, Alexis Lothore
In-Reply-To: <acatrPb1DdHuyVoL@shell.armlinux.org.uk>
> As I've recently said, I think we're more or less boxed in between a
> rock and a hard place because of the hacks that qcom-ethqos introduced
> with its PCS support. At the moment, I am failing to see any path where
> the stmmac PCS can be programmed for inband according to phylink's
> wishes and qcom-ethqos can be made to work without its hacks.
>
> This all ultimately comes back to the half-hearted phylink conversion
> of stmmac that was done behind my back without my review. Had the
> conversion been done fully, and kept up with the phylink changes for
> PCS support, then we probably wouldn't be in this situation today.
>
> Unfortunately, the driver is going to keep breaking all the time that
> it abuses phylink, and at the moment I see no way to stop this sodding
> driver abusing phylink.
"The needs of the many outweigh the needs of the few"
The MAC driver is used by a lot of different SoCs. If qcom-ethqos
caused this problem, should we consider breaking backwards
compatibility for it, if that opens up a path for getting clean
phylink integration for all the other variants?
Andrew
^ permalink raw reply
* Re: [PATCH net-next v4 03/10] selftests: net: extend lib.sh to parse drivers/net/net.config
From: Ioana Ciornei @ 2026-03-30 12:28 UTC (permalink / raw)
To: Petr Machata
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, willemb,
linux-kselftest
In-Reply-To: <87wlyt4juu.fsf@nvidia.com>
On Mon, Mar 30, 2026 at 01:28:49PM +0200, Petr Machata wrote:
>
> Ioana Ciornei <ioana.ciornei@nxp.com> writes:
>
> > Extend lib.sh so that it's able to parse driver/net/net.config and
> > environment variables such as NETIF, REMOTE_TYPE, LOCAL_V4 etc described
> > in drivers/net/README.rst.
> >
> > In order to make the transition towards running with a single local
> > interface smoother for the bash networking driver tests, beside sourcing
> > the net.config file also translate the new env variables into the old
> > style based on the NETIFS array. Since the NETIFS array only holds the
> > network interface names, also add a new array - TARGETS - which keeps
> > track of the target on which a specific interfaces resides - local,
> > netns or accesible through an ssh command.
> >
> > For example, a net.config which looks like below:
> >
> > NETIF=eth0
> > LOCAL_V4=192.168.1.1
> > REMOTE_V4=192.168.1.2
> > REMOTE_TYPE=ssh
> > REMOTE_ARGS=root@192.168.1.2
> >
> > will generate the NETIFS and TARGETS arrays with the following data.
> >
> > NETIFS[p1]="eth0"
> > NETIFS[p2]="eth2"
> >
> > TARGETS[eth0]="local:"
> > TARGETS[eth2]="ssh:root@192.168.1.2"
> >
> > The above will be true if on the remote target, the interface which has
> > the 192.168.1.2 address is named eth2.
> >
> > Since the TARGETS array is indexed by the network interface name,
> > document a new restriction README.rst which states that the remote
> > interface cannot have the same name as the local one.
> >
> > Also keep the old way of populating the NETIFS variable based on the
> > command line arguments. This will be invoked in case NETIF is not
> > defined.
> >
> > Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
> > ---
> > Changes in v4:
> > - reword the entry in README.rst to mention that the different interface
> > names is only a bash restriction and the python infrastructure does
> > not have the same problem.
> > - only declare the TARGETS array when necessary
>
> I guess you mean at the point where it's necessary, instead of it being
> a user API. Right now TARGETS is defined always, and I think that's
> better than having it only defined sometimes.
Yes.
(...)
> > diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
> > index 3009ce00c5dc..93b865681840 100644
> > --- a/tools/testing/selftests/net/forwarding/lib.sh
> > +++ b/tools/testing/selftests/net/forwarding/lib.sh
> > @@ -3,6 +3,7 @@
> >
> > ##############################################################################
> > # Topology description. p1 looped back to p2, p3 to p4 and so on.
> > +#shellcheck disable=SC2034 # SC doesn't see our uses of global variables
>
> The shellcheck line should be moved elsewhere. The comment is related to
> NETIFS, and it's weird there's a shellcheck declaration between a
> comment and the thing it's commenting.
Ok.
>
> > declare -A NETIFS=(
> > [p1]=veth0
> > @@ -85,6 +86,9 @@ declare -A NETIFS=(
> > # e.g. a low-power board.
> > : "${KSFT_MACHINE_SLOW:=no}"
> >
> > +# Whether the test is conforming to the requirements and usage described in
> > +# drivers/net/README.rst.
> > +: "${DRIVER_TEST_CONFORMANT:=no}"
>
> Not sure this makes sense as user API either honestly. Given the
> differences between requirements of the two test types I can't imagine a
> non-conformant test would be runnable like that. The actual line is OK,
> but it should be moved probably above the if check, so as not to
> indicate it's usable as a user API.
Ok, will move it before the if statement.
(...)
> > +
> > + NETIFS[p2]="$remote_netif"
> > + TARGETS[$remote_netif]="$REMOTE_TYPE:$REMOTE_ARGS"
> > +else
> > + count=0
>
> This leg is missing declare -A TARGETS.
>
> Since both legs need to declare it, why not move it up above the if?
Ok, will declare it above the if.
Thanks!
Ioana
^ permalink raw reply
* Re: [PATCH net-next] net: phylink: allow PHYs to be attached in 802.3z inband mode
From: Russell King (Oracle) @ 2026-03-30 12:32 UTC (permalink / raw)
To: Andrew Lunn
Cc: Maxime Chevallier, David S. Miller, Eric Dumazet, Jakub Kicinski,
netdev, Paolo Abeni, Alexis Lothore
In-Reply-To: <30a2cad9-fa2c-4abe-ad51-983d70564c7c@lunn.ch>
On Mon, Mar 30, 2026 at 02:26:58PM +0200, Andrew Lunn wrote:
> > As I've recently said, I think we're more or less boxed in between a
> > rock and a hard place because of the hacks that qcom-ethqos introduced
> > with its PCS support. At the moment, I am failing to see any path where
> > the stmmac PCS can be programmed for inband according to phylink's
> > wishes and qcom-ethqos can be made to work without its hacks.
> >
> > This all ultimately comes back to the half-hearted phylink conversion
> > of stmmac that was done behind my back without my review. Had the
> > conversion been done fully, and kept up with the phylink changes for
> > PCS support, then we probably wouldn't be in this situation today.
> >
> > Unfortunately, the driver is going to keep breaking all the time that
> > it abuses phylink, and at the moment I see no way to stop this sodding
> > driver abusing phylink.
>
> "The needs of the many outweigh the needs of the few"
>
> The MAC driver is used by a lot of different SoCs. If qcom-ethqos
> caused this problem, should we consider breaking backwards
> compatibility for it, if that opens up a path for getting clean
> phylink integration for all the other variants?
Consider something like mvneta (which supports 2.5G speeds) connected
to a 2.5G PHY that uses 2500BASE-X for 2.5G speeds, and SGMII for
slower speeds with inband.
How should this be described in DT?
phy-mode = "2500base-x";
managed = "in-band-status";
will fail today, as these tests will refuse to attach a PHY in that
setup, and without a PHY attached, we will never know if it switches
to SGMII mode.
This patch makes phylink more flexible and permissive.
However, as has been found, this patch causes problems for stmmac,
so it needs a different approach, because stmmac interprets an
optional PHY as "we must have a PHY if we don't we fail" at the
moment.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH RFC net-next] net: stmmac: qcom-ethqos: set clk_csr
From: Andrew Lunn @ 2026-03-30 12:35 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Konrad Dybcio, Mohd Ayaan Anwar, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, linux-arm-kernel,
linux-arm-msm, linux-stm32, netdev, Paolo Abeni
In-Reply-To: <acpqgpCsbo3lJs3l@shell.armlinux.org.uk>
On Mon, Mar 30, 2026 at 01:20:18PM +0100, Russell King (Oracle) wrote:
> On Mon, Mar 30, 2026 at 01:18:56PM +0200, Konrad Dybcio wrote:
> > On 3/27/26 6:02 PM, Russell King (Oracle) wrote:
> > > The clocks for qcom-ethqos return a rate of zero as firmware manages
> > > their rate. According to hardware documentation, the clock which is
> > > fed to the slave AHB interface can crange between 50 and 100MHz.
> >
> > FWIW this __may__ possibly differ between platforms, but I'm not sure
> > to what degree. Will there be visible impact if we e.g. have a 200 or
> > 300 MHz clock somewhere?
>
> When you add other platforms, you're going to have to deal with their
> differences.
>
> IEEE 802.3 states that the maximum clock rate for the MDIO bus is
> 2.5MHz. You need to ensure that is the case.
>
> Current qcom-ethqos code doesn't set clk_csr, and returns zero for
> clk_get_rate() on the stmmac clocks because they are managed entirely
> in firmware.
Could a fixed clock be used in DT to represent clk_csr? Different
platforms then set it to different frequencies, to represent whatever
the firmware is doing.
Andrew
^ permalink raw reply
* [PATCH AUTOSEL 6.19-5.10] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
From: Sasha Levin @ 2026-03-30 12:38 UTC (permalink / raw)
To: patches, stable
Cc: Florian Westphal, Stefano Brivio, Pablo Neira Ayuso, Sasha Levin,
davem, edumazet, kuba, pabeni, netfilter-devel, coreteam, netdev,
linux-kernel
In-Reply-To: <20260330123842.756154-1-sashal@kernel.org>
From: Florian Westphal <fw@strlen.de>
[ Upstream commit d3c0037ffe1273fa1961e779ff6906234d6cf53c ]
New test case fails unexpectedly when avx2 matching functions are used.
The test first loads a ranomly generated pipapo set
with 'ipv4 . port' key, i.e. nft -f foo.
This works. Then, it reloads the set after a flush:
(echo flush set t s; cat foo) | nft -f -
This is expected to work, because its the same set after all and it was
already loaded once.
But with avx2, this fails: nft reports a clashing element.
The reported clash is of following form:
We successfully re-inserted
a . b
c . d
Then we try to insert a . d
avx2 finds the already existing a . d, which (due to 'flush set') is marked
as invalid in the new generation. It skips the element and moves to next.
Due to incorrect masking, the skip-step finds the next matching
element *only considering the first field*,
i.e. we return the already reinserted "a . b", even though the
last field is different and the entry should not have been matched.
No such error is reported for the generic c implementation (no avx2) or when
the last field has to use the 'nft_pipapo_avx2_lookup_slow' fallback.
Bisection points to
7711f4bb4b36 ("netfilter: nft_set_pipapo: fix range overlap detection")
but that fix merely uncovers this bug.
Before this commit, the wrong element is returned, but erronously
reported as a full, identical duplicate.
The root-cause is too early return in the avx2 match functions.
When we process the last field, we should continue to process data
until the entire input size has been consumed to make sure no stale
bits remain in the map.
Link: https://lore.kernel.org/netfilter-devel/20260321152506.037f68c0@elisabeth/
Signed-off-by: Florian Westphal <fw@strlen.de>
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
All verified. Now I have the complete and verified picture. Let me write
the final answer.
===============================================================
## PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: Subject Line**
Record: [netfilter: nft_set_pipapo_avx2] [don't return / fix] [Prevent
AVX2 pipapo lookup helpers from returning a non-matching entry when an
expired/invalid element is skipped]
**Step 1.2: Tags**
Record:
- Link: `https://lore.kernel.org/netfilter-
devel/20260321152506.037f68c0@elisabeth/`
- Signed-off-by: Florian Westphal `<fw@strlen.de>` (author, netfilter
maintainer)
- Reviewed-by: Stefano Brivio `<sbrivio@redhat.com>` (original author of
pipapo AVX2 implementation)
- Signed-off-by: Pablo Neira Ayuso `<pablo@netfilter.org>` (netfilter
subsystem maintainer)
- No Fixes: tag (expected for manual review)
- No Reported-by: tag
- No Cc: stable (expected for manual review)
Notable: Reviewed by the original pipapo AVX2 code author — strong
correctness signal for this specialized code.
**Step 1.3: Commit Body Analysis**
Record: [Bug: After flushing and reloading a pipapo set with multi-field
keys (e.g., `ipv4 . port`), the AVX2 code incorrectly reports element
clashes] [Symptom: `nft -f` fails with clash error on valid set reload]
[Root cause: Early `return b` in AVX2 helper functions when processing
the last field causes the loop to exit before all 256-bit map chunks are
processed, leaving stale bits from previous field matching. When an
expired/inactive element is skipped via `pipapo_refill()`, those stale
bits produce matches based only on the first key field, returning
incorrect elements] [Bisection points to 7711f4bb4b36 which merely
exposed the bug]
**Step 1.4: Hidden Bug Fix Detection**
Record: Not hidden — the commit message explicitly describes a concrete
bug with a clear reproducer and root-cause explanation.
===============================================================
## PHASE 2: DIFF ANALYSIS
===============================================================
**Step 2.1: Inventory**
Record: Single file changed: `net/netfilter/nft_set_pipapo_avx2.c`. 10
identical one-token changes across 10 functions:
`nft_pipapo_avx2_lookup_4b_{2,4,8,12,32}` and
`nft_pipapo_avx2_lookup_8b_{1,2,4,6,16}`. Each changes `return b;` to
`ret = b;`. Scope: single-file surgical fix.
**Step 2.2: Code Flow Change**
Record: For each of the 10 hunks: Before — when processing the last
field (`last == true`) and a match is found via
`nft_pipapo_avx2_refill()`, the helper immediately returns `b`, skipping
processing of remaining 256-bit chunks. After — the match index is
stored in `ret` and the loop continues via `continue`, processing all
remaining chunks (clearing/updating the map for each). The function
returns `ret` at the end of the loop (line unchanged). This ensures the
full bitmap is consistent before the caller uses it.
**Step 2.3: Bug Mechanism**
Record: [Category: Logic/state correctness bug in bitmap processing] The
AVX2 helpers process the result map in `m256_size` chunks. When the last
field is being processed and a match is found in chunk `i`, the early
`return b` skips chunks `i+1`, `i+2`, etc. Those chunks retain stale
bits from the previous field's matching. When `pipapo_get_avx2()` (line
1240-1244) finds the matched element is expired/inactive and calls
`pipapo_refill()` on the same `res` map, those stale bits are found and
return incorrect matches that match only on a subset of key fields. The
generic C path and `nft_pipapo_avx2_lookup_slow` don't have this issue
because they process the full map in one pass.
**Step 2.4: Fix Quality**
Record: Obviously correct — mechanical, identical change across all 10
helpers. The loop simply continues processing remaining chunks (which is
already the behavior for non-last fields). Reviewed by the original AVX2
implementation author (Stefano Brivio). No public API changes.
Negligible regression risk — marginally more SIMD iterations when `last`
is true, but this is the correct behavior.
===============================================================
## PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: Blame**
Record: `git blame` confirms all buggy `if (last) return b;` lines were
introduced by commit `7400b063969bd` (Stefano Brivio, 2020-03-07) —
"nft_set_pipapo: Introduce AVX2-based lookup implementation", first in
v5.7-rc1. The bug has existed since the original AVX2 pipapo code was
written.
**Step 3.2: Fixes Tag**
Record: No explicit Fixes: tag. The commit body references
`7711f4bb4b36` ("fix range overlap detection") as the commit that
exposed the bug (by changing duplicate comparison from
`sizeof(*dup_key->data)` to `set->klen`). Verified: `7711f4bb4b36` first
appeared in v6.19-rc5, and has been backported to v6.18 stable (as
`704c0258f0d79`).
**Step 3.3: File History and Related Changes**
Record: The critical prerequisite chain is:
1. `416e53e395167` (v6.18-rc1) — Split `nft_pipapo_avx2_lookup()` into
`pipapo_get_avx2()` helper + thin wrapper. Created the shared AVX2
lookup function.
2. `84c1da7b38d9a` (v6.18-rc1) — Made control-plane `pipapo_get()`
dispatch to `pipapo_get_avx2()` when AVX2 is available. Made
`pipapo_get_avx2()` non-static.
3. `5823699a11cf3` (v6.18-rc1) — Fixed expired-entry retry from full-
loop restart to `pipapo_refill()` on existing map. This is the commit
that makes stale bits in the map a real problem — because now
`pipapo_refill()` operates on the existing `res` map which has stale
bits left by the early-returning helpers.
**Step 3.4: Author Context**
Record: Florian Westphal is a netfilter maintainer. Stefano Brivio
(reviewer) is the original pipapo AVX2 author. Pablo Neira Ayuso (co-
signer) is the netfilter subsystem maintainer.
**Step 3.5: Dependencies**
Record: The fix itself is self-contained and applies cleanly. However,
the bug is only reachable in trees that contain all three prerequisites
(`416e53e395167`, `84c1da7b38d9a`, `5823699a11cf3`). Verified via `git
merge-base --is-ancestor`:
- v5.15: none present (exit code 1)
- v6.1: none present (exit code 1)
- v6.6: none present (exit code 1)
- v6.12: none present (exit code 1)
- v6.18: all present (exit code 0)
- v6.19: all present (exit code 0)
===============================================================
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: Patch Discussion**
Record: Direct lore.kernel.org fetch blocked by Anubis anti-bot
protection. The Link: tag points to the netfilter-devel discussion.
**Step 4.2: Bug Report**
Record: Bug was found via a new test case (described in the commit
message), not via an external bug report. The test exercises
flush+reload of pipapo sets with multi-field keys.
**Step 4.3: Related Patches**
Record: This is patch 1 of a 2-patch series. Patch 2 adds a regression
selftest (`selftests: netfilter: nft_concat_range.sh`). The code fix is
standalone.
**Step 4.4: Stable Discussion**
Record: Could not search lore stable archives due to access
restrictions. No evidence found of prior stable discussion.
===============================================================
## PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: Functions Modified**
Record: 10 AVX2 lookup helpers: `nft_pipapo_avx2_lookup_{4b_2, 4b_4,
4b_8, 4b_12, 4b_32, 8b_1, 8b_2, 8b_4, 8b_6, 8b_16}`.
**Step 5.2: Callers**
Record: All 10 helpers are called from `pipapo_get_avx2()` via the
`NFT_SET_PIPAPO_AVX2_LOOKUP` macro (lines 1186-1222).
`pipapo_get_avx2()` is called from:
1. **Control plane**: `pipapo_get()` in `nft_set_pipapo.c` (line 534),
which is called during element insertion/duplicate checking
(`nft_pipapo_insert()` at lines 1309, 1330), element retrieval
(`nft_pipapo_get()` at line 605), and deactivation (line 1905).
2. **Data path**: `nft_pipapo_avx2_lookup()` (line 1298), called during
packet classification.
Both paths are affected when an expired/inactive element is found and
`pipapo_refill()` is invoked on a stale bitmap.
**Step 5.3: Callees**
Record: Each helper calls `nft_pipapo_avx2_refill()` which for
`last==true` returns the first set bit index in the current chunk. The
caller (`pipapo_get_avx2()`) then checks expiry/genmask and may call
`pipapo_refill()` for retry (line 1242).
**Step 5.4: Call Chain**
Record: Control plane: `nf_tables_newsetelem()` → `nft_add_set_elem()` →
`nft_pipapo_insert()` → `pipapo_get()` → `pipapo_get_avx2()` → AVX2
helpers. Data path: packet → `nft_pipapo_avx2_lookup()` →
`pipapo_get_avx2()` → AVX2 helpers. Both are reachable — control plane
from nftables netlink, data path from every packet hitting a pipapo set.
**Step 5.5: Similar Patterns**
Record: `nft_pipapo_avx2_lookup_slow()` has the same `if (last) return
b;` at line 1078 but is NOT affected because its `pipapo_refill()` call
processes the entire map, not per-chunk. All 10 affected helpers have
the identical pattern.
===============================================================
## PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
===============================================================
**Step 6.1: Bug Existence in Stable Trees**
Record: Verified via `git merge-base --is-ancestor`:
- The early `return b` pattern exists since v5.7 in all stable trees
- BUT the bug is only triggerable after the retry-via-`pipapo_refill()`
mechanism from `5823699a11cf3` is present
- `5823699a11cf3`, `416e53e395167`, and `84c1da7b38d9a` are NOT in
v5.15, v6.1, v6.6, or v6.12 (exit code 1)
- All three ARE in v6.18 and v6.19 (exit code 0)
- Confirmed: v6.18.20 and v6.19.10 still contain the buggy `return b;`
in all 10 helpers
- Confirmed: v6.18.20 and v6.19.10 both contain the `pipapo_refill()`
retry at line 1242
- Confirmed: v6.18.20 has the `pipapo_get_avx2()` dispatch in
`pipapo_get()`
- Confirmed: v6.6 has zero references to `pipapo_get_avx2` in
`nft_set_pipapo.c`
**Step 6.2: Backport Complications**
Record: Clean apply expected for v6.18.y and v6.19.y. The buggy lines
are identical in both trees. Not relevant to v6.12 and older.
**Step 6.3: Related Fixes in Stable**
Record: `7711f4bb4b36` (range overlap detection fix) was backported to
v6.18 stable as `704c0258f0d79`. This backport makes the false-clash
symptom more visible on v6.18. No equivalent of the commit under review
is present in any stable tree.
===============================================================
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: Subsystem Criticality**
Record: [netfilter / nftables] [IMPORTANT — core packet
filtering/firewalling infrastructure used by virtually all Linux
systems]
**Step 7.2: Subsystem Activity**
Record: Actively maintained with frequent correctness fixes. The pipapo
AVX2 implementation has seen several related fixes recently
(5823699a11cf3, 7711f4bb4b36, etc.), indicating this area is under
active maintenance.
===============================================================
## PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: Who Is Affected**
Record: All x86_64 systems with AVX2 (essentially all modern x86 since
Haswell/2013) running kernel v6.18+ that use nftables with concatenated
range sets (pipapo). Affects both:
- **Control plane**: Element insertion/overlap checking fails with false
clashes
- **Data path**: Packet lookup can return wrong element/verdict when
expired entries exist in the set
**Step 8.2: Trigger Conditions**
Record:
- Control plane: Load a pipapo set, flush it, reload it → false clash
reported
- Data path: Any packet hitting a pipapo set where an expired element
exists and the map spans >1 YMM chunk
- Trigger is realistic for any system that periodically reloads firewall
rules or uses element timeouts
- Requires no special privileges beyond nftables administration for
control-plane path
**Step 8.3: Failure Mode Severity**
Record:
- Control plane: False element clashes → nftables set reload fails →
**firewall update/deployment breaks** → HIGH
- Data path: Wrong element matched → **wrong firewall verdict applied to
packet** → **CRITICAL** (security-relevant: packets could be
incorrectly allowed or denied)
- Combined severity: **HIGH**
**Step 8.4: Risk-Benefit Ratio**
Record:
- Benefit: **HIGH** — fixes both control-plane set operations and
potential data-path incorrect matching
- Risk: **VERY LOW** — 10 identical one-token changes, each replacing
`return b` with `ret = b`, letting the loop finish naturally. Reviewed
by original pipapo AVX2 author. No API/ABI changes.
- Ratio: Strongly favors backporting
===============================================================
## PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: Evidence Compilation**
FOR backporting:
- Real, verified correctness bug in netfilter set matching with concrete
reproducer
- Affects both control-plane (set reload fails) and data-path (wrong
packet matching — security-relevant)
- Extremely minimal fix: 10 identical one-token changes in a single file
- Reviewed by original pipapo AVX2 implementation author (Stefano
Brivio)
- Applied through proper netfilter maintainer chain (Florian Westphal →
Pablo Neira Ayuso)
- Bug has existed since v5.7 but only reachable after v6.18-rc1
refactoring
- Both v6.18.20 and v6.19.10 confirmed to still contain the buggy
pattern
- Netfilter is critical security/networking infrastructure
AGAINST backporting:
- Only relevant to v6.18.y and v6.19.y (not older stable trees)
- No explicit Fixes: or Cc: stable tags (expected for manual review)
UNRESOLVED:
- Could not fetch lore.kernel.org discussion due to anti-bot protection
- Could not verify whether reviewer suggested Cc: stable in discussion
**Step 9.2: Stable Rules Checklist**
1. Obviously correct and tested? **YES** — trivial change, reviewed by
original author, companion selftest exists
2. Fixes a real bug? **YES** — false element clashes on set reload,
potential wrong packet matching
3. Important issue? **YES** — breaks firewall management, potentially
wrong packet filtering (security)
4. Small and contained? **YES** — 10 lines changed in 1 file, all
identical
5. No new features or APIs? **YES** — pure bug fix
6. Can apply to stable? **YES** — clean apply to v6.18.y and v6.19.y
(not relevant to older trees)
**Step 9.3: Exception Categories**
Record: Not applicable — standard bug fix, not an exception category.
**Step 9.4: Decision**
This is a clear, surgical fix for a serious correctness bug in
netfilter's AVX2 pipapo matching. The bug causes incorrect element
matching that can break firewall rule reload operations (false clashes)
and potentially cause wrong data-path packet classification (security-
relevant). The fix is minimal (10 identical one-token changes), reviewed
by the original code author, and carries virtually no regression risk.
It should be backported to v6.18.y and v6.19.y stable trees, which are
the only active stable branches containing the prerequisite code that
makes this bug reachable.
## Verification
- [Phase 1] Parsed tags: Reviewed-by from Stefano Brivio (original
pipapo AVX2 author), Signed-off-by from Pablo Neira Ayuso (netfilter
maintainer), Link to lore discussion
- [Phase 2] Diff analysis: 10 identical changes `return b;` → `ret = b;`
across 10 AVX2 lookup functions in single file `nft_set_pipapo_avx2.c`
- [Phase 3] `git blame -L 240,260`: buggy `return b` introduced by
`7400b063969bd` (Stefano Brivio, v5.7-rc1)
- [Phase 3] `git describe --contains 7400b063969bd`: confirmed v5.7-rc1
- [Phase 3] `git log --oneline -30 --
net/netfilter/nft_set_pipapo_avx2.c`: verified complete file history
including all prerequisite commits
- [Phase 3] `git show 84c1da7b38d9a`: confirmed it creates
`pipapo_get()` dispatcher calling `pipapo_get_avx2()` from control
plane, and renames old `pipapo_get()` to `pipapo_get_slow()`
- [Phase 3] `git show 5823699a11cf3`: confirmed it changes expired-entry
retry from full-loop restart to `pipapo_refill()` on existing map
- [Phase 3] `git show 416e53e395167`: confirmed it splits
`nft_pipapo_avx2_lookup()` into `pipapo_get_avx2()` helper
- [Phase 3] `git describe --contains`: 84c1da7b38d9a → v6.18-rc1,
5823699a11cf3 → v6.18-rc1, 7711f4bb4b36 → v6.19-rc5
- [Phase 3] `git merge-base --is-ancestor 84c1da7b38d9a v6.6`: exit code
1 (NOT present)
- [Phase 3] `git merge-base --is-ancestor 84c1da7b38d9a v6.12`: exit
code 1 (NOT present)
- [Phase 3] `git merge-base --is-ancestor 84c1da7b38d9a v6.18`: exit
code 0 (present)
- [Phase 3] `git merge-base --is-ancestor 84c1da7b38d9a v6.19`: exit
code 0 (present)
- [Phase 3] `git merge-base --is-ancestor 416e53e395167 v6.18`: exit
code 0 (present)
- [Phase 3] `git merge-base --is-ancestor 5823699a11cf3 v6.18`: exit
code 0 (present)
- [Phase 3] Same two commits also verified present in v6.19
- [Phase 4] UNVERIFIED: lore.kernel.org blocked by Anubis anti-bot
protection
- [Phase 5] Grep `pipapo_get_avx2` in `net/netfilter/`: confirmed called
from `pipapo_get()` (control plane, line 534 of nft_set_pipapo.c) and
`nft_pipapo_avx2_lookup()` (datapath, line 1298 of avx2 file)
- [Phase 5] Read lines 1228-1261: confirmed retry path with
`pipapo_refill()` at line 1242, `goto next_match` at line 1244
- [Phase 5] Grep `pipapo_get` in `nft_set_pipapo.c`: confirmed call
sites at insert path (lines 1309, 1330), get path (line 605),
deactivation (line 1905)
- [Phase 6] `git show v6.19.10:net/netfilter/nft_set_pipapo_avx2.c |
grep 'return b;'`: 11 matches confirm buggy pattern still present
- [Phase 6] `git show v6.18.20:net/netfilter/nft_set_pipapo_avx2.c |
grep 'return b;'`: 11 matches confirm buggy pattern still present
- [Phase 6] `git show v6.18.20:net/netfilter/nft_set_pipapo_avx2.c |
grep pipapo_refill`: confirmed retry mechanism at line 1242
- [Phase 6] `git show v6.18.20:net/netfilter/nft_set_pipapo.c | grep
pipapo_get_avx2`: confirmed control-plane dispatch present
- [Phase 6] `git show v6.6:net/netfilter/nft_set_pipapo.c | grep -c
pipapo_get_avx2`: 0 matches — NOT present in v6.6
- [Phase 6] `git log v6.18..v6.18.20 --
net/netfilter/nft_set_pipapo*.c`: confirmed no equivalent fix already
backported
- [Phase 8] Failure mode: control-plane false clashes (HIGH), data-path
wrong matching (CRITICAL for security), combined severity HIGH
**YES**
net/netfilter/nft_set_pipapo_avx2.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c
index 7ff90325c97fa..6395982e4d95c 100644
--- a/net/netfilter/nft_set_pipapo_avx2.c
+++ b/net/netfilter/nft_set_pipapo_avx2.c
@@ -242,7 +242,7 @@ static int nft_pipapo_avx2_lookup_4b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -319,7 +319,7 @@ static int nft_pipapo_avx2_lookup_4b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -414,7 +414,7 @@ static int nft_pipapo_avx2_lookup_4b_8(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -505,7 +505,7 @@ static int nft_pipapo_avx2_lookup_4b_12(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -641,7 +641,7 @@ static int nft_pipapo_avx2_lookup_4b_32(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -699,7 +699,7 @@ static int nft_pipapo_avx2_lookup_8b_1(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -764,7 +764,7 @@ static int nft_pipapo_avx2_lookup_8b_2(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -839,7 +839,7 @@ static int nft_pipapo_avx2_lookup_8b_4(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -925,7 +925,7 @@ static int nft_pipapo_avx2_lookup_8b_6(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
@@ -1019,7 +1019,7 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
b = nft_pipapo_avx2_refill(i_ul, &map[i_ul], fill, f->mt, last);
if (last)
- return b;
+ ret = b;
if (unlikely(ret == -1))
ret = b / XSAVE_YMM_SIZE;
--
2.53.0
^ permalink raw reply related
* [PATCH AUTOSEL 6.19-6.18] netfilter: ctnetlink: ensure safe access to master conntrack
From: Sasha Levin @ 2026-03-30 12:38 UTC (permalink / raw)
To: patches, stable
Cc: Pablo Neira Ayuso, Florian Westphal, Sasha Levin, davem, edumazet,
kuba, pabeni, netfilter-devel, coreteam, netdev, linux-kernel
In-Reply-To: <20260330123842.756154-1-sashal@kernel.org>
From: Pablo Neira Ayuso <pablo@netfilter.org>
[ Upstream commit bffcaad9afdfe45d7fc777397d3b83c1e3ebffe5 ]
Holding reference on the expectation is not sufficient, the master
conntrack object can just go away, making exp->master invalid.
To access exp->master safely:
- Grab the nf_conntrack_expect_lock, this gets serialized with
clean_from_lists() which also holds this lock when the master
conntrack goes away.
- Hold reference on master conntrack via nf_conntrack_find_get().
Not so easy since the master tuple to look up for the master conntrack
is not available in the existing problematic paths.
This patch goes for extending the nf_conntrack_expect_lock section
to address this issue for simplicity, in the cases that are described
below this is just slightly extending the lock section.
The add expectation command already holds a reference to the master
conntrack from ctnetlink_create_expect().
However, the delete expectation command needs to grab the spinlock
before looking up for the expectation. Expand the existing spinlock
section to address this to cover the expectation lookup. Note that,
the nf_ct_expect_iterate_net() calls already grabs the spinlock while
iterating over the expectation table, which is correct.
The get expectation command needs to grab the spinlock to ensure master
conntrack does not go away. This also expands the existing spinlock
section to cover the expectation lookup too. I needed to move the
netlink skb allocation out of the spinlock to keep it GFP_KERNEL.
For the expectation events, the IPEXP_DESTROY event is already delivered
under the spinlock, just move the delivery of IPEXP_NEW under the
spinlock too because the master conntrack event cache is reached through
exp->master.
While at it, add lockdep notations to help identify what codepaths need
to grab the spinlock.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have all the verification I need. Let me compile the final
analysis.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
Record: [netfilter: ctnetlink] [ensure] [safe access to master conntrack
— "ensure" strongly implies a missing safety mechanism, i.e., a bug fix]
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- Signed-off-by: Florian Westphal <fw@strlen.de> — Author, core
netfilter developer
- Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> — Netfilter
subsystem maintainer
- No Fixes: tag (expected for manual review)
- No Cc: stable tag (expected for manual review)
- No Reported-by:, Tested-by:, Reviewed-by:, or Link: tags
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record: The commit describes a real lifetime bug: holding a reference on
the expectation is not sufficient because the master conntrack object
`exp->master` can be freed independently, leaving a dangling pointer.
The fix serializes access via `nf_conntrack_expect_lock`, which overlaps
with `clean_from_lists()` in the conntrack teardown path. Affected
paths: expectation get, delete, and IPEXP_NEW event delivery. The
failure mode is a use-after-free / stale pointer dereference through
`exp->master`.
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: This IS a bug fix. Despite using "ensure" rather than "fix", the
body explicitly describes a use-after-free class bug where `exp->master`
becomes invalid.
===============================================================
PHASE 2: DIFF ANALYSIS — LINE BY LINE
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
Record:
- `include/net/netfilter/nf_conntrack_core.h`: +5 lines (new
`lockdep_nfct_expect_lock_held()` inline)
- `net/netfilter/nf_conntrack_ecache.c`: +2 lines (lockdep annotation in
`nf_ct_expect_event_report`)
- `net/netfilter/nf_conntrack_expect.c`: +9/-1 lines (lockdep
annotations; IPEXP_NEW moved inside lock)
- `net/netfilter/nf_conntrack_netlink.c`: +22/-12 lines (spinlock
sections extended in get/delete)
Total: ~38 insertions, ~13 deletions across 4 files. Scope: small,
single-subsystem surgical locking fix.
Functions modified: `lockdep_nfct_expect_lock_held()` (new trivial
helper), `nf_ct_expect_event_report()`, `nf_ct_unlink_expect_report()`,
`nf_ct_remove_expect()`, `nf_ct_find_expectation()`,
`__nf_ct_expect_check()`, `nf_ct_expect_related_report()`,
`ctnetlink_get_expect()`, `ctnetlink_del_expect()`.
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
- `ctnetlink_get_expect()`: Before — finds expectation via
`nf_ct_expect_find_get()` under RCU, then fills reply skb
dereferencing `exp->master` without expect lock. After — allocates skb
first (GFP_KERNEL), takes `nf_conntrack_expect_lock`, does lookup +
fill under lock, releases lock after put.
- `ctnetlink_del_expect()`: Before — finds expectation and checks ID
before taking lock; only deletion protected. After — takes lock first,
find + ID check + deletion all under lock.
- `nf_ct_expect_related_report()`: Before — unlocks before calling
`nf_ct_expect_event_report(IPEXP_NEW, ...)`. After — delivers
IPEXP_NEW under lock, then unlocks.
- Lockdep annotations added to `nf_ct_expect_event_report`,
`nf_ct_unlink_expect_report`, `nf_ct_remove_expect`,
`nf_ct_find_expectation`, and `__nf_ct_expect_check` to document and
enforce locking requirements.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record: Category: **Race condition → Use-after-free on `exp->master`**.
The race:
1. Thread A (ctnetlink GET/DELETE): calls `nf_ct_expect_find_get()`
which bumps `exp->use` but does NOT pin the master conntrack
2. Thread B (conntrack destruction): `clean_from_lists()` →
`nf_ct_remove_expectations()` → takes `nf_conntrack_expect_lock`,
removes expectations
3. Thread B continues: master conntrack `nf_conn` object freed via RCU
4. Thread A: dereferences `exp->master` → **USE-AFTER-FREE**
Verified: `clean_from_lists()` at line 511 of `nf_conntrack_core.c`
calls `nf_ct_remove_expectations(ct)`, which takes
`nf_conntrack_expect_lock` at line 238 of `nf_conntrack_expect.c`. After
expectations are removed, the master ct is freed.
The specific `exp->master` dereferences in `ctnetlink_exp_dump_expect()`
(lines 3012-3067 of `nf_conntrack_netlink.c`) include:
- `master->tuplehash[IP_CT_DIR_ORIGINAL].tuple` (line 3029)
- `nf_ct_l3num(master)` and `nf_ct_protonum(master)` (lines 3043, 3045)
- `nfct_help(master)` (line 3059)
In `nf_ct_expect_event_report()`, `nf_ct_ecache_find(exp->master)`
dereferences the master conntrack.
**Step 2.4: ASSESS THE FIX QUALITY**
Record: The fix is obviously correct — it extends the existing
`nf_conntrack_expect_lock` to cover accesses that were previously
unprotected. The `GFP_KERNEL` skb allocation is moved before the lock to
avoid sleeping under spinlock. IPEXP_DESTROY was already delivered under
the lock; now IPEXP_NEW is too. Very low regression risk — slightly
longer lock hold on admin netlink paths.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
Record: `git blame` confirms the core of `ctnetlink_get_expect()` was
introduced by `c1d10adb4a521d` (Pablo Neira Ayuso, 2006-01-05) —
original ctnetlink support. The unlocked expectation lookup pattern has
existed since kernel 2.6.x and is present in ALL active stable trees.
**Step 3.2: FOLLOW THE FIXES TAG**
Record: N/A — no Fixes: tag present.
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record: Recent git log for `nf_conntrack_netlink.c` shows multiple
related UAF/refcount fixes in the same code:
- `cd541f15b60e2` — "fix use-after-free in ctnetlink_dump_exp_ct()" —
KASAN slab-use-after-free confirmed in expectation dumping
- `1492e3dcb2be3` — "remove refcounting in expectation dumpers"
- `de788b2e62274` — "fix refcount leak on table dump"
The `cd541f15b60e2` commit includes an actual KASAN stack trace proving
UAF bugs in this exact code area are real and exploitable.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Florian Westphal is a core netfilter developer (verified via
MAINTAINERS and git log). Pablo Neira Ayuso is THE netfilter subsystem
maintainer. Both SOBs on this commit.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: The diff is self-contained. The new
`lockdep_nfct_expect_lock_held()` is trivial (wraps
`lockdep_assert_held`). All referenced functions and data structures
exist in current stable trees. No external dependencies detected.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: SEARCH LORE.KERNEL.ORG**
Record: Direct lore.kernel.org access blocked by Anubis proof-of-work.
Alternate sources: spinics.net netdev archive and patchwork.ozlabs.org
confirmed v1→v2→v3 iterations. The v3 cover letter states: "patches 5 to
10 address long-standing RCU safety bugs in conntrack's handling of
expectations."
**Step 4.2: SEARCH FOR BUG REPORT**
Record: No standalone syzbot report or specific bug report for this
exact race, but the related commit `cd541f15b60e2` has a confirmed KASAN
slab-use-after-free trace proving this class of bugs is real and
triggerable.
**Step 4.3: CHECK FOR RELATED PATCHES**
Record: Part of a multi-patch series addressing expectation safety. This
specific patch is standalone — it only extends locking and adds lockdep
annotations, independent of companion patches.
**Step 4.4: CHECK STABLE MAILING LIST**
Record: Could not verify stable-specific discussion due to lore access
issues.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: KEY FUNCTIONS**
Record: `ctnetlink_get_expect`, `ctnetlink_del_expect`,
`nf_ct_expect_related_report`, `nf_ct_expect_event_report`,
`ctnetlink_exp_dump_expect`
**Step 5.2: TRACE CALLERS**
Record: Verified `ctnetlink_get_expect` and `ctnetlink_del_expect` are
wired into the `ctnl_exp_cb` netlink callback table (lines 3830-3848 of
`nf_conntrack_netlink.c`) for `IPCTNL_MSG_EXP_GET` and
`IPCTNL_MSG_EXP_DELETE`. These are directly reachable from userspace via
`AF_NETLINK`/`NETLINK_NETFILTER` (requires `CAP_NET_ADMIN`).
`nf_ct_expect_related()` (wrapper around
`nf_ct_expect_related_report()`) is called from 16+ files including SIP,
H323, FTP, IRC, TFTP, AMANDA, broadcast, nft_ct, PPTP, SANE, NAT
helpers, and IPVS. This is NOT a niche path.
**Step 5.3: TRACE CALLEES**
Record: `ctnetlink_exp_dump_expect()` (lines 3008-3077) dereferences
`exp->master` extensively: `master->tuplehash`, `nf_ct_l3num(master)`,
`nf_ct_protonum(master)`, `nfct_help(master)`.
`nf_ct_expect_event_report()` calls `nf_ct_ecache_find(exp->master)`.
**Step 5.4: FOLLOW CALL CHAIN**
Record: Userspace → nfnetlink_rcv → nfnetlink_rcv_msg →
ctnetlink_get/del_expect → nf_ct_expect_find_get → exp->master
dereference. Directly reachable from userspace with CAP_NET_ADMIN.
**Step 5.5: SIMILAR PATTERNS**
Record: `expect_iter_name()` in v6.6 also dereferences `exp->master` via
`nfct_help(exp->master)` — same class of vulnerability in the same file.
===============================================================
PHASE 6: STABLE TREE ANALYSIS
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN STABLE TREES?**
Record: **YES** — verified directly. In v6.6:
- `ctnetlink_get_expect()` does `nf_ct_expect_find_get()` at line 3333
WITHOUT `nf_conntrack_expect_lock`, then calls
`ctnetlink_exp_fill_info()` which dereferences `exp->master`
- `ctnetlink_del_expect()` does `nf_ct_expect_find_get()` at line 3408
before taking `nf_conntrack_expect_lock` at line 3421
- `nf_ct_expect_related_report()` unlocks at line 501, THEN calls
`nf_ct_expect_event_report(IPEXP_NEW, ...)` at line 502
The buggy code originated in 2006 (`c1d10adb4a521d`) and is present in
ALL active stable trees (5.4.y, 5.10.y, 5.15.y, 6.1.y, 6.6.y, 6.12.y).
**Step 6.2: BACKPORT COMPLICATIONS**
Record: Minor API differences in older trees: `timer_delete` vs
`del_timer` (treewide rename in `8fa7292fee5c5`), and the `struct
nfnl_info` ctnetlink interface. Core logic change (extending spinlock
sections) applies conceptually. Expected: clean or near-clean apply on
6.6+; minor manual adaptation needed for 5.x trees.
**Step 6.3: RELATED FIXES IN STABLE**
Record: `cd541f15b60e2` (KASAN UAF fix in ctnetlink_dump_exp_ct) is
already in stable. No alternative fix for the specific
get/delete/IPEXP_NEW races addressed by this patch.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: SUBSYSTEM AND CRITICALITY**
Record: [netfilter / conntrack / ctnetlink] [IMPORTANT — widely deployed
in firewalls, NAT gateways, containers, Kubernetes, orchestration
tooling, conntrack-tools monitoring]
**Step 7.2: SUBSYSTEM ACTIVITY**
Record: Active subsystem with frequent fixes. The longstanding nature of
the bug (2006) means it affects more stable trees.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: Systems using conntrack expectations (FTP/SIP/H323/TFTP/IRC/etc.
helpers) AND userspace tools querying/managing expectations via
ctnetlink (conntrack-tools, orchestration). Also affected: any path
creating expectations that triggers IPEXP_NEW events.
**Step 8.2: DETERMINE TRIGGER CONDITIONS**
Record: Concurrent expectation access (GET/DELETE via netlink, or
IPEXP_NEW event delivery) with master conntrack destruction (normal
connection timeout/teardown). Timing-dependent race but realistic under
load — high conntrack churn with active monitoring is a real-world
scenario.
**Step 8.3: FAILURE MODE SEVERITY**
Record: UAF on `exp->master` → kernel crash/oops (accessing freed slab
memory), memory corruption, or potential security vulnerability.
Severity: **HIGH**. This is corroborated by the KASAN slab-use-after-
free trace in the related commit `cd541f15b60e2`.
**Step 8.4: RISK-BENEFIT RATIO**
Record:
- **Benefit: HIGH** — closes verified unsafe dereference windows in
userspace-facing netlink operations and expectation event delivery
- **Risk: VERY LOW** — extends existing lock scope slightly, no new
APIs, no logic changes, GFP_KERNEL allocation correctly moved out of
lock
- **Ratio: Strongly favorable**
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
Evidence FOR backporting:
- Fixes a real use-after-free race condition on `exp->master` in
ctnetlink
- Bug has existed since 2006 (commit `c1d10adb4a521d`), present in ALL
active stable trees
- Verified directly: v6.6 has the same vulnerable pattern (unlocked
expectation lookup + exp->master dereference)
- Written by netfilter subsystem maintainer (Pablo Neira Ayuso) and core
developer (Florian Westphal)
- Small, surgical fix (~38 insertions, ~13 deletions) across 4 files in
one subsystem
- Reachable from userspace via AF_NETLINK/NETLINK_NETFILTER
(CAP_NET_ADMIN)
- Related KASAN UAF confirmed in same code area (`cd541f15b60e2`)
- IPEXP_NEW event path affects 16+ helper/NAT modules — not a niche path
- Part of a series explicitly described as fixing "long-standing RCU
safety bugs" in expectations
- Low regression risk — only extends existing lock scope
Evidence AGAINST backporting:
- No specific crash report or syzbot report for THIS exact race window
- Part of a multi-patch series (but this patch is standalone)
- May need minor manual adaptation for older stable trees (API renames)
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **YES** — extends lock to cover
unprotected accesses; pattern matches existing serialization
2. Fixes a real bug? **YES** — UAF on `exp->master`, verified via code
analysis and corroborated by related KASAN reports
3. Important issue? **YES** — crash, memory corruption, potential
security (UAF from userspace-triggerable netlink path)
4. Small and contained? **YES** — ~50 lines net across 4 files in one
subsystem
5. No new features or APIs? **YES** — only lockdep annotation helper
added (debug instrumentation)
6. Can apply to stable? **YES** — may need minor adaptation for API
renames in older trees
**Step 9.3: EXCEPTION CATEGORIES**
Record: N/A — this is a standard bug fix, not an exception category.
**Step 9.4: DECISION**
This commit fixes a real, longstanding use-after-free race condition in
netfilter conntrack expectation handling that can lead to kernel crashes
or memory corruption. The fix is small, surgical, obviously correct,
authored by the subsystem maintainer and core developer, and applies to
all active stable trees. The risk-benefit ratio is strongly favorable.
## Verification
- [Phase 1] Parsed commit message: only SOBs from Florian Westphal and
Pablo Neira Ayuso; no Fixes/Reported-by/Cc:stable tags
- [Phase 2] Read `ctnetlink_exp_dump_expect()` at lines 3008-3077 of
`nf_conntrack_netlink.c`: confirmed multiple `exp->master`
dereferences (`master->tuplehash`, `nf_ct_l3num(master)`,
`nfct_help(master)`)
- [Phase 2] Read `clean_from_lists()` at line 511 of
`nf_conntrack_core.c`: confirmed it calls
`nf_ct_remove_expectations(ct)` before master ct is freed
- [Phase 2] Read `nf_ct_remove_expectations()` at line 228 of
`nf_conntrack_expect.c`: confirmed it takes `nf_conntrack_expect_lock`
at line 238 — this is the serialization point
- [Phase 2] Read `nf_ct_expect_related_report()` at line 500-507:
confirmed `spin_unlock_bh` at line 501 THEN
`nf_ct_expect_event_report(IPEXP_NEW)` at line 502 — the unlocked
window
- [Phase 3] `git blame` on `ctnetlink_get_expect`: lines 3342, 3354-3362
trace to `c1d10adb4a521d` (Pablo Neira Ayuso, 2006-01-05)
- [Phase 3] `git log --oneline -20 --
net/netfilter/nf_conntrack_netlink.c`: found related UAF fixes
`cd541f15b60e2`, `1492e3dcb2be3`, `de788b2e62274`
- [Phase 3] `git show cd541f15b60e2`: confirmed KASAN slab-use-after-
free stack trace in same ctnetlink expectation code
- [Phase 3] `git log --author` for both authors: verified as core
netfilter contributors
- [Phase 4] Lore.kernel.org blocked by Anubis; used spinics.net and
patchwork.ozlabs.org mirrors to verify v1→v2→v3 iterations and cover
letter content
- [Phase 5] Read `ctnl_exp_cb` callback table at lines 3830-3848:
confirmed `ctnetlink_get_expect` and `ctnetlink_del_expect` are
userspace-reachable via NETLINK_NETFILTER
- [Phase 5] `rg nf_ct_expect_related` across net/netfilter/: 16 files
use this function (SIP, FTP, H323, TFTP, IRC, AMANDA, broadcast,
nft_ct, PPTP, SANE, NAT helpers, IPVS)
- [Phase 6] `git show v6.6:net/netfilter/nf_conntrack_netlink.c`:
confirmed unlocked `nf_ct_expect_find_get()` at line 3333 and
`ctnetlink_exp_fill_info()` dereferences `exp->master` without expect
lock
- [Phase 6] `git show v6.6:net/netfilter/nf_conntrack_expect.c`:
confirmed `spin_unlock_bh` at line 501, then
`nf_ct_expect_event_report(IPEXP_NEW)` at line 502 — same vulnerable
pattern as mainline
- [Phase 8] Failure mode: UAF on `exp->master` → kernel
crash/corruption; severity HIGH (corroborated by KASAN trace in
related commit)
- UNVERIFIED: Exact privilege requirements for ctnetlink expectation
operations (assumed CAP_NET_ADMIN based on netfilter conventions)
- UNVERIFIED: Whether patch applies cleanly to stable trees older than
6.6 (API renames expected)
- UNVERIFIED: Full lore.kernel.org review thread (blocked by Anubis
proof-of-work)
- UNVERIFIED: Stable trees older than v5.15 individually checked
**YES**
include/net/netfilter/nf_conntrack_core.h | 5 ++++
net/netfilter/nf_conntrack_ecache.c | 2 ++
net/netfilter/nf_conntrack_expect.c | 10 +++++++-
net/netfilter/nf_conntrack_netlink.c | 28 +++++++++++++++--------
4 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index 3384859a89210..8883575adcc1e 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -83,6 +83,11 @@ void nf_conntrack_lock(spinlock_t *lock);
extern spinlock_t nf_conntrack_expect_lock;
+static inline void lockdep_nfct_expect_lock_held(void)
+{
+ lockdep_assert_held(&nf_conntrack_expect_lock);
+}
+
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
static inline void __nf_ct_set_timeout(struct nf_conn *ct, u64 timeout)
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index 81baf20826046..9df159448b897 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -247,6 +247,8 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
+ lockdep_nfct_expect_lock_held();
+
rcu_read_lock();
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
if (!notify)
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index cfc2daa3fc7f3..f9e65f03dc5ea 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -51,6 +51,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
struct net *net = nf_ct_exp_net(exp);
struct nf_conntrack_net *cnet;
+ lockdep_nfct_expect_lock_held();
WARN_ON(!master_help);
WARN_ON(timer_pending(&exp->timeout));
@@ -118,6 +119,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
{
+ lockdep_nfct_expect_lock_held();
+
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
@@ -177,6 +180,8 @@ nf_ct_find_expectation(struct net *net,
struct nf_conntrack_expect *i, *exp = NULL;
unsigned int h;
+ lockdep_nfct_expect_lock_held();
+
if (!cnet->expect_count)
return NULL;
@@ -442,6 +447,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
unsigned int h;
int ret = 0;
+ lockdep_nfct_expect_lock_held();
+
if (!master_help) {
ret = -ESHUTDOWN;
goto out;
@@ -498,8 +505,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
nf_ct_expect_insert(expect);
- spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+
return 0;
out:
spin_unlock_bh(&nf_conntrack_expect_lock);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index d9f33a6c807c8..f5cb09eb31a34 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3357,31 +3357,37 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
if (err < 0)
return err;
+ skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb2)
+ return -ENOMEM;
+
+ spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ kfree_skb(skb2);
return -ENOENT;
+ }
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+ kfree_skb(skb2);
return -ENOENT;
}
}
- skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!skb2) {
- nf_ct_expect_put(exp);
- return -ENOMEM;
- }
-
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
+
if (err <= 0) {
kfree_skb(skb2);
return -ENOMEM;
@@ -3431,22 +3437,26 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
if (err < 0)
return err;
+ spin_lock_bh(&nf_conntrack_expect_lock);
+
/* bump usage count to 2 */
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
- if (!exp)
+ if (!exp) {
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
+ }
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
+ spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
}
}
/* after list removal, usage count == 1 */
- spin_lock_bh(&nf_conntrack_expect_lock);
if (timer_delete(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
nlmsg_report(info->nlh));
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next v4 13/15] net/mlx5: Add a shared devlink instance for PFs on same chip
From: Mark Brown @ 2026-03-30 12:40 UTC (permalink / raw)
To: Ben Copeland; +Cc: Jiri Pirko, jiri, netdev, kuba
In-Reply-To: <CAL0q8a6OXJ8qQYYYDCfAdujrsJ-QPt75kB=YQ3rwdZq5L=eO5Q@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 748 bytes --]
On Tue, Mar 24, 2026 at 03:37:20PM +0000, Ben Copeland wrote:
> On Tue, 24 Mar 2026 at 15:21, Jiri Pirko <jiri@resnulli.us> wrote:
> > Tue, Mar 24, 2026 at 04:10:14PM +0100, ben.copeland@linaro.org wrote:
> > >@@ -33,7 +33,7 @@ int mlx5_shd_init(struct mlx5_core_dev *dev)
> > > start = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
> > > PCI_VPD_RO_KEYWORD_SERIALNO, &kw_len);
> > > if (start < 0)
> > >- return -ENOENT;
> > >+ return 0;
> > Okay. That is probably the best fall-back. Do you want to send the fix,
> > or should I?
> I will leave it with you.
Any news on this, the problem still seems to be showing up?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH net-next] FDDI: defxx: Rate-limit memory allocation errors
From: Andrew Lunn @ 2026-03-30 12:46 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel
In-Reply-To: <alpine.DEB.2.21.2603291236590.60268@angie.orcam.me.uk>
On Sun, Mar 29, 2026 at 01:32:25PM +0100, Maciej W. Rozycki wrote:
> Prevent the system from becoming unstable or unusable due to a flood of
> memory allocation error messages under memory pressure, e.g.:
>
> [...]
> fddi0: Could not allocate receive buffer. Dropping packet.
> fddi0: Could not allocate receive buffer. Dropping packet.
> fddi0: Could not allocate receive buffer. Dropping packet.
> fddi0: Could not allocate receive buffer. Dropping packet.
> rcu: INFO: rcu_sched self-detected stall on CPU
> rcu: 0-...!: (332 ticks this GP) idle=255c/1/0x40000000 softirq=16420123/16420123 fqs=0
> rcu: (t=2103 jiffies g=35680089 q=4 ncpus=1)
> rcu: rcu_sched kthread timer wakeup didn't happen for 2102 jiffies! g35680089 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402
> rcu: Possible timer handling issue on cpu=0 timer-softirq=12779658
> rcu: rcu_sched kthread starved for 2103 jiffies! g35680089 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402 ->cpu=0
> rcu: Unless rcu_sched kthread gets sufficient CPU time, OOM is now expected behavior.
> rcu: RCU grace-period kthread stack dump:
> task:rcu_sched state:I stack:0 pid:14 tgid:14 ppid:2 flags:0x00004000
> Call Trace:
> __schedule+0x258/0x580
> schedule+0x19/0xa0
> schedule_timeout+0x4a/0xb0
> ? hrtimers_cpu_dying+0x1b0/0x1b0
> rcu_gp_fqs_loop+0xb1/0x450
> rcu_gp_kthread+0x9d/0x130
> kthread+0xb2/0xe0
> ? rcu_gp_init+0x4a0/0x4a0
> ? kthread_park+0x90/0x90
> ret_from_fork+0x2d/0x50
> ? kthread_park+0x90/0x90
> ret_from_fork_asm+0x12/0x20
> entry_INT80_32+0x10d/0x10d
> CPU: 0 UID: 500 PID: 21895 Comm: 31370.exe Not tainted 6.13.0-dirty #2
>
> (here running the libstdc++-v3 testsuite).
>
> Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH net-next] FDDI: defza: Rate-limit memory allocation errors
From: Andrew Lunn @ 2026-03-30 12:47 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel
In-Reply-To: <alpine.DEB.2.21.2603291252380.60268@angie.orcam.me.uk>
On Sun, Mar 29, 2026 at 01:32:34PM +0100, Maciej W. Rozycki wrote:
> Prevent the system from becoming unstable or unusable due to a flood of
> memory allocation error messages under memory pressure.
>
> Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH net-next 1/2] declance: Rate-limit DMA errors
From: Andrew Lunn @ 2026-03-30 12:48 UTC (permalink / raw)
To: Maciej W. Rozycki
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel
In-Reply-To: <alpine.DEB.2.21.2603291303190.60268@angie.orcam.me.uk>
On Sun, Mar 29, 2026 at 01:32:51PM +0100, Maciej W. Rozycki wrote:
> Prevent the system from becoming unusable due to a flood of DMA error
> messages.
>
> Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH rdma v3] RDMA/mana_ib: Disable RX steering on RSS QP destroy
From: Leon Romanovsky @ 2026-03-30 13:00 UTC (permalink / raw)
To: Konstantin Taranov, Jakub Kicinski, David S . Miller, Paolo Abeni,
Eric Dumazet, Andrew Lunn, Jason Gunthorpe, Haiyang Zhang,
K . Y . Srinivasan, Wei Liu, Dexuan Cui, Long Li
Cc: Simon Horman, netdev, linux-rdma, linux-hyperv, linux-kernel,
stable
In-Reply-To: <20260325194100.1929056-1-longli@microsoft.com>
On Wed, 25 Mar 2026 12:40:57 -0700, Long Li wrote:
> When an RSS QP is destroyed (e.g. DPDK exit), mana_ib_destroy_qp_rss()
> destroys the RX WQ objects but does not disable vPort RX steering in
> firmware. This leaves stale steering configuration that still points to
> the destroyed RX objects.
>
> If traffic continues to arrive (e.g. peer VM is still transmitting) and
> the VF interface is subsequently brought up (mana_open), the firmware
> may deliver completions using stale CQ IDs from the old RX objects.
> These CQ IDs can be reused by the ethernet driver for new TX CQs,
> causing RX completions to land on TX CQs:
>
> [...]
Applied, thanks!
[1/1] RDMA/mana_ib: Disable RX steering on RSS QP destroy
https://git.kernel.org/rdma/rdma/c/187c8bd5e571f5
Best regards,
--
Leon Romanovsky <leon@kernel.org>
^ permalink raw reply
* Re: [PATCH 1/2] net: phy: microchip: add downshift tunable support for LAN88xx
From: Andrew Lunn @ 2026-03-30 13:01 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: netdev, Phil Elwell, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel
In-Reply-To: <20260329224202.500229-2-nb@tipi-net.de>
> + switch (val & LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK) {
> + case LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2:
> + *data = 2;
> + break;
> + case LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3:
> + *data = 3;
> + break;
> + case LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4:
> + *data = 4;
> + break;
> + case LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5:
> + *data = 5;
> + break;
> + }
> +#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2 (0 << 2)
> +#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3 (1 << 2)
> +#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4 (2 << 2)
> +#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5 (3 << 2)
It looks like you can replace the switch statement with a FIELD_GET() + 2.
Andrew
---
pw-bot: cr
^ permalink raw reply
* [PATCH net v6 0/4] macsec: Add support for VLAN filtering in offload mode
From: Cosmin Ratiu @ 2026-03-30 13:01 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Cosmin Ratiu, Dragos Tatulea
This short series adds support for VLANs in MACsec devices when offload
mode is enabled. This allows VLAN netdevs on top of MACsec netdevs to
function, which accidentally used to be the case in the past, but was
broken. This series adds back proper support.
As part of this, the existing nsim-only MACsec offload tests were
translated to Python so they can run against real HW and new
traffic-based tests were added for VLAN filter propagation, since
there's currently no uAPI to check VLAN filters.
---
V6:
- Resurrected nsim debugfs file for VLAN filters.
- Switched to WARN_ON_ONCE in nsim, which is always compiled in.
- Tweaked tests to check the nsim VLAN filter file on nsim.
- Disabled ping on nsim in offload mode, data plane doesn't work.
- Added CONFIG_VLAN_8021Q to the test config.
V5: https://lore.kernel.org/netdev/20260323123633.756163-1-cratiu@nvidia.com/T/#u
- Merged tests and macsec lib in a single file.
- Fixed Python linter issues.
- Added CONFIG_MACSEC to tools/testing/selftests/drivers/net/config
V4:
- Migrated nsim-only macsec tests to Python, usable against real hw.
- Ran these tests against both nsim and mlx5.
- Gave up on nsim patches since the tests no longer use them.
V3: https://lore.kernel.org/netdev/20260306151004.2862198-1-cratiu@nvidia.com/t/#u
- Moved back to net.
- Added proper rollback support for VLAN filters in case of failure.
- Added VLAN as a requirement for the new macsec tests.
V2: https://lore.kernel.org/netdev/20260227090227.1552512-1-cratiu@nvidia.com/
- Sent to net-next instead of net because of apparent complexity.
- Changed VLAN filtering to only function in offload mode.
- Added tests.
V1: https://lore.kernel.org/netdev/20260107104723.2750725-1-cratiu@nvidia.com/
Cosmin Ratiu (4):
selftests: Migrate nsim-only MACsec tests to Python
nsim: Add support for VLAN filters
selftests: Add MACsec VLAN propagation traffic test
macsec: Support VLAN-filtering lower devices
drivers/net/macsec.c | 44 ++-
drivers/net/netdevsim/netdev.c | 65 +++-
drivers/net/netdevsim/netdevsim.h | 8 +
tools/testing/selftests/drivers/net/Makefile | 1 +
tools/testing/selftests/drivers/net/config | 2 +
.../selftests/drivers/net/lib/py/env.py | 9 +
tools/testing/selftests/drivers/net/macsec.py | 330 ++++++++++++++++++
.../selftests/drivers/net/netdevsim/Makefile | 1 -
.../drivers/net/netdevsim/macsec-offload.sh | 117 -------
9 files changed, 452 insertions(+), 125 deletions(-)
create mode 100755 tools/testing/selftests/drivers/net/macsec.py
delete mode 100755 tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
--
2.53.0
^ permalink raw reply
* [PATCH net v6 2/4] nsim: Add support for VLAN filters
From: Cosmin Ratiu @ 2026-03-30 13:01 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Cosmin Ratiu, Dragos Tatulea
In-Reply-To: <20260330130130.989236-1-cratiu@nvidia.com>
Add support for storing the list of VLANs in nsim devices, together with
ops for adding/removing them and a debug file to show them.
This will be used in upcoming tests.
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
drivers/net/netdevsim/netdev.c | 65 ++++++++++++++++++++++++++++++-
drivers/net/netdevsim/netdevsim.h | 8 ++++
2 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index 3645ebde049a..19b0ff183c45 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -605,6 +605,36 @@ static int nsim_stop(struct net_device *dev)
return 0;
}
+static int nsim_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(test_and_set_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
+static int nsim_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (vid >= VLAN_N_VID)
+ return -EINVAL;
+
+ if (proto == htons(ETH_P_8021Q))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.ctag));
+ else if (proto == htons(ETH_P_8021AD))
+ WARN_ON_ONCE(!test_and_clear_bit(vid, ns->vlan.stag));
+
+ return 0;
+}
+
static int nsim_shaper_set(struct net_shaper_binding *binding,
const struct net_shaper *shaper,
struct netlink_ext_ack *extack)
@@ -662,6 +692,8 @@ static const struct net_device_ops nsim_netdev_ops = {
.ndo_bpf = nsim_bpf,
.ndo_open = nsim_open,
.ndo_stop = nsim_stop,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
.net_shaper_ops = &nsim_shaper_ops,
};
@@ -673,6 +705,8 @@ static const struct net_device_ops nsim_vf_netdev_ops = {
.ndo_change_mtu = nsim_change_mtu,
.ndo_setup_tc = nsim_setup_tc,
.ndo_set_features = nsim_set_features,
+ .ndo_vlan_rx_add_vid = nsim_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = nsim_vlan_rx_kill_vid,
};
/* We don't have true per-queue stats, yet, so do some random fakery here.
@@ -970,6 +1004,20 @@ static const struct file_operations nsim_pp_hold_fops = {
.owner = THIS_MODULE,
};
+static int nsim_vlan_show(struct seq_file *s, void *data)
+{
+ struct netdevsim *ns = s->private;
+ int vid;
+
+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ seq_printf(s, "ctag %d\n", vid);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ seq_printf(s, "stag %d\n", vid);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(nsim_vlan);
+
static void nsim_setup(struct net_device *dev)
{
ether_setup(dev);
@@ -982,14 +1030,18 @@ static void nsim_setup(struct net_device *dev)
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
- NETIF_F_TSO;
+ NETIF_F_TSO |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->hw_features |= NETIF_F_HW_TC |
NETIF_F_SG |
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
NETIF_F_TSO |
- NETIF_F_LOOPBACK;
+ NETIF_F_LOOPBACK |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_VLAN_STAG_FILTER;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
dev->max_mtu = ETH_MAX_MTU;
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
@@ -1156,6 +1208,8 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev,
ns->qr_dfs = debugfs_create_file("queue_reset", 0200,
nsim_dev_port->ddir, ns,
&nsim_qreset_fops);
+ ns->vlan_dfs = debugfs_create_file("vlan", 0400, nsim_dev_port->ddir,
+ ns, &nsim_vlan_fops);
return ns;
err_free_netdev:
@@ -1167,7 +1221,9 @@ void nsim_destroy(struct netdevsim *ns)
{
struct net_device *dev = ns->netdev;
struct netdevsim *peer;
+ u16 vid;
+ debugfs_remove(ns->vlan_dfs);
debugfs_remove(ns->qr_dfs);
debugfs_remove(ns->pp_dfs);
@@ -1193,6 +1249,11 @@ void nsim_destroy(struct netdevsim *ns)
if (nsim_dev_port_is_pf(ns->nsim_dev_port))
nsim_exit_netdevsim(ns);
+ for_each_set_bit(vid, ns->vlan.ctag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+ for_each_set_bit(vid, ns->vlan.stag, VLAN_N_VID)
+ WARN_ON_ONCE(1);
+
/* Put this intentionally late to exercise the orphaning path */
if (ns->page) {
page_pool_put_full_page(pp_page_to_nmdesc(ns->page)->pp,
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index f767fc8a7505..f844c27ca78b 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -18,6 +18,7 @@
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
+#include <linux/if_vlan.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/ptp_mock.h>
@@ -75,6 +76,11 @@ struct nsim_macsec {
u8 nsim_secy_count;
};
+struct nsim_vlan {
+ DECLARE_BITMAP(ctag, VLAN_N_VID);
+ DECLARE_BITMAP(stag, VLAN_N_VID);
+};
+
struct nsim_ethtool_pauseparam {
bool rx;
bool tx;
@@ -135,6 +141,7 @@ struct netdevsim {
bool bpf_map_accept;
struct nsim_ipsec ipsec;
struct nsim_macsec macsec;
+ struct nsim_vlan vlan;
struct {
u32 inject_error;
u32 __ports[2][NSIM_UDP_TUNNEL_N_PORTS];
@@ -146,6 +153,7 @@ struct netdevsim {
struct page *page;
struct dentry *pp_dfs;
struct dentry *qr_dfs;
+ struct dentry *vlan_dfs;
struct nsim_ethtool ethtool;
struct netdevsim __rcu *peer;
--
2.53.0
^ permalink raw reply related
* [PATCH net v6 3/4] selftests: Add MACsec VLAN propagation traffic test
From: Cosmin Ratiu @ 2026-03-30 13:01 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Cosmin Ratiu, Dragos Tatulea
In-Reply-To: <20260330130130.989236-1-cratiu@nvidia.com>
Add macsec_traffic.py using NetDrvEpEnv to verify VLAN filter
propagation through offloaded MACsec devices via actual traffic.
The test creates MACsec tunnels with matching SAs on both endpoints,
stacks VLANs on top, and verifies connectivity with ping. Covers:
- Offloaded MACsec with VLAN (filters propagate to HW)
- Software MACsec with VLAN (no HW filter propagation)
- Toggle offload on/off and verify traffic still works
On netdevsim this makes use of the VLAN filter debugfs file to actually
validate that filters are applied/removed correctly.
On real hardware the traffic should validate actual VLAN filter
propagation.
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
tools/testing/selftests/drivers/net/config | 1 +
.../selftests/drivers/net/lib/py/env.py | 9 ++
tools/testing/selftests/drivers/net/macsec.py | 151 ++++++++++++++++++
3 files changed, 161 insertions(+)
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index d4b31a317c09..fd16994366f4 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -8,4 +8,5 @@ CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_VLAN_8021Q=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index 41cc248ac848..b80666277e36 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -255,6 +255,15 @@ class NetDrvEpEnv(NetDrvEnvBase):
if nsim_test is False and self._ns is not None:
raise KsftXfailEx("Test does not work on netdevsim")
+ def get_local_nsim_dev(self):
+ """Returns the local netdevsim device or None.
+ Using this method is discouraged, as it makes tests nsim-specific.
+ Standard interfaces available on all HW should ideally be used.
+ This method is intended for the few cases where nsim-specific
+ assertions need to be verified which cannot be verified otherwise.
+ """
+ return self._ns
+
def _require_cmd(self, comm, key, host=None):
cached = self._required_cmd.get(comm, {})
if cached.get(key) is None:
diff --git a/tools/testing/selftests/drivers/net/macsec.py b/tools/testing/selftests/drivers/net/macsec.py
index a17b9f7ef584..b634d55e38c2 100755
--- a/tools/testing/selftests/drivers/net/macsec.py
+++ b/tools/testing/selftests/drivers/net/macsec.py
@@ -6,10 +6,14 @@
import os
from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises
+from lib.py import ksft_variants, KsftNamedVariant
from lib.py import CmdExitFailure, KsftSkipEx
from lib.py import NetDrvEpEnv
from lib.py import cmd, ip, defer, ethtool
+MACSEC_KEY = "12345678901234567890123456789012"
+MACSEC_VLAN_VID = 10
+
# Unique prefix per run to avoid collisions in the shared netns.
# Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".
MACSEC_PFX = f"ms{os.getpid()}_"
@@ -25,6 +29,16 @@ def _get_macsec_offload(dev):
return info.get("linkinfo", {}).get("info_data", {}).get("offload")
+def _require_ip_macsec(cfg):
+ """SKIP if iproute2 on local or remote lacks 'ip macsec' support."""
+ for host in [None, cfg.remote]:
+ out = cmd("ip macsec help", fail=False, host=host)
+ if "macsec" not in out.stdout + out.stderr:
+ where = "remote" if host else "local"
+ raise KsftSkipEx(f"iproute2 too old on {where},"
+ " missing macsec support")
+
+
def _require_ip_macsec_offload():
"""SKIP if local iproute2 doesn't understand 'ip macsec offload'."""
out = cmd("ip macsec help", fail=False)
@@ -44,6 +58,78 @@ def _require_macsec_offload(cfg):
raise KsftSkipEx("macsec-hw-offload not supported")
+def _get_mac(ifname, host=None):
+ """Gets MAC address of an interface."""
+ dev = ip(f"-d link show dev {ifname}", json=True, host=host)
+ return dev[0]["address"]
+
+
+def _setup_macsec_sa(cfg, name):
+ """Adds matching TX/RX SAs on both ends."""
+ local_mac = _get_mac(name)
+ remote_mac = _get_mac(name, host=cfg.remote)
+
+ ip(f"macsec add {name} tx sa 0 pn 1 on key 01 {MACSEC_KEY}")
+ ip(f"macsec add {name} rx port 1 address {remote_mac}")
+ ip(f"macsec add {name} rx port 1 address {remote_mac} "
+ f"sa 0 pn 1 on key 02 {MACSEC_KEY}")
+
+ ip(f"macsec add {name} tx sa 0 pn 1 on key 02 {MACSEC_KEY}",
+ host=cfg.remote)
+ ip(f"macsec add {name} rx port 1 address {local_mac}", host=cfg.remote)
+ ip(f"macsec add {name} rx port 1 address {local_mac} "
+ f"sa 0 pn 1 on key 01 {MACSEC_KEY}", host=cfg.remote)
+
+
+def _setup_macsec_devs(cfg, name, offload):
+ """Creates macsec devices on both ends.
+
+ Only the local device gets HW offload; the remote always uses software
+ MACsec since it may not support offload at all.
+ """
+ offload_arg = "mac" if offload else "off"
+
+ ip(f"link add link {cfg.ifname} {name} "
+ f"type macsec encrypt on offload {offload_arg}")
+ defer(ip, f"link del {name}")
+ ip(f"link add link {cfg.remote_ifname} {name} "
+ f"type macsec encrypt on", host=cfg.remote)
+ defer(ip, f"link del {name}", host=cfg.remote)
+
+
+def _set_offload(name, offload):
+ """Sets offload on the local macsec device only."""
+ offload_arg = "mac" if offload else "off"
+
+ ip(f"link set {name} type macsec encrypt on offload {offload_arg}")
+
+
+def _setup_vlans(cfg, name, vid):
+ """Adds VLANs on top of existing macsec devs."""
+ vlan_name = f"{name}.{vid}"
+
+ ip(f"link add link {name} {vlan_name} type vlan id {vid}")
+ defer(ip, f"link del {vlan_name}")
+ ip(f"link add link {name} {vlan_name} type vlan id {vid}", host=cfg.remote)
+ defer(ip, f"link del {vlan_name}", host=cfg.remote)
+
+
+def _setup_vlan_ips(cfg, name, vid):
+ """Adds VLANs and IPs and brings up the macsec + VLAN devices."""
+ local_ip = "198.51.100.1"
+ remote_ip = "198.51.100.2"
+ vlan_name = f"{name}.{vid}"
+
+ ip(f"addr add {local_ip}/24 dev {vlan_name}")
+ ip(f"addr add {remote_ip}/24 dev {vlan_name}", host=cfg.remote)
+ ip(f"link set {name} up")
+ ip(f"link set {name} up", host=cfg.remote)
+ ip(f"link set {vlan_name} up")
+ ip(f"link set {vlan_name} up", host=cfg.remote)
+
+ return vlan_name, remote_ip
+
+
def test_offload_api(cfg) -> None:
"""MACsec offload API: create SecY, add SA/rx, toggle offload."""
@@ -164,6 +250,69 @@ def test_offload_state(cfg) -> None:
"offload enabled after create: should be mac")
+def _check_nsim_vid(cfg, vid, expected) -> None:
+ """Checks if a VLAN is present. Only works on netdevsim."""
+
+ nsim = cfg.get_local_nsim_dev()
+ if not nsim:
+ return
+
+ vlan_path = nsim.nsims[0].dfs_dir + "vlan"
+ with open(vlan_path, encoding="utf-8") as f:
+ vids = f.read()
+ found = f"ctag {vid}\n" in vids
+ ksft_eq(found, expected,
+ f"VLAN {vid} {'expected' if expected else 'not expected'}"
+ f" in debugfs")
+
+
+@ksft_variants([
+ KsftNamedVariant("offloaded", True),
+ KsftNamedVariant("software", False),
+])
+def test_vlan(cfg, offload) -> None:
+ """Ping through VLAN-over-macsec."""
+
+ _require_ip_macsec(cfg)
+ if offload:
+ _require_macsec_offload(cfg)
+ else:
+ _require_ip_macsec_offload()
+ name = _macsec_name()
+ _setup_macsec_devs(cfg, name, offload=offload)
+ _setup_macsec_sa(cfg, name)
+ _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+ vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+ # nsim doesn't handle the data path for offloaded macsec, so skip
+ # the ping when offloaded on nsim.
+ if not offload or not cfg.get_local_nsim_dev():
+ cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
+@ksft_variants([
+ KsftNamedVariant("on_to_off", True),
+ KsftNamedVariant("off_to_on", False),
+])
+def test_vlan_toggle(cfg, offload) -> None:
+ """Toggle offload: VLAN filters propagate/remove correctly."""
+
+ _require_ip_macsec(cfg)
+ _require_macsec_offload(cfg)
+ name = _macsec_name()
+ _setup_macsec_devs(cfg, name, offload=offload)
+ _setup_vlans(cfg, name, MACSEC_VLAN_VID)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, offload)
+ _set_offload(name, offload=not offload)
+ _check_nsim_vid(cfg, MACSEC_VLAN_VID, not offload)
+ vlan_name, remote_ip = _setup_vlan_ips(cfg, name, MACSEC_VLAN_VID)
+ _setup_macsec_sa(cfg, name)
+ # nsim doesn't handle the data path for offloaded macsec, so skip
+ # the ping when the final state is offloaded on nsim.
+ if offload or not cfg.get_local_nsim_dev():
+ cmd(f"ping -I {vlan_name} -c 1 -W 5 {remote_ip}")
+
+
def main() -> None:
"""Main program."""
with NetDrvEpEnv(__file__) as cfg:
@@ -171,6 +320,8 @@ def main() -> None:
test_max_secy,
test_max_sc,
test_offload_state,
+ test_vlan,
+ test_vlan_toggle,
], args=(cfg,))
ksft_exit()
--
2.53.0
^ permalink raw reply related
* [PATCH net v6 1/4] selftests: Migrate nsim-only MACsec tests to Python
From: Cosmin Ratiu @ 2026-03-30 13:01 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Cosmin Ratiu, Dragos Tatulea
In-Reply-To: <20260330130130.989236-1-cratiu@nvidia.com>
Move MACsec offload API and ethtool feature tests from
tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh to
tools/testing/selftests/drivers/net/macsec.py using the NetDrvEnv
framework so tests can run against both netdevsim (default) and real
hardware (NETIF=ethX). As some real hardware requires MACsec to use
encryption, add that to the tests.
Netdevsim-specific limit checks (max SecY, max RX SC) were moved into
separate test cases to avoid failures on real hardware.
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
tools/testing/selftests/drivers/net/Makefile | 1 +
tools/testing/selftests/drivers/net/config | 1 +
tools/testing/selftests/drivers/net/macsec.py | 179 ++++++++++++++++++
.../selftests/drivers/net/netdevsim/Makefile | 1 -
.../drivers/net/netdevsim/macsec-offload.sh | 117 ------------
5 files changed, 181 insertions(+), 118 deletions(-)
create mode 100755 tools/testing/selftests/drivers/net/macsec.py
delete mode 100755 tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 8154d6d429d3..5e045dde0273 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -13,6 +13,7 @@ TEST_GEN_FILES := \
TEST_PROGS := \
gro.py \
hds.py \
+ macsec.py \
napi_id.py \
napi_threaded.py \
netpoll_basic.py \
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index 77ccf83d87e0..d4b31a317c09 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -3,6 +3,7 @@ CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=n
CONFIG_INET_PSP=y
CONFIG_IPV6=y
+CONFIG_MACSEC=m
CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
diff --git a/tools/testing/selftests/drivers/net/macsec.py b/tools/testing/selftests/drivers/net/macsec.py
new file mode 100755
index 000000000000..a17b9f7ef584
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/macsec.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""MACsec tests."""
+
+import os
+
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises
+from lib.py import CmdExitFailure, KsftSkipEx
+from lib.py import NetDrvEpEnv
+from lib.py import cmd, ip, defer, ethtool
+
+# Unique prefix per run to avoid collisions in the shared netns.
+# Keep it short: IFNAMSIZ is 16 (incl. NUL), and VLAN names append ".<vid>".
+MACSEC_PFX = f"ms{os.getpid()}_"
+
+
+def _macsec_name(idx=0):
+ return f"{MACSEC_PFX}{idx}"
+
+
+def _get_macsec_offload(dev):
+ """Returns macsec offload mode string from ip -d link show."""
+ info = ip(f"-d link show dev {dev}", json=True)[0]
+ return info.get("linkinfo", {}).get("info_data", {}).get("offload")
+
+
+def _require_ip_macsec_offload():
+ """SKIP if local iproute2 doesn't understand 'ip macsec offload'."""
+ out = cmd("ip macsec help", fail=False)
+ if "offload" not in out.stdout + out.stderr:
+ raise KsftSkipEx("iproute2 too old, missing macsec offload")
+
+
+def _require_macsec_offload(cfg):
+ """SKIP if local device doesn't support macsec-hw-offload."""
+ _require_ip_macsec_offload()
+ try:
+ feat = ethtool(f"-k {cfg.ifname}", json=True)[0]
+ except (CmdExitFailure, IndexError) as e:
+ raise KsftSkipEx(
+ f"can't query features: {e}") from e
+ if not feat.get("macsec-hw-offload", {}).get("active"):
+ raise KsftSkipEx("macsec-hw-offload not supported")
+
+
+def test_offload_api(cfg) -> None:
+ """MACsec offload API: create SecY, add SA/rx, toggle offload."""
+
+ _require_macsec_offload(cfg)
+ ms0 = _macsec_name(0)
+ ms1 = _macsec_name(1)
+ ms2 = _macsec_name(2)
+
+ # Create 3 SecY with offload
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+
+ ip(f"link add link {cfg.ifname} {ms1} type macsec "
+ f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+ defer(ip, f"link del {ms1}")
+
+ ip(f"link add link {cfg.ifname} {ms2} type macsec "
+ f"sci abbacdde01020304 encrypt on offload mac")
+ defer(ip, f"link del {ms2}")
+
+ # Add TX SA
+ ip(f"macsec add {ms0} tx sa 0 pn 1024 on "
+ "key 01 12345678901234567890123456789012")
+
+ # Add RX SC + SA
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef "
+ "sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef")
+
+ # Can't disable offload when SAs are configured
+ with ksft_raises(CmdExitFailure):
+ ip(f"link set {ms0} type macsec offload off")
+ with ksft_raises(CmdExitFailure):
+ ip(f"macsec offload {ms0} off")
+
+ # Toggle offload via rtnetlink on SA-free device
+ ip(f"link set {ms2} type macsec offload off")
+ ip(f"link set {ms2} type macsec encrypt on offload mac")
+
+ # Toggle offload via genetlink
+ ip(f"macsec offload {ms2} off")
+ ip(f"macsec offload {ms2} mac")
+
+
+def test_max_secy(cfg) -> None:
+ """nsim-only test for max number of SecYs."""
+
+ cfg.require_nsim()
+ _require_ip_macsec_offload()
+ ms0 = _macsec_name(0)
+ ms1 = _macsec_name(1)
+ ms2 = _macsec_name(2)
+ ms3 = _macsec_name(3)
+
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+
+ ip(f"link add link {cfg.ifname} {ms1} type macsec "
+ f"address aa:bb:cc:dd:ee:ff port 5 encrypt on offload mac")
+ defer(ip, f"link del {ms1}")
+
+ ip(f"link add link {cfg.ifname} {ms2} type macsec "
+ f"sci abbacdde01020304 encrypt on offload mac")
+ defer(ip, f"link del {ms2}")
+ with ksft_raises(CmdExitFailure):
+ ip(f"link add link {cfg.ifname} {ms3} "
+ f"type macsec port 8 encrypt on offload mac")
+
+
+def test_max_sc(cfg) -> None:
+ """nsim-only test for max number of SCs."""
+
+ cfg.require_nsim()
+ _require_ip_macsec_offload()
+ ms0 = _macsec_name(0)
+
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"port 4 encrypt on offload mac")
+ defer(ip, f"link del {ms0}")
+ ip(f"macsec add {ms0} rx port 1234 address 1c:ed:de:ad:be:ef")
+ with ksft_raises(CmdExitFailure):
+ ip(f"macsec add {ms0} rx port 1235 address 1c:ed:de:ad:be:ef")
+
+
+def test_offload_state(cfg) -> None:
+ """Offload state reflects configuration changes."""
+
+ _require_macsec_offload(cfg)
+ ms0 = _macsec_name(0)
+
+ # Create with offload on
+ ip(f"link add link {cfg.ifname} {ms0} type macsec "
+ f"encrypt on offload mac")
+ cleanup = defer(ip, f"link del {ms0}")
+
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "created with offload: should be mac")
+
+ ip(f"link set {ms0} type macsec offload off")
+ ksft_eq(_get_macsec_offload(ms0), "off",
+ "offload disabled: should be off")
+
+ ip(f"link set {ms0} type macsec encrypt on offload mac")
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "offload re-enabled: should be mac")
+
+ # Delete and recreate without offload
+ cleanup.exec()
+ ip(f"link add link {cfg.ifname} {ms0} type macsec")
+ defer(ip, f"link del {ms0}")
+ ksft_eq(_get_macsec_offload(ms0), "off",
+ "created without offload: should be off")
+
+ ip(f"link set {ms0} type macsec encrypt on offload mac")
+ ksft_eq(_get_macsec_offload(ms0), "mac",
+ "offload enabled after create: should be mac")
+
+
+def main() -> None:
+ """Main program."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_offload_api,
+ test_max_secy,
+ test_max_sc,
+ test_offload_state,
+ ], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/drivers/net/netdevsim/Makefile b/tools/testing/selftests/drivers/net/netdevsim/Makefile
index 1a228c5430f5..9808c2fbae9e 100644
--- a/tools/testing/selftests/drivers/net/netdevsim/Makefile
+++ b/tools/testing/selftests/drivers/net/netdevsim/Makefile
@@ -11,7 +11,6 @@ TEST_PROGS := \
fib.sh \
fib_notifications.sh \
hw_stats_l3.sh \
- macsec-offload.sh \
nexthop.sh \
peer.sh \
psample.sh \
diff --git a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh b/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
deleted file mode 100755
index 98033e6667d2..000000000000
--- a/tools/testing/selftests/drivers/net/netdevsim/macsec-offload.sh
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0-only
-
-source ethtool-common.sh
-
-NSIM_NETDEV=$(make_netdev)
-MACSEC_NETDEV=macsec_nsim
-
-set -o pipefail
-
-if ! ethtool -k $NSIM_NETDEV | grep -q 'macsec-hw-offload: on'; then
- echo "SKIP: netdevsim doesn't support MACsec offload"
- exit 4
-fi
-
-if ! ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac 2>/dev/null; then
- echo "SKIP: couldn't create macsec device"
- exit 4
-fi
-ip link del $MACSEC_NETDEV
-
-#
-# test macsec offload API
-#
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}" type macsec port 4 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}2" type macsec address "aa:bb:cc:dd:ee:ff" port 5 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}3" type macsec sci abbacdde01020304 offload mac
-check $?
-
-ip link add link $NSIM_NETDEV "${MACSEC_NETDEV}4" type macsec port 8 offload mac 2> /dev/null
-check $? '' '' 1
-
-ip macsec add "${MACSEC_NETDEV}" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef"
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on \
- key 00 0123456789abcdef0123456789abcdef
-check $?
-
-ip macsec add "${MACSEC_NETDEV}" rx port 1235 address "1c:ed:de:ad:be:ef" 2> /dev/null
-check $? '' '' 1
-
-# can't disable macsec offload when SAs are configured
-ip link set "${MACSEC_NETDEV}" type macsec offload off 2> /dev/null
-check $? '' '' 1
-
-ip macsec offload "${MACSEC_NETDEV}" off 2> /dev/null
-check $? '' '' 1
-
-# toggle macsec offload via rtnetlink
-ip link set "${MACSEC_NETDEV}2" type macsec offload off
-check $?
-
-ip link set "${MACSEC_NETDEV}2" type macsec offload mac
-check $?
-
-# toggle macsec offload via genetlink
-ip macsec offload "${MACSEC_NETDEV}2" off
-check $?
-
-ip macsec offload "${MACSEC_NETDEV}2" mac
-check $?
-
-for dev in ${MACSEC_NETDEV}{,2,3} ; do
- ip link del $dev
- check $?
-done
-
-
-#
-# test ethtool features when toggling offload
-#
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload off
-TMP_FEATS_OFF_1="$(ethtool -k $MACSEC_NETDEV)"
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-TMP_FEATS_ON_2="$(ethtool -k $MACSEC_NETDEV)"
-
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_2" ]
-check $?
-
-ip link del $MACSEC_NETDEV
-
-ip link add link $NSIM_NETDEV $MACSEC_NETDEV type macsec
-check $?
-
-TMP_FEATS_OFF_2="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_OFF_1" = "$TMP_FEATS_OFF_2" ]
-check $?
-
-ip link set $MACSEC_NETDEV type macsec offload mac
-check $?
-
-TMP_FEATS_ON_3="$(ethtool -k $MACSEC_NETDEV)"
-[ "$TMP_FEATS_ON_1" = "$TMP_FEATS_ON_3" ]
-check $?
-
-
-if [ $num_errors -eq 0 ]; then
- echo "PASSED all $((num_passes)) checks"
- exit 0
-else
- echo "FAILED $num_errors/$((num_errors+num_passes)) checks"
- exit 1
-fi
--
2.53.0
^ permalink raw reply related
* [PATCH net v6 4/4] macsec: Support VLAN-filtering lower devices
From: Cosmin Ratiu @ 2026-03-30 13:01 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Stanislav Fomichev,
Shuah Khan, linux-kselftest, Cosmin Ratiu, Dragos Tatulea
In-Reply-To: <20260330130130.989236-1-cratiu@nvidia.com>
VLAN-filtering is done through two netdev features
(NETIF_F_HW_VLAN_CTAG_FILTER and NETIF_F_HW_VLAN_STAG_FILTER) and two
netdev ops (ndo_vlan_rx_add_vid and ndo_vlan_rx_kill_vid).
Implement these and advertise the features if the lower device supports
them. This allows proper VLAN filtering to work on top of MACsec
devices, when the lower device is capable of VLAN filtering.
As a concrete example, having this chain of interfaces now works:
vlan_filtering_capable_dev(1) -> macsec_dev(2) -> macsec_vlan_dev(3)
Before the mentioned commit this used to accidentally work because the
MACsec device (and thus the lower device) was put in promiscuous mode
and the VLAN filter was not used. But after commit [1] correctly made
the macsec driver expose the IFF_UNICAST_FLT flag, promiscuous mode was
no longer used and VLAN filters on dev 1 kicked in. Without support in
dev 2 for propagating VLAN filters down, the register_vlan_dev ->
vlan_vid_add -> __vlan_vid_add -> vlan_add_rx_filter_info call from dev
3 is silently eaten (because vlan_hw_filter_capable returns false and
vlan_add_rx_filter_info silently succeeds).
For MACsec, VLAN filters are only relevant for offload, otherwise
the VLANs are encrypted and the lower devices don't care about them. So
VLAN filters are only passed on to lower devices in offload mode.
Flipping between offload modes now needs to offload/unoffload the
filters with vlan_{get,drop}_rx_*_filter_info().
To avoid the back-and-forth filter updating during rollback, the setting
of macsec->offload is moved after the add/del secy ops. This is safe
since none of the code called from those requires macsec->offload.
Fixes: 0349659fd72f ("macsec: set IFF_UNICAST_FLT priv flag")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
---
drivers/net/macsec.c | 44 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 5 deletions(-)
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index f6cad0746a02..3bdb6f3fae8e 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -2616,14 +2616,22 @@ static int macsec_update_offload(struct net_device *dev, enum macsec_offload off
if (!ops)
return -EOPNOTSUPP;
- macsec->offload = offload;
-
ctx.secy = &macsec->secy;
ret = offload == MACSEC_OFFLOAD_OFF ? macsec_offload(ops->mdo_del_secy, &ctx)
: macsec_offload(ops->mdo_add_secy, &ctx);
- if (ret) {
- macsec->offload = prev_offload;
+ if (ret)
return ret;
+
+ /* Remove VLAN filters when disabling offload. */
+ if (offload == MACSEC_OFFLOAD_OFF) {
+ vlan_drop_rx_ctag_filter_info(dev);
+ vlan_drop_rx_stag_filter_info(dev);
+ }
+ macsec->offload = offload;
+ /* Add VLAN filters when enabling offload. */
+ if (prev_offload == MACSEC_OFFLOAD_OFF) {
+ vlan_get_rx_ctag_filter_info(dev);
+ vlan_get_rx_stag_filter_info(dev);
}
macsec_set_head_tail_room(dev);
@@ -3486,7 +3494,8 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
}
#define MACSEC_FEATURES \
- (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
+ (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ NETIF_F_HW_VLAN_STAG_FILTER | NETIF_F_HW_VLAN_CTAG_FILTER)
#define MACSEC_OFFLOAD_FEATURES \
(MACSEC_FEATURES | NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES | \
@@ -3707,6 +3716,29 @@ static int macsec_set_mac_address(struct net_device *dev, void *p)
return err;
}
+static int macsec_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+
+ if (!macsec_is_offloaded(macsec))
+ return 0;
+
+ return vlan_vid_add(macsec->real_dev, proto, vid);
+}
+
+static int macsec_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct macsec_dev *macsec = netdev_priv(dev);
+
+ if (!macsec_is_offloaded(macsec))
+ return 0;
+
+ vlan_vid_del(macsec->real_dev, proto, vid);
+ return 0;
+}
+
static int macsec_change_mtu(struct net_device *dev, int new_mtu)
{
struct macsec_dev *macsec = macsec_priv(dev);
@@ -3748,6 +3780,8 @@ static const struct net_device_ops macsec_netdev_ops = {
.ndo_set_rx_mode = macsec_dev_set_rx_mode,
.ndo_change_rx_flags = macsec_dev_change_rx_flags,
.ndo_set_mac_address = macsec_set_mac_address,
+ .ndo_vlan_rx_add_vid = macsec_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = macsec_vlan_rx_kill_vid,
.ndo_start_xmit = macsec_start_xmit,
.ndo_get_stats64 = macsec_get_stats64,
.ndo_get_iflink = macsec_get_iflink,
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 1/2] net: phy: microchip: add downshift tunable support for LAN88xx
From: Andrew Lunn @ 2026-03-30 13:02 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: netdev, Phil Elwell, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel
In-Reply-To: <20260329224202.500229-2-nb@tipi-net.de>
> +static int lan88xx_set_downshift(struct phy_device *phydev, u8 cnt)
> +{
> + u32 mask = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK |
> + LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
> + u32 val;
> +
> + if (cnt == DOWNSHIFT_DEV_DISABLE)
> + return phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3,
> + LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT, 0);
> +
> + if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT)
> + cnt = 2;
> +
> + switch (cnt) {
> + case 2:
> + val = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2;
> + break;
> + case 3:
> + val = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3;
> + break;
> + case 4:
> + val = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4;
> + break;
> + case 5:
> + val = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5;
> + break;
> + default:
> + return -EINVAL;
And this could be a range check followed by a FIELD_PREP(cnt - 2);
Andrew
^ permalink raw reply
* Re: [PATCH 2/2] net: phy: microchip: enable downshift by default on LAN88xx
From: Andrew Lunn @ 2026-03-30 13:03 UTC (permalink / raw)
To: Nicolai Buchwitz
Cc: netdev, Phil Elwell, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel
In-Reply-To: <20260329224202.500229-3-nb@tipi-net.de>
On Mon, Mar 30, 2026 at 12:42:00AM +0200, Nicolai Buchwitz wrote:
> Enable auto-downshift from 1000BASE-T to 100BASE-TX after 2 failed
> auto-negotiation attempts by default. This ensures that links with
> faulty or missing cable pairs (C and D) fall back to 100Mbps without
> requiring userspace configuration.
>
> Users can override or disable downshift at runtime:
>
> ethtool --set-phy-tunable eth0 downshift off
>
> Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* [PATCH net v2] tipc: fix UAF race in tipc_mon_peer_up/down/remove_peer vs bearer teardown
From: Kai Zen @ 2026-03-30 13:03 UTC (permalink / raw)
To: netdev; +Cc: stable, jmaloy
CVE-2025-40280 fixed tipc_mon_reinit_self() accessing monitors[] from a
workqueue without RTNL. That patch closed the workqueue path by adding
rtnl_lock() around the call.
However, three additional functions in the same subsystem access
tipc_net->monitors[] from softirq context with no RCU protection at all:
tipc_mon_peer_up() - called from tipc_node_write_unlock()
tipc_mon_peer_down() - called from tipc_node_write_unlock()
tipc_mon_remove_peer() - called from tipc_node_link_down()
These three are invoked from the packet receive path (tipc_rcv ->
tipc_node_write_unlock / tipc_node_link_down) and hold only the per-node
rwlock, not RTNL.
Concurrently, bearer_disable() -- which always holds RTNL per its own
inline documentation -- calls tipc_mon_delete(), which:
1. acquires mon->lock
2. sets tn->monitors[bearer_id] = NULL
3. frees all peer entries
4. releases mon->lock
5. calls kfree(mon) <-- no synchronize_rcu()
The race is structural: there is no shared lock between the data-path
reader (which reads monitors[id] then acquires mon->lock) and the
teardown path (which acquires mon->lock, NULLs the slot, then frees).
A softirq thread can read a non-NULL mon pointer, get preempted, and
resume after kfree(mon) has run on another CPU, then call
write_lock_bh(&mon->lock) on freed memory:
CPU 0 (softirq / tipc_rcv) CPU 1 (RTNL / bearer_disable)
tipc_mon_peer_up()
mon = tipc_monitor(net, id)
[mon is non-NULL]
tipc_mon_delete()
write_lock_bh(&mon->lock)
tn->monitors[id] = NULL
...
write_unlock_bh(&mon->lock)
kfree(mon)
write_lock_bh(&mon->lock) <-- UAF
The fix mirrors the existing bearer_list[] pattern in the same module:
convert monitors[] to __rcu, use rcu_assign_pointer() on creation,
RCU_INIT_POINTER() + synchronize_rcu() on deletion (before the kfree),
and the appropriate rcu_dereference_bh() vs rtnl_dereference() variant
at each read site depending on execution context.
synchronize_rcu() in tipc_mon_delete() is placed after the
write_unlock_bh() and before timer_shutdown_sync() + kfree() to ensure
all softirq-context readers that already observed the old pointer have
completed before the memory is freed.
Fixes: 35c55c9877f8 ("tipc: add neighbor monitoring framework")
Cc: stable@vger.kernel.org
Signed-off-by: Kai Aizen <kai.aizen.dev@gmail.com>
---
v2: Resubmit targeting mainline via netdev per stable-kernel-rules (Option 1).
No code changes from v1.
net/tipc/core.h | 2 +-
net/tipc/monitor.c | 51 +++++++++++++++++++++++++++++++++--------------
2 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/net/tipc/core.h b/net/tipc/core.h
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -109,7 +109,7 @@
u32 num_links;
/* Neighbor monitoring list */
- struct tipc_monitor *monitors[MAX_BEARERS];
+ struct tipc_monitor __rcu *monito[MAX_BEARERS];
rs
+
^ permalink raw reply
* Re: [PATCH net-next] net: phylink: allow PHYs to be attached in 802.3z inband mode
From: Maxime Chevallier @ 2026-03-30 13:07 UTC (permalink / raw)
To: Russell King (Oracle), Andrew Lunn
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, netdev,
Paolo Abeni, Alexis Lothore
In-Reply-To: <acptbbfTt_FJgms6@shell.armlinux.org.uk>
On 30/03/2026 14:32, Russell King (Oracle) wrote:
> On Mon, Mar 30, 2026 at 02:26:58PM +0200, Andrew Lunn wrote:
>>> As I've recently said, I think we're more or less boxed in between a
>>> rock and a hard place because of the hacks that qcom-ethqos introduced
>>> with its PCS support. At the moment, I am failing to see any path where
>>> the stmmac PCS can be programmed for inband according to phylink's
>>> wishes and qcom-ethqos can be made to work without its hacks.
>>>
>>> This all ultimately comes back to the half-hearted phylink conversion
>>> of stmmac that was done behind my back without my review. Had the
>>> conversion been done fully, and kept up with the phylink changes for
>>> PCS support, then we probably wouldn't be in this situation today.
>>>
>>> Unfortunately, the driver is going to keep breaking all the time that
>>> it abuses phylink, and at the moment I see no way to stop this sodding
>>> driver abusing phylink.
>>
>> "The needs of the many outweigh the needs of the few"
>>
>> The MAC driver is used by a lot of different SoCs. If qcom-ethqos
>> caused this problem, should we consider breaking backwards
>> compatibility for it, if that opens up a path for getting clean
>> phylink integration for all the other variants?
>
> Consider something like mvneta (which supports 2.5G speeds) connected
> to a 2.5G PHY that uses 2500BASE-X for 2.5G speeds, and SGMII for
> slower speeds with inband.
>
> How should this be described in DT?
>
> phy-mode = "2500base-x";
> managed = "in-band-status";
>
> will fail today, as these tests will refuse to attach a PHY in that
> setup, and without a PHY attached, we will never know if it switches
> to SGMII mode.
>
> This patch makes phylink more flexible and permissive.
You're patch is clearly correct IMO
> However, as has been found, this patch causes problems for stmmac,
> so it needs a different approach, because stmmac interprets an
> optional PHY as "we must have a PHY if we don't we fail" at the
> moment.
What would be the consequences of changing stmmac's stmmac_init_phy so
that it doesn't error-out when no PHY is found AND we have a PCS AND
we're using any of the SGMII/1000BaseX/SFP-compatible modes ?
I don't think it would break current setups that work, but I may be
wrong here, especially as I don't have a good understanding on the
problem caused by qcom-ethqos here :(
Maxime
^ permalink raw reply
* [PATCH v7 1/3] PCI: AtomicOps: Do not enable requests by RCiEPs
From: Gerd Bayer @ 2026-03-30 13:09 UTC (permalink / raw)
To: Bjorn Helgaas, Jay Cornwall, Felix Kuehling, Ilpo Järvinen,
Christian Borntraeger, Niklas Schnelle
Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Sven Schnelle, Leon Romanovsky, Alexander Schmidt, linux-s390,
linux-pci, linux-kernel, netdev, linux-rdma, Gerd Bayer
In-Reply-To: <20260330-fix_pciatops-v7-0-f601818417e8@linux.ibm.com>
Since root complex integrated end points (RCiEPs) attach to a bus that
has no bridge device describing the root port, the capability to
complete AtomicOps requests cannot be determined with PCIe methods.
Change default of pci_enable_atomic_ops_to_root() to not enable
AtomicOps requests on RCiEPs.
Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
---
drivers/pci/pci.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 8479c2e1f74f1044416281aba11bf071ea89488a..135e5b591df405e87e7f520a618d7e2ccba55ce1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3692,15 +3692,14 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
/*
* Per PCIe r4.0, sec 6.15, endpoints and root ports may be
- * AtomicOp requesters. For now, we only support endpoints as
- * requesters and root ports as completers. No endpoints as
+ * AtomicOp requesters. For now, we only support (legacy) endpoints
+ * as requesters and root ports as completers. No endpoints as
* completers, and no peer-to-peer.
*/
switch (pci_pcie_type(dev)) {
case PCI_EXP_TYPE_ENDPOINT:
case PCI_EXP_TYPE_LEG_END:
- case PCI_EXP_TYPE_RC_END:
break;
default:
return -EINVAL;
--
2.51.0
^ permalink raw reply related
* [PATCH v7 3/3] PCI: AtomicOps: Update references to PCIe spec
From: Gerd Bayer @ 2026-03-30 13:09 UTC (permalink / raw)
To: Bjorn Helgaas, Jay Cornwall, Felix Kuehling, Ilpo Järvinen,
Christian Borntraeger, Niklas Schnelle
Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Sven Schnelle, Leon Romanovsky, Alexander Schmidt, linux-s390,
linux-pci, linux-kernel, netdev, linux-rdma, Gerd Bayer
In-Reply-To: <20260330-fix_pciatops-v7-0-f601818417e8@linux.ibm.com>
Point to the relevant sections in the most recent release 7.0 of the
PCIe spec. Text has mostly just moved around without any semantic
change.
Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
---
drivers/pci/pci.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 57af00ecdc97086a32c063ff86f8a39087ad1f5e..b99ab47678b006004af6cdb9b0e9f9ca4a28b6e1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3689,7 +3689,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
u32 ctl2;
/*
- * Per PCIe r5.0, sec 9.3.5.10, the AtomicOp Requester Enable bit
+ * Per PCIe r7.0, sec 7.5.3.16, the AtomicOp Requester Enable bit
* in Device Control 2 is reserved in VFs and the PF value applies
* to all associated VFs.
*/
@@ -3700,7 +3700,7 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
return -EINVAL;
/*
- * Per PCIe r4.0, sec 6.15, endpoints and root ports may be
+ * Per PCIe r7.0, sec 6.15, endpoints and root ports may be
* AtomicOp requesters. For now, we only support (legacy) endpoints
* as requesters and root ports as completers. No endpoints as
* completers, and no peer-to-peer.
--
2.51.0
^ permalink raw reply related
* [PATCH v7 0/3] PCI: AtomicOps: Fix pci_enable_atomic_ops_to_root()
From: Gerd Bayer @ 2026-03-30 13:09 UTC (permalink / raw)
To: Bjorn Helgaas, Jay Cornwall, Felix Kuehling, Ilpo Järvinen,
Christian Borntraeger, Niklas Schnelle
Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Sven Schnelle, Leon Romanovsky, Alexander Schmidt, linux-s390,
linux-pci, linux-kernel, netdev, linux-rdma, Gerd Bayer, stable
Hi Bjorn et al.
On s390, AtomicOp Requests are enabled on a PCI function that supports
them, despite the helper being ignorant about the root port's capability
to supporting their completion.
Patch 1: Do not enable AtomicOps Requests on RCiEPs
Patch 2: Fix the logic in pci_enable_atomic_ops_to_root()
Patch 3: Update references to PCIe spec in that function.
I did test that the issue is fixed with these patches. Also, I verified
that on a Mellanox/Nvidia ConnectX-6 adapter plugged straight into the
root port of a x86 system still gets AtomicOp Requests enabled.
Due to a lack of the required hardware, I did not test this with any PCIe
switches between root port and endpoint. So test exposure in other
environments is highly appreciated.
Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
---
Changes in v7:
- Prepend series with a patch to explicitly exclude RCiEPs from
enablement of AtomicOps Requests
- Limit the core patch 2 to enforce a full check of the entire
PCIe hierarchy for support of AtomicOps capabilities.
- Rebase to v7.0-rc6
- Link to v6: https://lore.kernel.org/r/20260325-fix_pciatops-v6-0-10bf19d76dd1@linux.ibm.com
Changes in v6:
- Incorporate Ilpo's editorial comments.
- Correct logic in pci_is_atomicops_capable_rp() (annotated by Sashiko)
- Link to v5: https://lore.kernel.org/r/20260323-fix_pciatops-v5-0-fada7233aea8@linux.ibm.com
Changes in v5:
- Introduce new pcibios_connects_to_atomicops_capable_rc() so arch's can
declare AtomicOps support outside of PCIe config space. Defaults to
"true" - except s390.
- rebase to 7.0-rc5
- Link to v4: https://lore.kernel.org/r/20260313-fix_pciatops-v4-0-93bc70a63935@linux.ibm.com
Changes in v4:
- drop patch 1 - it will become the base of a new series
- previous patch 2, now 1: reword commit message
- add a new patch to update references to PCI spec within
pci_enable_atomic_ops_to_root()
- rebase to latest master
- Link to v3: https://lore.kernel.org/r/20260306-fix_pciatops-v3-0-99d12bcafb19@linux.ibm.com
Changes in v3:
- rebase to 7.0-rc2
- gentle ping
- add netdev and rdma lists for awareness
- Link to v2: https://lore.kernel.org/r/20251216-fix_pciatops-v2-0-d013e9b7e2ee@linux.ibm.com
Changes in v2:
- rebase to 6.19-rc1
- otherwise unchanged to v1
- Link to v1: https://lore.kernel.org/r/20251110-fix_pciatops-v1-0-edc58a57b62e@linux.ibm.com
---
Gerd Bayer (3):
PCI: AtomicOps: Do not enable requests by RCiEPs
PCI: AtomicOps: Do not enable without support in root port
PCI: AtomicOps: Update references to PCIe spec
drivers/pci/pci.c | 48 ++++++++++++++++++++++++++----------------------
1 file changed, 26 insertions(+), 22 deletions(-)
---
base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
change-id: 20251106-fix_pciatops-7e8608eccb03
Best regards,
--
Gerd Bayer <gbayer@linux.ibm.com>
^ permalink raw reply
* [PATCH v7 2/3] PCI: AtomicOps: Do not enable without support in root port
From: Gerd Bayer @ 2026-03-30 13:09 UTC (permalink / raw)
To: Bjorn Helgaas, Jay Cornwall, Felix Kuehling, Ilpo Järvinen,
Christian Borntraeger, Niklas Schnelle
Cc: Gerald Schaefer, Heiko Carstens, Vasily Gorbik, Alexander Gordeev,
Sven Schnelle, Leon Romanovsky, Alexander Schmidt, linux-s390,
linux-pci, linux-kernel, netdev, linux-rdma, Gerd Bayer, stable
In-Reply-To: <20260330-fix_pciatops-v7-0-f601818417e8@linux.ibm.com>
When inspecting the config space of a Connect-X physical function in an
s390 system after it was initialized by the mlx5_core device driver, we
found the function to be enabled to request AtomicOps despite the
system's root-complex lacking support for completing them:
1ed0:00:00.1 Ethernet controller: Mellanox Technologies MT2894 Family [ConnectX-6 Lx]
Subsystem: Mellanox Technologies Device 0002
[...]
DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
AtomicOpsCtl: ReqEn+
IDOReq- IDOCompl- LTR- EmergencyPowerReductionReq-
10BitTagReq- OBFF Disabled, EETLPPrefixBlk-
Turns out the device driver calls pci_enable_atomic_ops_to_root() which
defaulted to enable AtomicOps requests even if it had no information
about the root port that the PCIe device is attached to.
Change the logic of pci_enable_atomic_ops_to_root() to fully traverse the
PCIe tree upwards, check that the bridge devices support delivering
AtomicOps transactions, and finally check that there is a root port at
the end that does support completing AtomicOps.
Reported-by: Alexander Schmidt <alexs@linux.ibm.com>
Cc: stable@vger.kernel.org
Fixes: 430a23689dea ("PCI: Add pci_enable_atomic_ops_to_root()")
Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
---
drivers/pci/pci.c | 39 ++++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 135e5b591df405e87e7f520a618d7e2ccba55ce1..57af00ecdc97086a32c063ff86f8a39087ad1f5e 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3660,6 +3660,14 @@ void pci_acs_init(struct pci_dev *dev)
pci_disable_broken_acs_cap(dev);
}
+static bool pci_is_atomicops_capable_rp(struct pci_dev *dev, u32 cap, u32 cap_mask)
+{
+ if (!dev || !(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT))
+ return false;
+
+ return (cap & cap_mask) == cap_mask;
+}
+
/**
* pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
* @dev: the PCI device
@@ -3676,8 +3684,9 @@ void pci_acs_init(struct pci_dev *dev)
int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
{
struct pci_bus *bus = dev->bus;
- struct pci_dev *bridge;
- u32 cap, ctl2;
+ struct pci_dev *bridge = NULL;
+ u32 cap = 0;
+ u32 ctl2;
/*
* Per PCIe r5.0, sec 9.3.5.10, the AtomicOp Requester Enable bit
@@ -3713,29 +3722,25 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
switch (pci_pcie_type(bridge)) {
/* Ensure switch ports support AtomicOp routing */
case PCI_EXP_TYPE_UPSTREAM:
- case PCI_EXP_TYPE_DOWNSTREAM:
- if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE))
- return -EINVAL;
- break;
-
- /* Ensure root port supports all the sizes we care about */
- case PCI_EXP_TYPE_ROOT_PORT:
- if ((cap & cap_mask) != cap_mask)
- return -EINVAL;
- break;
- }
-
- /* Ensure upstream ports don't block AtomicOps on egress */
- if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) {
+ /* Upstream ports must not block AtomicOps on egress */
pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2,
&ctl2);
if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK)
return -EINVAL;
+ fallthrough;
+ /* All switch ports need to route AtomicOps */
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE))
+ return -EINVAL;
+ break;
}
-
bus = bus->parent;
}
+ /* Finally, last bridge must be root port and support requested sizes */
+ if (!(pci_is_atomicops_capable_rp(bridge, cap, cap_mask)))
+ return -EINVAL;
+
pcie_capability_set_word(dev, PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_ATOMIC_REQ);
return 0;
--
2.51.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox