* Re: [PATCH net-next 2/3] netdevsim: psp: handle the new crypt-offset and spi-threshold get/set operations
From: Willem de Bruijn @ 2026-04-07 21:43 UTC (permalink / raw)
To: Akhilesh Samineni, davem, edumazet, kuba, pabeni, andrew+netdev,
horms, willemb, daniel.zahka
Cc: netdev, linux-kernel, jayakrishnan.udayavarma, ajit.khaparde,
kiran.kella, akhilesh.samineni, sachin.suman
In-Reply-To: <20260406222305.4111170-3-akhilesh.samineni@broadcom.com>
Akhilesh Samineni wrote:
> Implement the crypt-offset and spi-threshold get/set in netdevsim PSP.
>
> Signed-off-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
> Reviewed-by: Kiran Kella <kiran.kella@broadcom.com>
> Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
> ---
> drivers/net/netdevsim/netdevsim.h | 2 ++
> drivers/net/netdevsim/psp.c | 6 ++++++
> 2 files changed, 8 insertions(+)
>
> diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
> index c904e14f6b3f..3ad7d42391c0 100644
> --- a/drivers/net/netdevsim/netdevsim.h
> +++ b/drivers/net/netdevsim/netdevsim.h
> @@ -117,6 +117,8 @@ struct netdevsim {
> struct psp_dev *dev;
> u32 spi;
> u32 assoc_cnt;
> + u8 crypt_offset;
Minor: variable names are already not aligned. No need for two spaces.
> + u32 spi_threshold;
> } psp;
>
> struct nsim_bus_dev *nsim_bus_dev;
> diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
> index 0b4d717253b0..9098edf00c5c 100644
> --- a/drivers/net/netdevsim/psp.c
> +++ b/drivers/net/netdevsim/psp.c
> @@ -122,6 +122,11 @@ static int
> nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf,
> struct netlink_ext_ack *extack)
> {
> + struct netdevsim *ns = psd->drv_priv;
> +
> + ns->psp.crypt_offset = conf->crypt_offset;
> + ns->psp.spi_threshold = conf->spi_threshold;
> +
> return 0;
> }
>
> @@ -249,6 +254,7 @@ int nsim_psp_init(struct netdevsim *ns)
> if (err)
> return err;
>
> + ns->psp.spi_threshold = PSP_SPI_THRESHOLD_DEFAULT;
> debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops);
> return 0;
> }
> --
> 2.45.4
>
^ permalink raw reply
* Re: [PATCH net-next 1/3] psp: add crypt-offset and spi-threshold get/set attributes
From: Willem de Bruijn @ 2026-04-07 21:37 UTC (permalink / raw)
To: Akhilesh Samineni, davem, edumazet, kuba, pabeni, andrew+netdev,
horms, willemb, daniel.zahka
Cc: netdev, linux-kernel, jayakrishnan.udayavarma, ajit.khaparde,
kiran.kella, akhilesh.samineni, sachin.suman
In-Reply-To: <20260406222305.4111170-2-akhilesh.samineni@broadcom.com>
Akhilesh Samineni wrote:
> crypt-offset (Crypt Offset)
> ----------------------------------
> The crypt-offset attribute specifies the byte offset within a packet
> from which encryption begins. This is a per-device attribute that
> allows a portion of the packet header to remain in plaintext while
> the rest of the payload is encrypted. This is useful in scenarios
> where intermediate nodes need to inspect or process a fixed-size
> header before the encrypted payload.
>
> The default value is 0, meaning encryption starts from the beginning
> of the payload following the PSP header.
>
> spi-threshold (SPI Threshold)
> ------------------------------
> The SPI (Security Parameter Index) is a 32-bit per-device identifier
> used to distinguish security associations. As SPI values are allocated
> monotonically, a threshold is needed to trigger timely SPI rotation
> before the space is exhausted.
>
> The spi-threshold attribute allows userspace to configure the value at
> which an SPI rotation should be initiated. The default is set to
> PSP_SPI_THRESHOLD_DEFAULT (~90% of 0x7FFFFFFF), providing a comfortable
> margin to perform rotation without racing to exhaustion.
>
> NOTE: A follow-up series will add notification support to alert
> subscribed users when the configured spi-threshold is reached, enabling
> timely SPI rotation.
>
> Signed-off-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
> Reviewed-by: Kiran Kella <kiran.kella@broadcom.com>
> Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
> ---
> Documentation/netlink/specs/psp.yaml | 13 +++++++++++++
> include/net/psp/types.h | 7 +++++++
> include/uapi/linux/psp.h | 2 ++
> net/psp/psp-nl-gen.c | 6 ++++--
> net/psp/psp_main.c | 3 +++
> net/psp/psp_nl.c | 27 +++++++++++++++++++++++----
> 6 files changed, 52 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
> index f3a57782d2cf..b22869be91cf 100644
> --- a/Documentation/netlink/specs/psp.yaml
> +++ b/Documentation/netlink/specs/psp.yaml
> @@ -38,6 +38,15 @@ attribute-sets:
> type: u32
> enum: version
> enum-as-flags: true
> + -
> + name: crypt-offset
> + doc: The offset from the end of the PSP header to the start of the encrypted payload.
In 4 octet units?
> + type: u8
> + -
> + name: spi-threshold
> + doc: Threshold for the SPI to trigger notification to the user for appropriate rotate action.
> + type: u32
> +
> -
> name: assoc
> attributes:
> @@ -170,6 +179,8 @@ operations:
> - ifindex
> - psp-versions-cap
> - psp-versions-ena
> + - crypt-offset
> + - spi-threshold
> pre: psp-device-get-locked
> post: psp-device-unlock
> dump:
> @@ -193,6 +204,8 @@ operations:
> attributes:
> - id
> - psp-versions-ena
> + - crypt-offset
> + - spi-threshold
> reply:
> attributes: []
> pre: psp-device-get-locked
> diff --git a/include/net/psp/types.h b/include/net/psp/types.h
> index 25a9096d4e7d..875f7822557f 100644
> --- a/include/net/psp/types.h
> +++ b/include/net/psp/types.h
> @@ -25,6 +25,9 @@ struct psphdr {
> #define PSP_SPI_KEY_ID GENMASK(30, 0)
> #define PSP_SPI_KEY_PHASE BIT(31)
>
> +/* Default SPI threshold: ~90% of max SPI (0x7FFFFFFF) to allow rotation before exhaustion */
> +#define PSP_SPI_THRESHOLD_DEFAULT 0x73333333
Do you want to choose a more round number, in either hex or dec?
> +
> #define PSPHDR_CRYPT_OFFSET GENMASK(5, 0)
>
> #define PSPHDR_VERFL_SAMPLE BIT(7)
> @@ -38,9 +41,13 @@ struct psphdr {
> /**
> * struct psp_dev_config - PSP device configuration
> * @versions: PSP versions enabled on the device
> + * @crypt_offset: crypto offset configured on the device
> + * @spi_threshold: SPI threshold value on the device
> */
> struct psp_dev_config {
> u32 versions;
> + u8 crypt_offset;
> + u32 spi_threshold;
> };
>
> /**
> diff --git a/include/uapi/linux/psp.h b/include/uapi/linux/psp.h
> index a3a336488dc3..bb390159dc72 100644
> --- a/include/uapi/linux/psp.h
> +++ b/include/uapi/linux/psp.h
> @@ -22,6 +22,8 @@ enum {
> PSP_A_DEV_IFINDEX,
> PSP_A_DEV_PSP_VERSIONS_CAP,
> PSP_A_DEV_PSP_VERSIONS_ENA,
> + PSP_A_DEV_CRYPT_OFFSET,
> + PSP_A_DEV_SPI_THRESHOLD,
>
> __PSP_A_DEV_MAX,
> PSP_A_DEV_MAX = (__PSP_A_DEV_MAX - 1)
> diff --git a/net/psp/psp-nl-gen.c b/net/psp/psp-nl-gen.c
> index 22a48d0fa378..e50b8b80955c 100644
> --- a/net/psp/psp-nl-gen.c
> +++ b/net/psp/psp-nl-gen.c
> @@ -23,9 +23,11 @@ static const struct nla_policy psp_dev_get_nl_policy[PSP_A_DEV_ID + 1] = {
> };
>
> /* PSP_CMD_DEV_SET - do */
> -static const struct nla_policy psp_dev_set_nl_policy[PSP_A_DEV_PSP_VERSIONS_ENA + 1] = {
> +static const struct nla_policy psp_dev_set_nl_policy[PSP_A_DEV_SPI_THRESHOLD + 1] = {
> [PSP_A_DEV_ID] = NLA_POLICY_MIN(NLA_U32, 1),
> [PSP_A_DEV_PSP_VERSIONS_ENA] = NLA_POLICY_MASK(NLA_U32, 0xf),
> + [PSP_A_DEV_CRYPT_OFFSET] = { .type = NLA_U8, },
> + [PSP_A_DEV_SPI_THRESHOLD] = { .type = NLA_U32, },
> };
>
> /* PSP_CMD_KEY_ROTATE - do */
> @@ -75,7 +77,7 @@ static const struct genl_split_ops psp_nl_ops[] = {
> .doit = psp_nl_dev_set_doit,
> .post_doit = psp_device_unlock,
> .policy = psp_dev_set_nl_policy,
> - .maxattr = PSP_A_DEV_PSP_VERSIONS_ENA,
> + .maxattr = PSP_A_DEV_SPI_THRESHOLD,
> .flags = GENL_CMD_CAP_DO,
> },
> {
> diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
> index 9508b6c38003..536ee44db09d 100644
> --- a/net/psp/psp_main.c
> +++ b/net/psp/psp_main.c
> @@ -79,6 +79,9 @@ psp_dev_create(struct net_device *netdev,
> INIT_LIST_HEAD(&psd->stale_assocs);
> refcount_set(&psd->refcnt, 1);
>
> + /* ~90% of 0x7FFFFFFF; allows SPI rotation well before space is exhausted */
Repeat comment. Not needed here.
> + psd->config.spi_threshold = PSP_SPI_THRESHOLD_DEFAULT;
> +
> mutex_lock(&psp_devs_lock);
> err = xa_alloc_cyclic(&psp_devs, &psd->id, psd, xa_limit_16b,
> &last_id, GFP_KERNEL);
> diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
> index 6afd7707ec12..fbb77460a24b 100644
> --- a/net/psp/psp_nl.c
> +++ b/net/psp/psp_nl.c
> @@ -101,7 +101,9 @@ psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
> if (nla_put_u32(rsp, PSP_A_DEV_ID, psd->id) ||
> nla_put_u32(rsp, PSP_A_DEV_IFINDEX, psd->main_netdev->ifindex) ||
> nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_CAP, psd->caps->versions) ||
> - nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions))
> + nla_put_u32(rsp, PSP_A_DEV_PSP_VERSIONS_ENA, psd->config.versions) ||
> + nla_put_u8(rsp, PSP_A_DEV_CRYPT_OFFSET, psd->config.crypt_offset) ||
> + nla_put_u32(rsp, PSP_A_DEV_SPI_THRESHOLD, psd->config.spi_threshold))
> goto err_cancel_msg;
>
> genlmsg_end(rsp, hdr);
> @@ -193,6 +195,13 @@ int psp_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info)
>
> memcpy(&new_config, &psd->config, sizeof(new_config));
>
> + if (!info->attrs[PSP_A_DEV_PSP_VERSIONS_ENA] &&
> + !info->attrs[PSP_A_DEV_CRYPT_OFFSET] &&
> + !info->attrs[PSP_A_DEV_SPI_THRESHOLD]) {
> + NL_SET_ERR_MSG(info->extack, "No settings present");
> + return -EINVAL;
> + }
> +
> if (info->attrs[PSP_A_DEV_PSP_VERSIONS_ENA]) {
> new_config.versions =
> nla_get_u32(info->attrs[PSP_A_DEV_PSP_VERSIONS_ENA]);
> @@ -200,9 +209,19 @@ int psp_nl_dev_set_doit(struct sk_buff *skb, struct genl_info *info)
> NL_SET_ERR_MSG(info->extack, "Requested PSP versions not supported by the device");
> return -EINVAL;
> }
> - } else {
> - NL_SET_ERR_MSG(info->extack, "No settings present");
> - return -EINVAL;
> + }
> +
> + if (info->attrs[PSP_A_DEV_CRYPT_OFFSET])
> + new_config.crypt_offset =
> + nla_get_u8(info->attrs[PSP_A_DEV_CRYPT_OFFSET]);
PSP defines a 6-bit field in 4 octet units. Does this need bounds checking?
> +
> + if (info->attrs[PSP_A_DEV_SPI_THRESHOLD]) {
> + new_config.spi_threshold =
> + nla_get_u32(info->attrs[PSP_A_DEV_SPI_THRESHOLD]);
> + if (new_config.spi_threshold & PSP_SPI_KEY_PHASE) {
> + NL_SET_ERR_MSG(info->extack, "SPI threshold must not have bit 31 set");
> + return -EINVAL;
> + }
> }
>
> rsp = psp_nl_reply_new(info);
> --
> 2.45.4
>
^ permalink raw reply
* [net,PATCH] net: ks8851: Reinstate disabling of BHs around IRQ handler
From: Marek Vasut @ 2026-04-07 21:23 UTC (permalink / raw)
To: netdev
Cc: Marek Vasut, stable, David S. Miller, Andrew Lunn, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Ronald Wahl, Yicong Hui,
linux-kernel
If CONFIG_PREEMPT_RT=y is set AND the driver executes ks8851_irq() AND
KSZ_ISR register bit IRQ_RXI is set AND ks8851_rx_pkts() detects that
there are packets in the RX FIFO, then netdev_alloc_skb_ip_align() is
called to allocate SKBs. If netdev_alloc_skb_ip_align() is called with
BH enabled, local_bh_enable() at the end of netdev_alloc_skb_ip_align()
will call __local_bh_enable_ip(), which will call __do_softirq(), which
may trigger net_tx_action() softirq, which may ultimately call the xmit
callback ks8851_start_xmit_par(). The ks8851_start_xmit_par() will try
to lock struct ks8851_net_par .lock spinlock, which is already locked
by ks8851_irq() from which ks8851_start_xmit_par() was called. This
leads to a deadlock, which is reported by the kernel, including a trace
listed below.
Fix the problem by disabling BH around the IRQ handler, thus preventing
the net_tx_action() softirq from triggering during the IRQ handler. The
net_tx_action() softirq is now triggered at the end of the IRQ handler,
once all the other IRQ handler actions have been completed.
__schedule from schedule_rtlock+0x1c/0x34
schedule_rtlock from rtlock_slowlock_locked+0x538/0x894
rtlock_slowlock_locked from rt_spin_lock+0x44/0x5c
rt_spin_lock from ks8851_start_xmit_par+0x68/0x1a0
ks8851_start_xmit_par from netdev_start_xmit+0x1c/0x40
netdev_start_xmit from dev_hard_start_xmit+0xec/0x1b0
dev_hard_start_xmit from sch_direct_xmit+0xb8/0x25c
sch_direct_xmit from __qdisc_run+0x20c/0x4fc
__qdisc_run from qdisc_run+0x1c/0x28
qdisc_run from net_tx_action+0x1f4/0x244
net_tx_action from handle_softirqs+0x1c0/0x29c
handle_softirqs from __local_bh_enable_ip+0xdc/0xf4
__local_bh_enable_ip from __netdev_alloc_skb+0x140/0x194
__netdev_alloc_skb from ks8851_irq+0x348/0x4d8
ks8851_irq from irq_thread_fn+0x24/0x64
irq_thread_fn from irq_thread+0x110/0x1dc
irq_thread from kthread+0x104/0x10c
kthread from ret_from_fork+0x14/0x28
Fixes: e0863634bf9f ("net: ks8851: Queue RX packets in IRQ handler instead of disabling BHs")
Cc: stable@vger.kernel.org
Signed-off-by: Marek Vasut <marex@nabladev.com>
---
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Ronald Wahl <ronald.wahl@raritan.com>
Cc: Yicong Hui <yiconghui@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: netdev@vger.kernel.org
---
drivers/net/ethernet/micrel/ks8851_common.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c
index 8048770958d60..dadedea016fac 100644
--- a/drivers/net/ethernet/micrel/ks8851_common.c
+++ b/drivers/net/ethernet/micrel/ks8851_common.c
@@ -316,6 +316,7 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
unsigned int status;
struct sk_buff *skb;
+ local_bh_disable();
ks8851_lock(ks, &flags);
status = ks8851_rdreg16(ks, KS_ISR);
@@ -381,6 +382,7 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
if (status & IRQ_RXI)
while ((skb = __skb_dequeue(&rxq)))
netif_rx(skb);
+ local_bh_enable();
return IRQ_HANDLED;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v3 net-next 15/15] ip6mr: Replace RTNL with a dedicated mutex for MFC.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
ip6mr does not have rtnetlink interface for MFC unlike ipmr,
which uses dev_get_by_index_rcu() to set struct mfcctl.mfcc_parent.
ip6mr_mfc_add() and ip6mr_mfc_delete() are called under RTNL
from ip6_mroute_setsockopt() only.
There are no RTNL dependant, but ip6_mroute_setsockopt() reuses
RTNL just for mrt->mfc_hash and mrt->mfc_cache_list.
Let's replace RTNL with a new per-netns mutex.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
include/net/netns/ipv6.h | 1 +
net/ipv6/ip6mr.c | 21 ++++++++++++++-------
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index df00567374f4..6453d70d5946 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -114,6 +114,7 @@ struct netns_ipv6 {
#endif
struct fib_notifier_ops *ip6mr_notifier_ops;
atomic_t ipmr_seq;
+ struct mutex mfc_mutex;
#endif
atomic_t dev_addr_genid;
atomic_t fib6_sernum;
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index b1443fb65b40..e4c31d05744b 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1256,7 +1256,6 @@ static int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc,
{
struct mfc6_cache *c;
- /* The entries are added/deleted only under RTNL */
rcu_read_lock();
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
@@ -1346,6 +1345,8 @@ static int __net_init ip6mr_net_init(struct net *net)
LIST_HEAD(dev_kill_list);
int err;
+ mutex_init(&net->ipv6.mfc_mutex);
+
err = ip6mr_notifier_init(net);
if (err)
return err;
@@ -1474,7 +1475,6 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
ttls[i] = 1;
}
- /* The entries are added/deleted only under RTNL */
rcu_read_lock();
c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr,
&mfc->mf6cc_mcastgrp.sin6_addr, parent);
@@ -1553,6 +1553,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
static void mroute_clean_tables(struct mr_table *mrt, int flags,
struct list_head *dev_kill_list)
{
+ struct net *net = read_pnet(&mrt->net);
struct mr_mfc *c, *tmp;
int i;
@@ -1569,18 +1570,21 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
/* Wipe the cache */
if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) {
+ mutex_lock(&net->ipv6.mfc_mutex);
+
list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) ||
(!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC)))
continue;
rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
list_del_rcu(&c->list);
- call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
- FIB_EVENT_ENTRY_DEL,
+ call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
(struct mfc6_cache *)c, mrt->id);
mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
mr_cache_put(c);
}
+
+ mutex_unlock(&net->ipv6.mfc_mutex);
}
if (flags & MRT6_FLUSH_MFC) {
@@ -1763,15 +1767,18 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
return -EFAULT;
if (parent == 0)
parent = mfc.mf6cc_parent;
- rtnl_lock();
+
+ mutex_lock(&net->ipv6.mfc_mutex);
+
if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY)
ret = ip6mr_mfc_delete(mrt, &mfc, parent);
else
ret = ip6mr_mfc_add(net, mrt, &mfc,
sk ==
- rtnl_dereference(mrt->mroute_sk),
+ rcu_access_pointer(mrt->mroute_sk),
parent);
- rtnl_unlock();
+
+ mutex_unlock(&net->ipv6.mfc_mutex);
return ret;
case MRT6_FLUSH:
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 14/15] ip6mr: Define net->ipv6.{ip6mr_notifier_ops,ipmr_seq} under CONFIG_IP_MROUTE.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
net->ipv6.ip6mr_notifier_ops and net->ipv6.ipmr_seq are used
only in net/ipv6/ip6mr.c.
Let's move these definitions under CONFIG_IP_MROUTE.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
include/net/netns/ipv6.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 499e4288170f..df00567374f4 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -112,13 +112,13 @@ struct netns_ipv6 {
struct list_head mr6_tables;
struct fib_rules_ops *mr6_rules_ops;
#endif
+ struct fib_notifier_ops *ip6mr_notifier_ops;
+ atomic_t ipmr_seq;
#endif
atomic_t dev_addr_genid;
atomic_t fib6_sernum;
struct seg6_pernet_data *seg6_data;
struct fib_notifier_ops *notifier_ops;
- struct fib_notifier_ops *ip6mr_notifier_ops;
- atomic_t ipmr_seq;
struct {
struct hlist_head head;
spinlock_t lock;
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 13/15] ip6mr: Call fib_rules_unregister() without RTNL.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
fib_rules_unregister() removes ops from net->rules_ops under
spinlock, calls ops->delete() for each rule, and frees the ops.
ip6mr_rules_ops_template does not have ->delete(), and any
operation does not require RTNL there.
Let's move fib_rules_unregister() from ip6mr_rules_exit_rtnl()
to ip6mr_net_exit().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index e5a1d2c48b1b..b1443fb65b40 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -269,6 +269,11 @@ static int __net_init ip6mr_rules_init(struct net *net)
return err;
}
+static void __net_exit ip6mr_rules_exit(struct net *net)
+{
+ fib_rules_unregister(net->ipv6.mr6_rules_ops);
+}
+
static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{
@@ -278,8 +283,6 @@ static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
list_del(&mrt->list);
ip6mr_free_table(mrt, dev_kill_list);
}
-
- fib_rules_unregister(net->ipv6.mr6_rules_ops);
}
static int ip6mr_rules_dump(struct net *net, struct notifier_block *nb,
@@ -336,6 +339,10 @@ static int __net_init ip6mr_rules_init(struct net *net)
return 0;
}
+static void __net_exit ip6mr_rules_exit(struct net *net)
+{
+}
+
static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{
@@ -1364,6 +1371,7 @@ static int __net_init ip6mr_net_init(struct net *net)
remove_proc_entry("ip6_mr_vif", net->proc_net);
proc_vif_fail:
ip6mr_rules_exit_rtnl(net, &dev_kill_list);
+ ip6mr_rules_exit(net);
#endif
ip6mr_rules_fail:
ip6mr_notifier_exit(net);
@@ -1376,6 +1384,7 @@ static void __net_exit ip6mr_net_exit(struct net *net)
remove_proc_entry("ip6_mr_cache", net->proc_net);
remove_proc_entry("ip6_mr_vif", net->proc_net);
#endif
+ ip6mr_rules_exit(net);
ip6mr_notifier_exit(net);
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 12/15] ip6mr: Remove RTNL in ip6mr_rules_init() and ip6mr_net_init().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
When ip6mr_free_table() is called from ip6mr_rules_init() or
ip6mr_net_init(), the netns is not yet published.
Thus, no device should have been registered, and
mroute_clean_tables() will not call mif6_delete(), so
unregister_netdevice_many() is unnecessary.
unregister_netdevice_many() does nothing if the list is empty,
but it requires RTNL due to the unconditional ASSERT_RTNL()
at the entry of unregister_netdevice_many_notify().
Let's remove unnecessary RTNL and ASSERT_RTNL() and instead
add WARN_ON_ONCE() in ip6mr_free_table().
Note that we use a local list for the new WARN_ON_ONCE() because
dev_kill_list passed from ip6mr_rules_exit_rtnl() may have some
devices when other ops->init() fails after ipmr durnig setup_net().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 2220a2049781..e5a1d2c48b1b 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -263,10 +263,7 @@ static int __net_init ip6mr_rules_init(struct net *net)
return 0;
err2:
- rtnl_lock();
ip6mr_free_table(mrt, &dev_kill_list);
- unregister_netdevice_many(&dev_kill_list);
- rtnl_unlock();
err1:
fib_rules_unregister(ops);
return err;
@@ -277,7 +274,6 @@ static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
{
struct mr_table *mrt, *next;
- ASSERT_RTNL();
list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
list_del(&mrt->list);
ip6mr_free_table(mrt, dev_kill_list);
@@ -343,7 +339,6 @@ static int __net_init ip6mr_rules_init(struct net *net)
static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
struct list_head *dev_kill_list)
{
- ASSERT_RTNL();
ip6mr_free_table(net->ipv6.mrt6, dev_kill_list);
net->ipv6.mrt6 = NULL;
@@ -414,15 +409,19 @@ static void ip6mr_free_table(struct mr_table *mrt,
struct list_head *dev_kill_list)
{
struct net *net = read_pnet(&mrt->net);
+ LIST_HEAD(ip6mr_dev_kill_list);
WARN_ON_ONCE(!mr_can_free_table(net));
timer_shutdown_sync(&mrt->ipmr_expire_timer);
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC,
- dev_kill_list);
+ &ip6mr_dev_kill_list);
rhltable_destroy(&mrt->mfc_hash);
kfree(mrt);
+
+ WARN_ON_ONCE(!net_initialized(net) && !list_empty(&ip6mr_dev_kill_list));
+ list_splice(&ip6mr_dev_kill_list, dev_kill_list);
}
#ifdef CONFIG_PROC_FS
@@ -1364,10 +1363,7 @@ static int __net_init ip6mr_net_init(struct net *net)
proc_cache_fail:
remove_proc_entry("ip6_mr_vif", net->proc_net);
proc_vif_fail:
- rtnl_lock();
ip6mr_rules_exit_rtnl(net, &dev_kill_list);
- unregister_netdevice_many(&dev_kill_list);
- rtnl_unlock();
#endif
ip6mr_rules_fail:
ip6mr_notifier_exit(net);
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 11/15] ip6mr: Convert ip6mr_net_exit_batch() to ->exit_rtnl().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
ip6mr_net_ops uses ->exit_batch() to acquire RTNL only once
for dying network namespaces.
ip6mr does not depend on the ordering of ->exit_rtnl() and
->exit_batch() of other pernet_operations (unlike fib_net_ops).
Once ip6mr_free_table() is called and all devices are
queued for destruction in ->exit_rtnl(), later during
NETDEV_UNREGISTER, ip6mr_device_event() will not see anything
in vif table and just do nothing.
Let's convert ip6mr_net_exit_batch() to ->exit_rtnl().
Note that fib_rules_unregister() does not need RTNL and
we will remove RTNL and unregister_netdevice_many() in
ip6mr_rules_init().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 31 +++++++++++++------------------
1 file changed, 13 insertions(+), 18 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 30e1aece5f53..2220a2049781 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -272,18 +272,17 @@ static int __net_init ip6mr_rules_init(struct net *net)
return err;
}
-static void __net_exit ip6mr_rules_exit(struct net *net)
+static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
+ struct list_head *dev_kill_list)
{
struct mr_table *mrt, *next;
- LIST_HEAD(dev_kill_list);
ASSERT_RTNL();
list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
list_del(&mrt->list);
- ip6mr_free_table(mrt, &dev_kill_list);
+ ip6mr_free_table(mrt, dev_kill_list);
}
- unregister_netdevice_many(&dev_kill_list);
fib_rules_unregister(net->ipv6.mr6_rules_ops);
}
@@ -341,13 +340,11 @@ static int __net_init ip6mr_rules_init(struct net *net)
return 0;
}
-static void __net_exit ip6mr_rules_exit(struct net *net)
+static void __net_exit ip6mr_rules_exit_rtnl(struct net *net,
+ struct list_head *dev_kill_list)
{
- LIST_HEAD(dev_kill_list);
-
ASSERT_RTNL();
- ip6mr_free_table(net->ipv6.mrt6, &dev_kill_list);
- unregister_netdevice_many(&dev_kill_list);
+ ip6mr_free_table(net->ipv6.mrt6, dev_kill_list);
net->ipv6.mrt6 = NULL;
}
@@ -1340,6 +1337,7 @@ static void __net_exit ip6mr_notifier_exit(struct net *net)
/* Setup for IP multicast routing */
static int __net_init ip6mr_net_init(struct net *net)
{
+ LIST_HEAD(dev_kill_list);
int err;
err = ip6mr_notifier_init(net);
@@ -1367,7 +1365,8 @@ static int __net_init ip6mr_net_init(struct net *net)
remove_proc_entry("ip6_mr_vif", net->proc_net);
proc_vif_fail:
rtnl_lock();
- ip6mr_rules_exit(net);
+ ip6mr_rules_exit_rtnl(net, &dev_kill_list);
+ unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
#endif
ip6mr_rules_fail:
@@ -1384,20 +1383,16 @@ static void __net_exit ip6mr_net_exit(struct net *net)
ip6mr_notifier_exit(net);
}
-static void __net_exit ip6mr_net_exit_batch(struct list_head *net_list)
+static void __net_exit ip6mr_net_exit_rtnl(struct net *net,
+ struct list_head *dev_kill_list)
{
- struct net *net;
-
- rtnl_lock();
- list_for_each_entry(net, net_list, exit_list)
- ip6mr_rules_exit(net);
- rtnl_unlock();
+ ip6mr_rules_exit_rtnl(net, dev_kill_list);
}
static struct pernet_operations ip6mr_net_ops = {
.init = ip6mr_net_init,
.exit = ip6mr_net_exit,
- .exit_batch = ip6mr_net_exit_batch,
+ .exit_rtnl = ip6mr_net_exit_rtnl,
};
static const struct rtnl_msg_handler ip6mr_rtnl_msg_handlers[] __initconst_or_module = {
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 10/15] ip6mr: Move unregister_netdevice_many() out of ip6mr_free_table().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
This is a prep commit to convert ip6mr_net_exit_batch() to
->exit_rtnl().
Let's move unregister_netdevice_many() in ip6mr_free_table()
to its callers.
Now ip6mr_rules_exit() can do batching all tables per netns.
Note that later we will remove RTNL and unregister_netdevice_many()
in ip6mr_rules_init().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index c46f6a430d26..30e1aece5f53 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -85,7 +85,8 @@ static DEFINE_SPINLOCK(mfc_unres_lock);
static struct kmem_cache *mrt_cachep __read_mostly;
static struct mr_table *ip6mr_new_table(struct net *net, u32 id);
-static void ip6mr_free_table(struct mr_table *mrt);
+static void ip6mr_free_table(struct mr_table *mrt,
+ struct list_head *dev_kill_list);
static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
struct net_device *dev, struct sk_buff *skb,
@@ -238,6 +239,7 @@ static const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = {
static int __net_init ip6mr_rules_init(struct net *net)
{
struct fib_rules_ops *ops;
+ LIST_HEAD(dev_kill_list);
struct mr_table *mrt;
int err;
@@ -262,7 +264,8 @@ static int __net_init ip6mr_rules_init(struct net *net)
err2:
rtnl_lock();
- ip6mr_free_table(mrt);
+ ip6mr_free_table(mrt, &dev_kill_list);
+ unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
err1:
fib_rules_unregister(ops);
@@ -272,12 +275,15 @@ static int __net_init ip6mr_rules_init(struct net *net)
static void __net_exit ip6mr_rules_exit(struct net *net)
{
struct mr_table *mrt, *next;
+ LIST_HEAD(dev_kill_list);
ASSERT_RTNL();
list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) {
list_del(&mrt->list);
- ip6mr_free_table(mrt);
+ ip6mr_free_table(mrt, &dev_kill_list);
}
+
+ unregister_netdevice_many(&dev_kill_list);
fib_rules_unregister(net->ipv6.mr6_rules_ops);
}
@@ -337,8 +343,12 @@ static int __net_init ip6mr_rules_init(struct net *net)
static void __net_exit ip6mr_rules_exit(struct net *net)
{
+ LIST_HEAD(dev_kill_list);
+
ASSERT_RTNL();
- ip6mr_free_table(net->ipv6.mrt6);
+ ip6mr_free_table(net->ipv6.mrt6, &dev_kill_list);
+ unregister_netdevice_many(&dev_kill_list);
+
net->ipv6.mrt6 = NULL;
}
@@ -403,18 +413,17 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
ipmr_expire_process, ip6mr_new_table_set);
}
-static void ip6mr_free_table(struct mr_table *mrt)
+static void ip6mr_free_table(struct mr_table *mrt,
+ struct list_head *dev_kill_list)
{
struct net *net = read_pnet(&mrt->net);
- LIST_HEAD(dev_kill_list);
WARN_ON_ONCE(!mr_can_free_table(net));
timer_shutdown_sync(&mrt->ipmr_expire_timer);
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC,
- &dev_kill_list);
- unregister_netdevice_many(&dev_kill_list);
+ dev_kill_list);
rhltable_destroy(&mrt->mfc_hash);
kfree(mrt);
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 09/15] ip6mr: Move unregister_netdevice_many() out of mroute_clean_tables().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
This is a prep commit to convert ip6mr_net_exit_batch() to
->exit_rtnl().
Let's move unregister_netdevice_many() in mroute_clean_tables()
to its callers.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index b9c048b6f1ca..c46f6a430d26 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -99,7 +99,8 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack);
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
struct netlink_callback *cb);
-static void mroute_clean_tables(struct mr_table *mrt, int flags);
+static void mroute_clean_tables(struct mr_table *mrt, int flags,
+ struct list_head *dev_kill_list);
static void ipmr_expire_process(struct timer_list *t);
#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
@@ -405,12 +406,15 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
static void ip6mr_free_table(struct mr_table *mrt)
{
struct net *net = read_pnet(&mrt->net);
+ LIST_HEAD(dev_kill_list);
WARN_ON_ONCE(!mr_can_free_table(net));
timer_shutdown_sync(&mrt->ipmr_expire_timer);
mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC |
- MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC);
+ MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC,
+ &dev_kill_list);
+ unregister_netdevice_many(&dev_kill_list);
rhltable_destroy(&mrt->mfc_hash);
kfree(mrt);
}
@@ -1537,10 +1541,10 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
* Close the multicast socket, and clear the vif tables etc
*/
-static void mroute_clean_tables(struct mr_table *mrt, int flags)
+static void mroute_clean_tables(struct mr_table *mrt, int flags,
+ struct list_head *dev_kill_list)
{
struct mr_mfc *c, *tmp;
- LIST_HEAD(list);
int i;
/* Shut down all active vif entries */
@@ -1550,9 +1554,8 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags)
!(flags & MRT6_FLUSH_MIFS_STATIC)) ||
(!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS)))
continue;
- mif6_delete(mrt, i, 0, &list);
+ mif6_delete(mrt, i, 0, dev_kill_list);
}
- unregister_netdevice_many(&list);
}
/* Wipe the cache */
@@ -1615,6 +1618,7 @@ int ip6mr_sk_done(struct sock *sk)
{
struct net *net = sock_net(sk);
struct ipv6_devconf *devconf;
+ LIST_HEAD(dev_kill_list);
struct mr_table *mrt;
int err = -EACCES;
@@ -1642,11 +1646,13 @@ int ip6mr_sk_done(struct sock *sk)
NETCONFA_IFINDEX_ALL,
net->ipv6.devconf_all);
- mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC);
+ mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC,
+ &dev_kill_list);
err = 0;
break;
}
}
+ unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
return err;
@@ -1761,14 +1767,17 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
case MRT6_FLUSH:
{
+ LIST_HEAD(dev_kill_list);
int flags;
if (optlen != sizeof(flags))
return -EINVAL;
if (copy_from_sockptr(&flags, optval, sizeof(flags)))
return -EFAULT;
+
rtnl_lock();
- mroute_clean_tables(mrt, flags);
+ mroute_clean_tables(mrt, flags, &dev_kill_list);
+ unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
return 0;
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 08/15] net: Remove rtnl_held of struct fib_dump_filter.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
Commit 22e36ea9f5d7 ("inet: allow ip_valid_fib_dump_req() to
be called with RTNL or RCU") introduced the rtnl_held field in
struct fib_dump_filter to switch __dev_get_by_index() and
dev_get_by_index_rcu() depending on the caller's context.
This field served as an interim measure while we were incrementally
converting all callers of ip_valid_fib_dump_req() to RCU.
Now that all users (IPv4, IPv6, ipmr, ip6mr, and MPLS) have
been converted to RCU, the field is no longer necessary.
Let's remove it.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
include/net/ip_fib.h | 1 -
net/ipv4/fib_frontend.c | 19 ++++++-------------
net/ipv4/ipmr.c | 4 +---
net/ipv6/ip6_fib.c | 1 -
net/ipv6/ip6mr.c | 4 +---
net/mpls/af_mpls.c | 6 ++----
6 files changed, 10 insertions(+), 25 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 318593743b6e..1142ffad7444 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -269,7 +269,6 @@ struct fib_dump_filter {
bool filter_set;
bool dump_routes;
bool dump_exceptions;
- bool rtnl_held;
unsigned char protocol;
unsigned char rt_type;
unsigned int flags;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 1dab44e13d3b..ceeb87b13b93 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -946,9 +946,6 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
struct rtmsg *rtm;
int err, i;
- if (filter->rtnl_held)
- ASSERT_RTNL();
-
rtm = nlmsg_payload(nlh, sizeof(*rtm));
if (!rtm) {
NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request");
@@ -992,10 +989,8 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
break;
case RTA_OIF:
ifindex = nla_get_u32(tb[i]);
- if (filter->rtnl_held)
- filter->dev = __dev_get_by_index(net, ifindex);
- else
- filter->dev = dev_get_by_index_rcu(net, ifindex);
+
+ filter->dev = dev_get_by_index_rcu(net, ifindex);
if (!filter->dev)
return -ENODEV;
break;
@@ -1017,18 +1012,16 @@ EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req);
static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
{
+ const struct nlmsghdr *nlh = cb->nlh;
+ struct net *net = sock_net(skb->sk);
struct fib_dump_filter filter = {
.dump_routes = true,
.dump_exceptions = true,
- .rtnl_held = false,
};
- const struct nlmsghdr *nlh = cb->nlh;
- struct net *net = sock_net(skb->sk);
- unsigned int h, s_h;
- unsigned int e = 0, s_e;
- struct fib_table *tb;
+ unsigned int e = 0, s_e, h, s_h;
struct hlist_head *head;
int dumped = 0, err = 0;
+ struct fib_table *tb;
rcu_read_lock();
if (cb->strict_check) {
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 2566b4a1f80b..c0fc606b0ae9 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2770,9 +2770,7 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct fib_dump_filter filter = {
- .rtnl_held = false,
- };
+ struct fib_dump_filter filter = {};
int err;
rcu_read_lock();
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index b897b3c5023b..fc95738ded76 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -633,7 +633,6 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
struct rt6_rtnl_dump_arg arg = {
.filter.dump_exceptions = true,
.filter.dump_routes = true,
- .filter.rtnl_held = false,
};
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index cd28bea8c11a..b9c048b6f1ca 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2750,9 +2750,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
const struct nlmsghdr *nlh = cb->nlh;
- struct fib_dump_filter filter = {
- .rtnl_held = false,
- };
+ struct fib_dump_filter filter = {};
int err;
rcu_read_lock();
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index 26340a7306b5..ca504d9626cf 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -2221,12 +2221,10 @@ static bool mpls_rt_uses_dev(struct mpls_route *rt,
static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct mpls_route __rcu **platform_label;
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
- struct mpls_route __rcu **platform_label;
- struct fib_dump_filter filter = {
- .rtnl_held = false,
- };
+ struct fib_dump_filter filter = {};
unsigned int flags = NLM_F_MULTI;
size_t platform_labels;
unsigned int index;
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 07/15] ip6mr: Convert ip6mr_rtm_dumproute() to RCU.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
ip6mr_rtm_dumproute() calls mr_table_dump() or mr_rtm_dumproute(),
and mr_rtm_dumproute() finally calls mr_table_dump().
mr_table_dump() calls the passed function, _ip6mr_fill_mroute().
_ip6mr_fill_mroute() is a wrapper for ip6mr_fill_mroute() to cast
struct mr_mfc * to struct mfc6_cache *.
ip6mr_fill_mroute() can already be called safely under RCU.
Let's convert ip6mr_rtm_dumproute() to RCU.
Now there is no user of the rtnl_held field in struct
fib_dump_filter, and the next patch will remove it.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 389471e740bc..cd28bea8c11a 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1391,7 +1391,7 @@ static const struct rtnl_msg_handler ip6mr_rtnl_msg_handlers[] __initconst_or_mo
{.owner = THIS_MODULE, .protocol = RTNL_FAMILY_IP6MR,
.msgtype = RTM_GETROUTE,
.doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute,
- .flags = RTNL_FLAG_DOIT_UNLOCKED},
+ .flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED},
};
int __init ip6_mr_init(void)
@@ -2751,15 +2751,17 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
const struct nlmsghdr *nlh = cb->nlh;
struct fib_dump_filter filter = {
- .rtnl_held = true,
+ .rtnl_held = false,
};
int err;
+ rcu_read_lock();
+
if (cb->strict_check) {
err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
&filter, cb);
if (err < 0)
- return err;
+ goto unlock;
}
if (filter.table_id) {
@@ -2767,17 +2769,26 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
mrt = __ip6mr_get_table(sock_net(skb->sk), filter.table_id);
if (!mrt) {
- if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR)
- return skb->len;
+ if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) {
+ err = skb->len;
+ goto unlock;
+ }
NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
- return -ENOENT;
+ err = -ENOENT;
+ goto unlock;
}
+
err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute,
&mfc_unres_lock, &filter);
- return skb->len ? : err;
+ err = skb->len ? : err;
+ goto unlock;
}
- return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
- _ip6mr_fill_mroute, &mfc_unres_lock, &filter);
+ err = mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
+ _ip6mr_fill_mroute, &mfc_unres_lock, &filter);
+unlock:
+ rcu_read_unlock();
+
+ return err;
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 06/15] ip6mr: Convert ip6mr_rtm_getroute() to RCU.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
ip6mr_rtm_getroute() calls __ip6mr_get_table(), ip6mr_cache_find(),
and ip6mr_fill_mroute().
Once created, struct mr_table is not freed until netns dismantle,
so it's safe under RCU.
ip6mr_cache_find() iterates mrt->mfc_hash with rhl_for_each_entry_rcu().
struct mr_mfc is freed with call_rcu(), so this is also safe under
RCU.
ip6mr_fill_mroute() calls mr_fill_mroute(), which properly uses
RCU helpers.
Let's call them under RCU and register ip6mr_rtm_getroute() with
RTNL_FLAG_DOIT_UNLOCKED.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index db7376cbcc01..389471e740bc 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1390,7 +1390,8 @@ static struct pernet_operations ip6mr_net_ops = {
static const struct rtnl_msg_handler ip6mr_rtnl_msg_handlers[] __initconst_or_module = {
{.owner = THIS_MODULE, .protocol = RTNL_FAMILY_IP6MR,
.msgtype = RTM_GETROUTE,
- .doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute},
+ .doit = ip6mr_rtm_getroute, .dumpit = ip6mr_rtm_dumproute,
+ .flags = RTNL_FLAG_DOIT_UNLOCKED},
};
int __init ip6_mr_init(void)
@@ -2715,6 +2716,8 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
grp = nla_get_in6_addr(tb[RTA_DST]);
tableid = nla_get_u32_default(tb[RTA_TABLE], 0);
+ rcu_read_lock();
+
mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
if (!mrt) {
NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
@@ -2722,10 +2725,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
goto err;
}
- /* entries are added/deleted only under RTNL */
- rcu_read_lock();
cache = ip6mr_cache_find(mrt, &src, &grp);
- rcu_read_unlock();
if (!cache) {
NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found");
err = -ENOENT;
@@ -2737,9 +2737,12 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
if (err < 0)
goto err;
+ rcu_read_unlock();
+
return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
err:
+ rcu_read_unlock();
kfree_skb(skb);
return err;
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 05/15] ip6mr: Allocate skb earlier in ip6mr_rtm_getroute().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
We will convert ip6mr_rtm_getroute() to RCU in the following patch,
where __ip6mr_get_table() will be called under RCU.
nlmsg_new() uses GFP_KERNEL and needs to be called before holding
rcu_read_lock().
As a prep, let's move nlmsg_new() before __ip6mr_get_table().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 4d78041276c1..db7376cbcc01 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2705,6 +2705,10 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
if (err < 0)
return err;
+ skb = nlmsg_new(mr6_msgsize(false), GFP_KERNEL);
+ if (!skb)
+ return -ENOBUFS;
+
if (tb[RTA_SRC])
src = nla_get_in6_addr(tb[RTA_SRC]);
if (tb[RTA_DST])
@@ -2714,7 +2718,8 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT);
if (!mrt) {
NL_SET_ERR_MSG_MOD(extack, "MR table does not exist");
- return -ENOENT;
+ err = -ENOENT;
+ goto err;
}
/* entries are added/deleted only under RTNL */
@@ -2723,21 +2728,20 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
rcu_read_unlock();
if (!cache) {
NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found");
- return -ENOENT;
+ err = -ENOENT;
+ goto err;
}
- skb = nlmsg_new(mr6_msgsize(false), GFP_KERNEL);
- if (!skb)
- return -ENOBUFS;
-
err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid,
nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0);
- if (err < 0) {
- kfree_skb(skb);
- return err;
- }
+ if (err < 0)
+ goto err;
return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+
+err:
+ kfree_skb(skb);
+ return err;
}
static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 04/15] ip6mr: Use MAXMIFS in mr6_msgsize().
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
mr6_msgsize() calculates skb size needed for ip6mr_fill_mroute().
The size differs based on mrt->maxvif.
We will drop RTNL for ip6mr_rtm_getroute() and mrt->maxvif may
change under RCU.
To avoid -EMSGSIZE, let's calculate the size with the maximum
value of mrt->maxvif, MAXMIFS.
struct rtnexthop is 8 bytes and MAXMIFS is 32, so the maximum delta
is 256 bytes, which is small enough.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 5d368ee39b28..4d78041276c1 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2542,7 +2542,7 @@ static int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
cmd, flags);
}
-static int mr6_msgsize(bool unresolved, int maxvif)
+static int mr6_msgsize(bool unresolved)
{
size_t len =
NLMSG_ALIGN(sizeof(struct rtmsg))
@@ -2555,7 +2555,7 @@ static int mr6_msgsize(bool unresolved, int maxvif)
len = len
+ nla_total_size(4) /* RTA_IIF */
+ nla_total_size(0) /* RTA_MULTIPATH */
- + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
+ + MAXMIFS * NLA_ALIGN(sizeof(struct rtnexthop))
/* RTA_MFC_STATS */
+ nla_total_size_64bit(sizeof(struct rta_mfc_stats))
;
@@ -2570,8 +2570,7 @@ static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
struct sk_buff *skb;
int err = -ENOBUFS;
- skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif),
- GFP_ATOMIC);
+ skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS), GFP_ATOMIC);
if (!skb)
goto errout;
@@ -2727,7 +2726,7 @@ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
return -ENOENT;
}
- skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL);
+ skb = nlmsg_new(mr6_msgsize(false), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 03/15] ip6mr: Annotate access to mrt->mroute_do_{pim,assert,wrvifwhole}.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
These fields in struct mr_table are updated in ip6_mroute_setsockopt()
under RTNL:
* mroute_do_pim
* mroute_do_assert
* mroute_do_wrvifwhole
However, ip6_mroute_getsockopt() does not hold RTNL and read the first
two fields locklessly, and ip6_mr_forward() reads all the three under
RCU.
Let's use WRITE_ONCE() and READ_ONCE() for them.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv6/ip6mr.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 5755244e226c..5d368ee39b28 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1783,7 +1783,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
return -EINVAL;
if (copy_from_sockptr(&v, optval, sizeof(v)))
return -EFAULT;
- mrt->mroute_do_assert = v;
+ WRITE_ONCE(mrt->mroute_do_assert, v);
return 0;
}
@@ -1803,9 +1803,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval,
rtnl_lock();
ret = 0;
if (v != mrt->mroute_do_pim) {
- mrt->mroute_do_pim = v;
- mrt->mroute_do_assert = v;
- mrt->mroute_do_wrvifwhole = do_wrmifwhole;
+ WRITE_ONCE(mrt->mroute_do_pim, v);
+ WRITE_ONCE(mrt->mroute_do_assert, v);
+ WRITE_ONCE(mrt->mroute_do_wrvifwhole, do_wrmifwhole);
}
rtnl_unlock();
return ret;
@@ -1873,11 +1873,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval,
break;
#ifdef CONFIG_IPV6_PIMSM_V2
case MRT6_PIM:
- val = mrt->mroute_do_pim;
+ val = READ_ONCE(mrt->mroute_do_pim);
break;
#endif
case MRT6_ASSERT:
- val = mrt->mroute_do_assert;
+ val = READ_ONCE(mrt->mroute_do_assert);
break;
default:
return -ENOPROTOOPT;
@@ -2180,20 +2180,20 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt,
if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) {
atomic_long_inc(&c->_c.mfc_un.res.wrong_if);
- if (true_vifi >= 0 && mrt->mroute_do_assert &&
+ if (true_vifi >= 0 && READ_ONCE(mrt->mroute_do_assert) &&
/* pimsm uses asserts, when switching from RPT to SPT,
so that we cannot check that packet arrived on an oif.
It is bad, but otherwise we would need to move pretty
large chunk of pimd to kernel. Ough... --ANK
*/
- (mrt->mroute_do_pim ||
+ (READ_ONCE(mrt->mroute_do_pim) ||
c->_c.mfc_un.res.ttls[true_vifi] < 255) &&
time_after(jiffies,
c->_c.mfc_un.res.last_assert +
MFC_ASSERT_THRESH)) {
c->_c.mfc_un.res.last_assert = jiffies;
ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF);
- if (mrt->mroute_do_wrvifwhole)
+ if (READ_ONCE(mrt->mroute_do_wrvifwhole))
ip6mr_cache_report(mrt, skb, true_vifi,
MRT6MSG_WRMIFWHOLE);
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 02/15] ipmr: Convert mr_table.cache_resolve_queue_len to u32.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
mr_table.cache_resolve_queue_len is always updated under
spin_lock_bh(&mfc_unres_lock).
Let's convert it to u32.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
include/linux/mroute_base.h | 2 +-
net/ipv4/ipmr.c | 18 ++++++++++--------
net/ipv6/ip6mr.c | 11 +++++++----
3 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/include/linux/mroute_base.h b/include/linux/mroute_base.h
index cf3374580f74..3341acca002f 100644
--- a/include/linux/mroute_base.h
+++ b/include/linux/mroute_base.h
@@ -254,7 +254,7 @@ struct mr_table {
struct rhltable mfc_hash;
struct list_head mfc_cache_list;
int maxvif;
- atomic_t cache_resolve_queue_len;
+ u32 cache_resolve_queue_len;
bool mroute_do_assert;
bool mroute_do_pim;
bool mroute_do_wrvifwhole;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 8a08d09b4c30..2566b4a1f80b 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -756,7 +756,8 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
struct sk_buff *skb;
struct nlmsgerr *e;
- atomic_dec(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len - 1);
while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
@@ -1178,11 +1179,12 @@ static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
return err;
}
- atomic_inc(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len + 1);
list_add(&c->_c.list, &mrt->mfc_unres_queue);
mroute_netlink_event(mrt, c, RTM_NEWROUTE);
- if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
+ if (mrt->cache_resolve_queue_len == 1)
mod_timer(&mrt->ipmr_expire_timer,
c->_c.mfc_un.unres.expires);
}
@@ -1287,7 +1289,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
if (uc->mfc_origin == c->mfc_origin &&
uc->mfc_mcastgrp == c->mfc_mcastgrp) {
list_del(&_uc->list);
- atomic_dec(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len - 1);
found = true;
break;
}
@@ -1346,7 +1349,7 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags,
}
if (flags & MRT_FLUSH_MFC) {
- if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
+ if (READ_ONCE(mrt->cache_resolve_queue_len)) {
spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
list_del(&c->list);
@@ -2954,10 +2957,9 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh,
static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)
{
- u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len);
-
if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) ||
- nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) ||
+ nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN,
+ READ_ONCE(mrt->cache_resolve_queue_len)) ||
nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM,
READ_ONCE(mrt->mroute_reg_vif_num)) ||
nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT,
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 85010ff21c98..5755244e226c 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -792,7 +792,8 @@ static void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c)
struct net *net = read_pnet(&mrt->net);
struct sk_buff *skb;
- atomic_dec(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len - 1);
while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) {
if (ipv6_hdr(skb)->version == 0) {
@@ -1205,7 +1206,8 @@ static int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi,
return err;
}
- atomic_inc(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len + 1);
list_add(&c->_c.list, &mrt->mfc_unres_queue);
mr6_netlink_event(mrt, c, RTM_NEWROUTE);
@@ -1510,7 +1512,8 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) {
list_del(&_uc->list);
- atomic_dec(&mrt->cache_resolve_queue_len);
+ WRITE_ONCE(mrt->cache_resolve_queue_len,
+ mrt->cache_resolve_queue_len - 1);
found = true;
break;
}
@@ -1568,7 +1571,7 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags)
}
if (flags & MRT6_FLUSH_MFC) {
- if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
+ if (READ_ONCE(mrt->cache_resolve_queue_len)) {
spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
list_del(&c->list);
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 01/15] selftest: net: Extend ipmr.c for IP6MR.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
This commit extends most test cases in ipmr.c for IPV6MR.
Note that IP6MR does not provide rtnetlink interface for MFC,
so such tests will be skipped.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
tools/testing/selftests/net/forwarding/ipmr.c | 163 ++++++++++++------
1 file changed, 110 insertions(+), 53 deletions(-)
diff --git a/tools/testing/selftests/net/forwarding/ipmr.c b/tools/testing/selftests/net/forwarding/ipmr.c
index df870aad9ead..cfd00173bcd6 100644
--- a/tools/testing/selftests/net/forwarding/ipmr.c
+++ b/tools/testing/selftests/net/forwarding/ipmr.c
@@ -2,7 +2,9 @@
/* Copyright 2026 Google LLC */
#include <linux/if.h>
+#include <linux/in6.h>
#include <linux/mroute.h>
+#include <linux/mroute6.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/socket.h>
@@ -17,6 +19,14 @@ FIXTURE(ipmr)
int netlink_sk;
int raw_sk;
int veth_ifindex;
+ union {
+ struct vifctl vif;
+ struct mif6ctl vif6;
+ };
+ union {
+ struct mfcctl mfc;
+ struct mf6cctl mfc6;
+ };
};
FIXTURE_VARIANT(ipmr)
@@ -25,6 +35,11 @@ FIXTURE_VARIANT(ipmr)
int protocol;
int level;
int opts[MRT_MAX - MRT_BASE + 1];
+ int vif_size;
+ char vif_check_cmd_pimreg[64];
+ char vif_check_cmd_veth[64];
+ int mfc_size;
+ char mfc_check_cmd[1024];
};
FIXTURE_VARIANT_ADD(ipmr, ipv4)
@@ -47,6 +62,39 @@ FIXTURE_VARIANT_ADD(ipmr, ipv4)
MRT_DEL_MFC_PROXY,
MRT_FLUSH,
},
+ .vif_size = sizeof(struct vifctl),
+ .vif_check_cmd_pimreg = "cat /proc/net/ip_mr_vif | grep -q pimreg",
+ .vif_check_cmd_veth = "cat /proc/net/ip_mr_vif | grep -q veth",
+ .mfc_size = sizeof(struct mfcctl),
+ .mfc_check_cmd = "cat /proc/net/ip_mr_cache | grep -q '00000000 00000000'",
+};
+
+FIXTURE_VARIANT_ADD(ipmr, ipv6)
+{
+ .family = AF_INET6,
+ .protocol = IPPROTO_ICMPV6,
+ .level = IPPROTO_IPV6,
+ .opts = {
+ MRT6_INIT,
+ MRT6_DONE,
+ MRT6_ADD_MIF,
+ MRT6_DEL_MIF,
+ MRT6_ADD_MFC,
+ MRT6_DEL_MFC,
+ MRT6_VERSION,
+ MRT6_ASSERT,
+ MRT6_PIM,
+ MRT6_TABLE,
+ MRT6_ADD_MFC_PROXY,
+ MRT6_DEL_MFC_PROXY,
+ MRT_FLUSH,
+ },
+ .vif_size = sizeof(struct mif6ctl),
+ .vif_check_cmd_pimreg = "cat /proc/net/ip6_mr_vif | grep -q pim6reg",
+ .vif_check_cmd_veth = "cat /proc/net/ip6_mr_vif | grep -q veth",
+ .mfc_size = sizeof(struct mf6cctl),
+ .mfc_check_cmd = "cat /proc/net/ip6_mr_cache | "
+ "grep -q '0000:0000:0000:0000:0000:0000:0000:0000 0000:0000:0000:0000:0000:0000:0000:0000'",
};
struct mfc_attr {
@@ -144,6 +192,18 @@ FIXTURE_SETUP(ipmr)
ASSERT_EQ(0, err);
self->veth_ifindex = ifr.ifr_ifindex;
+
+ if (variant->family == AF_INET) {
+ self->vif = (struct vifctl){
+ .vifc_flags = VIFF_USE_IFINDEX,
+ .vifc_lcl_ifindex = self->veth_ifindex,
+ };
+ } else {
+ self->vif6 = (struct mif6ctl){
+ .mif6c_flags = 0,
+ .mif6c_pifi = self->veth_ifindex,
+ };
+ }
}
FIXTURE_TEARDOWN(ipmr)
@@ -169,41 +229,39 @@ TEST_F(ipmr, mrt_init)
TEST_F(ipmr, mrt_add_vif_register)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_REGISTER,
- };
int err;
+ memset(&self->vif, 0, variant->vif_size);
+
+ if (variant->family == AF_INET)
+ self->vif.vifc_flags = VIFF_REGISTER;
+ else
+ self->vif6.mif6c_flags = MIFF_REGISTER;
+
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_vif | grep -q pimreg");
+ err = system(variant->vif_check_cmd_pimreg);
ASSERT_EQ(0, err);
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
}
TEST_F(ipmr, mrt_del_vif_unreg)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_USE_IFINDEX,
- .vifc_lcl_ifindex = self->veth_ifindex,
- };
int err;
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_vif | grep -q veth0");
+ err = system(variant->vif_check_cmd_veth);
ASSERT_EQ(0, err);
/* VIF is removed along with its device. */
@@ -213,23 +271,18 @@ TEST_F(ipmr, mrt_del_vif_unreg)
/* mrt->vif_table[veth_ifindex]->dev is NULL. */
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_DEL_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(-1, err);
ASSERT_EQ(EADDRNOTAVAIL, errno);
}
TEST_F(ipmr, mrt_del_vif_netns_dismantle)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_USE_IFINDEX,
- .vifc_lcl_ifindex = self->veth_ifindex,
- };
int err;
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
/* Let cleanup_net() remove veth0 and VIF. */
@@ -237,49 +290,49 @@ TEST_F(ipmr, mrt_del_vif_netns_dismantle)
TEST_F(ipmr, mrt_add_mfc)
{
- struct mfcctl mfc = {};
int err;
/* MRT_ADD_MFC / MRT_ADD_MFC_PROXY does not need vif to exist (unlike netlink). */
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE],
- &mfc, sizeof(mfc));
+ &self->mfc, variant->mfc_size);
ASSERT_EQ(0, err);
/* (0.0.0.0 -> 0.0.0.0) */
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_DEL_MFC - MRT_BASE],
- &mfc, sizeof(mfc));
+ &self->mfc, variant->mfc_size);
}
TEST_F(ipmr, mrt_add_mfc_proxy)
{
- struct mfcctl mfc = {};
int err;
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_MFC_PROXY - MRT_BASE],
- &mfc, sizeof(mfc));
+ &self->mfc, variant->mfc_size);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_DEL_MFC_PROXY - MRT_BASE],
- &mfc, sizeof(mfc));
+ &self->mfc, variant->mfc_size);
}
+#define SKIP_IPV6() \
+ do { \
+ if (variant->family == AF_INET6) \
+ SKIP(return, \
+ "no netlink MFC interface"); \
+ } while (0)
+
TEST_F(ipmr, mrt_add_mfc_netlink)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_USE_IFINDEX,
- .vifc_lcl_ifindex = self->veth_ifindex,
- };
struct mfc_attr mfc_attr = {
.table = RT_TABLE_DEFAULT,
.origin = 0,
@@ -289,15 +342,17 @@ TEST_F(ipmr, mrt_add_mfc_netlink)
};
int err;
+ SKIP_IPV6();
+
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr);
@@ -306,11 +361,6 @@ TEST_F(ipmr, mrt_add_mfc_netlink)
TEST_F(ipmr, mrt_add_mfc_netlink_proxy)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_USE_IFINDEX,
- .vifc_lcl_ifindex = self->veth_ifindex,
- };
struct mfc_attr mfc_attr = {
.table = RT_TABLE_DEFAULT,
.origin = 0,
@@ -320,15 +370,17 @@ TEST_F(ipmr, mrt_add_mfc_netlink_proxy)
};
int err;
+ SKIP_IPV6();
+
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
err = nl_sendmsg_mfc(_metadata, self, RTM_DELROUTE, &mfc_attr);
@@ -345,6 +397,8 @@ TEST_F(ipmr, mrt_add_mfc_netlink_no_vif)
};
int err;
+ SKIP_IPV6();
+
/* netlink always requires RTA_IIF of an existing vif. */
mfc_attr.ifindex = 0;
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
@@ -378,6 +432,8 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
};
int i, err;
+ SKIP_IPV6();
+
for (i = 0; i < 2; i++) {
/* Create 2 VIFs just to avoid -ENFILE later. */
err = setsockopt(self->raw_sk,
@@ -390,7 +446,7 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
ASSERT_EQ(0, err);
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
/* Remove mrt->vif_table[0]. */
@@ -398,7 +454,7 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
ASSERT_EQ(0, err);
/* MFC entry is NOT removed even if the tied VIF is removed... */
- err = system("cat /proc/net/ip_mr_cache | grep -q '00000000 00000000' ");
+ err = system(variant->mfc_check_cmd);
ASSERT_EQ(0, err);
/* ... and netlink is not capable of removing such an entry
@@ -412,11 +468,6 @@ TEST_F(ipmr, mrt_del_mfc_netlink_netns_dismantle)
TEST_F(ipmr, mrt_table_flush)
{
- struct vifctl vif = {
- .vifc_vifi = 0,
- .vifc_flags = VIFF_USE_IFINDEX,
- .vifc_lcl_ifindex = self->veth_ifindex,
- };
struct mfc_attr mfc_attr = {
.origin = 0,
.group = 0,
@@ -436,11 +487,17 @@ TEST_F(ipmr, mrt_table_flush)
err = setsockopt(self->raw_sk,
variant->level, variant->opts[MRT_ADD_VIF - MRT_BASE],
- &vif, sizeof(vif));
+ &self->vif, variant->vif_size);
ASSERT_EQ(0, err);
- mfc_attr.table = table_id;
- err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
+ if (variant->family == AF_INET) {
+ mfc_attr.table = table_id;
+ err = nl_sendmsg_mfc(_metadata, self, RTM_NEWROUTE, &mfc_attr);
+ } else {
+ err = setsockopt(self->raw_sk,
+ variant->level, variant->opts[MRT_ADD_MFC - MRT_BASE],
+ &self->mfc, variant->mfc_size);
+ }
ASSERT_EQ(0, err);
/* Flush mrt->vif_table[] and all caches. */
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related
* [PATCH v3 net-next 00/15] ip6mr: No RTNL for RTNL_FAMILY_IP6MR rtnetlink.
From: Kuniyuki Iwashima @ 2026-04-07 21:19 UTC (permalink / raw)
To: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
This series is the IPv6 version of
https://lore.kernel.org/netdev/20260228221800.1082070-1-kuniyu@google.com/
and removes RTNL from ip6mr rtnetlink handlers.
After this series, there are a few RTNL left in net/ipv6/ipmr.c
and such users will be converted to per-netns RTNL in another
series.
Patch 1 extends the ipmr selftest to exercise most of the RTNL
paths in net/ipv6/ipmr.c
Patch 2 is misc cleanup.
Patch 3 - 7 converts RTM_GETROUTE handlers to RCU.
Patch 8 removes struct fib_dump_filter.rtnl_held.
Patch 9 - 11 converts ->exit_batch() to ->exit_rtnl() to
save one RTNL in cleanup_net().
Patch 12 - 13 removes unnecessary RTNL during setup_net()
failure.
Patch 14 is cleanup.
Patch 15 drops RTNL for MRT6_(ADD|DEL)_MFC(_PROXY)?.
Kuniyuki Iwashima (15):
selftest: net: Extend ipmr.c for IP6MR.
ipmr: Convert mr_table.cache_resolve_queue_len to u32.
ip6mr: Annotate access to mrt->mroute_do_{pim,assert,wrvifwhole}.
ip6mr: Use MAXMIFS in mr6_msgsize().
ip6mr: Allocate skb earlier in ip6mr_rtm_getroute().
ip6mr: Convert ip6mr_rtm_getroute() to RCU.
ip6mr: Convert ip6mr_rtm_dumproute() to RCU.
net: Remove rtnl_held of struct fib_dump_filter.
ip6mr: Move unregister_netdevice_many() out of mroute_clean_tables().
ip6mr: Move unregister_netdevice_many() out of ip6mr_free_table().
ip6mr: Convert ip6mr_net_exit_batch() to ->exit_rtnl().
ip6mr: Remove RTNL in ip6mr_rules_init() and ip6mr_net_init().
ip6mr: Call fib_rules_unregister() without RTNL.
ip6mr: Define net->ipv6.{ip6mr_notifier_ops,ipmr_seq} under
CONFIG_IP_MROUTE.
ip6mr: Replace RTNL with a dedicated mutex for MFC.
include/linux/mroute_base.h | 2 +-
include/net/ip_fib.h | 1 -
include/net/netns/ipv6.h | 5 +-
net/ipv4/fib_frontend.c | 19 +-
net/ipv4/ipmr.c | 22 +-
net/ipv6/ip6_fib.c | 1 -
net/ipv6/ip6mr.c | 195 +++++++++++-------
net/mpls/af_mpls.c | 6 +-
tools/testing/selftests/net/forwarding/ipmr.c | 163 ++++++++++-----
9 files changed, 252 insertions(+), 162 deletions(-)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply
* Re: [PATCH 0/8] smb: add kernel internal IPPROTO_SMBDIRECT
From: Steve French @ 2026-04-07 21:18 UTC (permalink / raw)
To: Stefan Metzmacher
Cc: linux-cifs, samba-technical, Tom Talpey, Long Li, Namjae Jeon,
David Howells, Henrique Carvalho, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
Willem de Bruijn, netdev, Xin Long, quic, linux-rdma,
linux-kernel, Mark Brown, linux-next
In-Reply-To: <cover.1775571957.git.metze@samba.org>
merged the first 4 into smb3-kernel ksmbd-for-next
On Tue, Apr 7, 2026 at 9:47 AM Stefan Metzmacher <metze@samba.org> wrote:
>
> Hi,
>
> as the work to unify the smbdirect code
> between cifs.ko and ksmbd.ko into an smbdirect.ko
> is in linux-next for a while this is the next
> step to also share the code with userspace
> e.g. Samba as client and server.
>
> The SMBDIRECT protocol, defined in [MS-SMBD] by Microsoft.
> It is used as wrapper around RDMA in order to provide a transport for SMB3,
> but Microsoft also uses it as transport for other protocols.
>
> SMBDIRECT works over Infiniband, RoCE and iWarp. RoCEv2 is based on IP/UDP
> and iWarp is based on IP/TCP, so these use IP addresses natively.
> Infiniband and RoCEv1 require IPOIB in order to be used for SMBDIRECT.
>
> So instead of adding a PF_SMBDIRECT, which would only use AF_INET[6],
> we use IPPROTO_SMBDIRECT instead, this uses a number not
> allocated from IANA, as it would not appear in an IP header.
>
> This is similar to IPPROTO_SMC, IPPROTO_MPTCP and IPPROTO_QUIC,
> which are linux specific values for the socket() syscall.
>
> socket(AF_INET, SOCK_STREAM, IPPROTO_SMBDIRECT);
> socket(AF_INET6, SOCK_STREAM, IPPROTO_SMBDIRECT);
>
> This will allow the existing smbdirect code used by
> cifs.ko and ksmbd.ko to be moved behind the socket layer [1],
> so that there's less special handling. Only sock_sendmsg()
> sock_recvmsg() are used, so that the main stream handling
> is done all the same for tcp, smbdirect and later also quic.
>
> The special RDMA read/write handling will be via direct
> function calls as they are currently done for the in kernel
> consumers.
>
> As a start __sock_create(kern=0)/sk->sk_kern_sock == 0 will
> still cause a -EPROTONOSUPPORT. So only in kernel consumers
> will be supported for now.
>
> For now the core smbdirect code still supports both
> modes, direct calls in indirect via the socket layer.
> The core code uses if (sc->sk.sk_family) as indication
> for the new socket mode. Once cifs.ko and ksmbd.ko
> are converted we can remove the old mode slowly,
> but I'll deferr that to a future patchset.
>
> There's still a way to go in order to make this
> as generic as tcp and quic e.g. adding MSG_SPLICE_PAGES support or
> splice_read/read_sock/read_skb.
>
> But it's a good start, which will make changes much easier.
>
> This patchset is based on top of the smbdirect.ko patches
> in ksmbd-for-next and it's also based on netdev-next
> because it needs this commit from there:
> 8341c989ac77d712c7d6e2bce29e8a4bcb2eeae4
> "net: remove addr_len argument of recvmsg() handlers"
>
> Patches 1-4 are some preparation fixes for the
> existing smbdirect.ko patchset. We may
> squash them into the existing patches before
> sending them to Linus. But for now I kept
> them separately. I also don't cc the network and rdma
> people on these...
>
> Patch 5 defines IPPROTO_SMBDIRECT and SOL_SMBDIRECT
> constants (and also reserve the number 288 for SOL_QUIC
> as applications are already using that and we don't
> want to conflict)
>
> Patch 6 adds basic IPPROTO_SMBDIRECT layering
> on top of the existing smbdirect code.
> This is just enough in order to let cifs.ko
> and ksmbd.ko use it in the following commits,
> so userspace still sees -EPROTONOSUPPORT.
>
> Patch 7 converts cifs.ko to use IPPROTO_SMBDIRECT
> as much as currently possible.
> Using sock_sendmsg is not yet possible, because of the
> tcp_sock_set_cork usage, but that will change in future.
>
> Patch 8 converts ksmbd.ko to use IPPROTO_SMBDIRECT.
>
> Because of the need for netdev-next commit
> 8341c989ac77d712c7d6e2bce29e8a4bcb2eeae4
> "net: remove addr_len argument of recvmsg() handlers"
> it's a bit tricky to prepare something that would not
> cause problems in linux-next.
>
> We could merge in netdev-next completely,
> but I saw commit 00f03539e3d97af925abf42992d8c46167d54243
> in next-20260406, indicates some resolved conflicts
> with (at least) the rdma tree, which comes
> just before netdev-next, while ksmbd-for-next is merged
> before all of them. So merging netdev-next into
> ksmbd-for-next changes the order of netdev-next vs. rdma.
>
> Or we add a new branch smbdirect-for-next that's
> merged into linux-next after netdev-next.
>
> Or a bit hacky but likely easier, ksmbd-for-next
> just cherry-picks 8341c989ac77d712c7d6e2bce29e8a4bcb2eeae4
> "net: remove addr_len argument of recvmsg() handlers"
> and that cherry picked commit is reverted as
> last commit in ksmbd-for-next, or only in linux-next
> right before netdev-next is merged.
>
> For now I have the patches in a branch with a
> cherry picked version of the needed commit only, see
> for-7.1/ipproto-smbdirect-20260407-v1 at commit:
> e1972e6f1fda9842c5724b7daf4a2aa7779901a5
> git fetch https://git.samba.org/metze/linux/wip.git for-7.1/ipproto-smbdirect-20260407-v1
> https://git.samba.org/?p=metze/linux/wip.git;a=shortlog;h=refs/heads/for-7.1/ipproto-smbdirect-20260407-v1
>
> It would be great to get this somehow into linux-next soon :-)
>
> Stefan Metzmacher (8):
> smb: smbdirect: change
> smbdirect_socket_parameters.{initiator_depth,responder_resources} to
> __u16
> smb: smbdirect: fix copyright header of smbdirect.h
> smb: smbdirect: fix the logic in smbdirect_socket_destroy_sync()
> without an error
> smb: smbdirect: let smbdirect_connection_deregister_mr_io unlock while
> waiting
> net: define IPPROTO_SMBDIRECT and SOL_SMBDIRECT constants
> smb: smbdirect: add in kernel only support for IPPROTO_SMBDIRECT
> smb: client: make use of IPPROTO_SMBDIRECT sockets
> smb: server: make use of IPPROTO_SMBDIRECT sockets
>
> fs/smb/client/cifs_debug.c | 2 +-
> fs/smb/client/cifsfs.c | 2 +-
> fs/smb/client/cifsglob.h | 7 +-
> fs/smb/client/connect.c | 123 +-
> fs/smb/client/file.c | 8 +-
> fs/smb/client/sess.c | 4 +-
> fs/smb/client/smb2ops.c | 8 +-
> fs/smb/client/smb2pdu.c | 10 +-
> fs/smb/client/smbdirect.c | 275 +--
> fs/smb/client/smbdirect.h | 27 +-
> fs/smb/client/transport.c | 2 +-
> fs/smb/common/smbdirect/Makefile | 1 +
> fs/smb/common/smbdirect/smbdirect.h | 69 +-
> fs/smb/common/smbdirect/smbdirect_accept.c | 14 +-
> .../common/smbdirect/smbdirect_connection.c | 58 +
> fs/smb/common/smbdirect/smbdirect_devices.c | 2 +-
> fs/smb/common/smbdirect/smbdirect_internal.h | 59 +-
> fs/smb/common/smbdirect/smbdirect_listen.c | 49 +-
> fs/smb/common/smbdirect/smbdirect_main.c | 45 +
> fs/smb/common/smbdirect/smbdirect_mr.c | 18 +
> fs/smb/common/smbdirect/smbdirect_proto.c | 1549 +++++++++++++++++
> fs/smb/common/smbdirect/smbdirect_public.h | 3 +
> fs/smb/common/smbdirect/smbdirect_rw.c | 29 +-
> fs/smb/common/smbdirect/smbdirect_socket.c | 180 +-
> fs/smb/common/smbdirect/smbdirect_socket.h | 26 +-
> fs/smb/server/transport_rdma.c | 119 +-
> include/linux/socket.h | 2 +
> include/uapi/linux/in.h | 2 +
> 28 files changed, 2320 insertions(+), 373 deletions(-)
> create mode 100644 fs/smb/common/smbdirect/smbdirect_proto.c
>
> --
> 2.43.0
>
--
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH net-next v2 3/4] bpf-timestamp: keep track of the skb when wait_for_space occurs
From: Willem de Bruijn @ 2026-04-07 21:17 UTC (permalink / raw)
To: Jason Xing, Willem de Bruijn
Cc: davem, edumazet, kuba, pabeni, horms, willemb, martin.lau, netdev,
bpf, Jason Xing, Yushan Zhou
In-Reply-To: <CAL+tcoCctfs=d9zz5ei1S999S94HmUDSToriZ8NJiLT8MmpQTA@mail.gmail.com>
Jason Xing wrote:
> On Tue, Apr 7, 2026 at 11:33 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> >
> > On Mon, Apr 6, 2026 at 10:37 PM Willem de Bruijn
> > <willemdebruijn.kernel@gmail.com> wrote:
> > >
> > > Jason Xing wrote:
> > > > On Mon, Apr 6, 2026 at 10:28 AM Willem de Bruijn
> > > > <willemdebruijn.kernel@gmail.com> wrote:
> > > > >
> > > > > Jason Xing wrote:
> > > > > > From: Jason Xing <kernelxing@tencent.com>
> > > > > >
> > > > > > The patch is the 1/2 part of push-level granularity feature.
> > > > > >
> > > > > > Tag the skb in tcp_sendmsg_locked() when wait_for_space occurs even
> > > > > > though it might not carry the last byte of the sendmsg.
> > > > > >
> > > > > > Prior to the patch, BPF timestamping cannot cover this case:
> > > > > > The following steps reproduce this:
> > > > > > 1) skb A is the current last skb before entering wait_for_space process
> > > > > > 2) tcp_push() pushes A without any tag
> > > > > > 3) A is transmitted from TCP to driver without putting any skb carrying
> > > > > > timestamps in the error queue, like SCHED, DRV/HARDWARE.
> > > > > > 4) sk_stream_wait_memory() sleeps for a while and then returns with an
> > > > > > error code. Note that the socket lock is released.
> > > > > > 5) skb A finally gets acked and removed from the rtx queue.
> > > > > > 6) continue with the rest of tcp_sendmsg_locked(): it will jump to(goto)
> > > > > > 'do_error' label and then 'out' label.
> > > > > > 7) at this moment, skb A turns out to be the last one in this send
> > > > > > syscall, and miss the following tcp_bpf_tx_timestamp() opportunity
> > > > > > before the final tcp_push()
> > > > > > 8) BPF script fails to see any timestamps this time
> > > > > >
> > > > > > Signed-off-by: Yushan Zhou <katrinzhou@tencent.com>
> > > > > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > > > > ---
> > > > > > net/ipv4/tcp.c | 4 +++-
> > > > > > 1 file changed, 3 insertions(+), 1 deletion(-)
> > > > > >
> > > > > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> > > > > > index c603b90057f6..7d030a11d004 100644
> > > > > > --- a/net/ipv4/tcp.c
> > > > > > +++ b/net/ipv4/tcp.c
> > > > > > @@ -1400,9 +1400,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
> > > > > > wait_for_space:
> > > > > > set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
> > > > > > tcp_remove_empty_skb(sk);
> > > > > > - if (copied)
> > > > > > + if (copied) {
> > > > > > + tcp_bpf_tx_timestamp(sk);
> > > > > > tcp_push(sk, flags & ~MSG_MORE, mss_now,
> > > > > > TCP_NAGLE_PUSH, size_goal);
> > > > >
> > > > > Now the number of skbs that will be tracked will be unpredictable,
> > > > > varying based on memory pressure.
> > > >
> > > > Right, I put some effort into writing a selftests to check how many
> > > > push functions get called at one time and failed to do so.
> > > >
> > > > >
> > > > > That sounds hard to use to me. Especially if these extra pushes
> > > > > cannot be identified as such.
> > > > >
> > > > > Perhaps if all skbs from the same sendmsg call can be identified,
> > > > > that would help explain pattern in data resulting from these
> > > > > uncommon extra data points.
> > > >
> > > > You meant move tcp_bpf_tx_timestamp before tcp_skb_entail()? That is
> > > > close to packet basis without considering fragmentation of skb :)
> > >
> > > No, I meant somehow in the notification having a way to identify all
> > > the skbs belonging to the same sendmsg call, to allow filtering on
> > > that. But I also don't immediately see how to do that (without adding
> > > yet another counter say).
> >
> > If we don't build the relationship between skb and sendmsg (just like
> > the SENDMSG sock option), we will have no clue on how to calculate. If
> > we only take care of the skb from the view of the syscall layer, it's
> > fine by moving tcp_bpf_tx_timestamp() before tcp_skb_entail(). But in
> > terms of per skb even generated beneath TCP due to gso/tso, there is
> > only one way to correlate: adding an additional member in the skb
> > structure to store its sendmsg time. This discussion is only suitable
> > for use cases like net_timestamping.
> >
> > Well, my key point is that, I have to admit, the above (including
> > existing bpf script net_timestamping) is a less effective way which
> > definitely harms the performance because of the extremely frequent
> > look-up process. It's not suitable for 7x24 observability in
> > production. What we've done internally is make the kernel layer as
> > lightweight/easy as possible and let the timestamping feature throw
> > each record into a ring buffer that the application can read, sort and
> > calculate. This arch survives the performance. But that's simply what
> > the design of the kernel module is, given the fast deployment in
> > production. I suppose in the future we could build a userspace tool
> > like blktrace to monitor efficiently instead of the selftest sample.
> > Honestly I don't like look-up action.
> >
> > Since we're modifying the kernel, how about adding a new member to
> > record sendmsg time which bpf script is able to read. The whole
> > scenario looks like this:
> > 1) in tcp_sendmsg_locked(), record the sendmsg time for each skb
> > 2) in either tso_fragment() or tcp_gso_tstamp(), each new skb will get
> > a copy of its original skb
> > 3) in each stage, bpf script reads the skb's sendmsg time and the
> > current time, and then effortlessly do the math.
> >
> > At this point, what I had in mind is we have two options:
> > 1) only handle the skb from the view of the send syscall layer, which
> > is, for sure, very simple but not thorough.
> > 2) stick to a pure authentic packet basis, then adding a new member
> > seems inevitable. so the question would be where to add? The space of
> > the skb structure is very precious :(
>
> Finding a suitable place to put this timestamp is really hard. IIRC,
> we can't expand the size of struct skb_shared_info so easily since
> it's a global effect.
>
> I'm wondering if we can turn the per-packet mode into a non-compatible
> feature by reusing 'u32 tskey' to store a microsecond timestamp of
> sendmsg.
Agreed that an extra field is hard. We should avoid that.
If the purpose is to group skbs by sendmsg call (e.g., to filter out
all but the last one), it is probably also unnecessary.
From a process PoV, since the process knows the sendmsg len and each
skb has a tskey in byte offset, it can correlate the skb with a given
sendmsg buffer.
The BPF program is under control of a third-party admin. So that does
not follow directly. But it can be passed additional metadata.
I thought about passing the offset of the skb from the start of the
sendmsg buffer to identify all consecutive skbs for a sendmsg call,
as each new buffer will start with an skb with offset 0 ..
.. but that won't work as there is no guarantee that a sendmsg call
will not append to an existing outstanding skb.
Anyway, the general idea is to pass to the BPF program through
bpf_skops_tx_timestamping some relevant signal , without having to
expand either skb or sk itself.
I hear you on that measuring every skb is too frequent. But is calling
the BPF program and letting it decide whether to measure too? BPF
program invocation itself should be cheap.
If per-push is preferable, with a filter ability like the above, it
seems more useful to me already.
^ permalink raw reply
* Re: [PATCH net-next] net: bcmasp: Switch to page pool for RX path
From: Nicolai Buchwitz @ 2026-04-07 21:04 UTC (permalink / raw)
To: Florian Fainelli
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Justin Chen, Vikas Gupta,
Bhargava Marreddy, Rajashekar Hudumula, Arnd Bergmann,
Eric Biggers, Markus Blöchl, Heiner Kallweit,
Fernando Fernandez Mancera, linux-kernel,
bcm-kernel-feedback-list
In-Reply-To: <20260407162658.4056141-1-florian.fainelli@broadcom.com>
On 7.4.2026 18:26, Florian Fainelli wrote:
> This shows an improvement of 1.9% in reducing the CPU cycles and data
> cache misses.
>
> Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
> ---
> [...]
>
> -static int bcmasp_alloc_buffers(struct bcmasp_intf *intf)
> +static struct page_pool *
> +bcmasp_rx_page_pool_create(struct bcmasp_intf *intf)
> +{
> + struct page_pool_params pp_params = {
> + .order = 0,
> + /* Pages are CPU-side copy targets; no DMA mapping needed. */
> + .flags = 0,
> + .pool_size = NUM_4K_BUFFERS,
> + .nid = NUMA_NO_NODE,
> + .dev = &intf->parent->pdev->dev,
> + .dma_dir = DMA_FROM_DEVICE,
Nit: .dma_dir has AFAIK no effect without PP_FLAG_DMA_MAP, so
it's a bit misleading next to the "no DMA mapping needed" comment.
> + .offset = 0,
> + .max_len = PAGE_SIZE,
> + };
Other Broadcom page pool drivers set .napi and .netdev. Adding those
here would enable direct recycling and expose pool stats in ethtool -S.
> [...]
Thanks
Nicolai
^ permalink raw reply
* Re: [PATCH] net: lpc_eth: Fix a possible memory leak in lpc_mii_probe()
From: Vladimir Zapolskiy @ 2026-04-07 20:58 UTC (permalink / raw)
To: Ma Ke
Cc: alexandre.belloni, andrew+netdev, davem, edumazet, kuba,
linux-arm-kernel, linux-kernel, netdev, pabeni, piotr.wojtaszczyk,
stable
In-Reply-To: <20260401131813.139167-1-make24@iscas.ac.cn>
Hello Ma Ke.
On 4/1/26 16:18, Ma Ke wrote:
> On 3/30/26 13:04, Vladimir Zapolskiy wrote:
>> On 3/30/26 11:16, Ma Ke wrote:
>>> lpc_mii_probe() calls of_phy_find_device() to obtain a phy_device
>>> pointer. of_phy_find_device() increments the refcount of the device.
>>> The current implementation does not decrement the refcount after using
>>> the pointer, which leads to a memory leak.
>>
>> this is correct, there is an actual detected bug.
>>
>>>
>>> Add phy_device_free() to balance the refcount.
>>
>> But this does not sound right, you shoud use of_node_put(pldat->phy_node).
>>
>>>
>>> Found by code review.
>>>
>>> Signed-off-by: Ma Ke <make24@iscas.ac.cn>
>>> Cc: stable@vger.kernel.org
>>> Fixes: 3503bf024b3e ("net: lpc_eth: parse phy nodes from device tree")
>>> ---
>>> drivers/net/ethernet/nxp/lpc_eth.c | 11 ++++++-----
>>> 1 file changed, 6 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
>>> index 8b9a3e3bba30..8ce7c9bb6dd6 100644
>>> --- a/drivers/net/ethernet/nxp/lpc_eth.c
>>> +++ b/drivers/net/ethernet/nxp/lpc_eth.c
>>> @@ -751,7 +751,7 @@ static void lpc_handle_link_change(struct net_device *ndev)
>>> static int lpc_mii_probe(struct net_device *ndev)
>>> {
>>> struct netdata_local *pldat = netdev_priv(ndev);
>>> - struct phy_device *phydev;
>>> + struct phy_device *phydev, *phydev_tmp;
>>>
>>> /* Attach to the PHY */
>>> if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII)
>>> @@ -760,17 +760,18 @@ static int lpc_mii_probe(struct net_device *ndev)
>>> netdev_info(ndev, "using RMII interface\n");
>>>
>>> if (pldat->phy_node)
>>> - phydev = of_phy_find_device(pldat->phy_node);
>>> + phydev_tmp = of_phy_find_device(pldat->phy_node);
>>> else
>>> - phydev = phy_find_first(pldat->mii_bus);
>>> - if (!phydev) {
>>> + phydev_tmp = phy_find_first(pldat->mii_bus);
>>> + if (!phydev_tmp) {
>>
>> I didn't get it, why the new phydev_tmp is needed above, please
>> restore the original code above.
>>
>>> netdev_err(ndev, "no PHY found\n");
>>> return -ENODEV;
>>> }
>>>
>>> - phydev = phy_connect(ndev, phydev_name(phydev),
>>> + phydev = phy_connect(ndev, phydev_name(phydev_tmp),
>>> &lpc_handle_link_change,
>>> lpc_phy_interface_mode(&pldat->pdev->dev));
>>> + phy_device_free(phydev_tmp);
>>
>> This is plainly wrong and has to be dropped or changed to
>>
>> if (pldat->phy_node)
>> of_node_put(pldat->phy_node);
>>
>>> if (IS_ERR(phydev)) {
>>> netdev_err(ndev, "Could not attach to PHY\n");
>>> return PTR_ERR(phydev);
>>
>> Is it AI generated fix or what?.. The change looks bad, it introduces
>> more severe issues than it fixes.
>>
>> If you think you cannot create a proper change, let me know.
>>
> Thank you very much for your detailed review and guidance.
>
> Now I think your point probably is: you are saying that the real leak
> is not from of_phy_find_device(), but from the device node
I was pretty indelicate in my comment, let's split the change into parts.
1) I still do not understand, why phydev_tmp is introduced, please explain
or remove this part of the change;
2) phydev = of_phy_find_device() requires phy_device_free(phydev), but
I do not see why phy_find_first() requires it, while it was added in your
change.
Let's start from resolving these two points.
> pldat->phy_node which was obtained earlier (probably by
> of_parse_phandle()) and never freed by of_node_put(). And you suggest
> to add of_node_put(pldat->phy_node) instead of my wrong
> phy_device_free().
>
> However, I am still a little confused. In lpc_mii_probe(),
> of_phy_find_device() is called. From my understanding, this function
> increases the reference count of the device. To balance it, I thought
> phy_device_free() (which calls put_device()) should be used.
>
> Could you please kindly advise the correct patch? I will follow your
> guidance and submit a proper fix.
>
> I apologize again for my previous wrong patch. Thank you very much for
> your help.
--
Best wishes,
Vladimir
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Jim Mattson @ 2026-04-07 20:53 UTC (permalink / raw)
To: Pawan Gupta
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <20260407191128.b2hr2ttkdpyunhrr@desk>
On Tue, Apr 7, 2026 at 12:11 PM Pawan Gupta
<pawan.kumar.gupta@linux.intel.com> wrote:
>
> On Tue, Apr 07, 2026 at 11:40:57AM -0700, Jim Mattson wrote:
> > My proposal is as follows:
> >
> > 1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
> > or MSR bit) that the short BHB clearing sequence is adequate. This may
> > mean either that the VM will only be hosted on pre-Alder Lake hardware
> > or that the hypervisor will set BHI_DIS_S behind the back of the
> > guest. Presumably, this bit would not be reported if BHI_CTRL is
> > advertised to the guest.
> > 2. If the guest sees this bit, then it can use the short sequence. If
> > it doesn't see this bit, it must use the long sequence.
>
> Thats a good middle ground. Let me check with folks internally what they
> think about defining a new software-only bit.
>
> Third case, for a guest that doesn't want BHI_DIS_S, userspace should be
> allowed to override setting BHI_DIS_S. Then this proposed bit can indicate
> that long sequence is required.
That case can be handled by the paravirtual mitigation MSRs, right?
^ permalink raw reply
* [syzbot] Monthly bridge report (Apr 2026)
From: syzbot @ 2026-04-07 20:34 UTC (permalink / raw)
To: bridge, idosch, linux-kernel, netdev, razor, syzkaller-bugs
Hello bridge maintainers/developers,
This is a 31-day syzbot report for the bridge subsystem.
All related reports/information can be found at:
https://syzkaller.appspot.com/upstream/s/bridge
During the period, 0 new issues were detected and 0 were fixed.
In total, 4 issues are still open and 42 have already been fixed.
Some of the still happening issues:
Ref Crashes Repro Title
<1> 59 Yes possible deadlock in br_multicast_rcv (3)
https://syzkaller.appspot.com/bug?extid=d7b7f1412c02134efa6d
<2> 6 Yes WARNING in nf_ct_bridge_post
https://syzkaller.appspot.com/bug?extid=a8ba738fe2db6b4bb27f
<3> 2 Yes INFO: task hung in br_ioctl_stub (2)
https://syzkaller.appspot.com/bug?extid=6bdeba54c73aa6a6219d
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
To disable reminders for individual bugs, reply with the following command:
#syz set <Ref> no-reminders
To change bug's subsystems, reply with:
#syz set <Ref> subsystems: new-subsystem
You may send multiple commands in a single email message.
^ 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