* Re: [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Luiz Angelo Daros de Luca @ 2026-07-18 5:40 UTC (permalink / raw)
To: Johan Alvarado
Cc: Mieczyslaw Nalewaj, linusw, alsi, andrew, olteanv, kuba, davem,
edumazet, pabeni, linux, maxime.chevallier, kuncy7, netdev,
linux-kernel
In-Reply-To: <db8f9fb11e7e250eab9c98eafab1ba3e@c127.dev>
If the cold-soak issue is indeed device-related, it might be that the
driver is loading too early in the boot process. Stanislaw reported
that reloading the module makes everything work as expected.
I don't see why the second reset would behave better than the first
one. In fact, if the reset pin is connected and configured, the driver
will reset the ASIC twice during the probe. Stanislaw, do you have a
reset pin or a reset controller configured? That might be related.
The driver currently performs a full software ASIC reset via bit(0),
but there are other reset bits available that you could try:
##### **Family B, C, D:**
```
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| RESERVED |GPH|NIC|805|SDS|CFG|SW |CHP|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
```
* **GPH (6:6)**: `GPHY_RESET`. Gigphy (Copper PHYs) Reset.
* **NIC (5:5)**: `NIC_RST`. Network Interface Controller Reset.
* **805 (4:4)**: `DW8051_RST`. Internal DW8051 MCU Reset.
* **SDS (3:3)**: `SDS_RST`. SerDes Reset.
* **CFG (2:2)**: `CONFIG_RST`. Configuration register soft reset.
* **SW (1:1)**: `SW_RST`. Software Reset (`0x0002`). Clears soft state.
* **CHP (0:0)**: `CHIP_RST`. Chip (ASIC) full reset (`0x0001`).
I would expect the CHIP_RST (bit 0) to clear everything, but you never
know... Also, keeping DW8051_RST asserted indefinitely might be
overkill.
This series adds a required feature for several devices. Even if it
introduces an edge-case issue for a few of them, lacking this feature
entirely is worse. If a patch is proposed to fix the cold-soak boot
issue, we can review it here as usual.
I would feel more comfortable if I could test this code myself, but my
device only uses RGMII. Anyway, everything looks good. Thanks, Johan.
Reviewed-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
^ permalink raw reply
* Re: [PATCH v4 2/5] drm/ras: Introduce error threshold
From: Raag Jadav @ 2026-07-18 5:26 UTC (permalink / raw)
To: Tauro, Riana
Cc: intel-xe, dri-devel, netdev, simona.vetter, airlied, kuba,
lijo.lazar, Hawking.Zhang, davem, pabeni, edumazet, dev,
zachary.mckevitt, rodrigo.vivi, michal.wajdeczko, matthew.d.roper,
mallesh.koujalagi
In-Reply-To: <0769389d-e73a-4dda-878c-2bc9e59deff3@intel.com>
On Fri, Jul 03, 2026 at 10:43:06AM +0530, Tauro, Riana wrote:
> On 23-06-2026 15:39, Raag Jadav wrote:
> > Add get-error-threshold and set-error-threshold command support which
> > allows querying/setting error threshold of the counter. Threshold in RAS
> > context means the number of errors the hardware is expected to accumulate
> > before it raises them to software. This is to have a fine grained control
> > over error notifications that are raised by the hardware.
...
> > * + The error counters in the driver doesn't need to be contiguous, but the
> > * driver must return -ENOENT to the query_error_counter as an indication
> > * that the ID should be skipped and not listed in the netlink API.
> > + * + The driver can optionally implement query_error_threshold() and
> > + * set_error_threshold() callbacks to facilitate getting/setting error
> > + * threshold of the counter. Threshold in RAS context means the number of
> > + * errors the hardware is expected to accumulate before it raises them to
> > + * software. This is to have a fine grained control over error notifications
> > + * that are raised by the hardware.
> > + * + The driver is responsible for error threshold bounds checking.
> > + * + Threshold of 0 can mean invalid threshold or act as a disable notifications
> > + * toggle for that counter depending on usecase and the driver is responsible
> > + * for handling it as needed.
>
> I know i asked you to add this in last rev. But after reading this,
> error-threshold bounds checking at driver level
> should be sufficient. It's upto the driver on what behavior needs to be
> implemented.
>
> Some may notify on reaching threshold or crossing threshold.
> I think we should drop this sentence here. Let me know your thoughts.
> sorry for the confusion.
I think it adds more context and good to have in case someone comes up
looking for the details.
...
> > + /**
> > + * @query_error_threshold:
> > + *
> > + * This callback is used by drm-ras to query error threshold of a
> > + * specific counter.
> > + *
> > + * Driver should expect query_error_threshold() to be called with
> > + * error_id from `error_counter_range.first` to
> > + * `error_counter_range.last`.
> > + *
> > + * Returns: 0 on success, negative error code on failure.
> > + */
> > + int (*query_error_threshold)(struct drm_ras_node *node, u32 error_id, const char **name,
> > + u32 *threshold);
>
> Add a blank line
Sure.
> With these fixed
>
> Reviewed-by: Riana Tauro <riana.tauro@intel.com>
Awesome.
Raag
^ permalink raw reply
* [PATCH net] net: mana: Return error code from mana_create_rxq()
From: Aditya Garg @ 2026-07-18 2:48 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, horms, dipayanroy, gargaditya,
shacharr, stephen, linux-hyperv, netdev, linux-kernel, ssengar,
gargaditya
mana_create_rxq() returns a struct mana_rxq pointer and returns NULL on
any failure. The caller, mana_add_rx_queues(), cannot tell what went
wrong and hardcodes the error as -ENOMEM. As a result the actual failure
reported by the lower layers (for example -EPROTO from a failed HW
request) is masked and every RX queue creation failure looks like an
out-of-memory error.
Change mana_create_rxq() to return an int and pass the created rxq back
to the caller through an output parameter. The caller now propagates the
returned error code directly instead of substituting -ENOMEM.
Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)")
Signed-off-by: Aditya Garg <gargaditya@linux.microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 21 ++++++++++---------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 9d9bfd116dab..9162e356e0c1 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -2811,9 +2811,9 @@ static int mana_create_page_pool(struct mana_rxq *rxq, struct gdma_context *gc)
return 0;
}
-static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
- u32 rxq_idx, struct mana_eq *eq,
- struct net_device *ndev)
+static int mana_create_rxq(struct mana_port_context *apc, u32 rxq_idx,
+ struct mana_eq *eq, struct net_device *ndev,
+ struct mana_rxq **rxq_out)
{
struct gdma_dev *gd = apc->ac->gdma_dev;
struct mana_obj_spec wq_spec;
@@ -2829,7 +2829,7 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
rxq = kvzalloc_flex(*rxq, rx_oobs, apc->rx_queue_size);
if (!rxq)
- return NULL;
+ return -ENOMEM;
rxq->ndev = ndev;
rxq->num_rx_buf = apc->rx_queue_size;
@@ -2923,14 +2923,16 @@ static struct mana_rxq *mana_create_rxq(struct mana_port_context *apc,
mana_gd_ring_cq(cq->gdma_cq, SET_ARM_BIT);
out:
- if (!err)
- return rxq;
+ if (!err) {
+ *rxq_out = rxq;
+ return 0;
+ }
netdev_err(ndev, "Failed to create RXQ: err = %d\n", err);
mana_destroy_rxq(apc, rxq, false);
- return NULL;
+ return err;
}
static void mana_create_rxq_debugfs(struct mana_port_context *apc, int idx)
@@ -2963,9 +2965,8 @@ static int mana_add_rx_queues(struct mana_port_context *apc,
int i;
for (i = 0; i < apc->num_queues; i++) {
- rxq = mana_create_rxq(apc, i, &apc->eqs[i], ndev);
- if (!rxq) {
- err = -ENOMEM;
+ err = mana_create_rxq(apc, i, &apc->eqs[i], ndev, &rxq);
+ if (err) {
netdev_err(ndev, "Failed to create rxq %d : %d\n", i, err);
goto out;
}
--
2.43.0
^ permalink raw reply related
* 答复: [外部邮件] Re: [PATCH] net: ipv6: fix a potential use-after-free in ip4ip6_err
From: Li,Rongqing @ 2026-07-18 0:43 UTC (permalink / raw)
To: Xin Long
Cc: David Ahern, Ido Schimmel, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <CADvbK_cEUR9m-QyKi5jj2MTqF2kGbBj7d1mUXmCsHgDLNJ-bPQ@mail.gmail.com>
> -----邮件原件-----
> 发件人: Xin Long <lucien.xin@gmail.com>
> 发送时间: 2026年7月17日 23:53
> 收件人: Li,Rongqing <lirongqing@baidu.com>
> 抄送: David Ahern <dsahern@kernel.org>; Ido Schimmel
> <idosch@nvidia.com>; David S . Miller <davem@davemloft.net>; Eric
> Dumazet <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo
> Abeni <pabeni@redhat.com>; Simon Horman <horms@kernel.org>;
> netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> 主题: [外部邮件] Re: [PATCH] net: ipv6: fix a potential use-after-free in
> ip4ip6_err
>
> On Fri, Jul 17, 2026 at 10:33 AM lirongqing <lirongqing@baidu.com> wrote:
> >
> > From: Li RongQing <lirongqing@baidu.com>
> >
> > Fix a use-after-free bug in ip4ip6_err() where rt->rt_flags is
> > accessed after the route entry object has been released via ip_rt_put(rt).
> >
> > If ip_rt_put() decrements the reference count to zero and frees the
> > rtable structure, reading rt->rt_flags immediately afterward results
> > in a use-after-free pointer dereference.
> >
> > Fix this by caching rt->rt_flags into a local variable before calling
> > ip_rt_put().
> >
> > Fixes: 77552cfa39c4 ("ip6_tunnel: clean up ip4ip6 and ip6ip6's
> > err_handlers")
> > Signed-off-by: Li RongQing <lirongqing@baidu.com>
> > ---
> > net/ipv6/ip6_tunnel.c | 4 +++-
> > 1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index
> > bf8e40a..984cb0c 100644
> > --- a/net/ipv6/ip6_tunnel.c
> > +++ b/net/ipv6/ip6_tunnel.c
> > @@ -569,6 +569,7 @@ ip4ip6_err(struct sk_buff *skb, struct
> > inet6_skb_parm *opt, {
> > __u32 rel_info = ntohl(info);
> > const struct iphdr *eiph;
> > + unsigned int rt_flags;
> > struct sk_buff *skb2;
> > int err, rel_msg = 0;
> > u8 rel_type = type;
> > @@ -627,10 +628,11 @@ ip4ip6_err(struct sk_buff *skb, struct
> inet6_skb_parm *opt,
> > goto out;
> >
> > skb2->dev = rt->dst.dev;
> > + rt_flags = rt->rt_flags;
> > ip_rt_put(rt);
> >
> > /* route "incoming" packet */
> > - if (rt->rt_flags & RTCF_LOCAL) {
> > + if (rt_flags & RTCF_LOCAL) {
> > rt = ip_route_output_ports(dev_net(skb->dev), &fl4,
> NULL,
> > eiph->daddr, eiph->saddr,
> 0, 0,
> > IPPROTO_IPIP,
> > --
> > 2.9.4
> >
> Have you already seen any problem triggered by this?
>
> I don't really think there's a use-after-free issue here.
>
> The entire IPv6 input path, including ICMPv6 error handling, runs under
> rcu_read_lock(). Since dst_release() uses call_rcu_hurry() to defer the actual
> freeing until after the RCU grace period, accessing rt->rt_flags after
> ip_rt_put(rt) is completely safe in this context.
>
Thanks for the review. However, The issue is that after ip_rt_put(rt), the rt pointer may be logically freed if refcnt drops to zero, and dereferencing it afterwards is illegal even if memory isn't recycled immediately. The patch avoids this by caching rt->rt_flags before the put operation. This is a defensive fix against potential UAF reported by static analyzers.
Thanks
[Li,Rongqing]
> Thanks.
^ permalink raw reply
* Re: [REGRESSION][BISECTED] stmmac: suspend hangs since 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
From: Andrew Lunn @ 2026-07-18 0:16 UTC (permalink / raw)
To: tresonic; +Cc: netdev, regressions, rmk+kernel, kuba, Maxime Chevallier
In-Reply-To: <8f6aca6d-6619-401c-bed7-8d42732d166a@mail.de>
> It seems your assumptions are correct. If i do
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> index a0249715fafa..487efc746b20 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
> @@ -129,6 +129,8 @@ void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
> const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
> u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
>
> + value &= ~DMA_CHAN_INTR_ENA_RPS;
> +
> if (rx)
> value &= ~DMA_CHAN_INTR_ENA_RIE;
> if (tx)
>
> I can suspend and resume!
Cool. So we might be going in the correct direction.
> Could it be sufficient to just do this and reenable on dwmac4_enable_dma_irq?
Unfortunately, it is not as simple as that.
The driver implements NAPI. Interrupts are expensive, so what NAPI
does is after there is an interrupt indicating there are received
packets, and there are more than 64 packets to be received, it
disables interrupts, and goes into polling made. Whenever it polls, if
there are packets available it keeps on polling. Only when polling
indicates there are no more packets, are interrupts re-enabled and
polling stopped.
You can see parts of this logic in stmmac_napi_poll_rx().
This means it cannot be done here. We need a function which is only
called on suspend, and probably release.
The interrupt is being enabled in the init_chan call in
stmmac_dma_ops. Ideally, it should be disabled in a mirror function,
which currently does not exist. So maybe deinit_chan() needs
adding. But where to call it from? init_chan() is called from
stmmac_init_dma_engine(), from stmmac_hw_setup(). stmmac_resume() does
call this. So we need something in stmmac_suspend(). Maybe in
stmmac_stop_all_dma()?
stmmac is messy, there are often not mirror functions. If there is a
stmmac_init_dma_engine() there should be
stmmac_deinit_dma_engine(). If there is stmmac_hw_setup() there should
be stmmac_hw_tairdown(). But none of these seem to exist.
Anyway, do you want to try to implement deinit_chan() and call it from
stmmac_stop_all_dma()?
Andrew
^ permalink raw reply
* [PATCH] net/mlx5e: ktls: guard RX resync against missing TLS context
From: Rishikesh Jethwani @ 2026-07-17 22:46 UTC (permalink / raw)
To: netdev
Cc: john.fastabend, kuba, sd, davem, pabeni, edumazet, leon,
nils.juenemann, borisp, saeedm, tariqt, mbloch, shshitrit,
Rishikesh Jethwani
resync_update_sn() handles CQE_TLS_OFFLOAD_RESYNC by looking up the
socket with inet_lookup_established() / __inet6_lookup_established()
and then dereferencing tls_get_ctx(sk) on the assumption that the
returned socket owns the TLS context that produced the CQE.
That assumption is not guaranteed. The established lookup matches only
on the 5-tuple and does not filter on TLS ULP state, so it can return a
TCP_ESTABLISHED socket with no TLS context attached. In that case
tls_get_ctx(sk) is NULL and the RX resync path dereferences it.
In the observed crash, the returned socket was TCP_ESTABLISHED with both
icsk_ulp_ops and icsk_ulp_data NULL, i.e. a socket without a TLS ULP
attached at lookup time. This can happen if a stale resync CQE is
matched to a different socket for the same 5-tuple, or otherwise
resolves to a socket without a TLS context.
Fetch tls_get_ctx(sk) once after the TIME_WAIT check and bail out if it
is NULL, then pass the sampled tls_context down to the resync helpers.
Fixes: 0419d8c9d8f8 ("net/mlx5e: kTLS, Add kTLS RX resync support")
Link: https://lore.kernel.org/netdev/20260705104419.4014-1-nils.juenemann@gmail.com/
Reported-by: Nils Juenemann <nils.juenemann@gmail.com>
Signed-off-by: Rishikesh Jethwani <rjethwani@purestorage.com>
---
.../mellanox/mlx5/core/en_accel/ktls_rx.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
index bca45679e201..a1cab11a07dc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
@@ -471,12 +471,12 @@ void mlx5e_ktls_handle_get_psv_completion(struct mlx5e_icosq_wqe_info *wi,
/* Runs in NAPI.
* Function elevates the refcount, unless no work is queued.
*/
-static bool resync_queue_get_psv(struct sock *sk)
+static bool resync_queue_get_psv(struct tls_context *tls_ctx)
{
struct mlx5e_ktls_offload_context_rx *priv_rx;
struct mlx5e_ktls_rx_resync_ctx *resync;
- priv_rx = mlx5e_get_ktls_rx_priv_ctx(tls_get_ctx(sk));
+ priv_rx = mlx5e_get_ktls_rx_priv_ctx(tls_ctx);
if (unlikely(!priv_rx))
return false;
@@ -500,6 +500,7 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb)
struct tls_offload_resync_async *resync_async;
struct net_device *netdev = rq->netdev;
struct net *net = dev_net(netdev);
+ struct tls_context *tls_ctx;
struct sock *sk = NULL;
unsigned int datalen;
struct iphdr *iph;
@@ -538,12 +539,20 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb)
if (unlikely(sk->sk_state == TCP_TIME_WAIT))
goto unref;
- if (unlikely(!resync_queue_get_psv(sk)))
+ /* Established lookup is tuple-based and may return a socket without
+ * a TLS ULP attached. Sample the TLS context once and bail out if
+ * none is present.
+ */
+ tls_ctx = tls_get_ctx(sk);
+ if (unlikely(!tls_ctx))
+ goto unref;
+
+ if (unlikely(!resync_queue_get_psv(tls_ctx)))
goto unref;
seq = th->seq;
datalen = skb->len - depth;
- resync_async = tls_offload_ctx_rx(tls_get_ctx(sk))->resync_async;
+ resync_async = tls_offload_ctx_rx(tls_ctx)->resync_async;
tls_offload_rx_resync_async_request_start(resync_async, seq, datalen);
rq->stats->tls_resync_req_start++;
--
2.25.1
^ permalink raw reply related
* Re: [REGRESSION][BISECTED] stmmac: suspend hangs since 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
From: tresonic @ 2026-07-17 22:34 UTC (permalink / raw)
To: Andrew Lunn; +Cc: netdev, regressions, rmk+kernel, kuba, Maxime Chevallier
In-Reply-To: <8f6aca6d-6619-401c-bed7-8d42732d166a@mail.de>
>> But i'm also having trouble finding where dwmac4_disable_dma_irq() is
>> actually called. It is called from stmmac_disable_rx_queue() and
>> stmmac_disable_tx_queue() but they only seem to be used in the XDP
>> code.
Afaict these are called through the stmmac_stop_rx macro which maps to the function pointers in dwmac410_dma_ops.
So this actually gets called from stmmac_suspend via stmmac_stop_all_dma.
^ permalink raw reply
* [PATCH net-next 2/2] selftests: net: verify RTA_DEL_REASON on route deletion
From: Yuyang Huang @ 2026-07-17 22:23 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, Bobby Eshleman, Chris J Arges, Daniel Zahka,
David Ahern, David Wei, Dimitri Daskalakis, Donald Hunter,
Eric Dumazet, Gal Pressman, Ido Schimmel, Jakub Kicinski,
Paolo Abeni, Shuah Khan, Simon Horman, Stanislav Fomichev,
Willem de Bruijn, linux-kernel, linux-kselftest, netdev
In-Reply-To: <20260717222327.7398-1-sigefriedhyy@gmail.com>
Extend rtnetlink.py to check the reason reported in RTM_DELROUTE:
- expired: route with a 2s lifetime collected by the fib6 GC
(gc_interval lowered like fib_tests.sh fib6_gc_test does);
- ra-withdrawn: a single RA advertises a default route (router
lifetime), an on-link prefix route (RFC 4861 prefix information
option) and a route information option route (RFC 4191), then a
second RA withdraws all three with zero lifetimes; the RAs are
crafted over a raw ICMPv6 socket so the test does not depend on an
external RA tool;
- absence: a userspace deletion request records no cause and must not
carry the attribute at all.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
.../testing/selftests/net/lib/py/__init__.py | 4 +-
tools/testing/selftests/net/lib/py/ynl.py | 7 +-
tools/testing/selftests/net/rtnetlink.py | 181 +++++++++++++++++-
3 files changed, 187 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py
index e58bdbdc58ee..34935886b6ad 100644
--- a/tools/testing/selftests/net/lib/py/__init__.py
+++ b/tools/testing/selftests/net/lib/py/__init__.py
@@ -17,7 +17,7 @@ from .utils import CmdExitFailure, fd_read_timeout, cmd, bkg, defer, \
wait_file, tool, tc
from .bpf import bpf_map_set, bpf_map_dump, bpf_prog_map_ids
from .ynl import NlError, NlctrlFamily, YnlFamily, \
- EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily
+ EthtoolFamily, NetdevFamily, RtnlFamily, RtnlAddrFamily, RtnlRouteFamily
from .ynl import NetshaperFamily, DevlinkFamily, PSPFamily, Netlink
__all__ = ["KSRC",
@@ -34,4 +34,4 @@ __all__ = ["KSRC",
"NetdevSim", "NetdevSimDev",
"NetshaperFamily", "DevlinkFamily", "PSPFamily", "NlError",
"YnlFamily", "EthtoolFamily", "NetdevFamily", "RtnlFamily",
- "NlctrlFamily", "RtnlAddrFamily", "Netlink"]
+ "NlctrlFamily", "RtnlAddrFamily", "RtnlRouteFamily", "Netlink"]
diff --git a/tools/testing/selftests/net/lib/py/ynl.py b/tools/testing/selftests/net/lib/py/ynl.py
index 2e567062aa6c..08deff756f29 100644
--- a/tools/testing/selftests/net/lib/py/ynl.py
+++ b/tools/testing/selftests/net/lib/py/ynl.py
@@ -29,7 +29,7 @@ except ModuleNotFoundError as e:
__all__ = [
"NlError", "NlPolicy", "Netlink", "YnlFamily", "SPEC_PATH",
- "EthtoolFamily", "RtnlFamily", "RtnlAddrFamily",
+ "EthtoolFamily", "RtnlFamily", "RtnlAddrFamily", "RtnlRouteFamily",
"NetdevFamily", "NetshaperFamily", "NlctrlFamily", "DevlinkFamily",
"PSPFamily",
]
@@ -54,6 +54,11 @@ class RtnlAddrFamily(YnlFamily):
super().__init__((SPEC_PATH / Path('rt-addr.yaml')).as_posix(),
schema='', recv_size=recv_size)
+class RtnlRouteFamily(YnlFamily):
+ def __init__(self, recv_size=0):
+ super().__init__((SPEC_PATH / Path('rt-route.yaml')).as_posix(),
+ schema='', recv_size=recv_size)
+
class NetdevFamily(YnlFamily):
def __init__(self, recv_size=0):
super().__init__((SPEC_PATH / Path('netdev.yaml')).as_posix(),
diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
index 0c67c7c00d84..8773e88b934b 100755
--- a/tools/testing/selftests/net/rtnetlink.py
+++ b/tools/testing/selftests/net/rtnetlink.py
@@ -5,7 +5,8 @@ import socket
import struct
import time
from lib.py import bkg, ip, ksft_exit, ksft_run, ksft_eq, ksft_ge, ksft_true, KsftSkipEx
-from lib.py import CmdExitFailure, NetNS, NetNSEnter, RtnlAddrFamily
+from lib.py import ksft_not_in, ksft_not_none
+from lib.py import CmdExitFailure, NetNS, NetNSEnter, RtnlAddrFamily, RtnlRouteFamily
IPV4_ALL_HOSTS_MULTICAST = b'\xe0\x00\x00\x01'
IPV4_TEST_MULTICAST = b'\xef\x01\x01\x01'
@@ -134,8 +135,184 @@ def ipv4_devconf_notify() -> None:
ksft_true(f"inet {ifname} forwarding on" in cmd_obj.stdout,
f"No 'forwarding on' notificiation found for interface {ifname}")
+def _rtnl_route_subscribe(ns):
+ with NetNSEnter(str(ns)):
+ rtnl = RtnlRouteFamily()
+ rtnl.ntf_subscribe("rtnlgrp-ipv6-route")
+ return rtnl
+
+
+def _wait_route_ntf(rtnl, name, dst_len, dst=None, deadline=10):
+ """Return the attrs of the first matching notification, None on timeout."""
+
+ for msg in rtnl.poll_ntf(duration=deadline):
+ if msg['name'] != name:
+ continue
+ attrs = msg['msg']
+ if attrs['rtm-dst-len'] != dst_len:
+ continue
+ if dst is not None and attrs.get('dst') != dst:
+ continue
+ return attrs
+ return None
+
+
+def _collect_route_ntfs(rtnl, name, want, deadline=10):
+ """Gather attrs of matching notifications, keyed by (dst_len, dst)."""
+
+ seen = {}
+ for msg in rtnl.poll_ntf(duration=deadline):
+ if msg['name'] != name:
+ continue
+ attrs = msg['msg']
+ key = (attrs['rtm-dst-len'], attrs.get('dst'))
+ if key in want:
+ seen[key] = attrs
+ if len(seen) == len(want):
+ break
+ return seen
+
+
+def _write_ipv6_sysctl(name, value):
+ with open(f"/proc/sys/net/ipv6/{name}", "w") as f:
+ f.write(f"{value}\n")
+
+
+def ipv6_route_del_reason_expired() -> None:
+ """An expired route reports RTA_DEL_REASON == expired."""
+
+ with NetNS() as ns:
+ rtnl = _rtnl_route_subscribe(ns)
+ with NetNSEnter(str(ns)):
+ _write_ipv6_sysctl("route/gc_interval", 2)
+ ip("link add name dummy1 type dummy", ns=str(ns))
+ ip("link set dev dummy1 up", ns=str(ns))
+ ip("-6 route add 2001:db8:2::/64 dev dummy1 expires 2", ns=str(ns))
+
+ attrs = _wait_route_ntf(rtnl, 'delroute-ntf', 64, '2001:db8:2::',
+ deadline=15)
+ ksft_not_none(attrs, "no RTM_DELROUTE for the expired route")
+ if attrs is not None:
+ ksft_eq(attrs.get('del-reason'), 'expired')
+
+
+def _send_ra(sock, ifindex, lifetime, rio=None, pio=None):
+ """The kernel fills in the ICMPv6 checksum on raw ICMPv6 sockets."""
+
+ # type, code, cksum, hop limit, flags, router lifetime,
+ # reachable time, retrans timer
+ ra = struct.pack('!BBHBBHII', 134, 0, 0, 64, 0, lifetime, 0, 0)
+ if rio is not None:
+ prefix, plen, rio_lifetime = rio
+ # RFC 4191 route information option, /64 prefix (8 bytes)
+ ra += struct.pack('!BBBBI', 24, 2, plen, 0, rio_lifetime)
+ ra += socket.inet_pton(socket.AF_INET6, prefix)[:8]
+ if pio is not None:
+ prefix, plen, valid_lft = pio
+ # RFC 4861 prefix information option, on-link only (L set, A clear)
+ ra += struct.pack('!BBBBIII', 3, 4, plen, 0x80, valid_lft, 0, 0)
+ ra += socket.inet_pton(socket.AF_INET6, prefix)
+ sock.sendto(ra, ('ff02::1', 0, 0, ifindex))
+
+
+def _ra_router_sock(ns_r, ifname):
+ with NetNSEnter(str(ns_r)):
+ sock = socket.socket(socket.AF_INET6, socket.SOCK_RAW,
+ socket.IPPROTO_ICMPV6)
+ sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 255)
+ return sock, socket.if_nametoindex(ifname)
+
+
+def _ra_advertise_routes(rtnl, sock, ifindex, want, **ra_opts):
+ """
+ Sending fails with EADDRNOTAVAIL until the router's link-local
+ address passes DAD, so retry.
+ """
+
+ seen = {}
+ for _ in range(10):
+ try:
+ _send_ra(sock, ifindex, **ra_opts)
+ except OSError:
+ time.sleep(0.2)
+ continue
+ seen.update(_collect_route_ntfs(rtnl, 'newroute-ntf',
+ want - seen.keys(), deadline=2))
+ if len(seen) == len(want):
+ break
+ return seen
+
+
+def ipv6_route_del_reason_ra_withdrawn() -> None:
+ """
+ Routes withdrawn by a zero-lifetime RA (router lifetime, RFC 4861
+ PIO, RFC 4191 RIO) report RTA_DEL_REASON == ra-withdrawn.
+ """
+
+ # (rtm-dst-len, dst); the default route carries no RTA_DST
+ routes = {(0, None), (64, '2001:db8:6::'), (64, '2001:db8:5::')}
+
+ with NetNS() as ns_h, NetNS() as ns_r:
+ ip(f"link add veth0 netns {ns_h} type veth peer name veth1 netns {ns_r}")
+ with NetNSEnter(str(ns_h)):
+ _write_ipv6_sysctl("conf/veth0/accept_ra", 2)
+ _write_ipv6_sysctl("conf/veth0/forwarding", 0)
+ try:
+ _write_ipv6_sysctl("conf/veth0/accept_ra_rt_info_max_plen", 64)
+ except FileNotFoundError:
+ raise KsftSkipEx("no CONFIG_IPV6_ROUTE_INFO")
+ with NetNSEnter(str(ns_r)):
+ # skip DAD so the router's link-local source is usable right away
+ _write_ipv6_sysctl("conf/veth1/accept_dad", 0)
+ ip("link set dev veth0 up", ns=str(ns_h))
+ ip("link set dev veth1 up", ns=str(ns_r))
+
+ rtnl = _rtnl_route_subscribe(ns_h)
+ sock, ifindex = _ra_router_sock(ns_r, "veth1")
+
+ seen = _ra_advertise_routes(rtnl, sock, ifindex, routes,
+ lifetime=1800,
+ rio=('2001:db8:5::', 64, 600),
+ pio=('2001:db8:6::', 64, 600))
+ ksft_eq(set(seen), routes, "not all RA routes were installed")
+ if set(seen) != routes:
+ return
+
+ _send_ra(sock, ifindex, 0, rio=('2001:db8:5::', 64, 0),
+ pio=('2001:db8:6::', 64, 0))
+ seen = _collect_route_ntfs(rtnl, 'delroute-ntf', routes)
+ for key in routes:
+ attrs = seen.get(key)
+ ksft_not_none(attrs, f"no RTM_DELROUTE for {key}")
+ if attrs is not None:
+ ksft_eq(attrs.get('del-reason'), 'ra-withdrawn')
+
+
+def ipv6_route_del_reason_absent() -> None:
+ """
+ A deletion path that records no cause (here a userspace request)
+ must not carry RTA_DEL_REASON at all.
+ """
+
+ with NetNS() as ns:
+ rtnl = _rtnl_route_subscribe(ns)
+ ip("link add name dummy1 type dummy", ns=str(ns))
+ ip("link set dev dummy1 up", ns=str(ns))
+ ip("-6 route add 2001:db8:1::/64 dev dummy1", ns=str(ns))
+ ip("-6 route del 2001:db8:1::/64 dev dummy1", ns=str(ns))
+
+ attrs = _wait_route_ntf(rtnl, 'delroute-ntf', 64, '2001:db8:1::')
+ ksft_not_none(attrs, "no RTM_DELROUTE for 2001:db8:1::/64")
+ if attrs is not None:
+ ksft_not_in('del-reason', attrs,
+ "user deletion must not carry del-reason")
+
+
def main() -> None:
- ksft_run([dump_mcaddr_check, dump_mcaddr6_check, ipv4_devconf_notify])
+ ksft_run([dump_mcaddr_check, dump_mcaddr6_check, ipv4_devconf_notify,
+ ipv6_route_del_reason_expired,
+ ipv6_route_del_reason_ra_withdrawn,
+ ipv6_route_del_reason_absent])
ksft_exit()
if __name__ == "__main__":
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 1/2] ipv6: report why a route was deleted in RTM_DELROUTE
From: Yuyang Huang @ 2026-07-17 22:23 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, Bobby Eshleman, Chris J Arges, Daniel Zahka,
David Ahern, David Wei, Dimitri Daskalakis, Donald Hunter,
Eric Dumazet, Gal Pressman, Ido Schimmel, Jakub Kicinski,
Paolo Abeni, Shuah Khan, Simon Horman, Stanislav Fomichev,
Willem de Bruijn, linux-kernel, linux-kselftest, netdev
In-Reply-To: <20260717222327.7398-1-sigefriedhyy@gmail.com>
Add RTA_DEL_REASON (u8) to RTM_DELROUTE notifications to report the
cause of kernel-initiated IPv6 route deletions, so user space can tell
routes that expired from routes explicitly withdrawn by Router
Advertisements.
The two causes call for different treatment in user space. An expired
RA route means the router failed to refresh it before its lifetime ran
out, which points at a misconfigured or unreliable router; user space
may want to react, for example by disabling IPv6 on that network
entirely. An RA withdrawal (zero lifetime) is valid, RFC-compliant
operation and needs no such recovery. Today both surface as an
identical RTM_DELROUTE.
Plumb the deletion reason through the IPv6 FIB and notification paths:
- Update `fib6_del`, `fib6_del_route`, and `inet6_rt_notify` signatures
to explicitly pass the `del_reason` (u8) down the call chain.
- Update `rt6_fill_node()` to emit `RTA_DEL_REASON` attribute when the
reason is not UNSPEC.
- Record the cause in the kernel-initiated IPv6 deletion paths:
- RTA_DEL_REASON_EXPIRED: for routes cleaned up by the FIB6 garbage
collector.
- RTA_DEL_REASON_RA_WITHDRAWN: for default routes, prefix routes, or
route information option routes withdrawn by Router Advertisements.
- Update the `rt-route` Netlink YAML spec to define the new attribute,
notifications, and multicast groups.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
Documentation/netlink/specs/rt-route.yaml | 33 ++++++++++++
include/net/ip6_fib.h | 4 +-
include/net/ip6_route.h | 2 +
include/uapi/linux/rtnetlink.h | 17 ++++++
net/ipv6/addrconf.c | 3 +-
net/ipv6/ip6_fib.c | 19 ++++---
net/ipv6/ndisc.c | 7 ++-
net/ipv6/route.c | 66 +++++++++++++++--------
8 files changed, 116 insertions(+), 35 deletions(-)
diff --git a/Documentation/netlink/specs/rt-route.yaml b/Documentation/netlink/specs/rt-route.yaml
index 33195db96746..d5f6824127c2 100644
--- a/Documentation/netlink/specs/rt-route.yaml
+++ b/Documentation/netlink/specs/rt-route.yaml
@@ -78,6 +78,15 @@ definitions:
-
name: rta-used
type: u32
+ -
+ name: del-reason
+ type: enum
+ name-prefix: rta-del-reason-
+ enum-name: rta-del-reason
+ entries:
+ - unspec
+ - expired
+ - ra-withdrawn
attribute-sets:
-
@@ -185,6 +194,10 @@ attribute-sets:
type: u32
byte-order: big-endian
display-hint: hex
+ -
+ name: del-reason
+ type: u8
+ enum: del-reason
-
name: metrics
name-prefix: rtax-
@@ -299,6 +312,7 @@ operations:
- dport
- nh-id
- flowlabel
+ - del-reason
dump:
request:
value: 26
@@ -322,3 +336,22 @@ operations:
request:
value: 25
attributes: *all-route-attrs
+ -
+ name: newroute-ntf
+ doc: Notification about a created route.
+ value: 24
+ notify: getroute
+ -
+ name: delroute-ntf
+ doc: Notification about a deleted route.
+ value: 25
+ notify: getroute
+
+mcast-groups:
+ list:
+ -
+ name: rtnlgrp-ipv4-route
+ value: 7
+ -
+ name: rtnlgrp-ipv6-route
+ value: 11
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 9cd27e1b9b69..c93a997bb41e 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -468,7 +468,7 @@ void fib6_clean_all_skip_notify(struct net *net,
int fib6_add(struct fib6_node *root, struct fib6_info *rt,
struct nl_info *info, struct netlink_ext_ack *extack);
-int fib6_del(struct fib6_info *rt, struct nl_info *info);
+int fib6_del(struct fib6_info *rt, struct nl_info *info, u8 del_reason);
static inline
void rt6_get_prefsrc(const struct rt6_info *rt, struct in6_addr *addr)
@@ -531,7 +531,7 @@ static inline void fib6_rt_update(struct net *net, struct fib6_info *rt,
}
#endif
void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
- unsigned int flags);
+ unsigned int flags, u8 del_reason);
void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
unsigned long now);
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 09ffe0f13ce7..bf6b39eaaa20 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -128,6 +128,8 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
int ip6_ins_rt(struct net *net, struct fib6_info *f6i);
#if IS_ENABLED(CONFIG_IPV6)
int ip6_del_rt(struct net *net, struct fib6_info *f6i, bool skip_notify);
+int ip6_del_rt_reason(struct net *net, struct fib6_info *f6i, bool skip_notify,
+ u8 del_reason);
#else
static inline int ip6_del_rt(struct net *net, struct fib6_info *f6i,
bool skip_notify)
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 27265fd31e5f..86b407bef4ce 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -399,6 +399,7 @@ enum rtattr_type_t {
RTA_DPORT,
RTA_NH_ID,
RTA_FLOWLABEL,
+ RTA_DEL_REASON,
__RTA_MAX
};
@@ -407,6 +408,22 @@ enum rtattr_type_t {
#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
+/* RTA_DEL_REASON: why the kernel deleted the route. u8.
+ * Emitted only on RTM_DELROUTE notifications, and only when the deletion
+ * path records a cause. Absence means either an older kernel or a
+ * deletion path that does not (yet) record its cause - consumers must
+ * treat "absent" and "unspec" identically. New causes may be appended.
+ * Currently only IPv6 deletion paths record a cause.
+ */
+enum rta_del_reason {
+ RTA_DEL_REASON_UNSPEC, /* cause not recorded */
+ RTA_DEL_REASON_EXPIRED, /* RTF_EXPIRES lifetime ran out (GC) */
+ RTA_DEL_REASON_RA_WITHDRAWN, /* zero-lifetime RA / PIO / RIO */
+ __RTA_DEL_REASON_MAX
+};
+
+#define RTA_DEL_REASON_MAX (__RTA_DEL_REASON_MAX - 1)
+
/* RTM_MULTIPATH --- array of struct rtnexthop.
*
* "struct rtnexthop" describes all necessary nexthop information,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f1fe9ede1edb..1ca9d82b5721 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2874,7 +2874,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
if (rt) {
/* Autoconf prefix route */
if (valid_lft == 0) {
- ip6_del_rt(net, rt, false);
+ ip6_del_rt_reason(net, rt, false,
+ RTA_DEL_REASON_RA_WITHDRAWN);
rt = NULL;
} else {
table = rt->fib6_table;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index a130cdfaebfb..2c9bce3213b7 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1286,7 +1286,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
rcu_assign_pointer(rt->fib6_node, fn);
rcu_assign_pointer(*ins, rt);
if (!info->skip_notify)
- inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
+ inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags,
+ RTA_DEL_REASON_UNSPEC);
info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
if (!(fn->fn_flags & RTN_RTINFO)) {
@@ -1317,7 +1318,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
rt->fib6_next = iter->fib6_next;
rcu_assign_pointer(*ins, rt);
if (!info->skip_notify)
- inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
+ inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE,
+ RTA_DEL_REASON_UNSPEC);
if (!(fn->fn_flags & RTN_RTINFO)) {
info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
fn->fn_flags |= RTN_RTINFO;
@@ -1966,7 +1968,8 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
}
static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
- struct fib6_info __rcu **rtp, struct nl_info *info)
+ struct fib6_info __rcu **rtp, struct nl_info *info,
+ u8 del_reason)
{
struct fib6_info *leaf, *replace_rt = NULL;
struct fib6_walker *w;
@@ -2055,13 +2058,13 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
call_fib6_entry_notifiers_replace(net, replace_rt);
}
if (!info->skip_notify)
- inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
+ inet6_rt_notify(RTM_DELROUTE, rt, info, 0, del_reason);
fib6_info_release(rt);
}
/* Need to own table->tb6_lock */
-int fib6_del(struct fib6_info *rt, struct nl_info *info)
+int fib6_del(struct fib6_info *rt, struct nl_info *info, u8 del_reason)
{
struct net *net = info->nl_net;
struct fib6_info __rcu **rtp;
@@ -2090,7 +2093,7 @@ int fib6_del(struct fib6_info *rt, struct nl_info *info)
if (rt == cur) {
if (fib6_requires_src(cur))
fib6_routes_require_src_dec(info->nl_net);
- fib6_del_route(table, fn, rtp, info);
+ fib6_del_route(table, fn, rtp, info, del_reason);
return 0;
}
rtp_next = &cur->fib6_next;
@@ -2252,7 +2255,7 @@ static int fib6_clean_node(struct fib6_walker *w)
res = c->func(rt, c->arg);
if (res == -1) {
w->leaf = rt;
- res = fib6_del(rt, &info);
+ res = fib6_del(rt, &info, RTA_DEL_REASON_UNSPEC);
if (res) {
#if RT6_DEBUG >= 2
pr_debug("%s: del failed: rt=%p@%p err=%d\n",
@@ -2400,7 +2403,7 @@ static void fib6_gc_table(struct net *net,
hlist_for_each_entry_safe(rt, n, &tb6->tb6_gc_hlist, gc_link)
if (fib6_age(rt, gc_args) == -1)
- fib6_del(rt, &info);
+ fib6_del(rt, &info, RTA_DEL_REASON_EXPIRED);
}
static void fib6_gc_all(struct net *net, struct fib6_gc_args *gc_args)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f867ec8d3d90..c3537fc0ec63 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1361,7 +1361,9 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric;
/* delete the route if lifetime is 0 or if metric needs change */
if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) {
- ip6_del_rt(net, rt, false);
+ ip6_del_rt_reason(net, rt, false,
+ lifetime == 0 ? RTA_DEL_REASON_RA_WITHDRAWN :
+ RTA_DEL_REASON_UNSPEC);
rt = NULL;
}
@@ -1396,7 +1398,8 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
.nl_net = net,
};
rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
- inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE);
+ inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE,
+ RTA_DEL_REASON_UNSPEC);
}
if (rt) {
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a1301334da48..ee7ba58e83c7 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -111,7 +111,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
struct fib6_info *rt, struct dst_entry *dst,
struct in6_addr *dest, struct in6_addr *src,
int iif, int type, u32 portid, u32 seq,
- unsigned int flags);
+ unsigned int flags, u8 del_reason);
static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
const struct in6_addr *daddr,
const struct in6_addr *saddr);
@@ -1020,7 +1020,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
gwaddr, dev);
if (rt && !lifetime) {
- ip6_del_rt(net, rt, false);
+ ip6_del_rt_reason(net, rt, false, RTA_DEL_REASON_RA_WITHDRAWN);
rt = NULL;
}
@@ -3973,7 +3973,8 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
return err;
}
-static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
+static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info,
+ u8 del_reason)
{
struct net *net = info->nl_net;
struct fib6_table *table;
@@ -3986,7 +3987,7 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
table = rt->fib6_table;
spin_lock_bh(&table->tb6_lock);
- err = fib6_del(rt, info);
+ err = fib6_del(rt, info, del_reason);
spin_unlock_bh(&table->tb6_lock);
out:
@@ -3994,14 +3995,20 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
return err;
}
-int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
+int ip6_del_rt_reason(struct net *net, struct fib6_info *rt, bool skip_notify,
+ u8 del_reason)
{
struct nl_info info = {
.nl_net = net,
- .skip_notify = skip_notify
+ .skip_notify = skip_notify,
};
- return __ip6_del_rt(rt, &info);
+ return __ip6_del_rt(rt, &info, del_reason);
+}
+
+int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
+{
+ return ip6_del_rt_reason(net, rt, skip_notify, RTA_DEL_REASON_UNSPEC);
}
static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
@@ -4028,7 +4035,8 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
if (rt6_fill_node(net, skb, rt, NULL,
NULL, NULL, 0, RTM_DELROUTE,
- info->portid, seq, 0) < 0) {
+ info->portid, seq, 0,
+ RTA_DEL_REASON_UNSPEC) < 0) {
kfree_skb(skb);
skb = NULL;
} else
@@ -4064,13 +4072,13 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
list_for_each_entry_safe(sibling, next_sibling,
&rt->fib6_siblings,
fib6_siblings) {
- err = fib6_del(sibling, info);
+ err = fib6_del(sibling, info, RTA_DEL_REASON_UNSPEC);
if (err)
goto out_unlock;
}
}
- err = fib6_del(rt, info);
+ err = fib6_del(rt, info, RTA_DEL_REASON_UNSPEC);
out_unlock:
spin_unlock_bh(&table->tb6_lock);
out_put:
@@ -4196,7 +4204,8 @@ static int ip6_route_del(struct fib6_config *cfg,
if (!fib6_info_hold_safe(rt))
continue;
- err = __ip6_del_rt(rt, &cfg->fc_nlinfo);
+ err = __ip6_del_rt(rt, &cfg->fc_nlinfo,
+ RTA_DEL_REASON_UNSPEC);
break;
}
if (cfg->fc_nh_id)
@@ -4215,7 +4224,8 @@ static int ip6_route_del(struct fib6_config *cfg,
/* if gateway was specified only delete the one hop */
if (cfg->fc_flags & RTF_GATEWAY)
- err = __ip6_del_rt(rt, &cfg->fc_nlinfo);
+ err = __ip6_del_rt(rt, &cfg->fc_nlinfo,
+ RTA_DEL_REASON_UNSPEC);
else
err = __ip6_del_rt_siblings(rt, cfg);
break;
@@ -5381,7 +5391,8 @@ static void ip6_route_mpath_notify(struct fib6_info *rt,
}
if (rt)
- inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
+ inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags,
+ RTA_DEL_REASON_UNSPEC);
rcu_read_unlock();
}
@@ -5739,6 +5750,7 @@ static size_t rt6_nlmsg_size(struct fib6_info *f6i)
+ nla_total_size(sizeof(struct rta_cacheinfo))
+ nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
+ nla_total_size(1) /* RTA_PREF */
+ + nla_total_size(1) /* RTA_DEL_REASON */
+ nexthop_len;
}
@@ -5775,7 +5787,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
struct fib6_info *rt, struct dst_entry *dst,
struct in6_addr *dest, struct in6_addr *src,
int iif, int type, u32 portid, u32 seq,
- unsigned int flags)
+ unsigned int flags, u8 del_reason)
{
struct rt6_info *rt6 = dst_rt6_info(dst);
struct rt6key *rt6_dst, *rt6_src;
@@ -5954,6 +5966,10 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
goto nla_put_failure;
+ if (type == RTM_DELROUTE && del_reason != RTA_DEL_REASON_UNSPEC &&
+ nla_put_u8(skb, RTA_DEL_REASON, del_reason))
+ goto nla_put_failure;
+
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
goto nla_put_failure;
@@ -6055,7 +6071,8 @@ static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
&rt6_ex->rt6i->dst, NULL, NULL, 0,
RTM_NEWROUTE,
NETLINK_CB(dump->cb->skb).portid,
- dump->cb->nlh->nlmsg_seq, w->flags);
+ dump->cb->nlh->nlmsg_seq, w->flags,
+ RTA_DEL_REASON_UNSPEC);
if (err)
return err;
@@ -6103,7 +6120,8 @@ int rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
0, RTM_NEWROUTE,
NETLINK_CB(arg->cb->skb).portid,
- arg->cb->nlh->nlmsg_seq, flags)) {
+ arg->cb->nlh->nlmsg_seq, flags,
+ RTA_DEL_REASON_UNSPEC)) {
return 0;
}
count++;
@@ -6336,12 +6354,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
iif, RTM_NEWROUTE,
NETLINK_CB(in_skb).portid,
- nlh->nlmsg_seq, 0);
+ nlh->nlmsg_seq, 0,
+ RTA_DEL_REASON_UNSPEC);
else
err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
&fl6.saddr, iif, RTM_NEWROUTE,
NETLINK_CB(in_skb).portid,
- nlh->nlmsg_seq, 0);
+ nlh->nlmsg_seq, 0,
+ RTA_DEL_REASON_UNSPEC);
} else {
err = -ENETUNREACH;
}
@@ -6358,7 +6378,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
}
void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
- unsigned int nlm_flags)
+ unsigned int nlm_flags, u8 del_reason)
{
struct net *net = info->nl_net;
struct sk_buff *skb;
@@ -6377,7 +6397,8 @@ void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
goto errout;
err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
- event, info->portid, seq, nlm_flags);
+ event, info->portid, seq, nlm_flags,
+ del_reason);
if (err < 0) {
kfree_skb(skb);
/* -EMSGSIZE implies needed space grew under us. */
@@ -6410,7 +6431,8 @@ void fib6_rt_update(struct net *net, struct fib6_info *rt,
goto errout;
err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
- RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
+ RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE,
+ RTA_DEL_REASON_UNSPEC);
if (err < 0) {
/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -6463,7 +6485,7 @@ void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
}
err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0,
- 0, 0);
+ 0, 0, RTA_DEL_REASON_UNSPEC);
if (err < 0) {
/* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 0/2] ipv6: report why a route was deleted in RTM_DELROUTE
From: Yuyang Huang @ 2026-07-17 22:23 UTC (permalink / raw)
To: Yuyang Huang
Cc: David S. Miller, Bobby Eshleman, Chris J Arges, Daniel Zahka,
David Ahern, David Wei, Dimitri Daskalakis, Donald Hunter,
Eric Dumazet, Gal Pressman, Ido Schimmel, Jakub Kicinski,
Paolo Abeni, Shuah Khan, Simon Horman, Stanislav Fomichev,
Willem de Bruijn, linux-kernel, linux-kselftest, netdev
When the kernel deletes an IPv6 route on its own, the RTM_DELROUTE
notification does not say why. User space cannot tell a route that
expired from one the router explicitly withdrew, yet the two call for
different reactions: an expired RA route means the router failed to
refresh it in time, which points at a misconfigured or unreliable
router and may warrant action such as disabling IPv6 on that network,
while a zero-lifetime withdrawal is normal, RFC-compliant operation.
Patch 1 adds RTA_DEL_REASON (u8) to RTM_DELROUTE notifications and
records the cause in the kernel-initiated IPv6 deletion paths:
RTA_DEL_REASON_EXPIRED for routes garbage collected after their
RTF_EXPIRES lifetime ran out, and RTA_DEL_REASON_RA_WITHDRAWN for
default routes, prefix routes and RFC 4191 route information routes
withdrawn by Router Advertisements. The rt-route Netlink spec is
extended with the attribute, the route notifications and their
multicast groups.
Only kernel-initiated deletions that user space cannot otherwise
explain are attributed. User-requested deletions are self-explanatory
to the requester, so they carry no reason; the UAPI documents that
absence and RTA_DEL_REASON_UNSPEC must be treated identically, which
keeps the door open for attributing more paths (nexthop removal
cascades, device removal) later.
Patch 2 adds selftests covering all three producer paths: a GC-expired
route, and a default route + PIO prefix route + RIO route advertised
and then withdrawn by hand-crafted RAs over a raw ICMPv6 socket (no
external RA tool needed), plus a check that user-requested deletions
carry no attribute. The notifications are decoded with YNL, which also
exercises the rt-route spec additions.
Yuyang Huang (2):
ipv6: report why a route was deleted in RTM_DELROUTE
selftests: net: verify RTA_DEL_REASON on route deletion
Documentation/netlink/specs/rt-route.yaml | 33 ++++
include/net/ip6_fib.h | 4 +-
include/net/ip6_route.h | 2 +
include/uapi/linux/rtnetlink.h | 17 ++
net/ipv6/addrconf.c | 3 +-
net/ipv6/ip6_fib.c | 19 +-
net/ipv6/ndisc.c | 7 +-
net/ipv6/route.c | 66 ++++---
.../testing/selftests/net/lib/py/__init__.py | 4 +-
tools/testing/selftests/net/lib/py/ynl.py | 7 +-
tools/testing/selftests/net/rtnetlink.py | 181 +++++++++++++++++-
11 files changed, 303 insertions(+), 40 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [REGRESSION][BISECTED] stmmac: suspend hangs since 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
From: tresonic @ 2026-07-17 22:23 UTC (permalink / raw)
To: Andrew Lunn; +Cc: netdev, regressions, rmk+kernel, kuba, Maxime Chevallier
In-Reply-To: <21068662-c44b-4266-b634-24e5eac8cd96@lunn.ch>
On 7/17/26 5:40 PM, Andrew Lunn wrote:
>> I tested the changes as you suggested and it seems DMA_CHAN_INTR_ENA_RPS in DMA_CHAN_INTR_ABNORMAL_4_10 broke it.
>> With this change on master suspend is working:
>>
>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>> index 43b036d4e95b..e907142c9ee2 100644
>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
>> @@ -121,7 +121,6 @@ static inline u32 dma_chanx_base_addr(const struct dwmac4_addrs *addrs,
>> DMA_CHAN_INTR_ENA_TIE)
>>
>> #define DMA_CHAN_INTR_ABNORMAL_4_10 (DMA_CHAN_INTR_ENA_AIE_4_10 | \
>> - DMA_CHAN_INTR_ENA_RPS | \
>> DMA_CHAN_INTR_ENA_RBU | \
>> DMA_CHAN_INTR_ENA_FBE)
>> /* DMA default interrupt mask for 4.10a */
>
> OK, that narrows it down a bit.
>
> I made the guess it is an interrupt storm during suspend, because
> interrupts are not disabled. DMA_CHAN_INTR_ABNORMAL_4_10 is written to
> hardware in dwmac410_dma_init_channel(). However, i don't see anywhere
> these interrupts are clear? struct stmmac_dma_ops has an init_chan
> operation, but there is no opposite operation to reset a channel?
> There is however disable_dma_irq. Maybe DMA_CHAN_INTR_ENA_RPS can be
> cleared there?
>
> But i'm also having trouble finding where dwmac4_disable_dma_irq() is
> actually called. It is called from stmmac_disable_rx_queue() and
> stmmac_disable_tx_queue() but they only seem to be used in the XDP
> code.
>
> Are the DMAs being stopped during suspend? I don't know this driver
> well enough to answer that.
>
> Can you scatter some printk() around and see what actually happens on
> suspend. See if you can find somewhere to disable that one interrupt?
>
> Andrew
It seems your assumptions are correct. If i do
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index a0249715fafa..487efc746b20 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -129,6 +129,8 @@ void dwmac4_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
u32 value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
+ value &= ~DMA_CHAN_INTR_ENA_RPS;
+
if (rx)
value &= ~DMA_CHAN_INTR_ENA_RIE;
if (tx)
I can suspend and resume!
Could it be sufficient to just do this and reenable on dwmac4_enable_dma_irq?
tresonic
^ permalink raw reply related
* Re: [PATCH net-next v6 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-17 22:12 UTC (permalink / raw)
To: Mieczyslaw Nalewaj
Cc: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux, luizluca, maxime.chevallier, kuncy7, netdev, linux-kernel
In-Reply-To: <e252cb3c-bfb2-4d62-aec7-e04b9aafe223@yahoo.com>
Hi Mieczyslaw,
On 7/16/2026 8:40 AM, Mieczyslaw Nalewaj wrote:
[...]
> For verification on real hardware:
>
> The SGMII/HSGMII link on RTL8367S (and compatible RTL8365MB-VC) can be
> intermittently unstable after cold boot, manifesting as egress stalls,
> CRC errors or complete packet loss on the SerDes-attached CPU port.
> This has been observed as a non-deterministic failure depending on the
> PLL lock state at power-on.
Where has this been observed? The RTL8365MB-VC cannot be affected:
it has no SGMII/HSGMII-capable external interface at all - its
external interfaces are MII/TMII/RMII/RGMII only, which is what the
driver's chip info table reflects.
The only cold boot observations reported against this series are
Stanisław's, and his results elsewhere in this thread now cover
exactly the two changes proposed here: a test image carrying both
the ~98 ms delay and the 0x060C-0x060F writes still came up with
the trunk degraded after a multi-hour power-off. What did recover
it, immediately and without a reboot, was a full re-probe of the
switch - the GPIO hardware reset plus the complete chip init -
while everything that resets only the SerDes left the bad state
in place. Whatever his unit is sensitive to after a long
power-off, it is not addressed by either proposed change, and it
lives in the full init sequence rather than in
rtl8365mb_pcs_config().
> Reverse-engineering of the vendor DW8051 firmware blob (Sgmii_Init[])
> shows that the vendor driver performs two critical steps after the
> SerDes data-path reset (BMCR DPRST_PHASE2) which were missing from the
> Linux driver:
>
> 1. A ~98 ms delay to let the SerDes PLL fully lock before any further
> register access. The vendor firmware uses a timer interrupt to count
> this delay; without it the analog front-end may still be settling.
The Sgmii_Init[] image shipped in the vendor's published U-Boot
sources for my board (1233 bytes, loaded into the 8051 through the
register window at 0xE000) does contain a one-shot startup wait
counted by the timer 0 interrupt: the ISR increments 16-bit tick
counters in the 8051's XDATA RAM, and the main flow busy-waits
until one of them reaches 98 (0x62), then latches a flag so the
wait never runs again.
But it sits on the other side of the data-path reset from where
your patch places it. The first thing the image does once the wait
expires is call the routine that performs the SerDes bring-up: the
un-reset (0x7106 to SerDes register 3 through the indirect access
registers), then the BMCR writes 0x1401 and 0x1403 - the same
data-path reset sequence Luiz described during the v2 review, which
pcs_config() already implements. Nothing in the image waits after
the BMCR writes. The wait is the firmware pacing its own startup
before it first touches the SerDes, which makes sense for an
autonomous microcontroller that the loader releases in the middle
of the bring-up; it is not a post-reset PLL lock delay.
> 2. Writing a "Local Jam Table" calibration vector to internal ASIC
> registers 0x060C-0x060F (values 0x83, 0xAA, 0x7E, 0x80). These
> registers configure the SerDes analog equalizer and DC-offset and
> are not exposed through the normal SDS_INDACS window. Omitting them
> leaves the analog front-end in an uncalibrated state.
Nothing in the image supports this:
- No instruction in the image addresses 0x060C-0x060F, by any
addressing form: immediate DPTR load, split DPH/DPL load, or the
register-pair arguments the image passes to its write helpers.
- The only XDATA accesses in that neighborhood are the image's own
tick counters and flags at 0x0624-0x062C - the ones its timer ISR
increments. That is plain 8051 work RAM, not switch registers.
- The byte sequence 0x83 0xAA 0x7E 0x80 occurs nowhere in the image
as data, in either order. 0xAA and 0x7E appear only as immediate
operands of register loads, and 0x83/0x80 only as bytes of the
instruction stream. A reading of the byte stream that slips out
of alignment with instruction boundaries could produce exactly
such a list.
- In the switch register space - which is what your proposed
regmap_write()s would actually hit - the vendor register headers
name 0x060C-0x060F as the ACL rule template 3 control registers.
The patch as posted would clobber an ACL template with values
that mean nothing there, and would not touch the SerDes.
For the record, on my RTL8367S hardware running exactly this code,
the HSGMII trunk has been up for over a week carrying several
hundred gigabytes with zero CRC/FCS/symbol errors on every port,
and cold boots throughout the development of this series never
produced the instability described above.
So I don't think either change is supportable: the sleep is placed
where the firmware has none, the register writes target unrelated
ACL registers, and both have now been tested together on the
affected unit without improvement. For the same reasons I don't
expect a re-run of the cold-soak test with only these two changes
to tell us anything: the writes cannot reach the SerDes, and the
delay exists in the firmware only as a one-shot wait before the
data-path reset the driver already performs. Stanisław's unit
reproduces the bad state reliably, which makes it a good bench for
instrumenting the early init path; that investigation is follow-up
material rather than part of this series.
Best regards,
Johan
^ permalink raw reply
* Re: [PATCH RFC 3/8] clk: sunxi-ng: a733: Add PRCM CCU
From: Andre Przywara @ 2026-07-17 21:57 UTC (permalink / raw)
To: Enzo Adriano
Cc: Junhui Liu, Brian Masney, Michael Turquette, Stephen Boyd,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Philipp Zabel,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran,
linux-clk, linux-arm-kernel, linux-sunxi, linux-riscv, devicetree,
netdev, linux-kernel
In-Reply-To: <20260702165944.828550-1-enzo.adriano.code@gmail.com>
On Thu, 2 Jul 2026 12:59:44 -0400
Enzo Adriano <enzo.adriano.code@gmail.com> wrote:
Hi Enzo,
> Register check for the PRCM driver against the public A733 User Manual
> V0.92, chapter 4.2.5: all 41 entries I could extract (13 reset-map
> entries, 18 bus gates, 10 mod/mux clocks) match the manual's register
> offsets and bit positions. No discrepancies found in this patch.
>
> For what it's worth, we have also exercised the R-domain at runtime on a
> Radxa Cubie A7S (r-ahb/r-apb0 rates with live consumers on r-pinctrl,
> r-rtc and the PPU, bus-r-cpucfg via its critical flag), with no
> misbehavior attributable to the PRCM model.
many thanks for doing this, but please add proper tags to your replies,
otherwise this won't be carried over to new revision, and your work
gets lost in the cracks - as it happened this time.
So you now would need to redo the review on v2. I'd suggest to compare
the patches between their RFC and v2 version, and if you agree that the
changes don't touch anything you checked (register offsets, bit
positions), then you can just (re-)post your review with the proper
tags - and a brief list of what you checked, as you did in the RFC
reply.
Thanks,
Andre
^ permalink raw reply
* Re: [PATCH] dt-bindings: net: nvidia,tegra234-mgbe: Add missing properties
From: Rob Herring @ 2026-07-17 21:27 UTC (permalink / raw)
To: Thierry Reding
Cc: Krzysztof Kozlowski, Conor Dooley, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jonathan Hunter,
netdev, devicetree, linux-tegra, linux-kernel
In-Reply-To: <20260716212001.989872-1-thierry.reding@kernel.org>
On Thu, Jul 16, 2026 at 11:20:01PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Being a DWMAC derivative, the Tegra234 MGBE supports AXI configuration
> nodes named stmmac-axi-config and phandle references to them using the
> snps,axi-config property.
>
> While at it, add the 10gbase-r PHY mode.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> This gets rid of the remaining warnings on half of the Tegra234 boards.
>
> .../devicetree/bindings/net/nvidia,tegra234-mgbe.yaml | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
> index 215f14d1897d..dc897e312c55 100644
> --- a/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
> +++ b/Documentation/devicetree/bindings/net/nvidia,tegra234-mgbe.yaml
> @@ -81,8 +81,9 @@ properties:
> phy-mode:
> contains:
> enum:
> - - usxgmii
> - 10gbase-kr
> + - 10gbase-r
> + - usxgmii
>
> mdio:
> $ref: mdio.yaml#
> @@ -90,6 +91,12 @@ properties:
> description:
> Optional node for embedded MDIO controller.
>
> + snps,axi-config:
> + $ref: snps,dwmac.yaml#/properties/snps,axi-config
> +
> + stmmac-axi-config:
> + $ref: snps,dwmac.yaml#/properties/stmmac-axi-config
You should be referencing the whole snps,dwmac.yaml.
Rob
^ permalink raw reply
* Re: [PATCH net v3] net: dpaa: fix mode setting
From: Sean Anderson @ 2026-07-17 21:10 UTC (permalink / raw)
To: Michael Walle, Madalin Bucur, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Christian Zigotzky
Cc: netdev, linux-kernel, linuxppc-dev
In-Reply-To: <20260717132401.2653252-1-mwalle@kernel.org>
On 7/17/26 09:20, Michael Walle wrote:
> Before converting to the phylink interface, the init function would have
> set a non-reserved I/F mode in the maccfg2 register. After converting to
> phylink, 0 is written as mode, which is a reserved value (although it's
> the hardware default). Without a valid mode, a SGMII link is never
> established between the MAC and the PHY and thus .link_up() is never
> called which could set the correct mode according to the actual speed.
>
> Fix it by setting the maximum speed of the phy_interface_t in use in
> .mac_config() - just like the driver did before the phylink conversion.
>
> Fixes: 5d93cfcf7360 ("net: dpaa: Convert to phylink")
> Suggested-by: Sean Anderson <sean.anderson@linux.dev>
> Signed-off-by: Michael Walle <mwalle@kernel.org>
> ---
> I didn't grab Sean's Rb tag as this is somewhat different.
>
> Changes in v3:
> - keep the mode setting also in .adjust_link().
> - reword the commit message, to be (hopefully) more precise
> - Link to v2: https://lore.kernel.org/r/20260710143430.2276141-1-mwalle@kernel.org/
>
> Changes in v2:
> - the setting is/was based on the maximum speed, not the current
> speed. thus, move the setting into mac_config().
> - Link to v1: https://lore.kernel.org/r/20260706121011.1948906-1-mwalle@kernel.org/
>
> .../net/ethernet/freescale/fman/fman_dtsec.c | 17 ++++++++++++-----
> 1 file changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
> index fe35703c509e..b8d70c0ecb6c 100644
> --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
> +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
> @@ -900,22 +900,28 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
> {
> struct mac_device *mac_dev = fman_config_to_mac(config);
> struct dtsec_regs __iomem *regs = mac_dev->fman_mac->regs;
> - u32 tmp;
> + u32 ecntrl, maccfg2;
> +
> + maccfg2 = ioread32be(®s->maccfg2);
> + maccfg2 &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);
>
> switch (state->interface) {
> case PHY_INTERFACE_MODE_RMII:
> - tmp = DTSEC_ECNTRL_RMM;
> + ecntrl = DTSEC_ECNTRL_RMM;
> + maccfg2 |= MACCFG2_NIBBLE_MODE;
> break;
> case PHY_INTERFACE_MODE_RGMII:
> case PHY_INTERFACE_MODE_RGMII_ID:
> case PHY_INTERFACE_MODE_RGMII_RXID:
> case PHY_INTERFACE_MODE_RGMII_TXID:
> - tmp = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
> + ecntrl = DTSEC_ECNTRL_GMIIM | DTSEC_ECNTRL_RPM;
> + maccfg2 |= MACCFG2_BYTE_MODE;
> break;
> case PHY_INTERFACE_MODE_SGMII:
> case PHY_INTERFACE_MODE_1000BASEX:
> case PHY_INTERFACE_MODE_2500BASEX:
> - tmp = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
> + ecntrl = DTSEC_ECNTRL_TBIM | DTSEC_ECNTRL_SGMIIM;
> + maccfg2 |= MACCFG2_BYTE_MODE;
> break;
> default:
> dev_warn(mac_dev->dev, "cannot configure dTSEC for %s\n",
> @@ -923,7 +929,8 @@ static void dtsec_mac_config(struct phylink_config *config, unsigned int mode,
> return;
> }
>
> - iowrite32be(tmp, ®s->ecntrl);
> + iowrite32be(ecntrl, ®s->ecntrl);
> + iowrite32be(maccfg2, ®s->maccfg2);
> }
>
> static void dtsec_link_up(struct phylink_config *config, struct phy_device *phy,
Reviewed-by: Sean Anderson <sean.anderson@linux.dev>
Christian, can you test this patch with ethernet at 100/1G speed if you still have
access to those P5020/P5040 boards?
https://lore.kernel.org/all/0bfc8f3d-cb62-25f4-2590-ff424adbe48a@xenosoft.de/
^ permalink raw reply
* Re: [PATCH net v2] net: dpaa: fix mode setting
From: Sean Anderson @ 2026-07-17 21:06 UTC (permalink / raw)
To: Michael Walle, Paolo Abeni, Madalin Bucur, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Russell King
Cc: netdev, linux-kernel
In-Reply-To: <DK0V3M8CTZFU.1T6GJTFM5DJ4M@kernel.org>
On 7/17/26 08:57, Michael Walle wrote:
> On Fri Jul 17, 2026 at 2:44 PM CEST, Paolo Abeni wrote:
>> On 7/17/26 2:05 PM, Sean Anderson wrote:
>>> On 7/17/26 07:11, Paolo Abeni wrote:
>>>> On 7/17/26 12:13 PM, Michael Walle wrote:
>>>>> I've seen that, but.. that was the actual change between v1 and v2
>>>>> as suggested by Sean. It does not depend on the actual link speed,
>>>>> but the maximum link speed. So it is not relevant if the link
>>>>> negotiates to a slower speed or not. At least that now matches the
>>>>> behavior prior to the phylink conversion. If that was working -
>>>>> that I can't tell you.
>>>>
>>>> I'm sorry, following all cross revision discussion is a bit hard here.
>>>>
>>>> I don't understand if the 'link never established' is specific of your
>>>> board, or it a constant with this driver. Could you please clarify?
>
> I don't think it's related to my board, but rather to the link being
> a SGMII one on my board. I assume Sean had an RGMII link while
> converting the driver (?) and moreover, that it worked correctly
> there :)
I didn't have a link at all! I only personally tested this conversion on
layerscape (memac). I'm pleased to learn that there were only two major
bugs, but a little concerned that it took four years to discover one of them.
>>> I think we should set byte/nibble mode in link_up as well as mac_config. That
>>> should fix the problem where the link never comes up (possibly broken since
>>> the phylink conversion). I think it's unlikely that this config is supposed
>>> to match the phy interface speed and not the link speed, but you never know...
>> FTR, the above does not answer my question ;)
>>
>> Still I think (mostly guess) bringing the mode initialization in both
>> link_up and mac_config should be safer (that is, I agree with the above
>> plan).
>
> I agree. I looked at the original driver and there adjust_link()
> will also select between byte and nibble mode. I'll prepare a v3.
>
> Sorry, I misunderstood Sean's intention/suggestion, thanks for
> pushing on this. FWIW, I also tried with nibble mode as the initial
> configuration on my board and link_up() is getting called. Whereas
> if it's 0 (as in being documented as "reserved", though it's the
> default), .link_up() is never called. Thus, it seems that it just
> has to be any valid setting until link_up() will set the correct
> value.
>
> -michael
^ permalink raw reply
* Re: [PATCH 2/2] padata: Remove serialized job support
From: Daniel Jordan @ 2026-07-17 20:32 UTC (permalink / raw)
To: Eric Biggers
Cc: linux-crypto, Herbert Xu, netdev, linux-kernel, Steffen Klassert,
Thomas Huth
In-Reply-To: <20260713223234.24812-3-ebiggers@kernel.org>
Hi Eric,
On Mon, Jul 13, 2026 at 06:32:34PM -0400, Eric Biggers wrote:
> Now that pcrypt has been removed, also remove all the code in padata
> whose only user was pcrypt.
>
> Cc: Steffen Klassert <steffen.klassert@secunet.com>
> Signed-off-by: Eric Biggers <ebiggers@kernel.org>
I don't know of any pcrypt users these days. This looks fine.
Reviewed-by: Daniel Jordan <daniel.m.jordan@oracle.com>
^ permalink raw reply
* Re: [PATCH v2] idpf: disable PCIe PTM on probe failure and removal
From: Tony Nguyen @ 2026-07-17 20:21 UTC (permalink / raw)
To: Myeonghun Pak, Przemek Kitszel, intel-wired-lan
Cc: Milena Olech, Emil Tantilov, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
Ijae Kim
In-Reply-To: <20260715074342.49281-1-mhun512@gmail.com>
On 7/15/2026 12:43 AM, Myeonghun Pak wrote:
> idpf_probe() enables PCIe Precision Time Measurement with
> pci_enable_ptm(pdev, NULL), which programs the PTM control bits and sets
> pdev->ptm_enabled when the bus/controller supports it.
>
> If a later probe step fails, the error path releases the allocated
> workqueues and adapter memory without disabling PTM. The remove path has
> the same imbalance when a successfully probed device is detached. In
> both cases, the PCI core's software PTM state and the device's PTM control
> bits remain set with no bound driver.
>
> Add pci_disable_ptm() to the common probe unwind after the PTM enable and
> to idpf_remove(). pci_disable_ptm() is a no-op when PTM was not enabled,
> so the non-fatal pci_enable_ptm() failure remains safe.
> pcim_enable_device() only arranges for pci_disable_device() and does not
> undo the PTM enable.
>
> Fixes: 8d5e12c5921c ("idpf: add initial PTP support")
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> ---
> Changes in v2:
> - Disable PTM in the probe error path, as requested by Emil Tantilov.
>
> drivers/net/ethernet/intel/idpf/idpf_main.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
> index 0dd741dcfc..f08ce13c20 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_main.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> @@ -159,6 +159,7 @@ static void idpf_remove(struct pci_dev *pdev)
> mutex_destroy(&adapter->queue_lock);
> mutex_destroy(&adapter->vc_buf_lock);
>
> + pci_disable_ptm(pdev);
Sashiko says:
If pci_enable_ptm() failed earlier during idpf_probe() because it was
unsupported by the bus or controller, does this unconditional call to
pci_disable_ptm() corrupt the upstream PTM enable counts?
Looking at pci_disable_ptm(), it does not check if the local device was
successfully enabled. It climbs the PCIe tree and erroneously decrements
atomic counters like ptm_enable_cnt on parent switches.
Could this eventually break PTM for other downstream devices sharing the
same
switch if a parent's count is incorrectly decremented to -1?
> pci_set_drvdata(pdev, NULL);
> kfree(adapter);
> }
> @@ -266,7 +267,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> if (err) {
> pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err));
>
> - goto err_free;
> + goto err_disable_ptm;
> }
>
> pci_set_master(pdev);
> @@ -279,7 +280,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> if (!adapter->init_wq) {
> dev_err(dev, "Failed to allocate init workqueue\n");
> err = -ENOMEM;
> - goto err_free;
> + goto err_disable_ptm;
> }
>
> adapter->serv_wq = alloc_workqueue("%s-%s-service",
> @@ -366,6 +367,8 @@ err_mbx_wq_alloc:
> destroy_workqueue(adapter->serv_wq);
> err_serv_wq_alloc:
> destroy_workqueue(adapter->init_wq);
> +err_disable_ptm:
> + pci_disable_ptm(pdev);
Like the issue in idpf_remove(), does this error path unconditionally
disable
PTM even if the earlier enable attempt failed?
If a subsequent probe step fails (such as dma_set_mask_and_coherent or
workqueue allocation), this path is taken regardless of whether
pci_enable_ptm() succeeded.
Would it make sense to track the PTM state in the adapter struct, and only
call pci_disable_ptm() when it was successfully enabled?
> err_free:
> kfree(adapter);
> return err;
^ permalink raw reply
* Re: [Patch net-next v3] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Krzysztof Kozlowski @ 2026-07-17 20:19 UTC (permalink / raw)
To: Kyle Switch, Frank.Sae, andrew, hkallweit1, linux, davem,
edumazet, kuba, pabeni
Cc: netdev, linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, jie.han
In-Reply-To: <20260717054807.1321374-1-kyle.switch@motor-comm.com>
On 17/07/2026 07:48, Kyle Switch wrote:
> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
> 2.5G/1000M/100M/10M speed.
>
> Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
Please run scripts/checkpatch.pl on the patches and fix reported
warnings. After that, run also 'scripts/checkpatch.pl --strict' on the
patches and (probably) fix more warnings. Some warnings can be ignored,
especially from --strict run, but the code here looks like it needs a
fix. Feel free to get in touch if the warning is not clear.
Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC (and consider --no-git-fallback argument, so you will
not CC people just because they made one commit years ago). It might
happen, that command when run on an older kernel, gives you outdated
entries. Therefore please be sure you base your patches on recent Linux
kernel.
Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline) or work on fork of kernel
(don't, instead use mainline). Just use b4 and everything should be
fine, although remember about `b4 prep --auto-to-cc` if you added new
patches to the patchset.
> ---
>
> changes in v3:
> 1. Using common apis defined in phy_package.c to handle shared top
> extend register space.
> 2. Add dts demo in motorcomm,yt8xxx.yaml.
> 3. Fix unnecessary redundant judgments.
> 4. Fix BMCR registers operation using magic number.
> 5. Rename funtion based on its approximate functionality.
>
> changes in v2:
> 1. Remove duplicate code and replace it with existing api.
>
> .../bindings/net/motorcomm,yt8xxx.yaml | 30 +
> drivers/net/phy/motorcomm.c | 1808 ++++++++++++++++-
> 2 files changed, 1750 insertions(+), 88 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
> index 26688e2302ea..e7592468f658 100644
> --- a/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
> +++ b/Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
> @@ -149,3 +149,33 @@ examples:
> motorcomm,auto-sleep-disabled;
> };
> };
> + - |
> + mdio {
> + #address-cells = <1>;
> + #size-cells = <0>;
I don't understand why you are doing this. Anyway, please read the
documents I linked and maybe there is some explanation for that change.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [GIT PULL] Networking for v7.2-rc4
From: pr-tracker-bot @ 2026-07-17 20:17 UTC (permalink / raw)
To: Paolo Abeni; +Cc: torvalds, kuba, davem, netdev, linux-kernel
In-Reply-To: <20260717132610.363857-1-pabeni@redhat.com>
The pull request you sent on Fri, 17 Jul 2026 15:26:10 +0200:
> git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git net-7.2-rc4
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/e13caf1c26587434f0b768193100440939c0fb91
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* [PATCH net-next v4 4/4] selftests: net: hsr: add PRP RedBox test
From: Xin Xie @ 2026-07-17 20:14 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, wsa+renesas, jie.luo, shuah,
linux-kernel, linux-kselftest, fmancera, bigeasy, fmaurer,
luka.gejak, Xin Xie
In-Reply-To: <20260717201457.54-1-xiexinet@gmail.com>
Add a kselftest that builds a PRP RedBox (interlink) with a SAN behind the
interlink and a peer DANP, and checks bidirectional unicast across the
interlink, preservation of the SAN source MAC on the PRP network, and that
the proxy-announce supervision frame carries the RedBox-MAC TLV (Type 30)
terminated by an EOT marker. It reuses the hsr_common.sh / lib.sh helpers
and skips cleanly on a kernel or iproute2 without PRP interlink support.
The background ping is killed by its exact PID: ip netns exec does not
isolate the PID namespace, so a pattern-based pkill could hit unrelated
processes on the host.
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
tools/testing/selftests/net/hsr/Makefile | 1 +
.../selftests/net/hsr/hsr_prp_redbox.sh | 99 +++++++++++++++++++
2 files changed, 100 insertions(+)
create mode 100755 tools/testing/selftests/net/hsr/hsr_prp_redbox.sh
diff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile
index 31fb9326cf5..2150e487ac7 100644
--- a/tools/testing/selftests/net/hsr/Makefile
+++ b/tools/testing/selftests/net/hsr/Makefile
@@ -4,6 +4,7 @@ top_srcdir = ../../../../..
TEST_PROGS := \
hsr_ping.sh \
+ hsr_prp_redbox.sh \
hsr_redbox.sh \
link_faults.sh \
prp_ping.sh \
diff --git a/tools/testing/selftests/net/hsr/hsr_prp_redbox.sh b/tools/testing/selftests/net/hsr/hsr_prp_redbox.sh
new file mode 100755
index 00000000000..479c892225b
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/hsr_prp_redbox.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test a PRP RedBox (PRP-SAN): a SAN that sits behind the interlink port must
+# reach, and be reached by, a peer DANP on the PRP network with its own MAC
+# preserved on the wire, and the RedBox must announce the SAN with a RedBox-MAC
+# TLV (terminated by an EOT marker) in its PRP supervision frames.
+#
+# RB PRP RedBox: prp0 over rb_a/rb_b (LAN A/B) + interlink rb_il
+# PEER peer DANP : prp0 over pe_a/pe_b, 100.64.0.2
+# SAN SAN : san_il, own MAC, 100.64.0.51 (behind the interlink)
+
+ipv6=false
+
+source ./hsr_common.sh
+
+check_prerequisites
+
+if ! command -v tcpdump >/dev/null 2>&1; then
+ echo "SKIP: This test requires tcpdump"
+ exit $ksft_skip
+fi
+
+if ! ip link help hsr 2>&1 | grep -q interlink; then
+ echo "SKIP: iproute2 too old (no hsr interlink support)"
+ exit $ksft_skip
+fi
+
+setup_ns RB PEER SAN
+trap 'cleanup_ns "$RB" "$PEER" "$SAN"' EXIT
+
+ip link add rb_a netns "$RB" type veth peer name pe_a netns "$PEER"
+ip link add rb_b netns "$RB" type veth peer name pe_b netns "$PEER"
+ip link add rb_il netns "$RB" type veth peer name san_il netns "$SAN"
+
+ip -n "$RB" link set rb_a up
+ip -n "$RB" link set rb_b up
+ip -n "$RB" link set rb_il up
+ip -n "$PEER" link set pe_a up
+ip -n "$PEER" link set pe_b up
+ip -n "$SAN" link set san_il up
+ip -n "$SAN" addr add 100.64.0.51/24 dev san_il
+
+# Feature gate: PRP interlink (RedBox) creation. A kernel without PRP RedBox
+# support rejects this with -EINVAL, so SKIP rather than FAIL.
+if ! ip -n "$RB" link add name prp0 type hsr slave1 rb_a slave2 rb_b \
+ interlink rb_il proto 1 2>/dev/null; then
+ echo "SKIP: kernel without PRP RedBox (interlink) support"
+ exit $ksft_skip
+fi
+ip -n "$RB" link set prp0 up
+ip -n "$PEER" link add name prp0 type hsr slave1 pe_a slave2 pe_b proto 1
+ip -n "$PEER" link set prp0 up
+ip -n "$PEER" addr add 100.64.0.2/24 dev prp0
+sleep 1
+
+san_mac=$(ip -n "$SAN" -br link show san_il | awk '{print $3}')
+rb_mac=$(ip -n "$RB" -br link show rb_il | awk '{print $3}')
+
+# Bidirectional unicast across the interlink.
+do_ping "$PEER" 100.64.0.51
+do_ping "$SAN" 100.64.0.2
+stop_if_error "PRP RedBox bidirectional unicast failed"
+
+# The SAN source MAC must be preserved on the PRP network, not laundered to the
+# RedBox MAC: the peer resolves the SAN IP to the SAN's own MAC.
+neigh=$(ip -n "$PEER" neigh show 100.64.0.51 | awk '{print $5}')
+if [ "$neigh" != "$san_mac" ]; then
+ echo "SAN MAC preservation [ FAIL ]: peer resolved 100.64.0.51 to" \
+ "'$neigh', expected $san_mac" 1>&2
+ ret=1
+fi
+stop_if_error "SAN MAC not preserved on the PRP network"
+
+# The proxy-announce supervision frame must carry, in order, the life-check TLV
+# (type 0x14, len 6) + MacAddressA == SAN MAC + the RedBox-MAC TLV (type 0x1e,
+# len 6) + MacAddressRedBox == RedBox MAC + the EOT marker (0x0000).
+ip netns exec "$SAN" ping -i 0.2 -q 100.64.0.2 >/dev/null 2>&1 &
+ping_pid=$!
+cap=$(ip netns exec "$PEER" timeout 5 tcpdump -i pe_a -nn -x \
+ "ether proto 0x88fb and ether src $rb_mac" 2>/dev/null || true)
+kill "$ping_pid" 2>/dev/null || true
+wait "$ping_pid" 2>/dev/null || true
+
+san_hex=$(echo "$san_mac" | tr -d ':')
+rb_hex=$(echo "$rb_mac" | tr -d ':')
+# Reassemble contiguous frame hex: drop the "0x0010:" offset labels and spaces.
+frame_hex=$(echo "$cap" | awk '/^[[:space:]]*0x[0-9a-f]+:/ {
+ sub(/^[[:space:]]*0x[0-9a-f]+:[[:space:]]*/, "");
+ gsub(/ /, ""); printf "%s", $0 }')
+if ! echo "$frame_hex" | grep -q "1406${san_hex}1e06${rb_hex}0000"; then
+ echo "supervision RedBox-MAC TLV [ FAIL ]: missing SAN MAC, Type-30" \
+ "payload, or EOT" 1>&2
+ ret=1
+fi
+stop_if_error "PRP RedBox supervision RedBox-MAC TLV/EOT check failed"
+
+echo "INFO: PRP RedBox (PRP-SAN) conformance checks passed"
+exit $ret
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v4 3/4] net: hsr: allow PRP RedBox (interlink) creation
From: Xin Xie @ 2026-07-17 20:14 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, wsa+renesas, jie.luo, shuah,
linux-kernel, linux-kselftest, fmancera, bigeasy, fmaurer,
luka.gejak, Xin Xie
In-Reply-To: <20260717201457.54-1-xiexinet@gmail.com>
With the PRP interlink datapath, duplicate discard and supervision support
in place, a PRP device can act as a RedBox. Remove the rtnetlink rejection
of "type hsr ... interlink <dev> proto 1"; the feature is implemented
unconditionally by the preceding patches.
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
net/hsr/hsr_netlink.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index 8099f2069a7..88940e8014b 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -121,14 +121,8 @@ static int hsr_newlink(struct net_device *dev,
}
}
- if (proto == HSR_PROTOCOL_PRP) {
+ if (proto == HSR_PROTOCOL_PRP)
proto_version = PRP_V1;
- if (interlink) {
- NL_SET_ERR_MSG_MOD(extack,
- "Interlink only works with HSR");
- return -EINVAL;
- }
- }
return hsr_dev_finalize(dev, link, interlink, multicast_spec,
proto_version, extack);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v4 2/4] net: hsr: emit RedBox-MAC TLV in PRP RedBox supervision frames
From: Xin Xie @ 2026-07-17 20:14 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, wsa+renesas, jie.luo, shuah,
linux-kernel, linux-kselftest, fmancera, bigeasy, fmaurer,
luka.gejak, Xin Xie, Hangbin Liu
In-Reply-To: <20260717201457.54-1-xiexinet@gmail.com>
A PRP RedBox must announce the SANs it proxies so peers populate their
proxy node tables. The proxy-announce machinery (hsr_proxy_announce(),
armed via hsr->redbox) already iterates proxy_node_db under RCU and calls
send_sv_frame() once per SAN, but the PRP sender emitted neither the
announced SAN MAC nor the RedBox-MAC TLV that IEC 62439-3 requires.
Extend send_prp_supervision_frame() so that, for a proxy-announce
(identified by the interlink port, an O(1) test), the frame carries the
proxied SAN MAC as MacAddressA followed by the RedBox-MAC TLV (Type 30)
and an explicit End-of-TLV marker before padding.
hsr_get_node() must also accept the reinjected proxy-announce: a PRP
supervision frame is an untagged ETH_P_PRP frame (mac_len == ETH_HLEN, the
RCT is appended only on egress) sourced from macaddress_redbox, which is
never learned from data. Exempt only PRP supervision frames from the
hsr_ethhdr length guard; HSR (ETH_P_HSR) supervision is front-tagged and
keeps the original guard, so HSR malformed-frame filtering is unchanged.
Also align macaddress_redbox so that ether_addr_copy() and
ether_addr_equal() on it are safe on architectures without efficient
unaligned access.
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
net/hsr/hsr_device.c | 33 ++++++++++++++++++++++++++++++---
net/hsr/hsr_framereg.c | 13 +++++++++++--
net/hsr/hsr_main.h | 2 +-
3 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 5af491ed2b7..0973f9a94f4 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -372,10 +372,21 @@ static void send_prp_supervision_frame(struct hsr_port *master,
{
struct hsr_priv *hsr = master->hsr;
struct hsr_sup_payload *hsr_sp;
+ struct hsr_sup_tlv *hsr_stlv;
struct hsr_sup_tag *hsr_stag;
struct sk_buff *skb;
+ bool redbox_proxy;
+ int extra = 0;
+
+ redbox_proxy = hsr->redbox && master->type == HSR_PT_INTERLINK;
+
+ /* A proxy-announce carries a RedBox-MAC TLV and an EOT marker. */
+ if (redbox_proxy)
+ extra = sizeof(struct hsr_sup_tlv) +
+ sizeof(struct hsr_sup_payload) +
+ sizeof(struct hsr_sup_tlv);
- skb = hsr_init_skb(master, 0);
+ skb = hsr_init_skb(master, extra);
if (!skb) {
netdev_warn_once(master->dev, "PRP: Could not send supervision frame\n");
return;
@@ -393,9 +404,25 @@ static void send_prp_supervision_frame(struct hsr_port *master,
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
hsr_stag->tlv.HSR_TLV_length = sizeof(struct hsr_sup_payload);
- /* Payload: MacAddressA */
+ /* Payload: MacAddressA, the announced node. */
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
- ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
+ ether_addr_copy(hsr_sp->macaddress_A, addr);
+
+ /* Proxy-announce: append the RedBox-MAC TLV (Type 30) and an explicit
+ * EOT to terminate the TLV chain before zero padding.
+ */
+ if (redbox_proxy) {
+ hsr_stlv = skb_put(skb, sizeof(struct hsr_sup_tlv));
+ hsr_stlv->HSR_TLV_type = PRP_TLV_REDBOX_MAC;
+ hsr_stlv->HSR_TLV_length = sizeof(struct hsr_sup_payload);
+
+ hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
+ ether_addr_copy(hsr_sp->macaddress_A, hsr->macaddress_redbox);
+
+ hsr_stlv = skb_put(skb, sizeof(struct hsr_sup_tlv));
+ hsr_stlv->HSR_TLV_type = HSR_TLV_EOT;
+ hsr_stlv->HSR_TLV_length = 0;
+ }
if (skb_put_padto(skb, ETH_ZLEN)) {
spin_unlock_bh(&hsr->seqnr_lock);
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 8f708b6e6c3..b3b106be692 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -293,8 +293,17 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
*/
if (ethhdr->h_proto == htons(ETH_P_PRP) ||
ethhdr->h_proto == htons(ETH_P_HSR)) {
- /* Check if skb contains hsr_ethhdr */
- if (skb->mac_len < sizeof(struct hsr_ethhdr))
+ bool prp_sup;
+
+ /* A PRP supervision frame is an untagged ETH_P_PRP frame
+ * (mac_len == ETH_HLEN); its RCT is appended only on egress.
+ * HSR (ETH_P_HSR) supervision is front-tagged and still must
+ * contain a struct hsr_ethhdr.
+ */
+ prp_sup = hsr->prot_version == PRP_V1 &&
+ ethhdr->h_proto == htons(ETH_P_PRP) && is_sup;
+
+ if (!prp_sup && skb->mac_len < sizeof(struct hsr_ethhdr))
return NULL;
} else {
rct = skb_get_PRP_rct(skb);
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
index 134e4f3fff6..53e95bae0ee 100644
--- a/net/hsr/hsr_main.h
+++ b/net/hsr/hsr_main.h
@@ -211,7 +211,7 @@ struct hsr_priv {
*/
bool fwd_offloaded; /* Forwarding offloaded to HW */
bool redbox; /* Device supports HSR RedBox */
- unsigned char macaddress_redbox[ETH_ALEN];
+ unsigned char macaddress_redbox[ETH_ALEN] __aligned(sizeof(u16));
unsigned char sup_multicast_addr[ETH_ALEN] __aligned(sizeof(u16));
/* Align to u16 boundary to avoid unaligned access
* in ether_addr_equal
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v4 1/4] net: hsr: add PRP interlink (RedBox) datapath and duplicate discard
From: Xin Xie @ 2026-07-17 20:14 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, wsa+renesas, jie.luo, shuah,
linux-kernel, linux-kselftest, fmancera, bigeasy, fmaurer,
luka.gejak, Xin Xie
In-Reply-To: <20260717201457.54-1-xiexinet@gmail.com>
A PRP RedBox proxies SANs that sit behind an interlink port: their frames
must reach the PRP network with the SAN source MAC preserved, and PRP
unicast must be steered between the LAN and the SAN segment correctly.
Add the PRP interlink forwarding rules to prp_drop_frame() and give RedBox
nodes a second duplicate-discard slot so the two LAN copies of a frame
destined to a SAN collapse to a single delivery out the interlink.
The destination classification (is the unicast DA a PRP-network node or a
proxied SAN) is resolved once per frame in fill_frame_info(), gated to PRP
RedBox devices, and cached in struct hsr_frame_info, so prp_drop_frame()
stays O(1) and does not walk the node tables for every candidate egress
port in the softIRQ path. HSR RedBox frame classification is untouched.
Factor the LAN A/B duplicate test into prp_is_lan_dup() so the new PRP
interlink rules do not change hsr_drop_frame() behaviour, including the
NETIF_F_HW_HSR_FWD path which keeps using the LAN-duplicate test only.
Publish the RedBox state before the first hsr_add_port(): the slave and
interlink rx handlers are live from hsr_add_port() on and rtnl does not
stop softirq processing, so a frame could otherwise be handled while
hsr->redbox is still false. hsr_add_node() sizes each node's per-port
sequence state from hsr->redbox; a node learned in that window would get
a single-port sequence block, breaking the interlink duplicate discard
(WARN_ON_ONCE plus duplicate delivery to the SAN) and letting the
supervision sequence-block merge read beyond the source node's allocated
sequence bitmap. Publishing the flag before any port exists makes the
per-node sizing uniform by construction. This is safe: the proxy
announce timer is only armed from hsr_check_announce() once the master
is running, the packet-path readers of hsr->redbox tolerate an empty
proxy node database and an absent interlink port, and the
prune_proxy_timer is still armed only after the interlink port has been
attached successfully.
Additionally bound the supervision sequence-block merge by the smaller
of the two nodes' seq_port_cnt as defense in depth against mismatched
node sizes.
Signed-off-by: Xin Xie <xiexinet@gmail.com>
---
net/hsr/hsr_device.c | 11 ++++++++--
net/hsr/hsr_forward.c | 46 ++++++++++++++++++++++++++++++++++++------
net/hsr/hsr_framereg.c | 14 +++++++++----
net/hsr/hsr_framereg.h | 2 ++
4 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 5555b71ab19..5af491ed2b7 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -768,6 +768,15 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
/* Make sure the 1st call to netif_carrier_on() gets through */
netif_carrier_off(hsr_dev);
+ /* Publish the RedBox state before any port is attached: the rx
+ * handlers are live from hsr_add_port() on, and hsr_add_node()
+ * sizes each node's per-port sequence state from hsr->redbox.
+ */
+ if (interlink) {
+ hsr->redbox = true;
+ ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr);
+ }
+
res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER, extack);
if (res)
goto err_add_master;
@@ -805,8 +814,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
if (res)
goto err_unregister;
- hsr->redbox = true;
- ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr);
mod_timer(&hsr->prune_proxy_timer,
jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD));
}
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 0774981a65c..7734a521a96 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -440,12 +440,34 @@ static int hsr_xmit(struct sk_buff *skb, struct hsr_port *port,
return dev_queue_xmit(skb);
}
+static bool prp_is_lan_dup(enum hsr_port_type rx, struct hsr_port *port)
+{
+ return (rx == HSR_PT_SLAVE_A && port->type == HSR_PT_SLAVE_B) ||
+ (rx == HSR_PT_SLAVE_B && port->type == HSR_PT_SLAVE_A);
+}
+
bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
{
- return ((frame->port_rcv->type == HSR_PT_SLAVE_A &&
- port->type == HSR_PT_SLAVE_B) ||
- (frame->port_rcv->type == HSR_PT_SLAVE_B &&
- port->type == HSR_PT_SLAVE_A));
+ enum hsr_port_type rx = frame->port_rcv->type;
+
+ /* Supervision frames are not delivered to a SAN on the interlink. */
+ if (frame->is_supervision && port->type == HSR_PT_INTERLINK)
+ return true;
+
+ if (prp_is_lan_dup(rx, port))
+ return true;
+
+ /* LAN to interlink: keep PRP-network unicast off the SAN segment. */
+ if ((rx == HSR_PT_SLAVE_A || rx == HSR_PT_SLAVE_B) &&
+ port->type == HSR_PT_INTERLINK)
+ return frame->dst_in_node_db;
+
+ /* Interlink to LAN: keep SAN-to-SAN unicast local. */
+ if ((port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) &&
+ rx == HSR_PT_INTERLINK)
+ return frame->dst_in_proxy_node_db;
+
+ return false;
}
bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
@@ -453,7 +475,7 @@ bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
struct sk_buff *skb;
if (port->dev->features & NETIF_F_HW_HSR_FWD)
- return prp_drop_frame(frame, port);
+ return prp_is_lan_dup(frame->port_rcv->type, port);
/* RedBox specific frames dropping policies
*
@@ -466,7 +488,7 @@ bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
* are addressed to interlink port (and are in the ProxyNodeTable).
*/
skb = frame->skb_hsr;
- if (skb && prp_drop_frame(frame, port) &&
+ if (skb && prp_is_lan_dup(frame->port_rcv->type, port) &&
is_unicast_ether_addr(eth_hdr(skb)->h_dest) &&
hsr_is_node_in_db(&port->hsr->proxy_node_db,
eth_hdr(skb)->h_dest)) {
@@ -706,6 +728,18 @@ static int fill_frame_info(struct hsr_frame_info *frame,
frame->is_vlan = false;
proto = ethhdr->h_proto;
+ /* PRP RedBox only: classify the unicast destination once so the
+ * per-egress-port decision in prp_drop_frame() stays O(1). HSR RedBox
+ * does its own classification and must not pay these node-table walks.
+ */
+ if (hsr->prot_version == PRP_V1 && hsr->redbox &&
+ is_unicast_ether_addr(ethhdr->h_dest)) {
+ frame->dst_in_node_db =
+ hsr_is_node_in_db(&hsr->node_db, ethhdr->h_dest);
+ frame->dst_in_proxy_node_db =
+ hsr_is_node_in_db(&hsr->proxy_node_db, ethhdr->h_dest);
+ }
+
if (proto == htons(ETH_P_8021Q))
frame->is_vlan = true;
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index e4492987127..8f708b6e6c3 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -199,7 +199,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
spin_lock_init(&new_node->seq_out_lock);
if (hsr->prot_version == PRP_V1)
- new_node->seq_port_cnt = 1;
+ new_node->seq_port_cnt = hsr->redbox ? 2 : 1;
else
new_node->seq_port_cnt = HSR_PT_PORTS - 1;
@@ -381,6 +381,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
struct ethhdr *ethhdr;
unsigned int total_pull_size = 0;
unsigned int pull_size = 0;
+ unsigned int seq_port_cnt;
unsigned long idx;
int i;
@@ -474,6 +475,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
}
}
+ seq_port_cnt = min(node_real->seq_port_cnt, node_curr->seq_port_cnt);
xa_for_each(&node_curr->seq_blocks, idx, src_blk) {
if (hsr_seq_block_is_old(src_blk))
continue;
@@ -482,7 +484,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
if (!merge_blk)
continue;
merge_blk->time = min(merge_blk->time, src_blk->time);
- for (i = 0; i < node_real->seq_port_cnt; i++) {
+ for (i = 0; i < seq_port_cnt; i++) {
bitmap_or(merge_blk->seq_nrs[i], merge_blk->seq_nrs[i],
src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE);
}
@@ -649,9 +651,13 @@ int prp_register_frame_out(struct hsr_port *port, struct hsr_frame_info *frame)
if (frame->port_rcv->type == HSR_PT_MASTER)
return 0;
- /* for PRP we should only forward frames from the slave ports
- * to the master port
+ /* RedBox: forward LAN frames out the interlink to a SAN, deduping the
+ * two LAN copies on a dedicated slot.
*/
+ if (port->type == HSR_PT_INTERLINK)
+ return hsr_check_duplicate(frame, 1);
+
+ /* For PRP only slave-to-master frames are forwarded. */
if (port->type != HSR_PT_MASTER)
return 1;
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index c65ecb92573..127a3fb64d5 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -27,6 +27,8 @@ struct hsr_frame_info {
bool is_local_dest;
bool is_local_exclusive;
bool is_from_san;
+ bool dst_in_node_db;
+ bool dst_in_proxy_node_db;
};
void hsr_del_self_node(struct hsr_priv *hsr);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v4 0/4] net: hsr: PRP RedBox (PRP-SAN) support
From: Xin Xie @ 2026-07-17 20:14 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, wsa+renesas, jie.luo, shuah,
linux-kernel, linux-kselftest, fmancera, bigeasy, fmaurer,
luka.gejak, Xin Xie
This series adds PRP RedBox support to the hsr driver: a PRP node that
proxies one or more SANs sitting behind an interlink port (IEC 62439-3,
PRP-SAN). HSR-SAN has been supported since commit 5055cccfc2d1 ("net: hsr:
Provide RedBox support (HSR-SAN)"); this extends the equivalent capability
to PRP, reusing the existing protocol-neutral proxy machinery
(proxy_node_db, hsr_proxy_announce(), hsr_prune_proxy_nodes()).
A SAN behind the interlink does bidirectional unicast with peers on the PRP
network, its source MAC is preserved on the wire, the PRP RCT is correct,
and the RedBox announces each proxied SAN with the RedBox-MAC TLV (Type 30)
in its supervision frames.
The series is bisect-safe: the datapath, duplicate discard and supervision
support are added first; the rtnetlink rejection of "type hsr ... interlink
<dev> proto 1" is removed only in patch 3, once the feature is complete.
Design notes:
- prp_drop_frame() does not walk the node tables. The destination
classification (PRP-network node vs proxied SAN) is resolved once per
frame in fill_frame_info() and cached in struct hsr_frame_info, so the
per egress-port drop decision is O(1) in the softIRQ path. The
classification is gated on PRP RedBox devices (prot_version == PRP_V1 &&
hsr->redbox), so HSR RedBox traffic is not affected.
- The LAN A/B duplicate test is factored into prp_is_lan_dup() so the new
PRP interlink rules in prp_drop_frame() do not change hsr_drop_frame()
behaviour, including the NETIF_F_HW_HSR_FWD path. This is software PRP
RedBox only; it adds no new hardware-offload contract.
- The supervision emitter uses pre-reserved tailroom (hsr_init_skb() +
skb_put()) on the existing GFP_ATOMIC path; no skb_linearize() or
pskb_expand_head(). The RedBox-MAC TLV is followed by an explicit EOT
(Type 0, Length 0); padding via skb_put_padto(ETH_ZLEN) and the 6-byte
PRP RCT remain at the absolute tail of the egress frame.
- The hsr_get_node() hsr_ethhdr length guard is relaxed only for PRP
supervision frames (prot_version == PRP_V1 && ETH_P_PRP && is_sup), which
are untagged with mac_len == ETH_HLEN. HSR (ETH_P_HSR) supervision is
front-tagged and keeps the original length requirement, so HSR
malformed-frame filtering is unchanged.
Testing (on a net-next v7.2-rc2 kernel built from this base, x86-64):
- checkpatch.pl --strict: patches 1-3 clean; patch 4 reports only the
expected "added file(s), does MAINTAINERS need updating?" note, which is
ignorable here -- MAINTAINERS already lists
tools/testing/selftests/net/hsr/ under HSR NETWORK PROTOCOL.
- git diff --check clean; the series git-am's onto the base commit.
- tools/testing/selftests/net/hsr/hsr_prp_redbox.sh: PASS on the patched
kernel (bidirectional unicast, SAN MAC preservation, RedBox-MAC TLV +
EOT in the proxy-announce).
- HSR regression on the same kernel: hsr_redbox.sh (HSR-SAN/RedBox),
hsr_ping.sh and prp_ping.sh all PASS, confirming the PRP changes do not
regress the existing HSR/PRP paths.
- netns checks: peer<->SAN 0% loss with no duplicates and a valid PRP RCT
on the wire; a silent SAN is pruned from the announce; zero driver
WARN/BUG/Oops/RCU-stall during the run.
Beyond the in-tree selftest, this exact series (applied to this base and
running as the net-next kernel on x86-64 hardware) was also validated with an
out-of-tree IEC 62439-3 conformance harness (supervision TLV chain, duplicate
discard, cross-LAN rejection, seqnr rollover, VLAN/multicast/GOOSE frame types
with the RCT verified at the absolute frame tail), and interoperability-tested
against a commercial PRP RedBox (Siemens SCALANCE X204RNA) over 100 Mbit/s
Fast Ethernet with NIC hardware (PTP) timestamping: a mid-stream single-LAN
outage of ~2 s at 10 kpps was bridged with zero lost and zero duplicate frames
(seamless PRP failover), and the duplicate-discard window held zero lost /
zero duplicates under netem asymmetric delay up to 100 ms (~1000 sequence
numbers in flight), 25% reorder, and 5% single-LAN loss. The failover and
impairment matrix was additionally repeated on a KASAN + lockdep + kmemleak
instrumented build of this kernel, including a 72-carrier-event link
flap-storm with deliberate double-LAN cuts: zero KASAN, lockdep, or kmemleak
findings. This out-of-tree testing is supplementary and not required to
evaluate the series.
v4:
- pass rx (enum hsr_port_type) to prp_is_lan_dup() instead of the frame
(Fernando Fernandez Mancera)
- restore the original macaddress_redbox indentation (Fernando Fernandez
Mancera)
v3: https://lore.kernel.org/netdev/20260707135937.18-1-xiexinet@gmail.com/
- no code changes; repost as a new thread (v2 was incorrectly posted in
reply to v1)
v2: https://lore.kernel.org/netdev/20260706134131.61659-1-xiexinet@gmail.com/
- publish the RedBox state before lower RX handlers can learn nodes:
fixes an initialization-ordering race where a node learned during
device setup got a single-port sequence block, breaking interlink
duplicate discard and letting the supervision merge read beyond the
source node's allocated sequence bitmap (reported by the NIPA Sashiko
review)
- harden the supervision sequence-block merge against mismatched
seq_port_cnt
- align macaddress_redbox for ether_addr_copy() on strict-alignment
architectures
- selftest: kill the background ping by exact PID and wrap an overlong
line
v1: https://lore.kernel.org/netdev/20260704234704.4297-1-xiexinet@gmail.com/
Xin Xie (4):
net: hsr: add PRP interlink (RedBox) datapath and duplicate discard
net: hsr: emit RedBox-MAC TLV in PRP RedBox supervision frames
net: hsr: allow PRP RedBox (interlink) creation
selftests: net: hsr: add PRP RedBox test
net/hsr/hsr_device.c | 44 ++++++++-
net/hsr/hsr_forward.c | 46 +++++++--
net/hsr/hsr_framereg.c | 27 +++--
net/hsr/hsr_framereg.h | 2 +
net/hsr/hsr_main.h | 2 +-
net/hsr/hsr_netlink.c | 8 +-
tools/testing/selftests/net/hsr/Makefile | 1 +
.../selftests/net/hsr/hsr_prp_redbox.sh | 99 +++++++++++++++++++
8 files changed, 204 insertions(+), 25 deletions(-)
create mode 100755 tools/testing/selftests/net/hsr/hsr_prp_redbox.sh
base-commit: f6f3b36c15ed44de1fbb44e645e4fae8c4a4453e
--
2.53.0
^ permalink raw reply
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