* RE: [PATCH net v2] tipc: serialize udp bearer replicast list updates
From: Tung Quang Nguyen @ 2026-07-09 6:42 UTC (permalink / raw)
To: Weiming Shi
Cc: netdev@vger.kernel.org, tipc-discussion@lists.sourceforge.net,
linux-kernel@vger.kernel.org, Xiang Mei, Jon Maloy,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
In-Reply-To: <20260707164248.821265-2-bestswngs@gmail.com>
>Subject: [PATCH net v2] tipc: serialize udp bearer replicast list updates
>
>tipc_udp_rcast_add() and cleanup_bearer() both update ub->rcast.list with
>list_add_rcu() / list_del_rcu(), but nothing serializes them. The add runs from
>the encap receive softirq (via tipc_udp_rcast_disc()) without rtnl_lock(), so it
>can race the cleanup delete and corrupt the
>list:
>
> list_del corruption. prev->next should be ffff8880298d7ab8,
> but was ffff88802449ad38. (prev=ffff888027e3ec98) kernel BUG at
>lib/list_debug.c:62!
> RIP: __list_del_entry_valid_or_report+0x17a/0x200
> Workqueue: events cleanup_bearer
> Call Trace:
> cleanup_bearer (net/tipc/udp_media.c:811)
> process_one_work (kernel/workqueue.c:3302)
> worker_thread (kernel/workqueue.c:3466)
>
>The bearer can be enabled from an unprivileged user namespace, as the
>TIPCv2 generic-netlink ops carry no GENL_ADMIN_PERM.
>
>Add a spinlock to struct udp_bearer and take it around the
>list_add_rcu() in tipc_udp_rcast_add() and the list_del_rcu() loop in
>cleanup_bearer() so the two writers can no longer corrupt the list.
>
>While here, switch the read-only walk in tipc_udp_is_known_peer() to
>list_for_each_entry_rcu(); it never deletes, so list_for_each_entry_safe() was
>misleading.
>
>Fixes: ef20cd4dd163 ("tipc: introduce UDP replicast")
>Reported-by: Xiang Mei <xmei5@asu.edu>
>Suggested-by: Tung Nguyen <tung.quang.nguyen@est.tech>
>Assisted-by: Claude:claude-opus-4-8
>Signed-off-by: Weiming Shi <bestswngs@gmail.com>
>---
>v2: (per Tung's review)
> - Narrow the lock to just list_add_rcu().
> - Drop the under-lock dup re-check; serializing the writers is enough.
> - Use list_for_each_entry_rcu() in tipc_udp_is_known_peer().
>
> net/tipc/udp_media.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
>diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index
>62ae7f5b5840..c6aa8c3c54ce 100644
>--- a/net/tipc/udp_media.c
>+++ b/net/tipc/udp_media.c
>@@ -94,6 +94,7 @@ struct udp_replicast {
> * @ifindex: local address scope
> * @work: used to schedule deferred work on a bearer
> * @rcast: associated udp_replicast container
>+ * @rcast_lock: serializes updates to @rcast.list
> */
> struct udp_bearer {
> struct tipc_bearer __rcu *bearer;
>@@ -101,6 +102,7 @@ struct udp_bearer {
> u32 ifindex;
> struct work_struct work;
> struct udp_replicast rcast;
>+ spinlock_t rcast_lock; /* protects rcast.list */
> };
>
> static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr) @@ -281,7
>+283,7 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
>static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
> struct udp_media_addr *addr)
> {
>- struct udp_replicast *rcast, *tmp;
>+ struct udp_replicast *rcast;
> struct udp_bearer *ub;
>
> ub = rcu_dereference_rtnl(b->media_ptr);
>@@ -290,7 +292,7 @@ static bool tipc_udp_is_known_peer(struct tipc_bearer
>*b,
> return false;
> }
>
>- list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
>+ list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
> if (!memcmp(&rcast->addr, addr, sizeof(struct
>udp_media_addr)))
> return true;
> }
sashiko reports:
...
Is there a possibility of triggering a lockdep warning here?
Since this function is called from tipc_udp_nl_bearer_add() during Netlink
configuration, rtnl_lock() is held but rcu_read_lock() is not.
Without an explicit lockdep condition like lockdep_rtnl_is_held() passed to
the list traversal macro, lockdep will complain about the RCU list being
traversed outside a reader section.
> if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
> return true;
> }
> @@ -326,7 +328,9 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
> pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
> #endif
> b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
> + spin_lock_bh(&ub->rcast_lock);
> list_add_rcu(&rcast->list, &ub->rcast.list);
> + spin_unlock_bh(&ub->rcast_lock);
> return 0;
> }
Could this allow duplicate peers to be added to the list?
The under-lock duplicate check was removed in this patch iteration. Since
tipc_udp_is_known_peer() is checked locklessly earlier in
tipc_udp_rcast_disc(), two concurrent packets from the same unknown peer
could both see the peer as missing.
They would then both acquire rcast_lock sequentially and add duplicate
entries, leading to memory leaks and traffic amplification when broadcasting.
[ ... ]
...
I think we have to check duplicate address in tipc_udp_rcast_add() before adding and remove tipc_udp_is_known_peer().
>@@ -326,7 +328,9 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b,
> pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
>#endif
> b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
>+ spin_lock_bh(&ub->rcast_lock);
> list_add_rcu(&rcast->list, &ub->rcast.list);
>+ spin_unlock_bh(&ub->rcast_lock);
> return 0;
> }
>
>@@ -679,6 +683,7 @@ static int tipc_udp_enable(struct net *net, struct
>tipc_bearer *b,
> return -ENOMEM;
>
> INIT_LIST_HEAD(&ub->rcast.list);
>+ spin_lock_init(&ub->rcast_lock);
>
> if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
> goto err;
>@@ -819,10 +824,12 @@ static void cleanup_bearer(struct work_struct
>*work)
> struct udp_replicast *rcast, *tmp;
> struct tipc_net *tn;
>
>+ spin_lock_bh(&ub->rcast_lock);
> list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
> list_del_rcu(&rcast->list);
> call_rcu_hurry(&rcast->rcu, rcast_free_rcu);
> }
>+ spin_unlock_bh(&ub->rcast_lock);
>
> tn = tipc_net(sock_net(ub->sk));
>
>--
>2.43.0
^ permalink raw reply
* [PATCH net-next 08/10] net: dsa: microchip: add KSZ8463 tail tag handling
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com>
KSZ8463 uses the KSZ9893 DSA TAG driver. However, the KSZ8463 doesn't
use the tail tag to convey timestamps to the host as KSZ9893 does. It
uses the reserved fields in the PTP header instead.
Add a KSZ8463-specific DSA_TAG driver to handle KSZ8463 timestamps.
There is no information in the tail tag to distinguish PTP packets from
others so use the ptp_classify_raw() helper to find the PTP packets and
extract the timestamp from their PTP headers.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 4 +--
include/net/dsa.h | 2 ++
net/dsa/tag_ksz.c | 62 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 5e5bfc5cae2d..ac9e8ef5774a 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2966,7 +2966,7 @@ static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
- return DSA_TAG_PROTO_KSZ9893;
+ return DSA_TAG_PROTO_KSZ8463;
}
static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
@@ -2974,7 +2974,7 @@ static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
{
struct ksz_tagger_data *tagger_data;
- if (proto != DSA_TAG_PROTO_KSZ9893)
+ if (proto != DSA_TAG_PROTO_KSZ8463)
return -EPROTONOSUPPORT;
tagger_data = ksz_tagger_data(ds);
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..6f7f5c17b532 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
+#define DSA_TAG_PROTO_KSZ8463_VALUE 34
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +96,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
+ DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f8b40437c5fa..633511679cad 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -12,6 +12,7 @@
#include "tag.h"
+#define KSZ8463_NAME "ksz8463"
#define KSZ8795_NAME "ksz8795"
#define KSZ9477_NAME "ksz9477"
#define KSZ9893_NAME "ksz9893"
@@ -396,6 +397,66 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
+#define KSZ8463_TAIL_TAG_PRIO GENMASK(4, 3)
+#define KSZ8463_TAIL_TAG_EG_PORT_M GENMASK(2, 0)
+
+static struct sk_buff *ksz8463_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u16 queue_mapping = skb_get_queue_mapping(skb);
+ u8 prio = netdev_txq_to_tc(dev, queue_mapping);
+
+ return ksz_common_xmit(skb, dev, false,
+ FIELD_PREP(KSZ8463_TAIL_TAG_PRIO, prio),
+ 0);
+}
+
+static struct sk_buff *ksz8463_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned int len = KSZ_EGRESS_TAG_LEN;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_class;
+ unsigned int port;
+ ktime_t ts;
+ u8 *tag;
+
+ if (skb_linearize(skb))
+ return NULL;
+
+ /* Tag decoding */
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+ port = tag[0] & KSZ8463_TAIL_TAG_EG_PORT_M;
+
+ __skb_push(skb, ETH_HLEN);
+ ptp_class = ptp_classify_raw(skb);
+ __skb_pull(skb, ETH_HLEN);
+ if (ptp_class == PTP_CLASS_NONE)
+ goto common_rcv;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_class);
+ if (ptp_hdr) {
+ ts = ksz_decode_tstamp(get_unaligned_be32(&ptp_hdr->reserved2));
+ KSZ_SKB_CB(skb)->tstamp = ts;
+ ptp_hdr->reserved2 = 0;
+ }
+
+common_rcv:
+ return ksz_common_rcv(skb, dev, port, len);
+}
+
+static const struct dsa_device_ops ksz8463_netdev_ops = {
+ .name = KSZ8463_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8463,
+ .xmit = ksz8463_xmit,
+ .rcv = ksz8463_rcv,
+ .connect = ksz_connect,
+ .disconnect = ksz_disconnect,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8463_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8463, KSZ8463_NAME);
+
/* For xmit, 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|tag1(1byte)|
@@ -468,6 +529,7 @@ DSA_TAG_DRIVER(lan937x_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X, LAN937X_NAME);
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
+ &DSA_TAG_DRIVER_NAME(ksz8463_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 07/10] net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com>
KSZ8795 and KSZ9893 have very similar tag handling in the xmit path,
leading to code duplication.
There are only two differences between the two ksz*_xmit():
- the KSZ8795 doesn't handle priorities between frames
- ksz8795_xmit() directly returns the SKB instead of calling
ksz_defer_xmit(). Yet, ksz_defer_xmit() also returns directly the SKB
if no clone is present inside the SKB. Clones are only created by the KSZ
driver when the PTP feature is enabled. Since KSZ8795 doesn't support
PTP, returning the SKB directly or ksz_defer_xmit() is the same.
The upcoming support for the KSZ8463 also requires a similar xmit().
Gather the common code from ksz8795_xmit() and ksz9893_xmit() into a new
ksz_common_xmit() function that takes three input arguments:
- do_tstamp to tell whether ksz_xmit_timestamp() should be called
- prio to give the priority tag (if any)
- override_mask to give the location of the override bit (if any)
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
net/dsa/tag_ksz.c | 73 ++++++++++++++++++++++++++-----------------------------
1 file changed, 35 insertions(+), 38 deletions(-)
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f58ce0f0e9e4..f8b40437c5fa 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -218,6 +218,37 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
return NULL;
}
+static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ bool do_tstamp,
+ u8 prio,
+ u8 override_mask)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag encoding */
+ if (do_tstamp)
+ ksz_xmit_timestamp(dp, skb);
+
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ *tag |= prio;
+
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= override_mask;
+
+ return ksz_defer_xmit(dp, skb);
+}
+
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -308,23 +339,7 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
+ return ksz_common_xmit(skb, dev, false, 0, KSZ8795_TAIL_TAG_OVERRIDE);
}
static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
@@ -362,28 +377,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
{
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 prio = netdev_txq_to_tc(dev, queue_mapping);
- struct dsa_port *dp = dsa_user_to_port(dev);
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- ksz_xmit_timestamp(dp, skb);
-
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- *tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ9893_TAIL_TAG_OVERRIDE;
-
- return ksz_defer_xmit(dp, skb);
+ return ksz_common_xmit(skb, dev, true,
+ FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio),
+ KSZ9893_TAIL_TAG_OVERRIDE);
}
static const struct dsa_device_ops ksz9893_netdev_ops = {
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 09/10] net: dsa: microchip: explicitly enable detection of L2 PTP frames
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com>
Detection of L2 PTP frames needs to be enabled for PTP to work at the L2
layer. The bit enabling this detection is set by default on the switches
currently supported by the driver, but it is unset by default on the
KSZ8463 for which support will be added in upcoming patches.
Explicitly enable the detection of L2 PTP frames for all switches when
PTP is enabled.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_ptp.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 7a74befda9ad..5323fdc8862e 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -953,8 +953,9 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
/* Currently only P2P mode is supported. When 802_1AS bit is set, it
* forwards all PTP packets to host port and none to other ports.
*/
- ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_TC_P2P | PTP_802_1AS,
- PTP_TC_P2P | PTP_802_1AS);
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1],
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE,
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE);
if (ret)
return ret;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 10/10] net: dsa: microchip: add two-steps PTP support for KSZ8463
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In-Reply-To: <20260709-ksz-new-ptp-v1-0-344f02fe739e@bootlin.com>
The KSZ8463 switch supports PTP but it's not supported by the driver.
Add L2 two-step PTP support for the KSZ8463. IPv4 and IPv6 layers aren't
supported. Neither is one-step PTP. Use KSZ8463-specific implementations
of the .get_ts_info and .port_hwtstamp_set callbacks.
The pdelay_req and pdelay_resp timestamps share one interrupt bit status
while they're located in two different registers. So introduce
last_tx_is_pdelayresp to keep track of the last sent event type. This
flag is set by the xmit worker right before sending the packet and then
used in the interrupt handler to retrieve the timestamp location.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 26 +++++--
drivers/net/dsa/microchip/ksz8_reg.h | 1 +
drivers/net/dsa/microchip/ksz_common.h | 1 +
drivers/net/dsa/microchip/ksz_ptp.c | 127 +++++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 7 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 4 +
6 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index ac9e8ef5774a..941ae9f66f70 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -242,8 +242,11 @@ static int ksz8463_girq_setup(struct ksz_device *dev)
static int ksz8463_reset_switch(struct ksz_device *dev)
{
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET,
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
+ true);
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
false);
return 0;
}
@@ -2474,17 +2477,24 @@ static int ksz8463_setup(struct dsa_switch *ds)
ret = ksz8463_ptp_irq_setup(ds);
if (ret)
goto free_girq;
+
+ ret = ksz_ptp_clock_register(ds);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register PTP clock: %d\n",
+ ret);
+ goto free_ptp_irq;
+ }
}
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
}
ret = ksz_dcb_init(dev);
if (ret)
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
@@ -2492,6 +2502,9 @@ static int ksz8463_setup(struct dsa_switch *ds)
return 0;
+ptp_clock_unregister:
+ if (dev->irq > 0)
+ ksz_ptp_clock_unregister(ds);
free_ptp_irq:
if (dev->irq > 0)
ksz8463_ptp_irq_free(ds);
@@ -2507,6 +2520,7 @@ static void ksz8463_teardown(struct dsa_switch *ds)
struct ksz_device *dev = ds->priv;
if (dev->irq > 0) {
+ ksz_ptp_clock_unregister(ds);
ksz8463_ptp_irq_free(ds);
ksz_irq_free(&dev->girq);
}
@@ -3129,9 +3143,9 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_max_mtu = ksz88xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
- .get_ts_info = ksz_get_ts_info,
+ .get_ts_info = ksz8463_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
- .port_hwtstamp_set = ksz_hwtstamp_set,
+ .port_hwtstamp_set = ksz8463_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
.port_setup_tc = ksz8_setup_tc,
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 981ab441d9b7..6bc511da1f7d 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -786,6 +786,7 @@
#define KSZ8463_REG_SW_RESET 0x126
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
+#define KSZ8463_PTP_SOFTWARE_RESET BIT(2)
#define KSZ8463_PTP_CLK_CTRL 0x600
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 0f2abb22ca91..cbe98494578c 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -194,6 +194,7 @@ struct ksz_port {
struct kernel_hwtstamp_config tstamp_config;
bool hwts_tx_en;
bool hwts_rx_en;
+ bool last_tx_is_pdelayresp;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
ktime_t tstamp_msg;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 5323fdc8862e..a3cc7b97caaf 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -297,6 +297,31 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)
tag_en ? PTP_ENABLE : 0);
}
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (!ptp_data->clock)
+ return -ENODEV;
+
+ ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
+
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
+
+ ts->phc_index = ptp_clock_index(ptp_data->clock);
+
+ return 0;
+}
+
/* The function is return back the capability of timestamping feature when
* requested through ethtool -T <interface> utility
*/
@@ -341,6 +366,72 @@ int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
return 0;
}
+static int ksz8463_set_hwtstamp_config(struct ksz_device *dev,
+ struct ksz_port *prt,
+ struct kernel_hwtstamp_config *config)
+{
+ const u16 *regs = dev->info->regs;
+ int ret;
+
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = false;
+ prt->hwts_tx_en = false;
+ break;
+ case HWTSTAMP_TX_ON:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = true;
+ prt->hwts_tx_en = true;
+
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_1STEP, 0);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ prt->hwts_rx_en = false;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ prt->hwts_rx_en = true;
+ break;
+ default:
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ return ksz_ptp_enable_mode(dev);
+}
+
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *prt;
+ int ret;
+
+ prt = &dev->ports[port];
+
+ ret = ksz8463_set_hwtstamp_config(dev, prt, config);
+ if (ret)
+ return ret;
+
+ prt->tstamp_config = *config;
+
+ return 0;
+}
+
static int ksz_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct kernel_hwtstamp_config *config)
@@ -571,6 +662,28 @@ static void ksz_ptp_txtstamp_skb(struct ksz_device *dev,
skb_complete_tx_timestamp(skb, &hwtstamps);
}
+static void ksz8463_set_pdelayresp_flag(struct ksz_port *prt,
+ struct sk_buff *skb)
+{
+ struct ptp_header *hdr;
+ unsigned int type;
+ u8 ptp_msg_type;
+
+ if (!ksz_is_ksz8463(prt->ksz_dev))
+ return;
+
+ type = ptp_classify_raw(skb);
+ if (type == PTP_CLASS_NONE)
+ return;
+
+ hdr = ptp_parse_header(skb, type);
+ if (!hdr)
+ return;
+
+ ptp_msg_type = ptp_get_msgtype(hdr, type);
+ prt->last_tx_is_pdelayresp = (ptp_msg_type == PTP_MSGTYPE_PDELAY_RESP);
+}
+
void ksz_port_deferred_xmit(struct kthread_work *work)
{
struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
@@ -587,6 +700,8 @@ void ksz_port_deferred_xmit(struct kthread_work *work)
reinit_completion(&prt->tstamp_msg_comp);
+ ksz8463_set_pdelayresp_flag(prt, skb);
+
dsa_enqueue_skb(skb, skb->dev);
ksz_ptp_txtstamp_skb(dev, prt, clone);
@@ -979,7 +1094,17 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
static int ksz_read_ts(struct ksz_port *port, u16 reg, u32 *ts)
{
- return ksz_read32(port->ksz_dev, reg, ts);
+ u16 ts_reg = reg;
+
+ /**
+ * On KSZ8463 DREQ and DRESP timestamps share one interrupt line
+ * so we have to check the nature of the latest event sent to know
+ * where the timestamp is located
+ */
+ if (ksz_is_ksz8463(port->ksz_dev) && port->last_tx_is_pdelayresp)
+ ts_reg += KSZ8463_DRESP_TS_OFFSET;
+
+ return ksz_read32(port->ksz_dev, ts_reg, ts);
}
static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 11408580031d..7067ec9bd1e6 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -39,11 +39,16 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds);
int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct kernel_ethtool_ts_info *ts);
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);
void ksz_port_deferred_xmit(struct kthread_work *work);
bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
@@ -82,10 +87,12 @@ static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
#define ksz_get_ts_info NULL
+#define ksz8463_get_ts_info NULL
#define ksz_hwtstamp_get NULL
#define ksz_hwtstamp_set NULL
+#define ksz8463_hwtstamp_set NULL
#define ksz_port_rxtstamp NULL
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index 1a669d6ee889..65ea8577af75 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -137,4 +137,8 @@
#define KSZ_XDREQ_MSG 1
#define KSZ_PDRES_MSG 0
+#define KSZ8463_DRESP_TS_OFFSET (KSZ8463_REG_PORT_DRESP_TS - KSZ8463_REG_PORT_DREQ_TS)
+#define KSZ8463_SYNC_MSG 0
+#define KSZ8463_XDREQ_PDRES_MSG 1
+
#endif
--
2.54.0
^ permalink raw reply related
* Re: [PATCH nf] netfilter: flowtable: tear down HW offloaded flows on FIB route changes
From: kernel test robot @ 2026-07-09 6:52 UTC (permalink / raw)
To: Ahmed Zaki, netfilter-devel
Cc: llvm, oe-kbuild-all, pablo, fw, kuba, edumazet, davem, pabeni,
horms, netdev
In-Reply-To: <20260708205404.911832-1-anzaki@gmail.com>
Hi Ahmed,
kernel test robot noticed the following build warnings:
[auto build test WARNING on netfilter-nf/main]
url: https://github.com/intel-lab-lkp/linux/commits/Ahmed-Zaki/netfilter-flowtable-tear-down-HW-offloaded-flows-on-FIB-route-changes/20260709-050000
base: https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git main
patch link: https://lore.kernel.org/r/20260708205404.911832-1-anzaki%40gmail.com
patch subject: [PATCH nf] netfilter: flowtable: tear down HW offloaded flows on FIB route changes
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20260709/202607091427.MRtlNy5G-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project b3e6e6dabdc02153552a64fc74ff5c7532447eed)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260709/202607091427.MRtlNy5G-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607091427.MRtlNy5G-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> net/netfilter/nf_flow_table_core.c:833:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
833 | struct fib_entry_notifier_info *fen;
| ^
net/netfilter/nf_flow_table_core.c:843:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
843 | struct fib6_entry_notifier_info *fen6;
| ^
2 warnings generated.
vim +833 net/netfilter/nf_flow_table_core.c
804
805 /* Called with rcu_read_lock() */
806 static int nf_flow_table_fib_event(struct notifier_block *nb,
807 unsigned long event, void *ptr)
808 {
809 struct nf_flowtable *flow_table =
810 container_of(nb, struct nf_flowtable, fib_nb);
811 struct fib_notifier_info *info = ptr;
812 struct nf_flow_fib_event *ev;
813
814 switch (event) {
815 case FIB_EVENT_ENTRY_REPLACE:
816 case FIB_EVENT_ENTRY_APPEND:
817 case FIB_EVENT_ENTRY_DEL:
818 break;
819 default:
820 return NOTIFY_DONE;
821 }
822
823 /* Skip events for an address family this table cannot hold. */
824 if (!nf_flowtable_fib_family_match(flow_table, info->family))
825 return NOTIFY_DONE;
826
827 ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
828 if (!ev)
829 return NOTIFY_DONE;
830
831 switch (info->family) {
832 case NFPROTO_IPV4:
> 833 struct fib_entry_notifier_info *fen;
834
835 fen = container_of(info, struct fib_entry_notifier_info, info);
836 ev->family = NFPROTO_IPV4;
837 ev->addr.ip4 = htonl(fen->dst);
838 ev->prefix_len = fen->dst_len;
839 break;
840
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Yun Zhou @ 2026-07-09 6:54 UTC (permalink / raw)
To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
Cc: netdev, linux-kernel, yun.zhou
From: Your Name <your.email@example.com>
erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
nested acquisition of _xmit_lock on the underlay device while already
holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
the same lockdep class, creating an ABBA deadlock:
sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
This is safe as erspan_xmit() has no shared mutable state: o_seqno is
atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
the sibling device with identical xmit structure, already sets lltx.
Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
---
net/ipv4/ip_gre.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 3efdfb4ffa21..9fbff16cda1d 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1363,6 +1363,8 @@ static int erspan_tunnel_init(struct net_device *dev)
dev->features |= GRE_FEATURES;
dev->hw_features |= GRE_FEATURES;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ /* Skip TX lock: xmit re-enters stack, risking ABBA with underlay */
+ dev->lltx = true;
netif_keep_dst(dev);
return ip_tunnel_init(dev);
--
2.43.0
^ permalink raw reply related
* [PATCH] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Yun Zhou @ 2026-07-09 6:56 UTC (permalink / raw)
To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
Cc: netdev, linux-kernel, yun.zhou
erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
nested acquisition of _xmit_lock on the underlay device while already
holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
the same lockdep class, creating an ABBA deadlock:
sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
This is safe as erspan_xmit() has no shared mutable state: o_seqno is
atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
the sibling device with identical xmit structure, already sets lltx.
Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
---
net/ipv4/ip_gre.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 3efdfb4ffa21..9fbff16cda1d 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1363,6 +1363,8 @@ static int erspan_tunnel_init(struct net_device *dev)
dev->features |= GRE_FEATURES;
dev->hw_features |= GRE_FEATURES;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ /* Skip TX lock: xmit re-enters stack, risking ABBA with underlay */
+ dev->lltx = true;
netif_keep_dst(dev);
return ip_tunnel_init(dev);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Zhou, Yun @ 2026-07-09 6:57 UTC (permalink / raw)
To: dsahern, idosch, davem, edumazet, kuba, pabeni, horms
Cc: netdev, linux-kernel
In-Reply-To: <20260709065400.3467803-1-yun.zhou@windriver.com>
Superseded, please ignore it.
On 7/9/26 14:54, Yun Zhou wrote:
> From: Your Name <your.email@example.com>
>
> erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
> nested acquisition of _xmit_lock on the underlay device while already
> holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
> the same lockdep class, creating an ABBA deadlock:
>
> sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
> ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
>
> Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
> This is safe as erspan_xmit() has no shared mutable state: o_seqno is
> atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
> the sibling device with identical xmit structure, already sets lltx.
>
> Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
> Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
> Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
> Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
> ---
> net/ipv4/ip_gre.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
> index 3efdfb4ffa21..9fbff16cda1d 100644
> --- a/net/ipv4/ip_gre.c
> +++ b/net/ipv4/ip_gre.c
> @@ -1363,6 +1363,8 @@ static int erspan_tunnel_init(struct net_device *dev)
> dev->features |= GRE_FEATURES;
> dev->hw_features |= GRE_FEATURES;
> dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
> + /* Skip TX lock: xmit re-enters stack, risking ABBA with underlay */
> + dev->lltx = true;
> netif_keep_dst(dev);
>
> return ip_tunnel_init(dev);
^ permalink raw reply
* Re: [PATCH iproute2-next v4] rdma: display resource limits in curr/max format
From: Leon Romanovsky @ 2026-07-09 7:01 UTC (permalink / raw)
To: Tao Cui; +Cc: dsahern, linux-rdma, netdev, cuitao
In-Reply-To: <20260708134003.85505-1-cui.tao@linux.dev>
On Wed, Jul 08, 2026 at 09:40:03PM +0800, Tao Cui wrote:
> From: Tao Cui <cuitao@kylinos.cn>
>
> Parse the new RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX netlink attribute
> to show resource limits alongside current counts in curr/max format:
>
> Before: 0: mlx5_0: qp 123 cq 45 mr 200 pd 10
> After: 0: mlx5_0: qp 123/131072 cq 45/65536 mr 200/1000000 pd 10/32768
>
> JSON output provides both current and max fields per resource type
> (e.g. "qp": 123, "qp-max": 131072). Backward compatible: no output
> change when kernel lacks the new attribute.
>
> Signed-off-by: Tao Cui <cuitao@kylinos.cn>
> Link: https://lore.kernel.org/all/20260615003646.168704-1-cui.tao@linux.dev/
> ---
> Changes in v4:
> - Add Link to the kernel patch that introduces the new uapi attribute.
> ---
> rdma/include/uapi/rdma/rdma_netlink.h | 5 +++++
Please move changes to rdma_netlink.h into a separate commit using
the following format:
https://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git/commit/rdma/include/uapi/rdma?id=85860c7dce2ce742ef0c6879b5c5bcbcecaaf717
Thanks
> rdma/res.c | 21 ++++++++++++++++++++-
> rdma/utils.c | 1 +
> 3 files changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/rdma/include/uapi/rdma/rdma_netlink.h b/rdma/include/uapi/rdma/rdma_netlink.h
> index 4356ec4a..e5b8b065 100644
> --- a/rdma/include/uapi/rdma/rdma_netlink.h
> +++ b/rdma/include/uapi/rdma/rdma_netlink.h
> @@ -604,6 +604,11 @@ enum rdma_nldev_attr {
> RDMA_NLDEV_ATTR_FRMR_POOL_PINNED_HANDLES, /* u32 */
> RDMA_NLDEV_ATTR_FRMR_POOL_KEY_KERNEL_VENDOR_KEY, /* u64 */
>
> + /*
> + * Resource summary entry maximum value.
> + */
> + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX, /* u64 */
> +
> /*
> * Always the end
> */
> diff --git a/rdma/res.c b/rdma/res.c
> index 062f0007..046935e2 100644
> --- a/rdma/res.c
> +++ b/rdma/res.c
> @@ -55,7 +55,26 @@ static int res_print_summary(struct nlattr **tb)
>
> name = mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]);
> curr = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]);
> - res_print_u64(name, curr, nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]);
> + if (nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX]) {
> + uint64_t max;
> + char max_name[64];
> +
> + max = mnl_attr_get_u64(
> + nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX]);
> + snprintf(max_name, sizeof(max_name), "%s-max", name);
> + print_u64(PRINT_JSON, name, NULL, curr);
> + print_u64(PRINT_JSON, max_name, NULL, max);
> + if (!is_json_context()) {
> + char buf[64];
> +
> + snprintf(buf, sizeof(buf), "%s %" PRIu64 "/%" PRIu64 " ",
> + name, curr, max);
> + pr_out("%s", buf);
> + }
> + } else {
> + res_print_u64(name, curr,
> + nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]);
> + }
> }
> return 0;
> }
> diff --git a/rdma/utils.c b/rdma/utils.c
> index 87003b2c..90ea1c55 100644
> --- a/rdma/utils.c
> +++ b/rdma/utils.c
> @@ -480,6 +480,7 @@ static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
> [RDMA_NLDEV_ATTR_EVENT_TYPE] = MNL_TYPE_U8,
> [RDMA_NLDEV_SYS_ATTR_MONITOR_MODE] = MNL_TYPE_U8,
> [RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED] = MNL_TYPE_U8,
> + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_MAX] = MNL_TYPE_U64,
> };
>
> static int rd_attr_check(const struct nlattr *attr, int *typep)
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH net] netfilter: nf_nat_masquerade: recalculate TCP TS offset when port is randomized
From: Florian Westphal @ 2026-07-09 7:19 UTC (permalink / raw)
To: xietangxin
Cc: Pablo Neira Ayuso, Phil Sutter, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, gaoxingwang, huyizhen,
netfilter-devel, coreteam, netdev, linux-kernel, stable
In-Reply-To: <e55d6327-78a9-41dc-9627-4414f408774b@h-partners.com>
xietangxin <xietangxin@h-partners.com> wrote:
> Would it be acceptable to a V2 patch that targets the local case?
Sure.
^ permalink raw reply
* Re: [PATCH net-next v4 2/2] net: dsa: realtek: rtl8365mb: add HSGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-09 7:34 UTC (permalink / raw)
To: Mieczyslaw Nalewaj
Cc: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux, luizluca, maxime.chevallier, netdev, linux-kernel
In-Reply-To: <577055ac-9378-4d43-9ff2-1a1dd17ae9dc@yahoo.com>
Hi Mieczyslaw,
On 7/3/2026 5:12 PM, Mieczyslaw Nalewaj wrote:
> Johan, looks like you forgot to include the scheduler bandwidth bits
> for the CPU port. Without this, HSGMII will still be capped at the
> old SGMII rate limits. Something like:
Good catch. I had dropped these on the assumption that the vendor
driver never touches them, but your mail made me look again: the
"Max rate" block in dal_rtl8367c_switch_init() programs exactly these
registers for EXT_PORT0 - the port 6 ingress and egress rate limiters
are raised to 0x7FFFF via rtl8367c_setAsicPortIngressBandwidth() and
rtl8367c_setAsicPortEgressRate(), followed by a raw write of 0x0007
to 0x03fa (LINE_RATE_HSG_H). I had missed it because those writes go
through the indexed register macros and a raw address rather than the
PORT6_* register names.
On my board this was masked by the bootloader: U-Boot runs the vendor
switch init (rtk_switch_init() from the ipq5018 gmac driver) and
leaves the limits raised, but the kernel driver then issues a full
chip reset in rtl8365mb_setup(), which puts these registers back to
their reset defaults. With the ingress/egress limiters back at their
defaults that would indeed cap HSGMII at roughly 1 Gbps, and I could
not have seen it in testing because my downstream ports are 1G only
(as noted in the cover letter, 2.5G line rate is unverified on my
hardware).
One caveat I want to be transparent about: for the same reason I
cannot verify the cap itself, and the vendor documentation leaves
some room for doubt. The rtl8367c_setAsicPortIngressBandwidth()
comment describes the bandwidth value 0x1FFFF as "disable", and
that is exactly the reset default - I read back both limiters as
0x1FFFF (LSBs at 0xFFFF, MSBs at 1) on my RTL8367S after the
driver's chip reset - so the limiter may in fact be disabled out
of reset (though that note looks like stale text from earlier
17-bit-granularity chips, given this family's maximum is 0x7FFFF).
Raising the limiters is safe under either reading: if the default
really is a ~1 Gbps limit, the write removes it, and if it means
"disabled", the new value (0x7FFFF * 8 Kbps, about 4.19 Gbps, well
above the 2.5G line rate) never engages.
While I cannot push more than 1 Gbps, I did verify that these
registers really are the live rate limiters for the SerDes CPU
port, by going the other way: on a build with the setup-time
writes described below already in place, I lowered the limiters at
runtime (via regmap debugfs) to 12500 * 8 Kbps = 100 Mbps
and ran iperf3 across the CPU port. Throughput clamps from ~460 to
~95 Mbps with the ingress limiter lowered (CPU-originated traffic)
and from ~600 to ~96 Mbps with the egress limiter lowered
(CPU-bound traffic); restoring the maximum returns both directions
to baseline. The ~95/100 ratio is the expected TCP goodput for a
100 Mbps wire-rate limit, so both limiters demonstrably meter this
port at the documented 8 Kbps granularity. The only thing this
cannot settle is the meaning of the 0x1FFFF default itself
(~1.048 Gbps cap vs. disabled), since any value at or above the
traffic I can generate behaves identically. If you have an
RTL8367S setup that can push more than 1 Gbps across the HSGMII
CPU port, a before/after measurement would be a very welcome
confirmation of the cap.
So I'll add the writes in v5, following the vendor init: raise the
port 6 ingress/egress limiters to their maximum once at setup time,
rather than per interface mode. Two differences from your snippet:
the vendor init writes the full 19-bit rate value, i.e. the LSB
halves too (0x00cf and 0x0398 set to 0xFFFF), so I'll program both
halves rather than only the *_CTRL1 registers, to avoid depending on
the reset defaults of the LSB registers. And LINE_RATE_HSG_H needs no
new write at all: the driver's common init jam table has always
written 0x03fa = 0x0007 in rtl8365mb_switch_init(), so that one is
already at its maximum on every probe (which also matches the 7 you
read back for it). I have this running on my board already: with the
setup-time writes in place both limiters read back 0x7FFFF after
probe, and the HSGMII link comes up and forwards as before.
As an aside, a per-interface-mode variant of these writes does exist
in the vendor code, but only for other chip families: in
rtl8367c_setAsicPortExtMode(), the chip id 0x0652/0x6368 branch
programs its rate registers (0x0130/0x039f/0x03fa) on every mode
change, with 7s for EXT_HSGMII and lower values otherwise, and the
0x0801/0x6511 branch writes 0x00d0/0x0399/0x03fa = 7 only for
EXT_HSGMII (which is where the pattern in your snippet comes from).
For the 0x6367 family the ext-mode path never touches them, and the
vendor only programs them once, unconditionally, in switch_init -
which is another reason I prefer the one-time setup write over doing
it from the PCS path.
For completeness, the same vendor block also touches three related
bits: rtl8367c_setAsicPortIngressBandwidth() additionally clears the
port 6 ingress metering pre-IFG bit and sets the "flow control when
ingress rate exceeded" bit (PORT6_MISC_CFG, 0x00ce bits 10/11), and
switch_init follows the egress rate write with
rtl8367c_setAsicPortEgressRateIfg(ENABLED), which sets the global
"egress rate accounting includes IFG" bit (SCHEDULE_WFQ_CTRL 0x0300
bit 0). With the limiters raised to maximum the meter never engages,
so neither the exceed action nor the IFG accounting has any
observable effect. I plan to leave those three bits at their reset
defaults and program only the rate values and LINE_RATE_HSG_H, to
keep the driver's footprint on the QoS block minimal - but I'm happy
to mirror the vendor init exactly if you'd rather not deviate from
it.
> One more thing while we're on this: I checked the equivalent
> registers for RGMII on the RTL8367S, and they come out to 1, 1, 7
> respectively (INGRESSBW_PORT6_RATE_CTRL1, PORT6_EGRESSBW_CTRL1,
> LINE_RATE_HSG_H). For correctness these should be set to those
> values in the RGMII path as well, rather than left at whatever
> reset/default state they're currently in.
The 1/1 you read back for the rate limiter MSBs match the reset
defaults I see on my RTL8367S as well, and the 7 for LINE_RATE_HSG_H
is the value the common init jam table writes on every probe, as
above. Since the driver hard-resets the chip and then replays the
init jam on every probe, those values are already guaranteed in
the RGMII case, so writing them again from the RGMII path would be
a no-op. These fields are also the MSBs of the per-port QoS rate
limiters rather than link-mode configuration, so I would prefer to
touch them as little as possible from the link paths. My plan for
v5 is therefore the single write at setup time described above,
which covers both SGMII and HSGMII and has no functional effect on
RGMII either way: both the default value (~1.048 Gbps, if it acts
as a limit at all) and the raised maximum sit above the 1G RGMII
line rate, so the meter never engages there. Let me know if you
see a problem with that.
Best regards,
Johan
^ permalink raw reply
* [PATCH net] rds: tcp: hold the net_device across ipv6_chk_addr() in rds_tcp_laddr_check()
From: Xiang Mei @ 2026-07-09 7:44 UTC (permalink / raw)
To: Allison Henderson, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: netdev, linux-rdma, rds-devel, linux-kernel, Santosh Shilimkar,
Ka-Cheong Poon, bestswngs, Xiang Mei
rds_tcp_laddr_check() looks up a scoped IPv6 interface with
dev_get_by_index_rcu(), drops the RCU read-side lock, and only then
passes the bare struct net_device * into ipv6_chk_addr().
dev_get_by_index_rcu() only keeps the device alive within the same RCU
read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can
free the net_device; ipv6_chk_addr() then dereferences the stale pointer
in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading
freed memory.
Take a reference with dev_hold() before dropping the RCU lock and release
it with dev_put() after ipv6_chk_addr(), so the device cannot be freed
while in use. dev_put(NULL) is a no-op, so the scope_id == 0 path is
unaffected.
BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
Read of size 8 at addr ffff8880106ec000 by task exploit/153
Call Trace:
...
kasan_report (mm/kasan/report.c:595)
__ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998)
ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972)
rds_tcp_laddr_check (net/rds/tcp.c:370)
rds_bind (net/rds/bind.c:248)
__sys_bind (net/socket.c:1920)
__x64_sys_bind (net/socket.c:1956)
do_syscall_64 (arch/x86/entry/syscall_64.c:63)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Fixes: eee2fa6ab322 ("rds: Changing IP address internal representation to struct in6_addr")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
net/rds/tcp.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index a1de114d5e2e..204dcdc33c27 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -363,12 +363,16 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
rcu_read_unlock();
return -EADDRNOTAVAIL;
}
+ dev_hold(dev);
rcu_read_unlock();
}
#if IS_ENABLED(CONFIG_IPV6)
ret = ipv6_chk_addr(net, addr, dev, 0);
+ dev_put(dev);
if (ret)
return 0;
+#else
+ dev_put(dev);
#endif
return -EADDRNOTAVAIL;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v21 net-next 04/12] net/nebula-matrix: add channel layer
From: Paolo Abeni @ 2026-07-09 8:09 UTC (permalink / raw)
To: illusion.wang, dimon.zhao, alvin.wang, sam.chen, netdev
Cc: andrew+netdev, corbet, kuba, horms, linux-doc, vadim.fedorenko,
lukas.bulwahn, edumazet, enelsonmoore, skhan, hkallweit1,
open list
In-Reply-To: <20260708064742.35391-5-illusion.wang@nebula-matrix.com>
On 7/8/26 8:47 AM, illusion.wang wrote:
> From: illusion wang <illusion.wang@nebula-matrix.com>
>
> A channel management layer provides a structured approach to handle
> communication between different components and drivers. Here's a summary
> of its key functionalities:
>
> 1. Message Handling Framework
> Message Registration: Functions (nbl_chan_register_msg) allow dynamic
> registration of message handlers for specific message types, enabling
> extensible communication protocols.
>
> Message Sending/Acknowledgment: Core functions (nbl_chan_send_msg,
> nbl_chan_send_ack) handle message transmission, including asynchronous
> operations with acknowledgment (ACK) support. Received ACKs are
> processed via nbl_chan_recv_ack_msg.
>
> Hash-Based Handler Lookup: A hash table (`handle_hash_tbl`) stores
> message handlers for efficient O(1) lookup by message type. The
> entire table is removed via `nbl_chan_remove_msg_handler` during
> driver teardown (per-message-type removal is not implemented
> in this version).
>
> 2. Channel Types and Queue Management
> Mailbox Channel: For direct communication between PF0 and Other PF.
>
> Queue Initialization: Functions (nbl_chan_init_queue,
> nbl_chan_init_tx_queue) allocate resources:
> - TX descriptors: dmam_alloc_coherent()
> - RX descriptors: dmam_alloc_coherent()
> - TX/RX buffer metadata arrays (txq->buf, rxq->buf): devm_kcalloc()
>
> Queue Teardown: nbl_chan_teardown_queue() stops queues, cancels
> pending work items (clean_task), and destroys mutexes. It does NOT
> free DMA memory, which is released automatically via devm on driver
> remove.
>
> IMPORTANT - Resource Lifecycle Design:
> DMA memory allocated with dmam_alloc_coherent() is intentionally NOT
> freed in nbl_chan_teardown_queue(). The queues are allocated once
> during driver probe and freed only during driver remove (when all
> devm_ resources are released). This assumes queues are NOT dynamically
> torn down and recreated per-PF during normal operation.
>
> Queue Configuration: Hardware-specific queue parameters (e.g., buffer
> sizes, entry counts) are set via nbl_chan_config_queue, with hardware
> interactions delegated to hw_ops.
>
> 3. Hardware Abstraction Layer (HW Ops)
> Hardware-Specific Operations: The nbl_hw_ops structure abstracts
> hardware interactions: queue configuration (config_mailbox_txq/rxq),
> tail pointer updates (update_mailbox_queue_tail_ptr).
>
> Signed-off-by: illusion wang <illusion.wang@nebula-matrix.com>
> ---
> .../net/ethernet/nebula-matrix/nbl/Makefile | 4 +-
> .../nbl/nbl_channel/nbl_channel.c | 1094 +++++++++++++++++
> .../nbl/nbl_channel/nbl_channel.h | 168 +++
> .../nebula-matrix/nbl/nbl_common/nbl_common.c | 172 +++
> .../nebula-matrix/nbl/nbl_common/nbl_common.h | 33 +
> .../net/ethernet/nebula-matrix/nbl/nbl_core.h | 11 +
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 173 +++
> .../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.h | 56 +
> .../nebula-matrix/nbl/nbl_hw/nbl_hw_reg.h | 45 +
> .../nbl/nbl_include/nbl_def_channel.h | 113 ++
> .../nbl/nbl_include/nbl_def_common.h | 17 +
> .../nbl/nbl_include/nbl_def_hw.h | 15 +
> .../nbl/nbl_include/nbl_include.h | 6 +
> .../net/ethernet/nebula-matrix/nbl/nbl_main.c | 7 +
> 14 files changed, 1913 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.h
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.c
> create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_common/nbl_common.h
>
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> index caa863d3a582..6dc1539cee1f 100644
> --- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
> @@ -3,5 +3,7 @@
>
> obj-$(CONFIG_NBL) := nbl.o
>
> -nbl-objs += nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
> +nbl-objs += nbl_common/nbl_common.o \
> + nbl_channel/nbl_channel.o \
> + nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
> nbl_main.o
> diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> new file mode 100644
> index 000000000000..220c740f68b9
> --- /dev/null
> +++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c
> @@ -0,0 +1,1094 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2025 Nebula Matrix Limited.
> + */
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/bitfield.h>
> +#include <linux/pci.h>
> +#include <linux/bits.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/atomic.h>
> +#include <linux/wait.h>
> +#include "nbl_channel.h"
> +
> +static int nbl_chan_add_msg_handler(struct nbl_channel_mgt *chan_mgt,
> + u16 msg_type, nbl_chan_resp func,
> + void *priv)
> +{
> + struct nbl_chan_msg_node_data handler = { 0 };
> + int ret;
> +
> + handler.func = func;
> + handler.priv = priv;
> + ret = nbl_common_alloc_hash_node(chan_mgt->handle_hash_tbl, &msg_type,
> + &handler, NULL);
> +
> + return ret;
> +}
> +
> +static int nbl_chan_init_msg_handler(struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_hash_tbl_key tbl_key = { 0 };
> +
> + tbl_key.dev = common->dev;
> + tbl_key.key_size = sizeof(u16);
> + tbl_key.data_size = sizeof(struct nbl_chan_msg_node_data);
> + tbl_key.bucket_size = NBL_CHAN_HANDLER_TBL_BUCKET_SIZE;
> +
> + chan_mgt->handle_hash_tbl = nbl_common_init_hash_table(&tbl_key);
> + if (!chan_mgt->handle_hash_tbl)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static void nbl_chan_remove_msg_handler(struct nbl_channel_mgt *chan_mgt)
> +{
> + if (!chan_mgt->handle_hash_tbl)
> + return;
> + nbl_common_remove_hash_table(chan_mgt->handle_hash_tbl);
> + chan_mgt->handle_hash_tbl = NULL;
> +}
> +
> +static void nbl_chan_init_queue_param(struct nbl_chan_info *chan_info,
> + u16 num_txq_entries, u16 num_rxq_entries,
> + u16 txq_buf_size, u16 rxq_buf_size)
> +{
> + mutex_init(&chan_info->txq_lock);
> + chan_info->num_txq_entries = num_txq_entries;
> + chan_info->num_rxq_entries = num_rxq_entries;
> + chan_info->txq_buf_size = txq_buf_size;
> + chan_info->rxq_buf_size = rxq_buf_size;
> + atomic_set(&chan_info->inflight_tx_cnt, 0);
> + chan_info->shutdown = false;
> +}
> +
> +static int nbl_chan_init_tx_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = common->dev;
> + size_t size =
> + chan_info->num_txq_entries * sizeof(struct nbl_chan_tx_desc);
> + int i;
> +
> + txq->desc.tx_desc =
> + dmam_alloc_coherent(dev, size, &txq->dma, GFP_KERNEL);
> + if (!txq->desc.tx_desc)
> + return -ENOMEM;
> +
> + chan_info->wait = devm_kcalloc(dev, chan_info->num_txq_entries,
> + sizeof(*chan_info->wait), GFP_KERNEL);
> + if (!chan_info->wait)
> + return -ENOMEM;
> + for (i = 0; i < chan_info->num_txq_entries; i++) {
> + init_waitqueue_head(&chan_info->wait[i].wait_queue);
> + chan_info->wait[i].status = NBL_MBX_STATUS_IDLE;
> + spin_lock_init(&chan_info->wait[i].status_lock);
> + }
> +
> + txq->buf = devm_kcalloc(dev, chan_info->num_txq_entries,
> + sizeof(*txq->buf), GFP_KERNEL);
> + if (!txq->buf)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_init_rx_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = common->dev;
> + size_t size =
> + chan_info->num_rxq_entries * sizeof(struct nbl_chan_rx_desc);
> +
> + rxq->desc.rx_desc =
> + dmam_alloc_coherent(dev, size, &rxq->dma, GFP_KERNEL);
> + if (!rxq->desc.rx_desc) {
> + dev_err(dev,
> + "Allocate DMA for chan rx descriptor ring failed\n");
> + return -ENOMEM;
> + }
> +
> + rxq->buf = devm_kcalloc(dev, chan_info->num_rxq_entries,
> + sizeof(*rxq->buf), GFP_KERNEL);
> + if (!rxq->buf)
> + return -ENOMEM;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_init_queue(struct nbl_common_info *common,
> + struct nbl_chan_info *chan_info)
> +{
> + int err;
> +
> + err = nbl_chan_init_tx_queue(common, chan_info);
> + if (err)
> + return err;
> +
> + err = nbl_chan_init_rx_queue(common, chan_info);
> +
> + return err;
> +}
> +
> +static void nbl_chan_config_queue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info, bool tx)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_hw_mgt *p = chan_mgt->hw_ops_tbl->priv;
> + int size_bwid;
> + struct nbl_chan_ring *ring;
> + dma_addr_t dma_addr;
> +
> + if (tx)
> + ring = &chan_info->txq;
> + else
> + ring = &chan_info->rxq;
> + dma_addr = ring->dma;
> + if (tx) {
> + size_bwid = ilog2(chan_info->num_txq_entries);
> + hw_ops->config_mailbox_txq(p, dma_addr, size_bwid);
> + } else {
> + size_bwid = ilog2(chan_info->num_rxq_entries);
> + hw_ops->config_mailbox_rxq(p, dma_addr, size_bwid);
> + }
> +}
> +
> +static int nbl_chan_alloc_all_tx_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_buf *buf;
> + u16 i;
> +
> + for (i = 0; i < chan_info->num_txq_entries; i++) {
> + buf = &txq->buf[i];
> + buf->va = dmam_alloc_coherent(dev, chan_info->txq_buf_size,
> + &buf->pa, GFP_KERNEL);
> + if (!buf->va) {
> + dev_err(dev,
> + "Allocate buffer for chan tx queue failed\n");
> + return -ENOMEM;
> + }
> + }
> +
> + txq->next_to_clean = 0;
> + txq->next_to_use = 0;
> + txq->tail_ptr = 0;
> +
> + return 0;
> +}
> +
> +static void nbl_chan_cfg_qinfo_map_table(struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_hw_mgt *p = chan_mgt->hw_ops_tbl->priv;
> + u8 func_id;
> + u32 pf_mask;
> +
> + pf_mask = hw_ops->get_host_pf_mask(p);
> + for (func_id = 0; func_id < NBL_MAX_PF; func_id++) {
> + if (!(pf_mask & (1 << func_id)))
> + hw_ops->cfg_mailbox_qinfo(p, func_id, common->hw_bus,
> + common->devid,
> + common->function + func_id);
> + }
> +}
> +
> +static inline void nbl_chan_update_tail_ptr(struct nbl_hw_ops *hw_ops,
> + void *hw_priv, u32 tail_ptr, u8 qid)
> +{
> + hw_ops->update_mailbox_queue_tail_ptr(hw_priv, tail_ptr, qid);
> +}
> +
> +static int nbl_chan_alloc_all_rx_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_rx_desc *desc;
> + struct nbl_chan_buf *buf;
> + u16 i;
> +
> + for (i = 0; i < chan_info->num_rxq_entries; i++) {
> + buf = &rxq->buf[i];
> + buf->va = dmam_alloc_coherent(dev, chan_info->rxq_buf_size,
> + &buf->pa, GFP_KERNEL);
> + if (!buf->va) {
> + dev_err(dev,
> + "Allocate buffer for chan rx queue failed\n");
> + goto err;
> + }
> + }
> +
> + desc = rxq->desc.rx_desc;
> + for (i = 0; i < chan_info->num_rxq_entries - 1; i++) {
> + buf = &rxq->buf[i];
> + desc[i].buf_addr = cpu_to_le64(buf->pa);
> + desc[i].buf_len = cpu_to_le32(chan_info->rxq_buf_size);
> + desc[i].flags = cpu_to_le16(BIT(NBL_CHAN_RX_DESC_AVAIL));
> + }
> +
> + rxq->next_to_clean = 0;
> + rxq->next_to_use = chan_info->num_rxq_entries - 1;
> + rxq->tail_ptr = chan_info->num_rxq_entries - 1;
> +
> + return 0;
> +err:
> + return -ENOMEM;
> +}
> +
> +static int nbl_chan_alloc_all_bufs(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + int err;
> +
> + err = nbl_chan_alloc_all_tx_bufs(chan_mgt, chan_info);
> + if (err)
> + return err;
> + err = nbl_chan_alloc_all_rx_bufs(chan_mgt, chan_info);
> +
> + return err;
> +}
> +
> +static void nbl_chan_stop_queue(struct nbl_channel_mgt *chan_mgt)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> +
> + hw_ops->stop_mailbox_rxq(chan_mgt->hw_ops_tbl->priv);
> + hw_ops->stop_mailbox_txq(chan_mgt->hw_ops_tbl->priv);
> +}
> +
> +static int nbl_chan_teardown_queue(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> + struct nbl_chan_waitqueue_head *wait_head;
> + u16 i;
> +
> + /* Step 1: Mark shutdown flag, reject all new send requests */
> + WRITE_ONCE(chan_info->shutdown, true);
> +
> + /* Stop hardware queues */
> + nbl_chan_stop_queue(chan_mgt);
> +
> + /* Cancel any pending cleanup work */
> + if (chan_info->clean_task)
> + cancel_work_sync(chan_info->clean_task);
> + for (i = 0; i < chan_info->num_txq_entries; i++) {
> + wait_head = &chan_info->wait[i];
> + spin_lock_irq(&wait_head->status_lock);
> + /* Only wake threads that are actually waiting */
> + if (READ_ONCE(wait_head->status) == NBL_MBX_STATUS_WAITING) {
> + /* Mark as timeout so waking threads know to abort */
> + wait_head->status = NBL_MBX_STATUS_TIMEOUT;
> + wait_head->acked = 1;
> + wait_head->ack_err = -EIO;
> + /* Ensure status is written */
> + smp_wmb();
> + }
> + spin_unlock_irq(&wait_head->status_lock);
> + if (READ_ONCE(wait_head->status) == NBL_MBX_STATUS_TIMEOUT)
> + wake_up(&wait_head->wait_queue);
> + }
> +
> + /* Step 2: Wait all in-flight send_msg threads exit via counter */
> + while (atomic_read(&chan_info->inflight_tx_cnt) != 0) {
> + /* synchronize atomic counter load with other CPUs */
> + smp_rmb();
> + usleep_range(100, 1000);
> + }
> +
> + /* No concurrent thread holds txq_lock now, safe destroy mutex */
> + mutex_destroy(&chan_info->txq_lock);
> +
> + return 0;
> +}
> +
> +static int nbl_chan_setup_queue(struct nbl_channel_mgt *chan_mgt, u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + int err;
> +
> + if (chan_info->init_done)
> + return 0;
> + nbl_chan_init_queue_param(chan_info, NBL_CHAN_QUEUE_LEN,
> + NBL_CHAN_QUEUE_LEN, NBL_CHAN_BUF_LEN,
> + NBL_CHAN_BUF_LEN);
> + err = nbl_chan_init_queue(common, chan_info);
> + if (err)
> + goto chan_setup_fail;
> + err = nbl_chan_alloc_all_bufs(chan_mgt, chan_info);
> + if (err)
> + goto chan_setup_fail;
> + nbl_chan_config_queue(chan_mgt, chan_info, true); /* tx */
> + nbl_chan_config_queue(chan_mgt, chan_info, false); /* rx */
> + nbl_chan_update_tail_ptr(hw_ops, chan_mgt->hw_ops_tbl->priv,
> + rxq->tail_ptr, NBL_MB_RX_QID);
> + chan_info->init_done = true;
> + return 0;
> +chan_setup_fail:
> + mutex_destroy(&chan_info->txq_lock);
> + return err;
> +}
> +
> +static int nbl_chan_update_txqueue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info,
> + struct nbl_chan_tx_param *param)
> +{
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct nbl_chan_tx_desc *tx_desc =
> + NBL_CHAN_TX_RING_TO_DESC(txq, txq->next_to_use);
> + struct nbl_chan_buf *tx_buf =
> + NBL_CHAN_TX_RING_TO_BUF(txq, txq->next_to_use);
> +
> + if (param->arg_len > NBL_CHAN_BUF_LEN - sizeof(*tx_desc))
> + return -EINVAL;
> +
> + tx_desc->dstid = cpu_to_le16(param->dstid);
> + tx_desc->msg_type = cpu_to_le16(param->msg_type);
> + tx_desc->msgid = cpu_to_le16(param->msgid);
> +
> + if (param->arg_len > NBL_CHAN_TX_DESC_EMBEDDED_DATA_LEN) {
> + memcpy(tx_buf->va, param->arg, param->arg_len);
> + tx_desc->buf_addr = cpu_to_le64(tx_buf->pa);
> + tx_desc->buf_len = cpu_to_le16(param->arg_len);
> + tx_desc->data_len = 0;
> + memset(tx_desc->data, 0, sizeof(tx_desc->data));
> + } else {
> + memset(tx_desc->data, 0, sizeof(tx_desc->data));
> + memset(&tx_desc->buf_addr, 0, sizeof(tx_desc->buf_addr));
> + memcpy(tx_desc->data, param->arg, param->arg_len);
> + tx_desc->buf_len = 0;
> + tx_desc->data_len = cpu_to_le16(param->arg_len);
> + }
> + dma_wmb();
> + tx_desc->flags = cpu_to_le16(BIT(NBL_CHAN_TX_DESC_AVAIL));
> +
> + txq->next_to_use =
> + NBL_NEXT_ID(txq->next_to_use, chan_info->num_txq_entries - 1);
> + txq->tail_ptr++;
> +
> + return 0;
> +}
> +
> +static int nbl_chan_kick_tx_ring(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_chan_ring *txq = &chan_info->txq;
> + struct device *dev = chan_mgt->common->dev;
> + int max_retries = NBL_CHAN_TX_WAIT_TIMES;
> + struct nbl_chan_tx_desc *tx_desc;
> + int retry_count = 0;
> +
> + nbl_chan_update_tail_ptr(hw_ops, chan_mgt->hw_ops_tbl->priv,
> + txq->tail_ptr, NBL_MB_TX_QID);
> +
> + tx_desc = NBL_CHAN_TX_RING_TO_DESC(txq, txq->next_to_clean);
> + while (retry_count < max_retries) {
> + if (le16_to_cpu(READ_ONCE(tx_desc->flags)) &
> + BIT(NBL_CHAN_TX_DESC_USED)) {
> + dma_rmb();
> + break;
> + }
> +
> + retry_count++;
> + if (retry_count == max_retries) {
> + dev_err(dev, "chan send message type: %d timeout\n",
> + le16_to_cpu(READ_ONCE(tx_desc->msg_type)));
> + txq->next_to_clean = txq->next_to_use;
> + return -ETIMEDOUT;
> + }
> + usleep_range(NBL_CHAN_TX_WAIT_US, NBL_CHAN_TX_WAIT_US_MAX);
> + }
> +
> + txq->next_to_clean = txq->next_to_use;
> +
> + return 0;
> +}
> +
> +static void nbl_chan_recv_ack_msg(void *priv, u16 srcid, u16 msgid, void *data,
> + u32 data_len)
> +{
> + struct nbl_channel_mgt *chan_mgt = (struct nbl_channel_mgt *)priv;
> + struct nbl_chan_waitqueue_head *wait_head = NULL;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_info *chan_info =
> + chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX];
> + u32 ack_datalen, ack_msgtype = 0;
> + u32 *payload = data;
> + u16 ack_msgid = 0;
> + u32 copy_len;
> +
> + if (data_len > NBL_CHAN_BUF_LEN ||
> + data_len < NBL_CHAN_ACK_HEAD_LEN * sizeof(u32)) {
> + dev_err(dev, "Invalid ACK data_len: %u\n", data_len);
> + return;
> + }
> + ack_datalen = data_len - NBL_CHAN_ACK_HEAD_LEN * sizeof(u32);
> + ack_msgtype = le16_to_cpu(*(__le16 *)(payload + NBL_CHAN_MSG_TYPE_POS));
> + ack_msgid = le16_to_cpu(*(__le16 *)(payload + NBL_CHAN_MSG_ID_POS));
> + if (FIELD_GET(NBL_CHAN_MSGID_LOC_MASK, ack_msgid) >=
> + chan_info->num_txq_entries) {
> + dev_err(dev, "chan recv msg id: %d err\n", ack_msgid);
> + return;
> + }
> + wait_head =
> + &chan_info->wait[FIELD_GET(NBL_CHAN_MSGID_LOC_MASK, ack_msgid)];
> + spin_lock_irq(&wait_head->status_lock);
> + if (srcid != wait_head->dstid) {
> + /* Do not modify the status; the slot remains WAITING,
> + * and the sender will time out normally
> + */
> + spin_unlock_irq(&wait_head->status_lock);
> + dev_err(dev, "ACK srcid=%u != dstid=%u, rejecting\n", srcid,
> + wait_head->dstid);
> + return;
> + }
> + if (READ_ONCE(wait_head->status) != NBL_MBX_STATUS_WAITING) {
> + spin_unlock_irq(&wait_head->status_lock);
> + dev_err(dev,
> + "Skip ack with invalid status, wait_head msgtype:%u msg_index:%u status:%d ack_data_len:%d, ack msgtype:%u msgid:%u datalen:%d\n",
> + READ_ONCE(wait_head->msg_type),
> + READ_ONCE(wait_head->msg_index),
> + READ_ONCE(wait_head->status), wait_head->ack_data_len,
> + ack_msgtype, ack_msgid, ack_datalen);
> + return;
> + }
> +
> + if (READ_ONCE(wait_head->msg_type) != ack_msgtype) {
> + /*
> + * Mismatched ACK. Restore state to WAITING so the original
> + * sender will time out and not reuse the slot.
> + */
> + wait_head->status = NBL_MBX_STATUS_WAITING;
> +
> + dev_err(dev,
> + "Skip ack msg type donot match, wait_head msgtype:%u msg_index:%u status:%d ack_data_len:%d, ack msgtype:%u msgid:%u datalen:%d\n",
> + READ_ONCE(wait_head->msg_type),
> + READ_ONCE(wait_head->msg_index),
> + READ_ONCE(wait_head->status), wait_head->ack_data_len,
> + ack_msgtype, ack_msgid, ack_datalen);
> + spin_unlock_irq(&wait_head->status_lock);
> + /* Wake up the sender to let it know the ACK was invalid */
> + wake_up(&wait_head->wait_queue);
> + return;
> + }
> + if (FIELD_GET(NBL_CHAN_MSGID_INDEX_MASK, ack_msgid) !=
> + READ_ONCE(wait_head->msg_index)) {
> + /*
> + * Stale ACK. Restore state to WAITING so the original
> + * sender will time out and not reuse the slot.
> + */
> + wait_head->status = NBL_MBX_STATUS_WAITING;
> +
> + dev_err(dev,
> + "Stale ACK: expected index=%u, got msgid %u\n",
> + READ_ONCE(wait_head->msg_index), ack_msgid);
> + spin_unlock_irq(&wait_head->status_lock);
> + /* Wake up the sender to let it know the ACK was stale */
> + wake_up(&wait_head->wait_queue);
> + return;
> + }
> +
> + wait_head->ack_err =
> + le32_to_cpu(*(__le32 *)(payload + NBL_CHAN_ACK_RET_POS));
> +
> + copy_len = min_t(u32, wait_head->ack_data_len, ack_datalen);
> + if (wait_head->ack_err >= 0 && copy_len > 0) {
> + if (!wait_head->ack_data) {
> + dev_err(dev, "ACK payload dropped: ack_data is NULL\n");
> + wait_head->ack_data_len = 0;
> + goto ack_done;
> + }
> + memcpy((char *)wait_head->ack_data,
> + payload + NBL_CHAN_ACK_HEAD_LEN, copy_len);
> + wait_head->ack_data_len = (u16)copy_len;
> + } else {
> + wait_head->ack_data_len = 0;
> + }
> +ack_done:
> + /*
> + * Ensure all writes to ack_data and ack_data_len are completed
> + * before setting the 'acked' flag. This prevents other threads
> + * from observing stale or partially updated data.
> + */
> + smp_wmb();
> + wait_head->acked = 1;
> + spin_unlock_irq(&wait_head->status_lock);
> + if (READ_ONCE(wait_head->acked))
> + wake_up(&wait_head->wait_queue);
> +}
> +
> +static void nbl_chan_recv_msg(struct nbl_channel_mgt *chan_mgt, void *data)
> +{
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_msg_node_data *msg_handler;
> + u16 msg_type, payload_len, srcid, msgid;
> + struct nbl_chan_tx_desc *tx_desc;
> + void *payload;
> +
> + tx_desc = data;
> + msg_type = le16_to_cpu(tx_desc->msg_type);
> + dev_dbg(dev, "recv msg_type: %d\n", msg_type);
> +
> + srcid = le16_to_cpu(tx_desc->srcid);
> + msgid = le16_to_cpu(tx_desc->msgid);
> + /* Only check if the value exceeds the maximum, relying on the hash
> + * table to filter invalid message IDs.
> + * The gap values are reserved for future protocol extensions.
> + */
> + if (msg_type >= NBL_CHAN_MSG_MAILBOX_MAX)
> + return;
> +
> + if (tx_desc->data_len) {
> + payload_len = le16_to_cpu(tx_desc->data_len);
> + if (payload_len > NBL_CHAN_TX_DESC_EMBEDDED_DATA_LEN) {
> + dev_err(dev,
> + "data_len=%u exceeds embedded buffer size=%u\n",
> + payload_len,
> + NBL_CHAN_TX_DESC_EMBEDDED_DATA_LEN);
> + return;
> + }
> + payload = tx_desc->data;
> + } else {
> + payload_len = le16_to_cpu(tx_desc->buf_len);
> + if (payload_len > NBL_CHAN_BUF_LEN - sizeof(*tx_desc)) {
> + dev_err(dev,
> + "buf_len=%u exceeds external buffer size=%zu\n",
> + payload_len,
> + NBL_CHAN_BUF_LEN - sizeof(*tx_desc));
> + return;
> + }
> + payload = tx_desc + 1;
> + }
> +
> + msg_handler =
> + nbl_common_get_hash_node(chan_mgt->handle_hash_tbl, &msg_type);
> + if (!msg_handler || !msg_handler->func) {
> + dev_err(dev,
> + "No handler for msg_type: %u (srcid=%u, msgid=%u)\n",
> + msg_type, srcid, msgid);
> + return;
> + }
> + msg_handler->func(msg_handler->priv, srcid, msgid, payload,
> + payload_len);
> +}
> +
> +static void nbl_chan_advance_rx_ring(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info,
> + struct nbl_chan_ring *rxq)
> +{
> + struct nbl_hw_ops *hw_ops = chan_mgt->hw_ops_tbl->ops;
> + struct nbl_chan_rx_desc *rx_desc;
> + struct nbl_chan_buf *rx_buf;
> + u16 next_to_use;
> +
> + next_to_use = rxq->next_to_use;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_use);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_use);
> +
> + rx_desc->buf_addr = cpu_to_le64(rx_buf->pa);
> + rx_desc->buf_len = cpu_to_le32(chan_info->rxq_buf_size);
> +
> + /*
> + * DMA Write Memory Barrier:
> + * Ensures all previous DMA-mapped writes (buffer address/length)
> + * are completed before the descriptor flags are updated.
> + * This prevents hardware from seeing a partially updated descriptor
> + * where flags are set but buffer info isn't ready yet.
> + */
> + dma_wmb();
> +
> + rx_desc->flags = cpu_to_le16(BIT(NBL_CHAN_RX_DESC_AVAIL));
> +
> + /*
> + * CPU Write Memory Barrier:
> + * Ensures the descriptor flags update is visible to other CPUs
> + * before we update the tail pointer. This is important for:
> + * 1. Software cleaning threads that might be checking the tail pointer
> + * 2. Maintaining proper memory ordering in multi-core systems
> + */
> + wmb();
> + rxq->next_to_use++;
> + if (rxq->next_to_use == chan_info->num_rxq_entries)
> + rxq->next_to_use = 0;
> + rxq->tail_ptr++;
> +
> + nbl_chan_update_tail_ptr(hw_ops, chan_mgt->hw_ops_tbl->priv,
> + rxq->tail_ptr, NBL_MB_RX_QID);
> +}
> +
> +/*
> + * Since the channel operates in either polling mode or interrupt mode
> + * (mutually exclusive, configured via set_queue_state), nbl_chan_clean_queue
> + * is always called in a serialized manner:
> + * 1. In polling mode: nbl_chan_clean_queue is called directly within
> + * nbl_chan_send_msg, in the same thread after txq_lock has been released.
> + * No other thread can call it concurrently.
> + * 2. In interrupt mode: nbl_chan_clean_queue is called from a workqueue
> + * (nbl_dev_clean_mailbox_task). Linux workqueue guarantees that the same
> + * work item never runs concurrently on multiple CPUs.
> + * Therefore, at any given time, only one execution context can be inside
> + * nbl_chan_clean_queue. There is no concurrency, and thus no need for
> + * locking
> + */
> +static void nbl_chan_clean_queue(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_info *chan_info)
> +{
> + struct nbl_chan_ring *rxq = &chan_info->rxq;
> + struct device *dev = chan_mgt->common->dev;
> + struct nbl_chan_rx_desc *rx_desc;
> + struct nbl_chan_buf *rx_buf;
> + bool more_work = false;
> + u16 next_to_clean;
> + u32 budget = 64;
> +
> + next_to_clean = rxq->next_to_clean;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_clean);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_clean);
> + while (le16_to_cpu(rx_desc->flags) & BIT(NBL_CHAN_RX_DESC_USED)) {
> + if (!(le16_to_cpu(rx_desc->flags) &
> + BIT(NBL_CHAN_RX_DESC_WRITE)))
> + dev_dbg(dev,
> + "mailbox rx flag 0x%x has no NBL_CHAN_RX_DESC_WRITE\n",
> + le16_to_cpu(rx_desc->flags));
> +
> + dma_rmb();
> + nbl_chan_recv_msg(chan_mgt, rx_buf->va);
> + nbl_chan_advance_rx_ring(chan_mgt, chan_info, rxq);
> + next_to_clean++;
> + if (next_to_clean == chan_info->num_rxq_entries)
> + next_to_clean = 0;
> + rx_desc = NBL_CHAN_RX_RING_TO_DESC(rxq, next_to_clean);
> + rx_buf = NBL_CHAN_RX_RING_TO_BUF(rxq, next_to_clean);
> + if (--budget == 0) {
> + more_work = true;
> + break;
> + }
> + }
> + rxq->next_to_clean = next_to_clean;
> + /* If descriptors remain, reschedule work to avoid stalled RX ring */
> + if (more_work)
> + schedule_work(chan_info->clean_task);
> +}
> +
> +static void nbl_chan_clean_queue_subtask(struct nbl_channel_mgt *chan_mgt,
> + u8 chan_type)
> +{
> + struct nbl_chan_info *chan_info = chan_mgt->chan_info[chan_type];
> +
> + if (!test_bit(NBL_CHAN_INTERRUPT_READY, chan_info->state))
> + return;
> +
> + nbl_chan_clean_queue(chan_mgt, chan_info);
> +}
> +
> +static int nbl_chan_get_msg_id(struct nbl_chan_info *chan_info,
> + u16 *msgid)
> +{
> + int valid_loc = chan_info->wait_head_index, i;
> + struct nbl_chan_waitqueue_head *wait = NULL;
> + int status;
> +
> + for (i = 0; i < NBL_CHAN_QUEUE_LEN; i++) {
> + wait = &chan_info->wait[valid_loc];
> + status = READ_ONCE(wait->status);
> + if (status == NBL_MBX_STATUS_IDLE ||
> + status == NBL_MBX_STATUS_TIMEOUT) {
> + wait->msg_index = NBL_NEXT_ID(wait->msg_index,
> + NBL_CHAN_MSG_INDEX_MAX);
> + *msgid =
> + FIELD_PREP(NBL_CHAN_MSGID_INDEX_MASK,
> + wait->msg_index) |
> + FIELD_PREP(NBL_CHAN_MSGID_LOC_MASK, valid_loc);
> + valid_loc = NBL_NEXT_ID(valid_loc,
> + chan_info->num_txq_entries - 1);
> + chan_info->wait_head_index = valid_loc;
> + return 0;
> + }
> +
> + valid_loc =
> + NBL_NEXT_ID(valid_loc, chan_info->num_txq_entries - 1);
> + }
> +
> + /*
> + * the current NBL_CHAN_QUEUE_LEN configuration meets the design
> + * requirements and theoretically should not return errors, the
> + * following scenarios may still cause the waiting queue to
> + * become full:
> + * High-concurrency scenarios:
> + * If the sender (calling nbl_chan_send_msg()) generates messages
> + * at a rate far exceeding the receiver's ability to process
> + * acknowledgments (ACKs),the waiting queue may become fully occupied.
> + * Delayed or failed ACK handling by the receiver:
> + * The receiver may fail to send ACKs in a timely manner due to
> + * processing delays, blocking, or faults, causing the sender's
> + * waiting queue slots to remain occupied for an extended period.
> + */
> + return -EAGAIN;
> +}
> +
> +static int nbl_chan_send_msg(struct nbl_channel_mgt *chan_mgt,
> + struct nbl_chan_send_info *chan_send)
> +{
> + struct nbl_common_info *common = chan_mgt->common;
> + struct nbl_chan_waitqueue_head *wait_head;
> + struct nbl_chan_tx_param tx_param = { 0 };
> + u16 msgid = 0;
> + int i = NBL_CHAN_TX_WAIT_ACK_TIMES, ret;
> + struct nbl_chan_info *chan_info =
> + chan_mgt->chan_info[NBL_CHAN_TYPE_MAILBOX];
> + struct device *dev = common->dev;
> +
> + if (chan_send->resp_len > NBL_CHAN_BUF_LEN) {
> + dev_err(dev, "resp_len %zu exceeds max %d\n",
> + chan_send->resp_len, NBL_CHAN_BUF_LEN);
> + return -EINVAL;
> + }
> +
> + /* Reject new send if shutdown already triggered */
> + if (READ_ONCE(chan_info->shutdown))
> + return -ESHUTDOWN;
> + mutex_lock(&chan_info->txq_lock);
> +
> + if (test_bit(NBL_CHAN_ABNORMAL, chan_info->state)) {
> + mutex_unlock(&chan_info->txq_lock);
> + return -EIO;
> + }
> + ret = nbl_chan_get_msg_id(chan_info, &msgid);
> + if (ret) {
> + mutex_unlock(&chan_info->txq_lock);
> + dev_err(dev,
> + "Channel tx wait head full, send msgtype:%u to dstid:%u failed\n",
> + chan_send->msg_type, chan_send->dstid);
> + return ret;
> + }
> +
> + tx_param.msg_type = chan_send->msg_type;
> + tx_param.arg = chan_send->arg;
> + tx_param.arg_len = chan_send->arg_len;
> + tx_param.dstid = chan_send->dstid;
> + tx_param.msgid = msgid;
> +
> + ret = nbl_chan_update_txqueue(chan_mgt, chan_info, &tx_param);
> + if (ret) {
> + mutex_unlock(&chan_info->txq_lock);
> + dev_err(dev,
> + "Channel tx queue full, send msgtype:%u to dstid:%u failed\n",
> + chan_send->msg_type, chan_send->dstid);
> + return ret;
> + }
> +
> + wait_head =
> + &chan_info->wait[FIELD_GET(NBL_CHAN_MSGID_LOC_MASK, msgid)];
> + spin_lock_irq(&wait_head->status_lock);
> + wait_head->acked = 0;
> + wait_head->ack_data = chan_send->resp;
> + wait_head->ack_data_len = chan_send->resp_len;
> + wait_head->msg_type = chan_send->msg_type;
> + wait_head->msg_index = FIELD_GET(NBL_CHAN_MSGID_INDEX_MASK, msgid);
> + wait_head->dstid = chan_send->dstid;
> + /* Ensure all fields above are visible before status update, so receiver
> + * won't see WAITING with stale data
> + */
> + smp_wmb();
> + wait_head->status = chan_send->ack ? NBL_MBX_STATUS_WAITING :
> + NBL_MBX_STATUS_IDLE;
> + spin_unlock_irq(&wait_head->status_lock);
> +
> + atomic_inc(&chan_info->inflight_tx_cnt);
> + ret = nbl_chan_kick_tx_ring(chan_mgt, chan_info);
> + if (ret) {
> + mutex_lock(&chan_info->txq_lock);
coccinelle says:
@@ -94,0 +95 @@
+/srv/nipa-builds-contest/testing/wt-cocci/drivers/net/ethernet/nebula-matrix/nbl/nbl_channel/nbl_channel.c:763:1-11: second lock on line 813
this will deadlock.
Note that sashiko has more comments:
https://sashiko.dev/#/patchset/20260708064742.35391-1-illusion.wang%40nebula-matrix.com
/P
^ permalink raw reply
* Re: [PATCH] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Paolo Abeni @ 2026-07-09 8:16 UTC (permalink / raw)
To: Yun Zhou, dsahern, idosch, davem, edumazet, kuba, horms
Cc: netdev, linux-kernel
In-Reply-To: <20260709065631.3488067-1-yun.zhou@windriver.com>
On 7/9/26 8:56 AM, Yun Zhou wrote:
> erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
> nested acquisition of _xmit_lock on the underlay device while already
> holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
> the same lockdep class, creating an ABBA deadlock:
>
> sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
> ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
>
> Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
> This is safe as erspan_xmit() has no shared mutable state: o_seqno is
> atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
> the sibling device with identical xmit structure, already sets lltx.
>
> Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
> Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
> Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
> Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
So this is actually a v2 of:
20260630183702.170798-2-hemendranaik@gmail.com/20260630183702.170798-2-hemendranaik@gmail.com/
right?
please have a look at:
https://elixir.bootlin.com/linux/v7.1/source/Documentation/process/maintainer-netdev.rst#L434
any kind of re-submission matters, no matter how trivial is the reason.
/P
^ permalink raw reply
* Re: [PATCH net-next] gtp: annotate PDP lookups under RTNL
From: Paolo Abeni @ 2026-07-09 8:18 UTC (permalink / raw)
To: Simon Horman
Cc: Pablo Neira Ayuso, Runyu Xiao, laforge, andrew+netdev, davem,
edumazet, kuba, osmocom-net-gprs, netdev, linux-kernel,
jianhao.xu
In-Reply-To: <20260708190451.GM1364329@horms.kernel.org>
On 7/8/26 9:04 PM, Simon Horman wrote:
> On Wed, Jul 08, 2026 at 07:32:52PM +0100, Simon Horman wrote:
>> On Wed, Jul 08, 2026 at 01:10:58PM +0200, Paolo Abeni wrote:
>>> On 7/8/26 12:35 PM, Simon Horman wrote:
>>>> On Tue, Jul 07, 2026 at 04:51:12PM +0200, Pablo Neira Ayuso wrote:
>
> ...
>
>>>>> I think this patch is not correct.
>>>>
>>>> Hi Pablo,
>>>>
>>>> Of course you are correct.
>>>> Sorry for not realising this earlier.
>>>
>>> Human slop here made me wrongly apply this patch. Could either of you
>>> please share a formal revert?
>>
>> Sure, will do.
>
> - [PATCH net-next] Revert "gtp: annotate PDP lookups under RTNL"
> https://lore.kernel.org/netdev/20260708-gtp-rtnl-v1-1-218091f171bc@kernel.org/T/
Thanks Simon!
/P
^ permalink raw reply
* [PATCH] net: mvneta: bm: fix device reference leak on failed lookup
From: Johan Hovold @ 2026-07-09 8:27 UTC (permalink / raw)
To: Marcin Wojtas, Andrew Lunn, David S Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Gregory CLEMENT, netdev, Johan Hovold, stable
Make sure to drop the reference taken to the buffer manager device when
attempting to look up its driver data before the driver has been bound.
Note that holding a reference to a device does not prevent its driver
data from going away.
Fixes: 965cbbec7f20 ("net: mvneta: remove data pointer usage from device_node structure")
Cc: stable@vger.kernel.org # 4.19
Cc: Gregory CLEMENT <gregory.clement@bootlin.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
---
drivers/net/ethernet/marvell/mvneta_bm.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/marvell/mvneta_bm.c b/drivers/net/ethernet/marvell/mvneta_bm.c
index 6bb380494919..128fe1f512b4 100644
--- a/drivers/net/ethernet/marvell/mvneta_bm.c
+++ b/drivers/net/ethernet/marvell/mvneta_bm.c
@@ -395,9 +395,20 @@ static void mvneta_bm_put_sram(struct mvneta_bm *priv)
struct mvneta_bm *mvneta_bm_get(struct device_node *node)
{
- struct platform_device *pdev = of_find_device_by_node(node);
+ struct platform_device *pdev;
+ struct mvneta_bm *priv;
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev)
+ return NULL;
+
+ priv = platform_get_drvdata(pdev);
+ if (!priv) {
+ platform_device_put(pdev);
+ return NULL;
+ }
- return pdev ? platform_get_drvdata(pdev) : NULL;
+ return priv;
}
EXPORT_SYMBOL_GPL(mvneta_bm_get);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH bpf-next v10 1/5] bpf: add bpf_icmp_send kfunc
From: Daniel Borkmann @ 2026-07-09 8:31 UTC (permalink / raw)
To: Mahe Tardy, Stanislav Fomichev
Cc: bpf, andrii, ast, john.fastabend, jordan, martin.lau,
yonghong.song, emil, netdev, edumazet, kuba, pabeni, davem, horms
In-Reply-To: <akJKY_x1GdO3MZJS@gmail.com>
On 6/29/26 12:35 PM, Mahe Tardy wrote:
> On Fri, Jun 26, 2026 at 09:18:39AM -0700, Stanislav Fomichev wrote:
>> On 06/25, Mahe Tardy wrote:
>>> On Thu, Jun 25, 2026 at 09:24:59AM -0700, Stanislav Fomichev wrote:
>>>> On 06/25, Mahe Tardy wrote:
>>>
>>> [...]
>>>
>>>>> +__bpf_kfunc int bpf_icmp_send(struct __sk_buff *skb_ctx, int type, int code)
>>>>> +{
>>>>> + struct sk_buff *skb = (struct sk_buff *)skb_ctx;
>>>>> + struct sk_buff *nskb;
>>>>> + struct sock *sk;
>>>>> +
>>>>> + sk = skb_to_full_sk(skb);
>>>>> + if (sk && sk->sk_kern_sock &&
>>>>> + (sk->sk_protocol == IPPROTO_ICMP || sk->sk_protocol == IPPROTO_ICMPV6))
>>>>> + return -EBUSY;
>>>>> +
>>>>> + switch (skb->protocol) {
>>>>> +#if IS_ENABLED(CONFIG_INET)
>>>>> + case htons(ETH_P_IP): {
>>>>> + if (type != ICMP_DEST_UNREACH)
>>>>> + return -EOPNOTSUPP;
>>>>> + if (code < 0 || code > NR_ICMP_UNREACH ||
>>>>> + code == ICMP_FRAG_NEEDED) /* needs a valid next-hop MTU */
>>>>> + return -EINVAL;
>>>>> +
>>>>> + /* icmp_send expects skb_dst to be a real rtable. */
>>>>> + if (!skb_valid_dst(skb))
>>>>> + return -ENETUNREACH;
>>>>> +
>>>>> + nskb = skb_clone(skb, GFP_ATOMIC);
>>>>> + if (!nskb)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + memset(IPCB(nskb), 0, sizeof(*IPCB(nskb)));
>>>>> + icmp_send(nskb, type, code, 0);
>>>>> + consume_skb(nskb);
>>>>> + break;
>>>>> + }
>>>>> +#endif
>>>>> +#if IS_ENABLED(CONFIG_IPV6)
>>>>> + case htons(ETH_P_IPV6):
>>>>> + if (type != ICMPV6_DEST_UNREACH)
>>>>> + return -EOPNOTSUPP;
>>>>> + if (code < 0 || code > ICMPV6_REJECT_ROUTE)
>>>>> + return -EINVAL;
>>>>
>>>> [..]
>>>>
>>>>> + /* icmpv6_send may treat skb_dst as rt6_info. */
>>>>> + if (skb_metadata_dst(skb))
>>>>> + return -ENETUNREACH;
>>>>
>>>> A bit confused about this. Which part of icmpv6_send treats skb_dst as rt6_info?
>>>> (I see the original sashiko report about dst, but icmp6 seems to be not
>>>> requiring it)
>>>
>>> Yeah I was also a bit confused because this came out of nowhere as soon
>>> as I put the skb_valid_dst only on the IPv4 path (for different
>>> reasons), but there is actually a potential trace in which we have type
>>> confusion indeed:
>>>
>>> - icmp6_send() checks scoped source addresses and calls icmp6_iif() at net/ipv6/icmp.c:702
>>> - icmp6_iif() calls icmp6_dev() at net/ipv6/icmp.c:441
>>> - icmp6_dev() does skb_rt6_info(skb) for loopback/L3 master devices at net/ipv6/icmp.c:428
>>> - skb_rt6_info() casts any non-NULL dst to struct rt6_info at include/net/ip6_route.h:233
>>> - rt6->rt6i_idev is then dereferenced at net/ipv6/icmp.c:434
>>>
>>> When checking with pahole, we can find this on my local kernel:
>>>
>>> struct rt6_info {
>>> struct dst_entry dst; /* 0 136 */
>>> /* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
>>> struct fib6_info * from; /* 136 8 */
>>> int sernum; /* 144 4 */
>>> struct rt6key rt6i_dst; /* 148 20 */
>>> struct rt6key rt6i_src; /* 168 20 */
>>> struct in6_addr rt6i_gateway; /* 188 16 */
>>>
>>> /* XXX 4 bytes hole, try to pack */
>>>
>>> /* --- cacheline 3 boundary (192 bytes) was 16 bytes ago --- */
>>> struct inet6_dev * rt6i_idev; /* 208 8 */ <--- we dereference this
>>> u32 rt6i_flags; /* 216 4 */
>>> short unsigned int rt6i_nfheader_len; /* 220 2 */
>>>
>>> /* size: 224, cachelines: 4, members: 9 */
>>> /* sum members: 218, holes: 1, sum holes: 4 */
>>> /* padding: 2 */
>>> /* last cacheline: 32 bytes */
>>> };
>>>
>>> And the metadata_dst would look like this:
>>>
>>> struct metadata_dst {
>>> struct dst_entry dst; /* 0 136 */
>>> /* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
>>> enum metadata_type type; /* 136 4 */
>>>
>>> /* XXX 4 bytes hole, try to pack */
>>>
>>> union {
>>> struct ip_tunnel_info tun_info; /* 144 96 */
>>> struct hw_port_info port_info; /* 144 16 */
>>> struct macsec_info macsec_info; /* 144 8 */
>>> struct xfrm_md_info xfrm_info; /* 144 16 */
>>> } u; /* 144 96 */ <--- we land on this union
>>>
>>> /* size: 240, cachelines: 4, members: 3 */
>>> /* sum members: 236, holes: 1, sum holes: 4 */
>>> /* last cacheline: 48 bytes */
>>> };
>>>
>>> Let's say it's a struct ip_tunnel_info:
>>>
>>> struct ip_tunnel_info {
>>> struct ip_tunnel_key key; /* 0 64 */
>>>
>>> /* XXX last struct has 7 bytes of padding */
>>>
>>> /* --- cacheline 1 boundary (64 bytes) --- */
>>> struct ip_tunnel_encap encap; /* 64 8 */ <--- 144 + 64 = 208 we land here
>>> struct dst_cache dst_cache; /* 72 16 */
>>> u8 options_len; /* 88 1 */
>>> u8 mode; /* 89 1 */
>>>
>>> /* size: 96, cachelines: 2, members: 5 */
>>> /* padding: 6 */
>>> /* paddings: 1, sum paddings: 7 */
>>> /* last cacheline: 32 bytes */
>>> };
>>>
>>> So I imagine this is fairly tricky to trigger but still a case of type
>>> confusion. I have actually no idea how likely this can happen from my
>>> call but the trace makes sense at least.
>>
>> That logic seems to exist for the icmp6_send to find the input device
>> (since the expected use-case for calling icmp6_send is to the incoming
>> skb). And since you're mainly doing egress, I don't think this path will
>> ever trigger (iow the check is not needed)?
>>
>> Maybe you can add cgroup_ingress test case? Looks like this rt6_info
>> path might trigger for ipv6 lo? I don't see any ingress test in your
>> series, so might be good to have one regardless?
>
> The initial reason I added only egress is because the use case of this
> makes more sense if that's your local kernel giving you feedback about a
> connection you are trying to establish, as a process, but is prevented.
>
> But indeed, I could extend the test to ingress as well, I'd just like
> ideally getting an ack from networking maintainers since this is already
> v10 of this, before making some new changes.
Sry for the delay, Mahe. Not speaking for net maintainers, but if the
skb_metadata_dst() test in IPv6 would be made more strict and align with
IPv4 by erroring out on !skb_valid_dst(skb), would this work? It would
reject NULL dst for IPv6 case when someone would push a synthetic TEST_RUN
skb to spoofe an icmpv6 injection without CAP_NET_ADMIN fwiw. Otherwise
lgtm.
Thanks,
Daniel
^ permalink raw reply
* Re: [PATCH v10 rdma-next] RDMA: Change capability fields in ib_device_attr from int to u32
From: Andy Shevchenko @ 2026-07-09 8:31 UTC (permalink / raw)
To: Erni Sri Satya Vennela
Cc: Jason Gunthorpe, Leon Romanovsky, mkalderon, zyjzyj2000, sagi,
mgurtovoy, haris.iqbal, jinpu.wang, bvanassche, kbusch,
Jens Axboe, Christoph Hellwig, kch, smfrench, linkinjeon, metze,
tom, cel, jlayton, neil, okorniev, Dai.Ngo, trondmy, anna,
achender, davem, edumazet, kuba, pabeni, horms, kees, michaelgur,
edwards, phaddad, eadavis, yishaih, kalesh-anakkur.purayil, clm,
ebadger, linux-rdma, linux-kernel, target-devel, linux-nvme,
linux-cifs, samba-technical, linux-nfs, netdev, rds-devel,
Jason Gunthorpe
In-Reply-To: <20260709055211.2498307-1-ernis@linux.microsoft.com>
On Wed, Jul 08, 2026 at 10:51:29PM -0700, Erni Sri Satya Vennela wrote:
> The capability counter fields in struct ib_device_attr are declared
> as signed int, but these values are inherently non-negative. Drivers
> maintain their cached caps as u32 and assign them directly into these
> int fields; if a cap exceeds INT_MAX the implicit narrowing yields a
> negative value visible to the IB core.
>
> Change the signed int capability fields to u32 to match the
> underlying nature of the data. Also update consumers across the IB
> core, ULPs, NVMe-oF target, RDS, and NFS/RDMA so the new u32 values
> are not forced back through signed int or u8 via min()/min_t() or
> narrowing local variables.
>
> The nvmet-rdma consumer of max_srq clamps it against
> ib_device.num_comp_vectors, which stays a signed int, so that site
> uses min_t() instead of min() to handle the signed/unsigned mismatch.
Assuming the functionality is left untouched, from code perspective LGTM,
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
...
> int ipoib_cm_dev_init(struct net_device *dev)
> {
> struct ipoib_dev_priv *priv = ipoib_priv(dev);
> - int max_srq_sge, i;
> + u32 max_srq_sge;
> + int i;
> u8 addr;
This can keep the reversed xmas tree ordering.
...
> static int srq_size_set(const char *val, const struct kernel_param *kp)
> {
> - int n = 0, ret;
> + unsigned int n;
> + int ret;
>
> - ret = kstrtoint(val, 10, &n);
> + ret = kstrtouint(val, 10, &n);
> if (ret != 0 || n < 256)
> return -EINVAL;
Side note (perhaps another patch?)
ret = kstrtouint(val, 10, &n);
if (ret)
return ret;
if (n < 256)
return -ERANGE,
> - return param_set_int(val, kp);
> + return param_set_uint(val, kp);
> }
...
Have you considered also replacing min_not_zero() or do some refactoring
around there?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH net-next] ipv6: initialize ipcm6_cookie before parsing control messages
From: Wayen Yan @ 2026-07-09 8:32 UTC (permalink / raw)
To: netdev
Cc: lorenzo, horms, pabeni, kuba, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
ip6_datagram_send_ctl() parses both SOL_IPV6 and SOL_SOCKET control
messages. For SOL_SOCKET messages it passes ipc6->sockc to
__sock_cmsg_send(), which updates fields such as tsflags with
read-modify-write operations.
The IPV6_2292PKTOPTIONS and flowlabel option paths only set ipc6.opt
before calling ip6_datagram_send_ctl(). If a SOL_SOCKET control message
such as SO_TIMESTAMPING_* or SCM_TS_OPT_ID is present, this can read
uninitialized sockc state.
Initialize the ipcm6_cookie with ipcm6_init_sk(), as the normal IPv6
sendmsg paths do, before overriding ->opt with the temporary option
buffer.
Signed-off-by: Wayen Yan <win847@gmail.com>
---
net/ipv6/ip6_flowlabel.c | 1 +
net/ipv6/ipv6_sockglue.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 1ab5ad0dcf24..5cbfb82710f4 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -405,6 +405,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
msg.msg_control = (void *)(fl->opt+1);
memset(&flowi6, 0, sizeof(flowi6));
+ ipcm6_init_sk(&ipc6, sk);
ipc6.opt = fl->opt;
err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, &ipc6);
if (err)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b4c977434c2e..0dcd2c224014 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -839,6 +839,7 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
msg.msg_controllen = optlen;
msg.msg_control_is_user = false;
msg.msg_control = (void *)(opt+1);
+ ipcm6_init_sk(&ipc6, sk);
ipc6.opt = opt;
retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, &ipc6);
--
2.51.0
^ permalink raw reply related
* Re: [PATCH] selftests/net: fix EVP_MD_CTX leak in tcp_mmap
From: patchwork-bot+netdevbpf @ 2026-07-09 8:40 UTC (permalink / raw)
To: Wang Yan
Cc: davem, edumazet, kuba, pabeni, shuah, horms, lixiaoyan, netdev,
linux-kselftest, linux-kernel
In-Reply-To: <20260702025949.442523-1-wangyan01@kylinos.cn>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Thu, 2 Jul 2026 10:59:49 +0800 you wrote:
> In tcp_mmap.c, both child_thread() and main() allocate an EVP_MD_CTX
> via EVP_MD_CTX_new() when integrity checking is enabled, but neither
> function releases the context. child_thread() misses the free in its
> common cleanup block, and main() returns without freeing the context.
>
> This results in a SHA256 context leak on every run that uses the
> ‑i (integrity) option. Add the missing EVP_MD_CTX_free() calls to
> the appropriate cleanup paths to fix the leak.
>
> [...]
Here is the summary with links:
- selftests/net: fix EVP_MD_CTX leak in tcp_mmap
https://git.kernel.org/netdev/net/c/f4ef35efbb49
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net v3 0/2] Fix MANA RX with bounce buffering
From: patchwork-bot+netdevbpf @ 2026-07-09 8:40 UTC (permalink / raw)
To: Dexuan Cui
Cc: kys, haiyangz, wei.liu, longli, andrew+netdev, davem, edumazet,
kuba, pabeni, kotaranov, horms, ernis, dipayanroy, kees,
jacob.e.keller, ssengar, linux-hyperv, netdev, linux-kernel,
linux-rdma, stable
In-Reply-To: <20260702041237.617719-1-decui@microsoft.com>
Hello:
This series was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Wed, 1 Jul 2026 21:12:35 -0700 you wrote:
> With swiotlb=force, the MANA NIC fails to work properly due to commit
> 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers instead
> of full pages to improve memory efficiency.").
>
> This happens because, with the standard MTU=1500, the aforementioned
> commit uses page pool frags with PP_FLAG_DMA_MAP, but fails to call
> page_pool_dma_sync_for_cpu() to sync the received packet for CPU acces
> before handing the RX buffer to the stack.
>
> [...]
Here is the summary with links:
- [net,v3,1/2] net: mana: Validate the packet length reported by the NIC
https://git.kernel.org/netdev/net/c/2e2a83b4998a
- [net,v3,2/2] net: mana: Sync page pool RX frags for CPU
https://git.kernel.org/netdev/net/c/c72a0f09c57f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net v4] net: macb: drop in-flight Tx SKBs on close
From: Paolo Abeni @ 2026-07-09 8:47 UTC (permalink / raw)
To: Théo Lebrun, Nicolas Ferre, Claudiu Beznea, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Jeff Garzik,
Conor Dooley
Cc: Paolo Valerio, Nicolai Buchwitz, netdev, linux-kernel,
Vladimir Kondratiev, Gregory CLEMENT, Benoît Monin,
Tawfik Bayouk, Thomas Petazzoni, Maxime Chevallier, stable
In-Reply-To: <20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com>
On 7/2/26 5:37 PM, Théo Lebrun wrote:
> The MACB driver has since forever leaked the outgoing SKBs that
> have not yet been marked as completed. They live in queue->tx_skb
> which gets freed without remorse nor checking.
>
> macb_free_consistent() gets called in a few codepaths, but only close will
> trigger the added expressions. In macb_open() and macb_alloc_consistent()
> failure cases, queues' tx_skb just got allocated and are empty.
>
> Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver")
> Cc: stable@vger.kernel.org
> Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de>
> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
> ---
> Changes in v4:
> - Drop the skb_drop_reason code. No other Ethernet driver does that and
> the reasoning (because our stats are broken) is a bad one.
> - Take Rb trailer from Nicolai.
> - Drop <hskinnemoen@atmel.com> email that gets rejected.
> - Rebase upon latest net/main (d8e8b85a85fe).
> - Link to v3: https://patch.msgid.link/20260617-macb-drop-tx-v3-0-d4c7e57d890b@bootlin.com
>
> Changes in v3:
> - Drop stats fixing. A proper fix deserves its own net-next refactoring
> series to migrate to netdev_stat_ops (ynltool uAPI), which will come
> in later. We keep the tx_dropped++ because they are safe as every
> other context is disabled when macb_free_consistent() is called.
> - Rebased to latest net/main (406e8a651a7b), nothing to report.
> - Link to v2: https://patch.msgid.link/20260428-macb-drop-tx-v2-0-647f5199d8df@bootlin.com
>
> Changes in v2:
> - Increment tx_dropped stat once per SKB, not once per frame.
> - Reset tx_head & tx_tail to avoid keeping stalled cursors.
> - Fix SKB dropped reasons throughout by adding the reason as parameter
> to macb_tx_unmap(). This is a new patch. Then the drop-all-on-close
> fix can use this ability to report we are not consuming SKBs.
> - Add increment to stats->tx_dropped on DMA mapping failure and
> tx_error_task. Done as separate patches (3 and 4).
> - Rebase upon net/main @ 46f74a3f7d57, nothing to report.
> - Link to v1: https://patch.msgid.link/20260424-macb-drop-tx-v1-1-b3ecb787d84d@bootlin.com
> ---
> drivers/net/ethernet/cadence/macb_main.c | 21 +++++++++++++++++++--
> 1 file changed, 19 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> index fd282a1700fb..d394f1f43b68 100644
> --- a/drivers/net/ethernet/cadence/macb_main.c
> +++ b/drivers/net/ethernet/cadence/macb_main.c
> @@ -2668,8 +2668,25 @@ static void macb_free_consistent(struct macb *bp)
> dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma);
>
> for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
> - kfree(queue->tx_skb);
> - queue->tx_skb = NULL;
> + if (queue->tx_skb) {
> + unsigned int dropped = 0, tail;
> +
> + for (tail = queue->tx_tail; tail != queue->tx_head;
> + tail++) {
> + if (macb_tx_skb(queue, tail)->skb)
> + dropped++;
> + macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
> + }
> +
> + queue->stats.tx_dropped += dropped;
> + bp->dev->stats.tx_dropped += dropped;
> +
> + kfree(queue->tx_skb);
> + queue->tx_skb = NULL;
> + }
> +
> + queue->tx_head = 0;
> + queue->tx_tail = 0;
Both sashikos noted this could race vs tx_error_task, but it looks like
a pre-existing issue, and I think it should be addressed with a
follow-up patch.
/P
^ permalink raw reply
* Re: [PATCH net v4] net: macb: drop in-flight Tx SKBs on close
From: patchwork-bot+netdevbpf @ 2026-07-09 8:50 UTC (permalink / raw)
To: =?utf-8?q?Th=C3=A9o_Lebrun_=3Ctheo=2Elebrun=40bootlin=2Ecom=3E?=
Cc: nicolas.ferre, claudiu.beznea, andrew+netdev, davem, edumazet,
kuba, pabeni, jeff, conor.dooley, pvalerio, nb, netdev,
linux-kernel, vladimir.kondratiev, gregory.clement, benoit.monin,
tawfik.bayouk, thomas.petazzoni, maxime.chevallier, stable
In-Reply-To: <20260702-macb-drop-tx-v4-1-1c833eebdbc8@bootlin.com>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Thu, 02 Jul 2026 17:37:02 +0200 you wrote:
> The MACB driver has since forever leaked the outgoing SKBs that
> have not yet been marked as completed. They live in queue->tx_skb
> which gets freed without remorse nor checking.
>
> macb_free_consistent() gets called in a few codepaths, but only close will
> trigger the added expressions. In macb_open() and macb_alloc_consistent()
> failure cases, queues' tx_skb just got allocated and are empty.
>
> [...]
Here is the summary with links:
- [net,v4] net: macb: drop in-flight Tx SKBs on close
https://git.kernel.org/netdev/net/c/27f575836cfe
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH nf-next v5 0/6] Add IPv4 over IPv6 and SIT flowtable SW acceleration
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
Similar to IPIP and IP6I6 tunnels, introduce sw acceleration for IPv4 over
IPv6 and SIT tunnels in the netfilter flowtable infrastructure.
---
Changes in v5:
- Fix ipip6_tunnel_fill_forward_path() to take into account not only
IPv6 packet since SIT tunnels can encapsulate even IPv4/MPLS traffic.
- Return an error in ipip6_tunnel_fill_forward_path() if tunnel daddr is
not set since NBMA tunnels are not supported yet.
- Return an error in ipip6_tunnel_fill_forward_path() if encap.type is
not TUNNEL_ENCAP_NONE
- cosmetics
- Link to v4: https://lore.kernel.org/r/20260703-b4-flowtable-sw-accel-ip6ip-v4-0-00398cd12382@kernel.org
Changes in v4:
- Rebase on top of nf-next and fixed conflicts.
- Link to v3: https://lore.kernel.org/r/20260531-b4-flowtable-sw-accel-ip6ip-v3-0-56a2826f3279@kernel.org
Changes in v3:
- Drop nf_flow_tunnel_v4_push and nf_flow_tunnel_v6_push routines
- Rebase on top of net-next tree.
- Link to v2: https://lore.kernel.org/r/20260506-b4-flowtable-sw-accel-ip6ip-v2-0-439fd427726e@kernel.org
Changes in v2:
- Fix MTU check in nf_flow_offload_forward() and in
nf_flow_offload_ipv6_forward()
- Add SIT sw acceleration support
- Link to v1: https://lore.kernel.org/r/20260505-b4-flowtable-sw-accel-ip6ip-v1-0-9ac39ccc9ea9@kernel.org
---
Lorenzo Bianconi (6):
net: netfilter: add ether_type to net_device_path_ctx
net: netfilter: add encap_proto to flow_offload_tunnel
net: netfilter: add IPv4 over IPv6 tunnel flowtable acceleration
selftests: netfilter: nft_flowtable.sh: add IPv4 over IPv6 flowtable selftest
net: netfilter: add SIT tunnel flowtable acceleration
selftests: netfilter: nft_flowtable.sh: add SIT flowtable selftest
drivers/net/ethernet/airoha/airoha_ppe.c | 13 +-
drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 13 +-
include/linux/netdevice.h | 5 +-
include/net/netfilter/nf_flow_table.h | 1 +
net/core/dev.c | 6 +-
net/ipv4/ipip.c | 1 +
net/ipv6/ip6_tunnel.c | 6 +-
net/ipv6/sit.c | 51 +++
net/netfilter/nf_flow_table_core.c | 16 +-
net/netfilter/nf_flow_table_ip.c | 411 +++++++++++++--------
net/netfilter/nf_flow_table_path.c | 20 +-
tools/testing/selftests/net/netfilter/config | 1 +
.../selftests/net/netfilter/nft_flowtable.sh | 78 +++-
13 files changed, 434 insertions(+), 188 deletions(-)
---
base-commit: a88e11651a59b616a8e614e178f68cd730eed0fe
change-id: 20260505-b4-flowtable-sw-accel-ip6ip-7101034cd147
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ 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