* [PATCH v4 net-next 09/14] net: dsa: add NETC switch tag support
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The NXP NETC switch tag is a proprietary header added to frames after the
source MAC address. The switch tag has 3 types, and each type has 1 ~ 4
subtypes, the details are as follows.
Forward NXP switch tag (Type=0): Represents forwarded frames.
- SubType = 0 - Normal frame processing.
To_Port NXP switch tag (Type=1): Represents frames that are to be sent
to a specific switch port.
- SubType = 0. No request to perform timestamping.
- SubType = 1. Request to perform one-step timestamping.
- SubType = 2. Request to perform two-step timestamping.
- SubType = 3. Request to perform both one-step timestamping and
two-step timestamping.
To_Host NXP switch tag (Type=2): Represents frames redirected or copied
to the switch management port.
- SubType = 0. Received frames redirected or copied to the switch
management port.
- SubType = 1. Received frames redirected or copied to the switch
management port with captured timestamp at the switch port where
the frame was received.
- SubType = 2. Transmit timestamp response (two-step timestamping).
In addition, the length of different type switch tag is different, the
minimum length is 6 bytes, the maximum length is 14 bytes. Currently,
Forward tag, SubType 0 of To_Port tag and Subtype 0 of To_Host tag are
supported. More tags will be supported in the future.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
include/linux/dsa/tag_netc.h | 14 +++
include/net/dsa.h | 2 +
include/uapi/linux/if_ether.h | 1 +
net/dsa/Kconfig | 10 ++
net/dsa/Makefile | 1 +
net/dsa/tag_netc.c | 185 ++++++++++++++++++++++++++++++++++
6 files changed, 213 insertions(+)
create mode 100644 include/linux/dsa/tag_netc.h
create mode 100644 net/dsa/tag_netc.c
diff --git a/include/linux/dsa/tag_netc.h b/include/linux/dsa/tag_netc.h
new file mode 100644
index 000000000000..fe964722e5b0
--- /dev/null
+++ b/include/linux/dsa/tag_netc.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2025-2026 NXP
+ */
+
+#ifndef __NET_DSA_TAG_NETC_H
+#define __NET_DSA_TAG_NETC_H
+
+#include <linux/skbuff.h>
+#include <net/dsa.h>
+
+#define NETC_TAG_MAX_LEN 14
+
+#endif
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6c17446f3dcc..6bed824d1f07 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -58,6 +58,7 @@ struct tc_action;
#define DSA_TAG_PROTO_YT921X_VALUE 30
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
+#define DSA_TAG_PROTO_NETC_VALUE 33
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -93,6 +94,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE,
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,
};
struct dsa_switch;
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index df9d44a11540..fb5efc8e06cc 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -123,6 +123,7 @@
#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */
#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_NXP_NETC 0xFD3A /* NXP NETC DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value
* then the frame is Ethernet II. Else it is 802.3 */
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 5ed8c704636d..d5e725b90d78 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -125,6 +125,16 @@ config NET_DSA_TAG_KSZ
Say Y if you want to enable support for tagging frames for the
Microchip 8795/937x/9477/9893 families of switches.
+config NET_DSA_TAG_NETC
+ tristate "Tag driver for NXP NETC switches"
+ help
+ Say Y or M if you want to enable support for the NXP Switch Tag (NST),
+ as implemented by NXP NETC switches having version 4.3 or later. The
+ switch tag is a proprietary header added to frames after the source
+ MAC address, it has 3 types and each type has different subtypes, so
+ its length depends on the type and subtype of the tag, the maximum
+ length is 14 bytes.
+
config NET_DSA_TAG_OCELOT
tristate "Tag driver for Ocelot family of switches, using NPI port"
select PACKING
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index bf7247759a64..b8c2667cd14a 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
+obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o
obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c
new file mode 100644
index 000000000000..addd41f7f1b6
--- /dev/null
+++ b/net/dsa/tag_netc.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025-2026 NXP
+ */
+
+#include <linux/dsa/tag_netc.h>
+
+#include "tag.h"
+
+#define NETC_NAME "nxp_netc"
+
+/* Forward NXP switch tag */
+#define NETC_TAG_FORWARD 0
+
+/* To_Port NXP switch tag */
+#define NETC_TAG_TO_PORT 1
+/* SubType0: No request to perform timestamping */
+#define NETC_TAG_TP_SUBTYPE0 0
+
+/* To_Host NXP switch tag */
+#define NETC_TAG_TO_HOST 2
+/* SubType0: frames redirected or copied to CPU port */
+#define NETC_TAG_TH_SUBTYPE0 0
+/* SubType1: frames redirected or copied to CPU port with timestamp */
+#define NETC_TAG_TH_SUBTYPE1 1
+/* SubType2: Transmit timestamp response (two-step timestamping) */
+#define NETC_TAG_TH_SUBTYPE2 2
+
+/* NETC switch tag lengths */
+#define NETC_TAG_FORWARD_LEN 6
+#define NETC_TAG_TP_SUBTYPE0_LEN 6
+#define NETC_TAG_TH_SUBTYPE0_LEN 6
+#define NETC_TAG_TH_SUBTYPE1_LEN 14
+#define NETC_TAG_TH_SUBTYPE2_LEN 14
+#define NETC_TAG_CMN_LEN 5
+
+#define NETC_TAG_SUBTYPE GENMASK(3, 0)
+#define NETC_TAG_TYPE GENMASK(7, 4)
+#define NETC_TAG_QV BIT(0)
+#define NETC_TAG_IPV GENMASK(4, 2)
+#define NETC_TAG_SWITCH GENMASK(2, 0)
+#define NETC_TAG_PORT GENMASK(7, 3)
+
+struct netc_tag_cmn {
+ __be16 tpid;
+ u8 type;
+ u8 qos;
+ u8 switch_port;
+} __packed;
+
+static void netc_fill_common_tag(struct netc_tag_cmn *tag, u8 type,
+ u8 subtype, u8 sw_id, u8 port, u8 ipv)
+{
+ tag->tpid = htons(ETH_P_NXP_NETC);
+ tag->type = FIELD_PREP(NETC_TAG_TYPE, type) |
+ FIELD_PREP(NETC_TAG_SUBTYPE, subtype);
+ tag->qos = NETC_TAG_QV | FIELD_PREP(NETC_TAG_IPV, ipv);
+ tag->switch_port = FIELD_PREP(NETC_TAG_SWITCH, sw_id) |
+ FIELD_PREP(NETC_TAG_PORT, port);
+}
+
+static void *netc_fill_common_tp_tag(struct sk_buff *skb,
+ struct net_device *ndev,
+ u8 subtype, int tag_len)
+{
+ struct dsa_port *dp = dsa_user_to_port(ndev);
+ u16 queue = skb_get_queue_mapping(skb);
+ u8 ipv = netdev_txq_to_tc(ndev, queue);
+ void *tag;
+
+ skb_push(skb, tag_len);
+ dsa_alloc_etype_header(skb, tag_len);
+
+ tag = dsa_etype_header_pos_tx(skb);
+ memset(tag + NETC_TAG_CMN_LEN, 0, tag_len - NETC_TAG_CMN_LEN);
+ netc_fill_common_tag(tag, NETC_TAG_TO_PORT, subtype,
+ dp->ds->index, dp->index, ipv);
+
+ return tag;
+}
+
+static void netc_fill_tp_tag_subtype0(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ netc_fill_common_tp_tag(skb, ndev, NETC_TAG_TP_SUBTYPE0,
+ NETC_TAG_TP_SUBTYPE0_LEN);
+}
+
+/* Currently only support To_Port tag, subtype 0 */
+static struct sk_buff *netc_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ netc_fill_tp_tag_subtype0(skb, ndev);
+
+ return skb;
+}
+
+static int netc_get_rx_tag_len(int rx_type)
+{
+ int type = FIELD_GET(NETC_TAG_TYPE, rx_type);
+
+ if (type == NETC_TAG_TO_HOST) {
+ u8 subtype = rx_type & NETC_TAG_SUBTYPE;
+
+ if (subtype == NETC_TAG_TH_SUBTYPE1)
+ return NETC_TAG_TH_SUBTYPE1_LEN;
+ else if (subtype == NETC_TAG_TH_SUBTYPE2)
+ return NETC_TAG_TH_SUBTYPE2_LEN;
+ else
+ return NETC_TAG_TH_SUBTYPE0_LEN;
+ }
+
+ return NETC_TAG_FORWARD_LEN;
+}
+
+static struct sk_buff *netc_rcv(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct netc_tag_cmn *tag_cmn;
+ int tag_len, sw_id, port;
+
+ if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN)))
+ return NULL;
+
+ tag_cmn = dsa_etype_header_pos_rx(skb);
+ tag_len = netc_get_rx_tag_len(tag_cmn->type);
+
+ if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) {
+ dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n",
+ ntohs(tag_cmn->tpid));
+
+ return NULL;
+ }
+
+ if (tag_cmn->qos & NETC_TAG_QV)
+ skb->priority = FIELD_GET(NETC_TAG_IPV, tag_cmn->qos);
+
+ sw_id = NETC_TAG_SWITCH & tag_cmn->switch_port;
+ /* ENETC VEPA switch ID (0) is not supported yet */
+ if (!sw_id) {
+ dev_warn_ratelimited(&ndev->dev,
+ "VEPA switch ID is not supported yet\n");
+
+ return NULL;
+ }
+
+ port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port);
+ skb->dev = dsa_conduit_find_user(ndev, sw_id, port);
+ if (!skb->dev)
+ return NULL;
+
+ if (tag_cmn->type == NETC_TAG_FORWARD)
+ dsa_default_offload_fwd_mark(skb);
+
+ /* Remove Switch tag from the frame */
+ skb_pull_rcsum(skb, tag_len);
+ dsa_strip_etype_header(skb, tag_len);
+
+ return skb;
+}
+
+static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto,
+ int *offset)
+{
+ struct netc_tag_cmn *tag_cmn = (struct netc_tag_cmn *)(skb->data - 2);
+ int tag_len = netc_get_rx_tag_len(tag_cmn->type);
+
+ *offset = tag_len;
+ *proto = ((__be16 *)skb->data)[(tag_len / 2) - 1];
+}
+
+static const struct dsa_device_ops netc_netdev_ops = {
+ .name = NETC_NAME,
+ .proto = DSA_TAG_PROTO_NETC,
+ .xmit = netc_xmit,
+ .rcv = netc_rcv,
+ .needed_headroom = NETC_TAG_MAX_LEN,
+ .flow_dissect = netc_flow_dissect,
+};
+
+MODULE_DESCRIPTION("DSA tag driver for NXP NETC switch family");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_NAME);
+module_dsa_tag_driver(netc_netdev_ops);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 08/14] net: enetc: add multiple command BD rings support
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
All the tables of NETC switch are managed through the command BD ring,
but unlike ENETC, the switch has two command BD rings, if the current
ring is busy, the switch driver can switch to another ring to manage
the table. Currently, the NTMP driver does not support multiple rings.
Therefore, netc_select_cbdr() is added to select a appropriate ring to
execute the command for the switch.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 960d5be8ec42..5679b866af48 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -125,12 +125,21 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd)
u16 status;
u32 val;
- /* Currently only i.MX95 ENETC is supported, and it only has one
- * command BD ring
+ for (i = 0; i < user->cbdr_num; i++) {
+ cbdr = &user->ring[i];
+ if (spin_trylock_bh(&cbdr->ring_lock))
+ break;
+ }
+
+ /* If all command BD rings are locked, we need to select
+ * one of them and wait for it.
*/
- cbdr = &user->ring[0];
+ if (i == user->cbdr_num) {
+ int cpu = raw_smp_processor_id();
- spin_lock_bh(&cbdr->ring_lock);
+ cbdr = &user->ring[cpu % user->cbdr_num];
+ spin_lock_bh(&cbdr->ring_lock);
+ }
if (unlikely(!ntmp_get_free_cbd_num(cbdr)))
ntmp_clean_cbdr(cbdr);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 07/14] net: enetc: add support for "Add" and "Delete" operations to IPFT
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The ingress port filter table (IPFT )contains a set of filters each
capable of classifying incoming traffic using a mix of L2, L3, and L4
parsed and arbitrary field data. As a result of a filter match, several
actions can be specified such as on whether to deny or allow a frame,
overriding internal QoS attributes associated with the frame and setting
parameters for the subsequent frame processing functions, such as stream
identification, policing, ingress mirroring. Each entry corresponds to a
filter. The ingress port filter entries are added using a precedence
value. If a frame matches multiple entries, the entry with the higher
precedence is used. Currently, this patch only adds "Add" and "Delete"
operations to the ingress port filter table. These two interfaces will
be used by both ENETC driver and NETC switch driver.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 76 +++++++++++++
.../ethernet/freescale/enetc/ntmp_private.h | 36 ++++++
include/linux/fsl/ntmp.h | 104 ++++++++++++++++++
3 files changed, 216 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 3c59b355c142..960d5be8ec42 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -20,6 +20,7 @@
/* Define NTMP Table ID */
#define NTMP_MAFT_ID 1
#define NTMP_RSST_ID 3
+#define NTMP_IPFT_ID 13
#define NTMP_FDBT_ID 15
#define NTMP_VFT_ID 18
#define NTMP_BPT_ID 41
@@ -234,6 +235,8 @@ static const char *ntmp_table_name(int tbl_id)
return "MAC Address Filter Table";
case NTMP_RSST_ID:
return "RSS Table";
+ case NTMP_IPFT_ID:
+ return "Ingress Port Filter Table";
case NTMP_FDBT_ID:
return "FDB Table";
case NTMP_VFT_ID:
@@ -468,6 +471,79 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
}
EXPORT_SYMBOL_GPL(ntmp_rsst_query_entry);
+/**
+ * ntmp_ipft_add_entry - add an entry into the ingress port filter table
+ * @user: target ntmp_user struct
+ * @entry: the entry data, entry->cfge (configuration element data) and
+ * entry->keye (key element data) are used as input. Since the entry ID
+ * is assigned by the hardware, so entry->entry_id is a returned value
+ * for the driver to use, the driver can update/delete/query the entry
+ * based on the entry_id.
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ipft_add_entry(struct ntmp_user *user,
+ struct ipft_entry_data *entry)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct ipft_resp_query),
+ };
+ struct ipft_resp_query *resp;
+ struct ipft_req_ua *req;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ ntmp_fill_crd(&req->crd, user->tbl.ipft_ver, NTMP_QA_ENTRY_ID,
+ NTMP_GEN_UA_CFGEU | NTMP_GEN_UA_STSEU);
+ req->ak.keye = entry->keye;
+ req->cfge = entry->cfge;
+
+ len = NTMP_LEN(sizeof(*req), data.size);
+ ntmp_fill_request_hdr(&cbd, data.dma, len, NTMP_IPFT_ID,
+ NTMP_CMD_AQ, NTMP_AM_TERNARY_KEY);
+
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err) {
+ dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
+ ntmp_table_name(NTMP_IPFT_ID), ERR_PTR(err));
+
+ goto end;
+ }
+
+ resp = (struct ipft_resp_query *)req;
+ entry->entry_id = le32_to_cpu(resp->entry_id);
+
+end:
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_ipft_add_entry);
+
+/**
+ * ntmp_ipft_delete_entry - delete a specified ingress port filter table entry
+ * @user: target ntmp_user struct
+ * @entry_id: the specified ID of the ingress port filter table entry
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id)
+{
+ u32 req_len = sizeof(struct ipft_req_qd);
+
+ return ntmp_delete_entry_by_id(user, NTMP_IPFT_ID,
+ user->tbl.ipft_ver,
+ entry_id, req_len,
+ NTMP_STATUS_RESP_LEN);
+}
+EXPORT_SYMBOL_GPL(ntmp_ipft_delete_entry);
+
/**
* ntmp_fdbt_add_entry - add an entry into the FDB table
* @user: target ntmp_user struct
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 7d50af7745c7..6d519d59a433 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -103,6 +103,42 @@ struct rsst_req_update {
u8 groups[];
};
+/* Ingress Port Filter Table Response Data Buffer Format of Query action */
+struct ipft_resp_query {
+ __le32 status;
+ __le32 entry_id;
+ struct ipft_keye_data keye;
+ __le64 match_count; /* STSE_DATA */
+ struct ipft_cfge_data cfge;
+} __packed;
+
+struct ipft_ak_eid {
+ __le32 entry_id;
+ __le32 resv[52];
+};
+
+union ipft_access_key {
+ struct ipft_ak_eid eid;
+ struct ipft_keye_data keye;
+};
+
+/* Ingress Port Filter Table Request Data Buffer Format of Update and
+ * Add actions
+ */
+struct ipft_req_ua {
+ struct ntmp_cmn_req_data crd;
+ union ipft_access_key ak;
+ struct ipft_cfge_data cfge;
+};
+
+/* Ingress Port Filter Table Request Data Buffer Format of Query and
+ * Delete actions
+ */
+struct ipft_req_qd {
+ struct ntmp_req_by_eid rbe;
+ __le32 resv[52];
+};
+
/* Access Key Format of FDB Table */
struct fdbt_ak_eid {
__le32 entry_id;
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index a54945dcdc61..efda35e5e22c 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -7,6 +7,7 @@
#include <linux/if_ether.h>
#define NTMP_NULL_ENTRY_ID 0xffffffffU
+#define IPFT_MAX_PLD_LEN 24
struct maft_keye_data {
u8 mac_addr[ETH_ALEN];
@@ -34,6 +35,7 @@ struct netc_tbl_vers {
u8 fdbt_ver;
u8 vft_ver;
u8 bpt_ver;
+ u8 ipft_ver;
};
struct netc_cbdr {
@@ -66,6 +68,94 @@ struct maft_entry_data {
struct maft_cfge_data cfge;
};
+struct ipft_pld_byte {
+ u8 data;
+ u8 mask;
+};
+
+struct ipft_keye_data {
+ __le16 precedence;
+ __le16 resv0[3];
+ __le16 frm_attr_flags;
+#define IPFT_FAF_OVLAN BIT(2)
+#define IPFT_FAF_IVLAN BIT(3)
+#define IPFT_FAF_IP_HDR BIT(7)
+#define IPFT_FAF_IP_VER6 BIT(8)
+#define IPFT_FAF_L4_CODE GENMASK(11, 10)
+#define IPFT_FAF_TCP_HDR 1
+#define IPFT_FAF_UDP_HDR 2
+#define IPFT_FAF_SCTP_HDR 3
+#define IPFT_FAF_WOL_MAGIC BIT(12)
+ __le16 frm_attr_flags_mask;
+ __le16 dscp;
+#define IPFT_DSCP GENMASK(5, 0)
+#define IPFT_DSCP_MASK GENMASK(11, 0)
+#define IPFT_DSCP_MASK_ALL 0x3f
+ __le16 src_port; /* This field is reserved for ENETC */
+#define IPFT_SRC_PORT GENMASK(4, 0)
+#define IPFT_SRC_PORT_MASK GENMASK(9, 5)
+#define IPFT_SRC_PORT_MASK_ALL 0x1f
+ __be16 outer_vlan_tci;
+ __be16 outer_vlan_tci_mask;
+ u8 dmac[ETH_ALEN];
+ u8 dmac_mask[ETH_ALEN];
+ u8 smac[ETH_ALEN];
+ u8 smac_mask[ETH_ALEN];
+ __be16 inner_vlan_tci;
+ __be16 inner_vlan_tci_mask;
+ __be16 ethertype;
+ __be16 ethertype_mask;
+ u8 ip_protocol;
+ u8 ip_protocol_mask;
+ __le16 resv1[7];
+ __be32 ip_src[4];
+ __le32 resv2[2];
+ __be32 ip_src_mask[4];
+ __be16 l4_src_port;
+ __be16 l4_src_port_mask;
+ __le32 resv3;
+ __be32 ip_dst[4];
+ __le32 resv4[2];
+ __be32 ip_dst_mask[4];
+ __be16 l4_dst_port;
+ __be16 l4_dst_port_mask;
+ __le32 resv5;
+ struct ipft_pld_byte byte[IPFT_MAX_PLD_LEN];
+};
+
+struct ipft_cfge_data {
+ __le32 cfg;
+#define IPFT_IPV GENMASK(3, 0)
+#define IPFT_OIPV BIT(4)
+#define IPFT_DR GENMASK(6, 5)
+#define IPFT_ODR BIT(7)
+#define IPFT_FLTFA GENMASK(10, 8)
+#define IPFT_FLTFA_DISCARD 0
+#define IPFT_FLTFA_PERMIT 1
+/* Redirect is only for switch */
+#define IPFT_FLTFA_REDIRECT 2
+#define IPFT_IMIRE BIT(11)
+#define IPFT_WOLTE BIT(12)
+#define IPFT_FLTA GENMASK(14, 13)
+#define IPFT_FLTA_RP 1
+#define IPFT_FLTA_IS 2
+#define IPFT_FLTA_SI_BITMAP 3
+#define IPFT_RPR GENMASK(16, 15)
+#define IPFT_CTD BIT(17)
+#define IPFT_HR GENMASK(21, 18)
+#define IPFT_TIMECAPE BIT(22)
+#define IPFT_RRT BIT(23)
+#define IPFT_BL2F BIT(24)
+#define IPFT_EVMEID GENMASK(31, 28)
+ __le32 flta_tgt;
+};
+
+struct ipft_entry_data {
+ u32 entry_id; /* hardware assigns entry ID */
+ struct ipft_keye_data keye;
+ struct ipft_cfge_data cfge;
+};
+
struct fdbt_keye_data {
u8 mac_addr[ETH_ALEN]; /* big-endian */
__le16 resv0;
@@ -155,6 +245,9 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
int count);
int ntmp_rsst_query_entry(struct ntmp_user *user,
u32 *table, int count);
+int ntmp_ipft_add_entry(struct ntmp_user *user,
+ struct ipft_entry_data *entry);
+int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id);
int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
const struct fdbt_keye_data *keye,
const struct fdbt_cfge_data *cfge);
@@ -208,6 +301,17 @@ static inline int ntmp_rsst_query_entry(struct ntmp_user *user,
return 0;
}
+static inline int ntmp_ipft_add_entry(struct ntmp_user *user,
+ struct ipft_entry_data *entry)
+{
+ return 0;
+}
+
+static inline int ntmp_ipft_delete_entry(struct ntmp_user *user, u32 entry_id)
+{
+ return 0;
+}
+
static inline int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
const struct fdbt_keye_data *keye,
const struct fdbt_cfge_data *data)
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 06/14] net: enetc: add support for the "Update" operation to buffer pool table
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The buffer pool table contains buffer pool configuration and operational
information. Each entry corresponds to a buffer pool. The Entry ID value
represents the buffer pool ID to access.
The buffer pool table is a static bounded index table, buffer pools are
always present and enabled. It only supports Update and Query operations,
This patch only adds ntmp_bpt_update_entry() helper to support updating
the specified entry of the buffer pool table. Query action to the table
will be added in the future.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 39 +++++++++++++++++++
.../ethernet/freescale/enetc/ntmp_private.h | 6 +++
include/linux/fsl/ntmp.h | 32 +++++++++++++++
3 files changed, 77 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index d7d8a37078d1..3c59b355c142 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -22,11 +22,15 @@
#define NTMP_RSST_ID 3
#define NTMP_FDBT_ID 15
#define NTMP_VFT_ID 18
+#define NTMP_BPT_ID 41
/* Generic Update Actions for most tables */
#define NTMP_GEN_UA_CFGEU BIT(0)
#define NTMP_GEN_UA_STSEU BIT(1)
+/* Specific Update Actions for some tables */
+#define BPT_UA_BPSEU BIT(1)
+
/* Query Action: 0: Full query, 1: Only query entry ID */
#define NTMP_QA_ENTRY_ID 1
@@ -234,6 +238,8 @@ static const char *ntmp_table_name(int tbl_id)
return "FDB Table";
case NTMP_VFT_ID:
return "VLAN Filter Table";
+ case NTMP_BPT_ID:
+ return "Buffer Pool Table";
default:
return "Unknown Table";
}
@@ -702,5 +708,38 @@ int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
}
EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
+int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct bpt_cfge_data *cfge)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct bpt_req_update),
+ };
+ struct bpt_req_update *req;
+ union netc_cbd cbd;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ ntmp_fill_crd_eid(&req->rbe, user->tbl.bpt_ver, 0,
+ NTMP_GEN_UA_CFGEU | BPT_UA_BPSEU, entry_id);
+ req->cfge = *cfge;
+ ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0),
+ NTMP_BPT_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
+
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to update %s entry 0x%x, err: %pe\n",
+ ntmp_table_name(NTMP_BPT_ID), entry_id, ERR_PTR(err));
+
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_bpt_update_entry);
+
MODULE_DESCRIPTION("NXP NETC Library");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index d2a6399b0a36..7d50af7745c7 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -179,4 +179,10 @@ struct vft_req_ua {
struct vft_cfge_data cfge;
};
+/* Buffer Pool Table Request Data Buffer Format of Update action */
+struct bpt_req_update {
+ struct ntmp_req_by_eid rbe;
+ struct bpt_cfge_data cfge;
+};
+
#endif
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 2aedea17307f..a54945dcdc61 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -33,6 +33,7 @@ struct netc_tbl_vers {
u8 rsst_ver;
u8 fdbt_ver;
u8 vft_ver;
+ u8 bpt_ver;
};
struct netc_cbdr {
@@ -116,6 +117,29 @@ struct vft_cfge_data {
__le32 et_eid;
};
+struct bpt_bpse_data {
+ __le32 amount_used;
+ __le32 amount_used_hwm;
+ u8 bpd_fc_state;
+#define BPT_FC_STATE BIT(0)
+#define BPT_BPD BIT(1)
+} __packed;
+
+struct bpt_cfge_data {
+ u8 fccfg_sbpen;
+#define BPT_SBP_EN BIT(0)
+#define BPT_FC_CFG GENMASK(2, 1)
+#define BPT_FC_CFG_EN_BPFC 1
+ u8 pfc_vector;
+ __le16 max_thresh;
+ __le16 fc_on_thresh;
+ __le16 fc_off_thresh;
+ __le16 sbp_thresh;
+ __le16 resv;
+ __le32 sbp_eid;
+ __le32 fc_ports;
+};
+
#if IS_ENABLED(CONFIG_NXP_NETC_LIB)
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs);
@@ -142,6 +166,8 @@ int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
struct fdbt_entry_data *entry);
int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
const struct vft_cfge_data *cfge);
+int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct bpt_cfge_data *cfge);
#else
static inline int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs)
@@ -213,6 +239,12 @@ static inline int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
return 0;
}
+static inline int ntmp_bpt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct bpt_cfge_data *cfge)
+{
+ return 0;
+}
+
#endif
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 05/14] net: enetc: add support for the "Add" operation to VLAN filter table
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The VLAN filter table contains configuration and control information for
each VLAN configured on the switch. Each VLAN entry includes the VLAN
port membership, which FID to use in the FDB lookup, which spanning tree
group to use, the egress frame modification actions to apply to a frame
exiting form this VLAN, and various configuration and control parameters
for this VLAN.
The VLAN filter table can only be managed by the command BD ring using
table management protocol version 2.0. The table supports Add, Delete,
Update and Query operations. And the table supports 3 access methods:
Entry ID, Exact Match Key Element and Search. But currently we only add
the ntmp_vft_add_entry() helper to support the upcoming switch driver to
add an entry to the VLAN filter table. Other interfaces will be added in
the future.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 50 +++++++++++++++++++
.../ethernet/freescale/enetc/ntmp_private.h | 19 +++++++
include/linux/fsl/ntmp.h | 30 +++++++++++
3 files changed, 99 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index a4a99954baf2..d7d8a37078d1 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -21,6 +21,7 @@
#define NTMP_MAFT_ID 1
#define NTMP_RSST_ID 3
#define NTMP_FDBT_ID 15
+#define NTMP_VFT_ID 18
/* Generic Update Actions for most tables */
#define NTMP_GEN_UA_CFGEU BIT(0)
@@ -231,6 +232,8 @@ static const char *ntmp_table_name(int tbl_id)
return "RSS Table";
case NTMP_FDBT_ID:
return "FDB Table";
+ case NTMP_VFT_ID:
+ return "VLAN Filter Table";
default:
return "Unknown Table";
}
@@ -652,5 +655,52 @@ int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
}
EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry);
+/**
+ * ntmp_vft_add_entry - add an entry into the VLAN filter table
+ * @user: target ntmp_user struct
+ * @vid: VLAN ID
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct vft_req_ua),
+ };
+ struct vft_req_ua *req;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.vft_ver, 0,
+ NTMP_GEN_UA_CFGEU);
+ req->ak.exact.vid = cpu_to_le16(vid);
+ req->cfge = *cfge;
+
+ /* Request header */
+ len = NTMP_LEN(data.size, NTMP_STATUS_RESP_LEN);
+ ntmp_fill_request_hdr(&cbd, data.dma, len, NTMP_VFT_ID,
+ NTMP_CMD_ADD, NTMP_AM_EXACT_KEY);
+
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err)
+ dev_err(user->dev,
+ "Failed to add %s entry, vid: %u, err: %pe\n",
+ ntmp_table_name(NTMP_VFT_ID), vid, ERR_PTR(err));
+
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_vft_add_entry);
+
MODULE_DESCRIPTION("NXP NETC Library");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 8999eafe1920..d2a6399b0a36 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -160,4 +160,23 @@ struct fdbt_resp_query {
u8 resv[3];
};
+/* Access Key Format of VLAN Filter Table */
+struct vft_ak_exact {
+ __le16 vid; /* bit0~11: VLAN ID, other bits are reserved */
+ __le16 resv;
+};
+
+union vft_access_key {
+ __le32 entry_id; /* entry_id match */
+ struct vft_ak_exact exact;
+ __le32 resume_entry_id; /* search */
+};
+
+/* VLAN Filter Table Request Data Buffer Format of Update and Add actions */
+struct vft_req_ua {
+ struct ntmp_cmn_req_data crd;
+ union vft_access_key ak;
+ struct vft_cfge_data cfge;
+};
+
#endif
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index a9f3e6cbf422..2aedea17307f 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -32,6 +32,7 @@ struct netc_tbl_vers {
u8 maft_ver;
u8 rsst_ver;
u8 fdbt_ver;
+ u8 vft_ver;
};
struct netc_cbdr {
@@ -94,6 +95,27 @@ struct fdbt_entry_data {
#define FDBT_ACT_FLAG BIT(7)
};
+struct vft_cfge_data {
+ __le32 bitmap_stg;
+#define VFT_PORT_MEMBERSHIP GENMASK(23, 0)
+#define VFT_STG_ID_MASK GENMASK(27, 24)
+#define VFT_STG_ID(g) FIELD_PREP(VFT_STG_ID_MASK, (g))
+ __le16 fid;
+#define VFT_FID GENMASK(11, 0)
+ __le16 cfg;
+#define VFT_MLO GENMASK(2, 0)
+#define VFT_MFO GENMASK(4, 3)
+#define VFT_IPMFE BIT(6)
+#define VFT_IPMFLE BIT(7)
+#define VFT_PGA BIT(8)
+#define VFT_SFDA BIT(10)
+#define VFT_OSFDA BIT(11)
+#define VFT_FDBAFSS BIT(12)
+ __le32 eta_port_bitmap;
+#define VFT_ETA_PORT_BITMAP GENMASK(23, 0)
+ __le32 et_eid;
+};
+
#if IS_ENABLED(CONFIG_NXP_NETC_LIB)
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs);
@@ -118,6 +140,8 @@ int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id);
int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
u32 *resume_entry_id,
struct fdbt_entry_data *entry);
+int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge);
#else
static inline int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs)
@@ -183,6 +207,12 @@ static inline int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
return 0;
}
+static inline int ntmp_vft_add_entry(struct ntmp_user *user, u16 vid,
+ const struct vft_cfge_data *cfge)
+{
+ return 0;
+}
+
#endif
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 04/14] net: enetc: add basic operations to the FDB table
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The FDB table is used for MAC learning lookups and MAC forwarding lookups.
Each table entry includes information such as a FID and MAC address that
may be unicast or multicast and a forwarding destination field containing
a port bitmap identifying the associated port(s) with the MAC address.
FDB table entries can be static or dynamic. Static entries are added from
software whereby dynamic entries are added either by software or by the
hardware as MAC addresses are learned in the datapath.
The FDB table can only be managed by the command BD ring using table
management protocol version 2.0. Table management command operations Add,
Delete, Update and Query are supported. And the FDB table supports three
access methods: Entry ID, Exact Match Key Element and Search. This patch
adds the following basic supports to the FDB table.
ntmp_fdbt_update_entry() - update the configuration element data of a
specified FDB entry
ntmp_fdbt_delete_entry() - delete a specified FDB entry
ntmp_fdbt_add_entry() - add an entry into the FDB table
ntmp_fdbt_search_port_entry() - Search the FDB entry on the specified
port based on RESUME_ENTRY_ID.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/ntmp.c | 201 +++++++++++++++++-
.../ethernet/freescale/enetc/ntmp_private.h | 61 +++++-
include/linux/fsl/ntmp.h | 69 +++++-
3 files changed, 328 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c
index 703752995e93..a4a99954baf2 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp.c
+++ b/drivers/net/ethernet/freescale/enetc/ntmp.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* NETC NTMP (NETC Table Management Protocol) 2.0 Library
- * Copyright 2025 NXP
+ * Copyright 2025-2026 NXP
*/
#include <linux/dma-mapping.h>
@@ -20,11 +20,15 @@
/* Define NTMP Table ID */
#define NTMP_MAFT_ID 1
#define NTMP_RSST_ID 3
+#define NTMP_FDBT_ID 15
/* Generic Update Actions for most tables */
#define NTMP_GEN_UA_CFGEU BIT(0)
#define NTMP_GEN_UA_STSEU BIT(1)
+/* Query Action: 0: Full query, 1: Only query entry ID */
+#define NTMP_QA_ENTRY_ID 1
+
#define NTMP_ENTRY_ID_SIZE 4
#define RSST_ENTRY_NUM 64
#define RSST_STSE_DATA_SIZE(n) ((n) * 8)
@@ -225,6 +229,8 @@ static const char *ntmp_table_name(int tbl_id)
return "MAC Address Filter Table";
case NTMP_RSST_ID:
return "RSS Table";
+ case NTMP_FDBT_ID:
+ return "FDB Table";
default:
return "Unknown Table";
}
@@ -453,5 +459,198 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count)
}
EXPORT_SYMBOL_GPL(ntmp_rsst_query_entry);
+/**
+ * ntmp_fdbt_add_entry - add an entry into the FDB table
+ * @user: target ntmp_user struct
+ * @entry_id: returned value, the entry ID of the new added entry
+ * @keye: key element data
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
+ const struct fdbt_keye_data *keye,
+ const struct fdbt_cfge_data *cfge)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct fdbt_req_ua),
+ };
+ struct fdbt_resp_query *resp;
+ struct fdbt_req_ua *req;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, NTMP_QA_ENTRY_ID,
+ NTMP_GEN_UA_CFGEU);
+ req->ak.exact.keye = *keye;
+ req->cfge = *cfge;
+
+ len = NTMP_LEN(data.size, sizeof(*resp));
+ /* The entry ID is allotted by hardware, so we need to perform
+ * a query action after the add action to get the entry ID from
+ * hardware.
+ */
+ ntmp_fill_request_hdr(&cbd, data.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_AQ, NTMP_AM_EXACT_KEY);
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err) {
+ dev_err(user->dev, "Failed to add %s entry, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
+ goto end;
+ }
+
+ if (entry_id) {
+ resp = (struct fdbt_resp_query *)req;
+ *entry_id = le32_to_cpu(resp->entry_id);
+ }
+
+end:
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_add_entry);
+
+/**
+ * ntmp_fdbt_update_entry - update the configuration element data of the
+ * specified FDB entry
+ * @user: target ntmp_user struct
+ * @entry_id: the specified entry ID of the FDB table
+ * @cfge: configuration element data
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct fdbt_cfge_data *cfge)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct fdbt_req_ua),
+ };
+ struct fdbt_req_ua *req;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, NTMP_GEN_UA_CFGEU);
+ req->ak.eid.entry_id = cpu_to_le32(entry_id);
+ req->cfge = *cfge;
+
+ /* Request header */
+ len = NTMP_LEN(data.size, NTMP_STATUS_RESP_LEN);
+ ntmp_fill_request_hdr(&cbd, data.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID);
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err)
+ dev_err(user->dev, "Failed to update %s entry, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), ERR_PTR(err));
+
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_update_entry);
+
+/**
+ * ntmp_fdbt_delete_entry - delete the specified FDB entry
+ * @user: target ntmp_user struct
+ * @entry_id: the specified ID of the FDB entry
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id)
+{
+ u32 req_len = sizeof(struct fdbt_req_qd);
+
+ return ntmp_delete_entry_by_id(user, NTMP_FDBT_ID,
+ user->tbl.fdbt_ver,
+ entry_id, req_len,
+ NTMP_STATUS_RESP_LEN);
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_delete_entry);
+
+/**
+ * ntmp_fdbt_search_port_entry - Search the FDB entry on the specified
+ * port based on RESUME_ENTRY_ID
+ * @user: target ntmp_user struct
+ * @port: the specified switch port ID
+ * @resume_entry_id: it is both an input and an output. As an input, it
+ * represents the FDB entry ID to be searched. If it is a NULL entry ID,
+ * it indicates that the first FDB entry for that port is being searched.
+ * As an output, it represents the next FDB entry ID to be searched.
+ * @entry: returned value, the response data of the searched FDB entry
+ *
+ * Return: 0 on success, otherwise a negative error code
+ */
+int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
+ u32 *resume_entry_id,
+ struct fdbt_entry_data *entry)
+{
+ struct ntmp_dma_buf data = {
+ .dev = user->dev,
+ .size = sizeof(struct fdbt_req_qd),
+ };
+ struct fdbt_resp_query *resp;
+ struct fdbt_req_qd *req;
+ union netc_cbd cbd;
+ u32 len;
+ int err;
+
+ err = ntmp_alloc_data_mem(&data, (void **)&req);
+ if (err)
+ return err;
+
+ /* Request data */
+ ntmp_fill_crd(&req->crd, user->tbl.fdbt_ver, 0, 0);
+ req->ak.search.resume_eid = cpu_to_le32(*resume_entry_id);
+ req->ak.search.cfge.port_bitmap = cpu_to_le32(BIT(port));
+ /* Match CFGE_DATA[PORT_BITMAP] field */
+ req->ak.search.cfge_mc = FDBT_CFGE_MC_PORT_BITMAP;
+
+ /* Request header */
+ len = NTMP_LEN(data.size, sizeof(*resp));
+ ntmp_fill_request_hdr(&cbd, data.dma, len, NTMP_FDBT_ID,
+ NTMP_CMD_QUERY, NTMP_AM_SEARCH);
+
+ err = netc_xmit_ntmp_cmd(user, &cbd);
+ if (err) {
+ dev_err(user->dev,
+ "Failed to search %s entry on port %d, err: %pe\n",
+ ntmp_table_name(NTMP_FDBT_ID), port, ERR_PTR(err));
+ goto end;
+ }
+
+ if (!cbd.resp_hdr.num_matched) {
+ entry->entry_id = NTMP_NULL_ENTRY_ID;
+ *resume_entry_id = NTMP_NULL_ENTRY_ID;
+ goto end;
+ }
+
+ resp = (struct fdbt_resp_query *)req;
+ *resume_entry_id = le32_to_cpu(resp->status);
+ entry->entry_id = le32_to_cpu(resp->entry_id);
+ entry->keye = resp->keye;
+ entry->cfge = resp->cfge;
+ entry->acte = resp->acte;
+
+end:
+ ntmp_free_data_mem(&data);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ntmp_fdbt_search_port_entry);
+
MODULE_DESCRIPTION("NXP NETC Library");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
index 34394e40fddd..8999eafe1920 100644
--- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h
+++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* NTMP table request and response data buffer formats
- * Copyright 2025 NXP
+ * Copyright 2025-2026 NXP
*/
#ifndef __NTMP_PRIVATE_H
@@ -11,6 +11,7 @@
#include <linux/fsl/ntmp.h>
#define NTMP_EID_REQ_LEN 8
+#define NTMP_STATUS_RESP_LEN 4
#define NETC_CBDR_BD_NUM 256
union netc_cbd {
@@ -27,6 +28,7 @@ union netc_cbd {
#define NTMP_CMD_QUERY BIT(2)
#define NTMP_CMD_ADD BIT(3)
#define NTMP_CMD_QU (NTMP_CMD_QUERY | NTMP_CMD_UPDATE)
+#define NTMP_CMD_AQ (NTMP_CMD_ADD | NTMP_CMD_QUERY)
u8 access_method;
#define NTMP_ACCESS_METHOD GENMASK(7, 4)
#define NTMP_AM_ENTRY_ID 0
@@ -101,4 +103,61 @@ struct rsst_req_update {
u8 groups[];
};
+/* Access Key Format of FDB Table */
+struct fdbt_ak_eid {
+ __le32 entry_id;
+ __le32 resv[7];
+};
+
+struct fdbt_ak_exact {
+ struct fdbt_keye_data keye;
+ __le32 resv[5];
+};
+
+struct fdbt_ak_search {
+ __le32 resume_eid;
+ struct fdbt_keye_data keye;
+ struct fdbt_cfge_data cfge;
+ u8 acte;
+ u8 keye_mc;
+#define FDBT_KEYE_MAC GENMASK(1, 0)
+ u8 cfge_mc;
+#define FDBT_CFGE_MC GENMASK(2, 0)
+#define FDBT_CFGE_MC_ANY 0
+#define FDBT_CFGE_MC_DYNAMIC 1
+#define FDBT_CFGE_MC_PORT_BITMAP 2
+#define FDBT_CFGE_MC_DYNAMIC_AND_PORT_BITMAP 3
+ u8 acte_mc;
+#define FDBT_ACTE_MC BIT(0)
+};
+
+union fdbt_access_key {
+ struct fdbt_ak_eid eid;
+ struct fdbt_ak_exact exact;
+ struct fdbt_ak_search search;
+};
+
+/* FDB Table Request Data Buffer Format of Update and Add actions */
+struct fdbt_req_ua {
+ struct ntmp_cmn_req_data crd;
+ union fdbt_access_key ak;
+ struct fdbt_cfge_data cfge;
+};
+
+/* FDB Table Request Data Buffer Format of Query and Delete actions */
+struct fdbt_req_qd {
+ struct ntmp_cmn_req_data crd;
+ union fdbt_access_key ak;
+};
+
+/* FDB Table Response Data Buffer Format of Query action */
+struct fdbt_resp_query {
+ __le32 status;
+ __le32 entry_id;
+ struct fdbt_keye_data keye;
+ struct fdbt_cfge_data cfge;
+ u8 acte;
+ u8 resv[3];
+};
+
#endif
diff --git a/include/linux/fsl/ntmp.h b/include/linux/fsl/ntmp.h
index 916dc4fe7de3..a9f3e6cbf422 100644
--- a/include/linux/fsl/ntmp.h
+++ b/include/linux/fsl/ntmp.h
@@ -1,11 +1,13 @@
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
-/* Copyright 2025 NXP */
+/* Copyright 2025-2026 NXP */
#ifndef __NETC_NTMP_H
#define __NETC_NTMP_H
#include <linux/bitops.h>
#include <linux/if_ether.h>
+#define NTMP_NULL_ENTRY_ID 0xffffffffU
+
struct maft_keye_data {
u8 mac_addr[ETH_ALEN];
__le16 resv;
@@ -29,6 +31,7 @@ struct netc_cbdr_regs {
struct netc_tbl_vers {
u8 maft_ver;
u8 rsst_ver;
+ u8 fdbt_ver;
};
struct netc_cbdr {
@@ -61,6 +64,36 @@ struct maft_entry_data {
struct maft_cfge_data cfge;
};
+struct fdbt_keye_data {
+ u8 mac_addr[ETH_ALEN]; /* big-endian */
+ __le16 resv0;
+ __le16 fid;
+#define FDBT_FID GENMASK(11, 0)
+ __le16 resv1;
+};
+
+struct fdbt_cfge_data {
+ __le32 port_bitmap;
+#define FDBT_PORT_BITMAP GENMASK(23, 0)
+ __le32 cfg;
+#define FDBT_OETEID GENMASK(1, 0)
+#define FDBT_EPORT GENMASK(6, 2)
+#define FDBT_IMIRE BIT(7)
+#define FDBT_CTD GENMASK(10, 9)
+#define FDBT_DYNAMIC BIT(11)
+#define FDBT_TIMECAPE BIT(12)
+ __le32 et_eid;
+};
+
+struct fdbt_entry_data {
+ u32 entry_id;
+ struct fdbt_keye_data keye;
+ struct fdbt_cfge_data cfge;
+ u8 acte;
+#define FDBT_ACT_CNT GENMASK(6, 0)
+#define FDBT_ACT_FLAG BIT(7)
+};
+
#if IS_ENABLED(CONFIG_NXP_NETC_LIB)
int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs);
@@ -76,6 +109,15 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table,
int count);
int ntmp_rsst_query_entry(struct ntmp_user *user,
u32 *table, int count);
+int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
+ const struct fdbt_keye_data *keye,
+ const struct fdbt_cfge_data *cfge);
+int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct fdbt_cfge_data *cfge);
+int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id);
+int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
+ u32 *resume_entry_id,
+ struct fdbt_entry_data *entry);
#else
static inline int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev,
const struct netc_cbdr_regs *regs)
@@ -116,6 +158,31 @@ static inline int ntmp_rsst_query_entry(struct ntmp_user *user,
return 0;
}
+static inline int ntmp_fdbt_add_entry(struct ntmp_user *user, u32 *entry_id,
+ const struct fdbt_keye_data *keye,
+ const struct fdbt_cfge_data *data)
+{
+ return 0;
+}
+
+static inline int ntmp_fdbt_update_entry(struct ntmp_user *user, u32 entry_id,
+ const struct fdbt_cfge_data *cfge)
+{
+ return 0;
+}
+
+static inline int ntmp_fdbt_delete_entry(struct ntmp_user *user, u32 entry_id)
+{
+ return 0;
+}
+
+static inline int ntmp_fdbt_search_port_entry(struct ntmp_user *user, int port,
+ u32 *resume_entry_id,
+ struct fdbt_entry_data *entry)
+{
+ return 0;
+}
+
#endif
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 03/14] net: enetc: add pre-boot initialization for i.MX94 switch
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
Before probing the NETC switch driver, some pre-initialization needs to
be set in NETCMIX and IERB to ensure that the switch can work properly.
For example, i.MX94 NETC switch has three external ports and each port
is bound to a link. And each link needs to be configured so that it can
work properly, such as I/O variant and MII protocol.
In addition, the switch port 2 (MAC 2) and ENETC 0 (MAC 3) share the same
parallel interface, they cannot be used at the same time due to the SoC
constraint. And the MAC selection is controlled by the mac2_mac3_sel bit
of EXT_PIN_CONTROL register. Currently, the interface is set for ENETC 0
by default unless the switch port 2 is enabled in the DT node.
Like ENETC, each external port of the NETC switch can manage its external
PHY through its port MDIO registers. And the port can only access its own
external PHY by setting the PHY address to the LaBCR[MDIO_PHYAD_PRTAD].
If the accessed PHY address is not equal to LaBCR[MDIO_PHYAD_PRTAD], then
the MDIO access initiated by port MDIO will be invalid.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../ethernet/freescale/enetc/netc_blk_ctrl.c | 188 ++++++++++++++++--
1 file changed, 166 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
index 92a0f824dae7..c21230f7a7fb 100644
--- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
+++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
@@ -261,40 +261,112 @@ static int imx94_link_config(struct netc_blk_ctrl *priv,
}
static int imx94_enetc_link_config(struct netc_blk_ctrl *priv,
- struct device_node *np)
+ struct device_node *np,
+ bool *enetc0_en)
{
int link_id = imx94_enetc_get_link_id(np);
if (link_id < 0)
return link_id;
+ if (link_id == IMX94_ENETC0_LINK && of_device_is_available(np))
+ *enetc0_en = true;
+
return imx94_link_config(priv, np, link_id);
}
+static struct device_node *netc_get_switch_ports(struct device_node *np)
+{
+ struct device_node *ports;
+
+ ports = of_get_child_by_name(np, "ports");
+ if (!ports)
+ ports = of_get_child_by_name(np, "ethernet-ports");
+
+ return ports;
+}
+
+static int imx94_switch_link_config(struct netc_blk_ctrl *priv,
+ struct device_node *np,
+ bool *swp2_en)
+{
+ struct device_node *ports;
+ int port_id, err = 0;
+
+ ports = netc_get_switch_ports(np);
+ if (!ports)
+ return -ENODEV;
+
+ for_each_available_child_of_node_scoped(ports, child) {
+ if (of_property_read_u32(child, "reg", &port_id) < 0) {
+ err = -ENODEV;
+ goto end;
+ }
+
+ switch (port_id) {
+ case 0 ... 2: /* External ports */
+ err = imx94_link_config(priv, child, port_id);
+ if (err)
+ goto end;
+
+ if (port_id == 2)
+ *swp2_en = true;
+
+ break;
+ case 3: /* CPU port */
+ break;
+ default:
+ err = -EINVAL;
+ goto end;
+ }
+ }
+
+end:
+ of_node_put(ports);
+
+ return err;
+}
+
static int imx94_netcmix_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node;
+ bool enetc0_en = false, swp2_en = false;
u32 val;
int err;
for_each_child_of_node_scoped(np, child) {
for_each_child_of_node_scoped(child, gchild) {
- if (!of_device_is_compatible(gchild, "pci1131,e101"))
- continue;
-
- err = imx94_enetc_link_config(priv, gchild);
- if (err)
- return err;
+ if (of_device_is_compatible(gchild, "pci1131,e101")) {
+ err = imx94_enetc_link_config(priv, gchild,
+ &enetc0_en);
+ if (err)
+ return err;
+ } else if (of_device_is_compatible(gchild,
+ "pci1131,eef2")) {
+ err = imx94_switch_link_config(priv, gchild,
+ &swp2_en);
+ if (err)
+ return err;
+ }
}
}
- /* ENETC 0 and switch port 2 share the same parallel interface.
- * Currently, the switch is not supported, so this interface is
- * used by ENETC 0 by default.
+ if (enetc0_en && swp2_en) {
+ dev_err(&pdev->dev,
+ "Cannot enable swp2 and enetc0 at the same time\n");
+ return -EINVAL;
+ }
+
+ /* ENETC 0 and switch port 2 share the same parallel interface, they
+ * cannot be enabled at the same time. The interface is set for the
+ * ENETC 0 by default unless the switch port 2 is enabled in the DTS.
*/
val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL);
- val |= MAC2_MAC3_SEL;
+ if (!swp2_en)
+ val |= MAC2_MAC3_SEL;
+ else
+ val &= ~MAC2_MAC3_SEL;
netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val);
return 0;
@@ -610,6 +682,77 @@ static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
return 0;
}
+static int imx94_ierb_enetc_init(struct netc_blk_ctrl *priv,
+ struct device_node *np,
+ u32 phy_mask)
+{
+ int err;
+
+ err = imx94_enetc_update_tid(priv, np);
+ if (err)
+ return err;
+
+ return imx94_enetc_mdio_phyaddr_config(priv, np, phy_mask);
+}
+
+static int imx94_switch_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
+ struct device_node *np,
+ int port_id, u32 phy_mask)
+{
+ int addr;
+
+ /* The switch has 3 external ports at most */
+ if (port_id > 2)
+ return 0;
+
+ addr = netc_get_phy_addr(np);
+ if (addr < 0) {
+ if (addr == -ENODEV)
+ return 0;
+
+ return addr;
+ }
+
+ if (phy_mask & BIT(addr)) {
+ dev_err(&priv->pdev->dev,
+ "Found same PHY address in EMDIO and switch node\n");
+ return -EINVAL;
+ }
+
+ netc_reg_write(priv->ierb, IERB_LBCR(port_id),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+
+ return 0;
+}
+
+static int imx94_ierb_switch_init(struct netc_blk_ctrl *priv,
+ struct device_node *np,
+ u32 phy_mask)
+{
+ struct device_node *ports;
+ int port_id, err = 0;
+
+ ports = netc_get_switch_ports(np);
+ if (!ports)
+ return -ENODEV;
+
+ for_each_available_child_of_node_scoped(ports, child) {
+ err = of_property_read_u32(child, "reg", &port_id);
+ if (err)
+ goto end;
+
+ err = imx94_switch_mdio_phyaddr_config(priv, child,
+ port_id, phy_mask);
+ if (err)
+ goto end;
+ }
+
+end:
+ of_node_put(ports);
+
+ return err;
+}
+
static int imx94_ierb_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
@@ -625,17 +768,18 @@ static int imx94_ierb_init(struct platform_device *pdev)
for_each_child_of_node_scoped(np, child) {
for_each_child_of_node_scoped(child, gchild) {
- if (!of_device_is_compatible(gchild, "pci1131,e101"))
- continue;
-
- err = imx94_enetc_update_tid(priv, gchild);
- if (err)
- return err;
-
- err = imx94_enetc_mdio_phyaddr_config(priv, gchild,
- phy_mask);
- if (err)
- return err;
+ if (of_device_is_compatible(gchild, "pci1131,e101")) {
+ err = imx94_ierb_enetc_init(priv, gchild,
+ phy_mask);
+ if (err)
+ return err;
+ } else if (of_device_is_compatible(gchild,
+ "pci1131,eef2")) {
+ err = imx94_ierb_switch_init(priv, gchild,
+ phy_mask);
+ if (err)
+ return err;
+ }
}
}
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 02/14] dt-bindings: net: dsa: add NETC switch
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
Add bindings for NETC switch. This switch is a PCIe function of NETC IP,
it supports advanced QoS with 8 traffic classes and 4 drop resilience
levels, and a full range of TSN standards capabilities. The switch CPU
port connects to an internal ENETC port, which is also a PCIe function
of NETC IP. So these two ports use a light-weight "pseudo MAC" instead
of a back-to-back MAC, because the "pseudo MAC" provides the delineation
between switch and ENETC, this translates to lower power (less logic and
memory) and lower delay (as there is no serialization delay across this
link).
Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
.../bindings/net/dsa/nxp,netc-switch.yaml | 130 ++++++++++++++++++
1 file changed, 130 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
diff --git a/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
new file mode 100644
index 000000000000..5577f3ef987f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/nxp,netc-switch.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NETC Switch family
+
+description: >
+ The NETC presents itself as a multi-function PCIe Root Complex Integrated
+ Endpoint (RCiEP) and provides full 802.1Q Ethernet switch functionality,
+ advanced QoS with 8 traffic classes and 4 drop resilience levels, and a
+ full range of TSN standards capabilities.
+
+ The CPU port of the switch connects to an internal ENETC. The switch and
+ the internal ENETC are fully integrated into the NETC IP, a back-to-back
+ MAC is not required. Instead, a light-weight "pseudo MAC" provides the
+ delineation between the switch and ENETC. This translates to lower power
+ (less logic and memory) and lower delay (as there is no serialization
+ delay across this link).
+
+maintainers:
+ - Wei Fang <wei.fang@nxp.com>
+
+properties:
+ compatible:
+ enum:
+ - pci1131,eef2
+
+ reg:
+ maxItems: 1
+
+ dsa,member:
+ description: >
+ The property indicates DSA cluster and switch index. For NETC switch,
+ the valid range of the switch index is 1 ~ 7, the index is reflected
+ in the switch tag as an indication of the switch ID where the frame
+ originated. The value 0 is reserved for ENETC VEPA switch, whose ID
+ is hardwired to zero.
+
+$ref: dsa.yaml#
+
+patternProperties:
+ "^(ethernet-)?ports$":
+ type: object
+ additionalProperties: true
+ patternProperties:
+ "^(ethernet-)?port@[0-9a-f]$":
+ type: object
+
+ $ref: dsa-port.yaml#
+
+ properties:
+ clocks:
+ items:
+ - description: MAC transmit/receive reference clock.
+
+ clock-names:
+ items:
+ - const: ref
+
+ mdio:
+ $ref: /schemas/net/mdio.yaml#
+ unevaluatedProperties: false
+ description:
+ Optional child node for switch port, otherwise use NETC EMDIO.
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - dsa,member
+
+allOf:
+ - $ref: /schemas/pci/pci-device.yaml
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ pcie {
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ ethernet-switch@0,2 {
+ compatible = "pci1131,eef2";
+ reg = <0x200 0 0 0 0>;
+ dsa,member = <0 1>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_switch>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ phy-handle = <ðphy0>;
+ phy-mode = "mii";
+ };
+
+ port@1 {
+ reg = <1>;
+ phy-handle = <ðphy1>;
+ phy-mode = "mii";
+ };
+
+ port@2 {
+ reg = <2>;
+ clocks = <&scmi_clk 103>;
+ clock-names = "ref";
+ phy-handle = <ðphy2>;
+ phy-mode = "rgmii-id";
+ };
+
+ port@3 {
+ reg = <3>;
+ ethernet = <&enetc3>;
+ phy-mode = "internal";
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ pause;
+ };
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 01/14] dt-bindings: net: dsa: update the description of 'dsa,member' property
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
In-Reply-To: <20260331113025.1566878-1-wei.fang@nxp.com>
The current description indicates that the 'dsa,member' property cannot
be set for a switch that is not part of any cluster. Vladimir thinks
that this is a case where the actual technical limitation was poorly
transposed into words when this restriction was first documented, in
commit 8c5ad1d6179d ("net: dsa: Document new binding").
The true technical limitation is that many DSA tagging protocols are
topology-unaware, and always call dsa_conduit_find_user() with a
switch_id of 0. Specifying a custom "dsa,member" property with a
non-zero switch_id would break them.
Therefore, for topology-aware switches, it is fine to specify this
property for them, even if they are not part of any cluster. Our NETC
switch is a good example which is topology-aware, the switch_id is
carried in the switch tag, but the switch_id 0 is reserved for VEPA
switch and cannot be used, so we need to use this property to assign
a non-zero switch_id for it.
Suggested-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
Documentation/devicetree/bindings/net/dsa/dsa.yaml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.yaml b/Documentation/devicetree/bindings/net/dsa/dsa.yaml
index 2abd036578d1..801e1411e5c2 100644
--- a/Documentation/devicetree/bindings/net/dsa/dsa.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/dsa.yaml
@@ -28,7 +28,11 @@ properties:
A two element list indicates which DSA cluster, and position within the
cluster a switch takes. <0 0> is cluster 0, switch 0. <0 1> is cluster 0,
switch 1. <1 0> is cluster 1, switch 0. A switch not part of any cluster
- (single device hanging off a CPU port) must not specify this property
+ (single device hanging off a CPU port) does not usually need to specify
+ this property, and then it becomes cluster 0, switch 0. For a topology
+ aware switch, its switch index can be specified through this property,
+ even if it is not part of any cluster. Also, topology-unaware switches
+ must always be defined as index 0 of their cluster.
$ref: /schemas/types.yaml#/definitions/uint32-array
additionalProperties: true
--
2.34.1
^ permalink raw reply related
* [PATCH v4 net-next 00/14] Add preliminary NETC switch support for i.MX94
From: Wei Fang @ 2026-03-31 11:30 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni, robh, krzk+dt, conor+dt,
f.fainelli, frank.li, chleroy, horms, linux, andrew
Cc: netdev, linux-kernel, devicetree, linuxppc-dev, linux-arm-kernel,
imx
i.MX94 NETC (v4.3) integrates 802.1Q Ethernet switch functionality, the
switch provides advanced QoS with 8 traffic classes and a full range of
TSN standards capabilities. It has 3 user ports and 1 CPU port, and the
CPU port is connected to an internal ENETC through the pseduo link, so
instead of a back-to-back MAC, the lightweight "pseudo MAC" is used at
both ends of the pseudo link to transfer Ethernet frames. The pseudo
link provides a zero-copy interface (no serialization delay) and lower
power (less logic and memory).
Like most Ethernet switches, the NETC switch also supports a proprietary
switch tag, is used to carry in-band metadata information about frames.
This in-band metadata information can include the source port from which
the frame was received, what was the reason why this frame got forwarded
to the entity, and for the entity to indicate the precise destination
port of a frame. The NETC switch tag is added to frames after the source
MAC address. There are three types of switch tags, and each type has 1
to 4 subtypes, more details are as follows.
Forward switch tag (Type = 0): Represents forwarded frames.
- SubType = 0 - Normal frame processing.
To_Port switch tag (Type = 1): Represents frames that are to be sent to
a specific switch port.
- SubType = 0. No request to perform timestamping.
- SubType = 1. Request to perform one-step timestamping.
- SubType = 2. Request to perform two-step timestamping.
- SubType = 3. Request to perform both one-step timestamping and
two-step timestamping.
To_Host switch tag (Type = 2): Represents frames redirected or copied to
the switch management port.
- SubType = 0. Received frames redirected or copied to the switch
management port.
- SubType = 1. Received frames redirected or copied to the switch
management port with captured timestamp at the switch port where
the frame was received.
- SubType = 2. Transmit timestamp response (two-step timestamping).
Currently, this patch set supports Forward tag, SubType 0 of To_Port tag
and SubType 0 of To_Host tag. More tags will be supported in the future.
In addition, the switch supports NETC Table Management Protocol (NTMP),
some switch functionality is controlled using control messages sent to
the hardware using BD ring interface with 32B descriptors similar to the
packet Transmit BD ring used on ENETC. This interface is referred to as
the command BD ring. This is used to configure functionality where the
underlying resources may be shared between different entities or being
too large to configure using direct registers.
For this patch set, we have supported the following tables through the
command BD ring interface.
FDB Table: It contains forwarding and/or filtering information about MAC
addresses. The FDB table is used for MAC learning lookups and MAC
forwarding lookups.
VLAN Filter Table: It contains configuration and control information for
each VLAN configured on the switch.
Buffer Pool Table: It contains buffer pool configuration and operational
information. Each entry corresponds to a buffer pool. Currently, we use
this table to implement flow control feature on each port.
Ingress Port Filter Table: It contains a set of filters each capable of
classifying incoming traffic using a mix of L2, L3, and L4 parsed and
arbitrary field data. We use this table to implement host flood support
to the switch port.
The switch also supports other tables, and we will add more advanced
features through them in the future.
---
v4:
1. Fix some nits in patch 2.
2. Revert netc_ipv_to_buffer_pool_mapping() to v2, the new added code in
v3 is actually a dead code and was only added to address false positives
reported by AI review. Therefore, v4 optimizes the comments in
netc_switch_bpt_default_config() to avoid the false positives.
v3 link: https://lore.kernel.org/imx/20260326062917.3552334-1-wei.fang@nxp.com/
v2 link: https://lore.kernel.org/imx/20260323060752.1157031-1-wei.fang@nxp.com/
v1 link: https://lore.kernel.org/imx/20260316094152.1558671-1-wei.fang@nxp.com/
---
Wei Fang (14):
dt-bindings: net: dsa: update the description of 'dsa,member' property
dt-bindings: net: dsa: add NETC switch
net: enetc: add pre-boot initialization for i.MX94 switch
net: enetc: add basic operations to the FDB table
net: enetc: add support for the "Add" operation to VLAN filter table
net: enetc: add support for the "Update" operation to buffer pool
table
net: enetc: add support for "Add" and "Delete" operations to IPFT
net: enetc: add multiple command BD rings support
net: dsa: add NETC switch tag support
net: dsa: netc: introduce NXP NETC switch driver for i.MX94
net: dsa: netc: add phylink MAC operations
net: dsa: netc: add more basic functions support
net: dsa: netc: initialize buffer bool table and implement
flow-control
net: dsa: netc: add support for the standardized counters
.../devicetree/bindings/net/dsa/dsa.yaml | 6 +-
.../bindings/net/dsa/nxp,netc-switch.yaml | 130 ++
MAINTAINERS | 11 +
drivers/net/dsa/Kconfig | 3 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/netc/Kconfig | 14 +
drivers/net/dsa/netc/Makefile | 3 +
drivers/net/dsa/netc/netc_ethtool.c | 192 +++
drivers/net/dsa/netc/netc_main.c | 1528 +++++++++++++++++
drivers/net/dsa/netc/netc_platform.c | 90 +
drivers/net/dsa/netc/netc_switch.h | 155 ++
drivers/net/dsa/netc/netc_switch_hw.h | 335 ++++
.../ethernet/freescale/enetc/netc_blk_ctrl.c | 188 +-
drivers/net/ethernet/freescale/enetc/ntmp.c | 383 ++++-
.../ethernet/freescale/enetc/ntmp_private.h | 122 +-
include/linux/dsa/tag_netc.h | 14 +
include/linux/fsl/netc_global.h | 6 +
include/linux/fsl/ntmp.h | 235 ++-
include/net/dsa.h | 2 +
include/uapi/linux/if_ether.h | 1 +
net/dsa/Kconfig | 10 +
net/dsa/Makefile | 1 +
net/dsa/tag_netc.c | 185 ++
23 files changed, 3585 insertions(+), 30 deletions(-)
create mode 100644 Documentation/devicetree/bindings/net/dsa/nxp,netc-switch.yaml
create mode 100644 drivers/net/dsa/netc/Kconfig
create mode 100644 drivers/net/dsa/netc/Makefile
create mode 100644 drivers/net/dsa/netc/netc_ethtool.c
create mode 100644 drivers/net/dsa/netc/netc_main.c
create mode 100644 drivers/net/dsa/netc/netc_platform.c
create mode 100644 drivers/net/dsa/netc/netc_switch.h
create mode 100644 drivers/net/dsa/netc/netc_switch_hw.h
create mode 100644 include/linux/dsa/tag_netc.h
create mode 100644 net/dsa/tag_netc.c
--
2.34.1
^ permalink raw reply
* (no subject)
From: Wang Jun @ 2026-03-31 11:14 UTC (permalink / raw)
To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: netdev, linux-kernel, gszhai, 25125332, 25125283, 23120469,
Wang Jun
In-Reply-To: <51dfd8ae-dc4e-4837-9b00-c596c457117e@redhat.com>
Hi Paolo Abeni,
This is v2 of the DMA mapping error handling fix for ns83820. Changes since v1:
- Added queue restart check in error path to avoid potential TX queue stall
(as pointed out by the AI review)
- Adjusted variable declarations to follow reverse christmas tree order
- Switched from dma_unmap_single to dma_unmap_page for fragments
Thanks to reviewers for the feedback.
Subject: [PATCH v2] net: ns83820: fix DMA mapping error handling in
hard_start_xmit
The ns83820 driver currently ignores the return values of dma_map_single()
and skb_frag_dma_map() in the transmit path. If DMA mapping fails due to
IOMMU exhaustion or SWIOTLB pressure, the driver may proceed with invalid
DMA addresses, potentially causing hardware errors, data corruption, or
system instability.
Additionally, if mapping fails midway through processing fragmented
packets, previously mapped DMA resources are not released, leading to
DMA resource leaks.
Fix this by:
1. Checking dma_mapping_error() after each DMA mapping call.
2. Implementing an error handling path to unmap successfully mapped
buffers (both linear and fragments) using dma_unmap_single() /
dma_unmap_page().
3. Freeing the skb using dev_kfree_skb_any() to safely handle both
process and softirq contexts.
4. Returning NETDEV_TX_OK to drop the packet gracefully and prevent
TX queue stagnation.
5. Moving the queue restart check (for race conditions when the queue
was stopped) into a common label, so that the error path also
executes it, avoiding a possible permanent TX queue stall.
This ensures compliance with the DMA API guidelines and improves driver
stability under memory pressure.
Signed-off-by: Wang Jun <1742789905@qq.com>
---
drivers/net/ethernet/natsemi/ns83820.c | 31 +++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c
index cdbf82affa7b..f8d037db4ffb 100644
--- a/drivers/net/ethernet/natsemi/ns83820.c
+++ b/drivers/net/ethernet/natsemi/ns83820.c
@@ -1051,6 +1051,12 @@ static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
int stopped = 0;
int do_intr = 0;
volatile __le32 *first_desc;
+ int i;
+ int frag_mapped_count = 0;
+ unsigned int main_len = 0;
+ unsigned int frag_dma_len[MAX_SKB_FRAGS];
+ dma_addr_t main_buf = 0;
+ dma_addr_t frag_dma_addr[MAX_SKB_FRAGS];
dprintk("ns83820_hard_start_xmit\n");
@@ -1120,6 +1126,12 @@ static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
len -= skb->data_len;
buf = dma_map_single(&dev->pci_dev->dev, skb->data, len,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->pci_dev->dev, buf)) {
+ dev_kfree_skb_any(skb);
+ goto check_queue_and_return;
+ }
+ main_buf = buf;
+ main_len = len;
first_desc = dev->tx_descs + (free_idx * DESC_SIZE);
@@ -1144,6 +1156,15 @@ static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
buf = skb_frag_dma_map(&dev->pci_dev->dev, frag, 0,
skb_frag_size(frag), DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->pci_dev->dev, buf))
+ goto dma_map_error;
+
+ if (frag_mapped_count < MAX_SKB_FRAGS) {
+ frag_dma_addr[frag_mapped_count] = buf;
+ frag_dma_len[frag_mapped_count] = skb_frag_size(frag);
+ frag_mapped_count++;
+ }
+
dprintk("frag: buf=%08Lx page=%08lx offset=%08lx\n",
(long long)buf, (long) page_to_pfn(frag->page),
frag->page_offset);
@@ -1161,12 +1182,20 @@ static netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb,
spin_unlock_irq(&dev->tx_lock);
kick_tx(dev);
-
+check_queue_and_return:
/* Check again: we may have raced with a tx done irq */
if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev))
netif_start_queue(ndev);
return NETDEV_TX_OK;
+dma_map_error:
+ dma_unmap_single(&dev->pci_dev->dev, main_buf, main_len, DMA_TO_DEVICE);
+ for (i = 0; i < frag_mapped_count; i++) {
+ dma_unmap_page(&dev->pci_dev->dev, frag_dma_addr[i],
+ frag_dma_len[i], DMA_TO_DEVICE);
+ }
+ dev_kfree_skb_any(skb);
+ goto check_queue_and_return;
}
static void ns83820_update_stats(struct ns83820 *dev)
--
2.43.0
^ permalink raw reply related
* [PATCH net 1/2] seg6: separate dst_cache for input and output paths in seg6 lwtunnel
From: Andrea Mayer @ 2026-03-31 11:07 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, david.lebrun,
stefano.salsano, paolo.lungaroni, nicolas.dichtel, linux-kernel,
Andrea Mayer, stable
In-Reply-To: <20260331110755.25042-1-andrea.mayer@uniroma2.it>
The seg6 lwtunnel uses a single dst_cache per encap route, shared
between seg6_input_core() and seg6_output_core(). These two paths
can perform the post-encap SID lookup in different routing contexts
(e.g., ip rules matching on the ingress interface, or VRF table
separation). Whichever path runs first populates the cache, and the
other reuses it blindly, bypassing its own lookup.
Fix this by splitting the cache into cache_input and cache_output,
so each path maintains its own cached dst independently.
Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels")
Cc: stable@vger.kernel.org
Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
---
net/ipv6/seg6_iptunnel.c | 34 +++++++++++++++++++++++-----------
1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 3e1b9991131a..d6a0f7df9080 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -48,7 +48,8 @@ static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
}
struct seg6_lwt {
- struct dst_cache cache;
+ struct dst_cache cache_input;
+ struct dst_cache cache_output;
struct seg6_iptunnel_encap tuninfo[];
};
@@ -488,7 +489,7 @@ static int seg6_input_core(struct net *net, struct sock *sk,
slwt = seg6_lwt_lwtunnel(lwtst);
local_bh_disable();
- dst = dst_cache_get(&slwt->cache);
+ dst = dst_cache_get(&slwt->cache_input);
local_bh_enable();
err = seg6_do_srh(skb, dst);
@@ -504,7 +505,7 @@ static int seg6_input_core(struct net *net, struct sock *sk,
/* cache only if we don't create a dst reference loop */
if (!dst->error && lwtst != dst->lwtstate) {
local_bh_disable();
- dst_cache_set_ip6(&slwt->cache, dst,
+ dst_cache_set_ip6(&slwt->cache_input, dst,
&ipv6_hdr(skb)->saddr);
local_bh_enable();
}
@@ -564,7 +565,7 @@ static int seg6_output_core(struct net *net, struct sock *sk,
slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate);
local_bh_disable();
- dst = dst_cache_get(&slwt->cache);
+ dst = dst_cache_get(&slwt->cache_output);
local_bh_enable();
err = seg6_do_srh(skb, dst);
@@ -591,7 +592,7 @@ static int seg6_output_core(struct net *net, struct sock *sk,
/* cache only if we don't create a dst reference loop */
if (orig_dst->lwtstate != dst->lwtstate) {
local_bh_disable();
- dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr);
+ dst_cache_set_ip6(&slwt->cache_output, dst, &fl6.saddr);
local_bh_enable();
}
@@ -701,11 +702,13 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,
slwt = seg6_lwt_lwtunnel(newts);
- err = dst_cache_init(&slwt->cache, GFP_ATOMIC);
- if (err) {
- kfree(newts);
- return err;
- }
+ err = dst_cache_init(&slwt->cache_input, GFP_ATOMIC);
+ if (err)
+ goto err_free_newts;
+
+ err = dst_cache_init(&slwt->cache_output, GFP_ATOMIC);
+ if (err)
+ goto err_destroy_input;
memcpy(&slwt->tuninfo, tuninfo, tuninfo_len);
@@ -720,11 +723,20 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,
*ts = newts;
return 0;
+
+err_destroy_input:
+ dst_cache_destroy(&slwt->cache_input);
+err_free_newts:
+ kfree(newts);
+ return err;
}
static void seg6_destroy_state(struct lwtunnel_state *lwt)
{
- dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache);
+ struct seg6_lwt *slwt = seg6_lwt_lwtunnel(lwt);
+
+ dst_cache_destroy(&slwt->cache_input);
+ dst_cache_destroy(&slwt->cache_output);
}
static int seg6_fill_encap_info(struct sk_buff *skb,
--
2.20.1
^ permalink raw reply related
* [PATCH net 2/2] selftests: seg6: add test for dst_cache isolation in seg6 lwtunnel
From: Andrea Mayer @ 2026-03-31 11:07 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, david.lebrun,
stefano.salsano, paolo.lungaroni, nicolas.dichtel, linux-kernel,
Andrea Mayer, Shuah Khan, linux-kselftest
In-Reply-To: <20260331110755.25042-1-andrea.mayer@uniroma2.it>
Add a selftest that verifies the dst_cache in seg6 lwtunnel is not
shared between the input (forwarding) and output (locally generated)
paths.
The test creates three namespaces (ns_src, ns_router, ns_dst)
connected in a line. An SRv6 encap route on ns_router encapsulates
traffic destined to cafe::1 with SID fc00::100. The SID is
reachable only for forwarded traffic (from ns_src) via an ip rule
matching the ingress interface (iif veth-r0 lookup 100), and
blackholed in the main table.
The test verifies that:
1. A packet generated locally on ns_router does not reach
ns_dst with an empty cache, since the SID is blackholed;
2. A forwarded packet from ns_src populates the input cache
from table 100 and reaches ns_dst;
3. A packet generated locally on ns_router still does not
reach ns_dst after the input cache is populated,
confirming the output path does not reuse the input
cache entry.
Both the forwarded and local packets are pinned to the same CPU
with taskset, since dst_cache is per-cpu.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
---
tools/testing/selftests/net/Makefile | 1 +
.../selftests/net/srv6_iptunnel_cache.sh | 177 ++++++++++++++++++
2 files changed, 178 insertions(+)
create mode 100755 tools/testing/selftests/net/srv6_iptunnel_cache.sh
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 605c54c0e8a3..c709523c99c6 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -89,6 +89,7 @@ TEST_PROGS := \
srv6_end_x_next_csid_l3vpn_test.sh \
srv6_hencap_red_l3vpn_test.sh \
srv6_hl2encap_red_l2vpn_test.sh \
+ srv6_iptunnel_cache.sh \
stress_reuseport_listen.sh \
tcp_fastopen_backup_key.sh \
test_bpf.sh \
diff --git a/tools/testing/selftests/net/srv6_iptunnel_cache.sh b/tools/testing/selftests/net/srv6_iptunnel_cache.sh
new file mode 100755
index 000000000000..4e932f45a054
--- /dev/null
+++ b/tools/testing/selftests/net/srv6_iptunnel_cache.sh
@@ -0,0 +1,177 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# author: Andrea Mayer <andrea.mayer@uniroma2.it>
+
+# This test verifies that the seg6 lwtunnel does not share the dst_cache
+# between the input (forwarding) and output (locally generated) paths.
+#
+# A shared dst_cache allows a forwarded packet to populate the cache and a
+# subsequent locally generated packet to silently reuse that entry, bypassing
+# its own route lookup. To expose this, the SID is made reachable only for
+# forwarded traffic (via an ip rule matching iif) and blackholed for everything
+# else. A local ping on ns_router must always hit the blackhole;
+# if it succeeds after a forwarded packet has populated the
+# cache, the bug is confirmed.
+#
+# Both forwarded and local packets are pinned to the same CPU with taskset,
+# since dst_cache is per-cpu.
+#
+#
+# +--------------------+ +--------------------+
+# | ns_src | | ns_dst |
+# | | | |
+# | veth-s0 | | veth-d0 |
+# | fd00::1/64 | | fd01::2/64 |
+# +-------|------------+ +----------|---------+
+# | |
+# | +--------------------+ |
+# | | ns_router | |
+# | | | |
+# +----------->+ veth-r0 veth-r1 +<-------------+
+# | fd00::2 fd01::1 |
+# +--------------------+
+#
+#
+# ns_router: encap (main table)
+# +---------+---------------------------------------+
+# | dst | action |
+# +---------+---------------------------------------+
+# | cafe::1 | encap seg6 mode encap segs fc00::100 |
+# +---------+---------------------------------------+
+#
+# ns_router: post-encap SID resolution
+# +-------+------------+----------------------------+
+# | table | dst | action |
+# +-------+------------+----------------------------+
+# | 100 | fc00::100 | via fd01::2 dev veth-r1 |
+# +-------+------------+----------------------------+
+# | main | fc00::100 | blackhole |
+# +-------+------------+----------------------------+
+#
+# ns_router: ip rule
+# +------------------+------------------------------+
+# | match | action |
+# +------------------+------------------------------+
+# | iif veth-r0 | lookup 100 |
+# +------------------+------------------------------+
+#
+# ns_dst: SRv6 decap (main table)
+# +--------------+----------------------------------+
+# | SID | action |
+# +--------------+----------------------------------+
+# | fc00::100 | End.DT6 table 255 (local) |
+# +--------------+----------------------------------+
+
+source lib.sh
+
+readonly SID="fc00::100"
+readonly DEST="cafe::1"
+
+readonly SRC_MAC="02:00:00:00:00:01"
+readonly RTR_R0_MAC="02:00:00:00:00:02"
+readonly RTR_R1_MAC="02:00:00:00:00:03"
+readonly DST_MAC="02:00:00:00:00:04"
+
+cleanup()
+{
+ cleanup_ns "${NS_SRC}" "${NS_RTR}" "${NS_DST}"
+}
+
+check_prerequisites()
+{
+ if ! command -v taskset &>/dev/null; then
+ echo "SKIP: taskset not found"
+ exit "${ksft_skip}"
+ fi
+}
+
+setup()
+{
+ setup_ns NS_SRC NS_RTR NS_DST
+
+ ip link add veth-s0 netns "${NS_SRC}" type veth \
+ peer name veth-r0 netns "${NS_RTR}"
+ ip link add veth-r1 netns "${NS_RTR}" type veth \
+ peer name veth-d0 netns "${NS_DST}"
+
+ ip -n "${NS_SRC}" link set veth-s0 address "${SRC_MAC}"
+ ip -n "${NS_RTR}" link set veth-r0 address "${RTR_R0_MAC}"
+ ip -n "${NS_RTR}" link set veth-r1 address "${RTR_R1_MAC}"
+ ip -n "${NS_DST}" link set veth-d0 address "${DST_MAC}"
+
+ # ns_src
+ ip -n "${NS_SRC}" link set veth-s0 up
+ ip -n "${NS_SRC}" addr add fd00::1/64 dev veth-s0 nodad
+ ip -n "${NS_SRC}" -6 route add "${DEST}"/128 via fd00::2
+
+ # ns_router
+ ip -n "${NS_RTR}" link set veth-r0 up
+ ip -n "${NS_RTR}" addr add fd00::2/64 dev veth-r0 nodad
+ ip -n "${NS_RTR}" link set veth-r1 up
+ ip -n "${NS_RTR}" addr add fd01::1/64 dev veth-r1 nodad
+ ip netns exec "${NS_RTR}" sysctl -qw net.ipv6.conf.all.forwarding=1
+
+ ip -n "${NS_RTR}" -6 route add "${DEST}"/128 \
+ encap seg6 mode encap segs "${SID}" dev veth-r0
+ ip -n "${NS_RTR}" -6 route add "${SID}"/128 table 100 \
+ via fd01::2 dev veth-r1
+ ip -n "${NS_RTR}" -6 route add blackhole "${SID}"/128
+ ip -n "${NS_RTR}" -6 rule add iif veth-r0 lookup 100
+
+ # ns_dst
+ ip -n "${NS_DST}" link set veth-d0 up
+ ip -n "${NS_DST}" addr add fd01::2/64 dev veth-d0 nodad
+ ip -n "${NS_DST}" addr add "${DEST}"/128 dev lo nodad
+ ip -n "${NS_DST}" -6 route add "${SID}"/128 \
+ encap seg6local action End.DT6 table 255 dev veth-d0
+ ip -n "${NS_DST}" -6 route add fd00::/64 via fd01::1
+
+ # static neighbors
+ ip -n "${NS_SRC}" -6 neigh add fd00::2 dev veth-s0 \
+ lladdr "${RTR_R0_MAC}" nud permanent
+ ip -n "${NS_RTR}" -6 neigh add fd00::1 dev veth-r0 \
+ lladdr "${SRC_MAC}" nud permanent
+ ip -n "${NS_RTR}" -6 neigh add fd01::2 dev veth-r1 \
+ lladdr "${DST_MAC}" nud permanent
+ ip -n "${NS_DST}" -6 neigh add fd01::1 dev veth-d0 \
+ lladdr "${RTR_R1_MAC}" nud permanent
+}
+
+test_cache_isolation()
+{
+ RET=0
+
+ # local ping with empty cache: must fail (SID is blackholed)
+ if ip netns exec "${NS_RTR}" taskset -c 0 \
+ ping6 -c 1 -W 2 "${DEST}" &>/dev/null; then
+ echo "SKIP: local ping succeeded with empty cache"
+ exit "${ksft_skip}"
+ fi
+
+ # forward from ns_src to populate the input cache
+ if ! ip netns exec "${NS_SRC}" taskset -c 0 \
+ ping6 -c 1 -W 2 "${DEST}" &>/dev/null; then
+ echo "SKIP: forwarded ping failed, topology broken"
+ exit "${ksft_skip}"
+ fi
+
+ # local ping again: must still fail; if the output path reuses
+ # the input cache, it bypasses the blackhole and the ping succeeds
+ if ip netns exec "${NS_RTR}" taskset -c 0 \
+ ping6 -c 1 -W 2 "${DEST}" &>/dev/null; then
+ echo "FAIL: output path used dst cached by input path"
+ RET="${ksft_fail}"
+ else
+ echo "PASS: output path dst_cache is independent"
+ fi
+
+ return "${RET}"
+}
+
+trap cleanup EXIT
+
+check_prerequisites
+setup
+test_cache_isolation
+exit "${RET}"
--
2.20.1
^ permalink raw reply related
* [PATCH net 0/2] seg6: fix dst_cache sharing in seg6 lwtunnel
From: Andrea Mayer @ 2026-03-31 11:07 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, dsahern, david.lebrun,
stefano.salsano, paolo.lungaroni, nicolas.dichtel, linux-kernel,
Andrea Mayer
The seg6 lwtunnel encap uses a single per-route dst_cache shared
between seg6_input_core() and seg6_output_core(). These two paths
can perform the post-encap SID lookup in different routing contexts
(e.g., ip rules matching on the ingress interface, or VRF table
separation). Whichever path runs first populates the cache, and the
other reuses it blindly, bypassing its own lookup.
Patch 1 fixes this by splitting the cache into cache_input and
cache_output. Patch 2 adds a selftest that validates the isolation.
Andrea Mayer (2):
seg6: separate dst_cache for input and output paths in seg6 lwtunnel
selftests: seg6: add test for dst_cache isolation in seg6 lwtunnel
net/ipv6/seg6_iptunnel.c | 34 ++--
tools/testing/selftests/net/Makefile | 1 +
.../selftests/net/srv6_iptunnel_cache.sh | 177 ++++++++++++++++++
3 files changed, 201 insertions(+), 11 deletions(-)
create mode 100755 tools/testing/selftests/net/srv6_iptunnel_cache.sh
--
2.20.1
^ permalink raw reply
* Re: [PATCH net] net: airoha: Add missing cleanup bits in airoha_qdma_cleanup_rx_queue()
From: Lorenzo Bianconi @ 2026-03-31 11:01 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal
In-Reply-To: <20260330172857.0c94685d@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 4147 bytes --]
> On Fri, 27 Mar 2026 10:48:21 +0100 Lorenzo Bianconi wrote:
> > In order to properly cleanup hw rx QDMA queues and bring the device to
> > the initial state, reset rx DMA queue head/tail index. Moreover, reset
> > queued DMA descriptor fields.
> >
> > Fixes: 23020f049327 ("net: airoha: Introduce ethernet support for EN7581 SoC")
> > Tested-by: Madhur Agrawal <Madhur.Agrawal@airoha.com>
> > Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
>
> Take a look at sashiko, please:
> https://sashiko.dev/#/patchset/20260327-airoha_qdma_cleanup_rx_queue-fix-v1-1-369d6ab1511a@kernel.org
>
> Looks somewhat orthogonal to the current patch but probably worth
> fixing.
Hi Jakub,
thx for pointing me to the sashiko's issues.
1- Could this code execute while the interface is still administratively up and
the hardware DMA engines are actively receiving packets?
Looking at the teardown paths, airoha_hw_cleanup() is called before
unregister_netdev(). Unmapping buffers and zeroing descriptor addresses
while the hardware might still be actively writing to them could cause
physical memory corruption and IOMMU faults.
Should unregister_netdev() (which quiesces the DMA via ndo_stop) be called
before airoha_hw_cleanup() to avoid this?
I think the issue described above is already fixed in the following commit
available in net-next:
commit b1c803d5c8167026791abfaed96fd3e6a1fcd750
Author: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat Mar 21 15:41:44 2026 +0100
net: airoha: Rework the code flow in airoha_remove() and in airoha_probe() error path
As suggested by Simon in [0], rework the code flow in airoha_remove()
and in the airoha_probe() error path in order to rely on a more common
approach un-registering configured net-devices first and destroying the
hw resources at the end of the code.
Introduce airoha_qdma_cleanup routine to release QDMA resources.
[0] https://lore.kernel.org/netdev/20251214-airoha-fix-dev-registration-v1-1-860e027ad4c6@kernel.org/
Suggested-by: Simon Horman <horms@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260321-airoha-remove-rework-v2-1-16c7bade5fe5@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
How can we go ahead on this?
2- This is a pre-existing issue, but while reviewing this cleanup path, I
noticed a potential NULL pointer dereference if initialization fails earlier.
If devm_kzalloc() or dmam_alloc_coherent() fails in airoha_qdma_init_rx_queue(),
it returns an error before netif_napi_add() is called, leaving the embedded
q->napi struct zero-filled.
However, q->ndesc is set earlier in that function. Since q->ndesc is now
non-zero, the error cleanup path will try to disable and delete this
uninitialized NAPI structure, leading to a crash in napi_disable() when it
calls hrtimer_cancel() on the uninitialized timer.
Could we defer setting q->ndesc until after the allocations succeed?
I think it is fine to set 'q->ndesc' at the end of airoha_qdma_init_rx_queue()
routine but, considering net codebase, it seems the issue can't occur since if
airoha_qdma_init_rx_queue() fails as described above, airoha_probe() will jump
to error_hw_cleanup and netif_napi_del() in airoha_hw_cleanup() will return if
NAPI_STATE_LISTED is not set in __netif_napi_del_locked().
Am I missing something?
3- Is there a missing reset for the CPU producer index (REG_RX_CPU_IDX) here?
The hardware DMA relies on the gap between the CPU and DMA indices to
identify valid descriptors. By rewinding the DMA consumer index (REG_RX_DMA_IDX)
to q->tail while leaving the CPU index at its old, advanced value, could
this create a phantom gap of descriptors?
If the DMA engine fetches these zeroed descriptors (with addr = 0), it might
attempt to write incoming packets to physical address 0x0. Both indices might
need to be synchronized.
I will post a fix for it.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH V3 1/3] net: stmmac: Fix PTP ref clock for Tegra234
From: Jon Hunter @ 2026-03-31 10:48 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, netdev, devicetree, linux-tegra
In-Reply-To: <5b3eaba5-eba2-43f9-8ef5-d305ca50ddf0@kernel.org>
On 31/03/2026 11:18, Krzysztof Kozlowski wrote:
> On 31/03/2026 12:14, Jon Hunter wrote:
>>
>> On 26/03/2026 08:32, Krzysztof Kozlowski wrote:
>>
>> ...
>>
>>>> @@ -257,9 +258,23 @@ static int tegra_mgbe_probe(struct platform_device *pdev)
>>>> if (!mgbe->clks)
>>>> return -ENOMEM;
>>>>
>>>> - for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++)
>>>> + /*
>>>> + * Older device-trees use 'ptp-ref' rather than 'ptp_ref'.
>>>> + * Fall back to the legacy name when 'ptp_ref' is absent.
>>>> + */
>>>> + use_legacy_ptp = of_property_match_string(pdev->dev.of_node,
>>>> + "clock-names", "ptp_ref") < 0;
>>>> +
>>>> + for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++) {
>>>> mgbe->clks[i].id = mgbe_clks[i];
>>>>
>>>> + if (use_legacy_ptp && !strcmp(mgbe_clks[i], "ptp_ref")) {
>>>
>>> Why index 0 is not valid? And why -EINVAL would be considered as legacy
>>> clock present?
>>
>> Index 0 is valid. However, yes I guess that treating an -EINVAL from
>> of_property_match_string() is not correct. I will switch the logic to be ...
>>
>> use_legacy_ptp = of_property_match_string(pdev->dev.of_node,
>> "clock-names", "ptp-ref") >= 0;
>
> No, apologies, I think my comment was not correct and your reply made me
> rethink. I missed final "< 0" in of_property_match_string().
>
> So the code is good, you do not store index in 'use_legacy_ptp', but the
> error return value.
>
> Can you however make it more obvious code, so something like:
>
> if (of_property_match_string...))
> use_legacy_ptp = true;;
Yes I can do that. Before I was avoiding the need to pre-initialise the
variable, but I am happy either way to get this fixed!
Jon
--
nvpublic
^ permalink raw reply
* Re: [EXTERNAL] Re: [REGRESSION] Discussion on "xfrm: Duplicate SPI Handling"
From: Antony Antony @ 2026-03-31 10:47 UTC (permalink / raw)
To: Aakash Kumar Shankarappa
Cc: antony.antony@secunet.com, Yan Yan, Nathan Harold, Tobias Brunner,
Steffen Klassert, paul@nohats.ca, netdev@vger.kernel.org,
Herbert Xu, David S . Miller, Eric Dumazet, Jakub Kicinski,
pabeni@redhat.com, horms@kernel.org, akamluddin@marvell.com,
greg@kroah.com
In-Reply-To: <BL1PPF236BDCF3E1BDA74118AE2A6ECFAFFDA52A@BL1PPF236BDCF3E.namprd18.prod.outlook.com>
[-- Attachment #1: Type: text/plain, Size: 13150 bytes --]
Hi,
I have tweaked the patch a bit more, uniqueness is only when
x->dir == XFRM_SA_DIR_IN. See the attached patch.
I have added tags Fixes:
and
Reported-by: Yan Yan <evitayan@google.com>
Yan, are you ok with this?
So fart I don't have any tests for this, so more tags welcome:)
regards,
-antony
PS: recent libreswan is setting direction. Thta should not be problem.
On Mon, Mar 30, 2026 at 20:34:07 +0000, Aakash Kumar Shankarappa wrote:
> Hi Antony,
>
> Thanks for the patch. Yes the x->dir based gating approach looks good
> to me and it works for Marvell.
>
> Also this seems like the right direction. It preserves backward
> compatibility for existing users, while still allowing strict RFC 4301
> complaint SPI uniqueness as an opt-in feature. Anybody who wants the
> stricter behaviour can upgrade to Strongswan 6.0.0+ and leverage
> XFRM_SA_DIR_IN.
>
> Thanks, Aakash
>
> From: Antony Antony <antony.antony@secunet.com>
> Date: Monday, 30 March 2026 at 10:24 PM
> To: Yan Yan <evitayan@google.com>
> Cc: Nathan Harold <nharold@google.com>, Tobias Brunner
> <tobias@strongswan.org>, antony.antony@secunet.com
> <antony.antony@secunet.com>, Steffen Klassert
> <steffen.klassert@secunet.com>, paul@nohats.ca <paul@nohats.ca>,
> netdev@vger.kernel.org <netdev@vger.kernel.org>, Herbert Xu
> <herbert@gondor.apana.org.au>, David S . Miller <davem@davemloft.net>,
> Eric Dumazet <edumazet@google.com>, Jakub Kicinski <kuba@kernel.org>,
> pabeni@redhat.com <pabeni@redhat.com>, horms@kernel.org
> <horms@kernel.org>, Aakash Kumar Shankarappa
> <saakashkumar@marvell.com>, akamluddin@marvell.com
> <akamluddin@marvell.com>, greg@kroah.com <greg@kroah.com>
> Subject: [EXTERNAL] Re: [REGRESSION] Discussion on "xfrm: Duplicate SPI
> Handling"
> Prioritize security for external emails:
> Confirm sender and content safety before clicking links or opening
> attachments
> [1]Report Suspicious
>
>
> Hi, I looked into this. I feel a simple solution is use x->dir as
> Nathan proposed. When dir is not set we get pre commit 94f39804d891
> ("xfrm: Duplicate SPI Handling") behaviour. When XFRM_SA_DIR is set
> alloc_spi() returns per direction unique spi. Another benfit is, this
> would also keep PF_KEY use case as it was before that comit. Here is
> simple RFC patch attached. How does this look? strongswan 6.0.0, from
> Dec 2024, sets x->dir. Aakash would this work for for marvell? regads,
> -antony On Fri, Mar 27, 2026 at 17:05:13 -0700, Yan Yan wrote: > Hi
> all, > I wanted to send a friendly ping to see if we are aligning on
> making > the strict global SPI uniqueness requirement optional, perhaps
> via a > toggle or by leveraging the XFRM_SA_DIR attribute as previously
> > discussed. > Are there any other questions or concerns regarding this
> approach, or > anything else we should clarify to ensure backward
> compatibility while > meeting the needs of modern standards? > Best, >
> Yan > > On Tue, Feb 24, 2026 at 3:53 PM Nathan Harold
> <[1]nharold@google.com> > wrote: > > > That should still be allowed
> when using the intended APIs (i.e. > ALLOCSPI > > for the inbound and
> NEWSA for the outbound SA). ALLOCSPI might > enforce > > a unique SPI
> without considering the address, as that's intended > for > > local,
> inbound SAs, where the kernel has full control (looking at > the > >
> the patch, it's certainly not ideal, as it goes through all > installed
> > > SAs to find a duplicate and it prevents an inbound SPI that >
> matches an > > existing outbound SPI - I guess that could be resolved
> by using > separate > > tables for in- and outbound SAs). But that must
> not prevent > installing > > outbound SAs with the same SPI to another
> peer using NEWSA, which > still > > uses a hash that includes the
> destination address (that must > always be > > the case because peers
> are free to allocate whatever SPI they > want). > Agreed that there are
> some unfortunate limitations with the current > patch. Keying off the
> inclusion of XFRM_SA_DIR would resolve the > issue > you noted
> (conflating inbound and outbound SPIs) and function as an > opt-in for
> this enforcement. Whatever the mechanism though, the new > behavior
> should be opt-in rather than opt-out in order to maintain > backwards
> compatibility. > > In my opinion, you are using the API incorrectly...
> I also > > don't think there are any benefits in that "consistent
> larval > lifecycle" > > (if you found any, please let us know). > The
> Android architecture is multi-tenant and allows userspace apps > to >
> establish SAs. At the time we designed it, this felt like the >
> cleanest > way to facilitate leak-free resource management because the
> chain of > associations between kernel resources could be symmetrical
> (and > managing them was already quite complicated). Mea culpa
> (Nathan). > But, > correctly or not, it has/had worked for many years.
> > > By the way, are you using the min/max option for inbound SAs as >
> well, > > with an SPI generated in userland? That would seem like a >
> violation of > > the intention of the API as well (i.e. letting the
> kernel control > the > > local SPIs). > We provide following two
> Android APIs for app developers: >
> [2][2]https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.an
> droid.com_reference_android_net_IpSecManager-23&d=DwIDaQ&c=nKjWec2b6R0m
> OyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWI
> SL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=9txNFXC-wFiRFV_Q
> JlIQLW2AWSbERIOWcGHJfuQP2ZA&e= >
> allocateSecurityParameterIndex(java.net.InetAddress) >
> [3][3]https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.an
> droid.com_reference_android_net_IpSecManager-23&d=DwIDaQ&c=nKjWec2b6R0m
> OyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWI
> SL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=9txNFXC-wFiRFV_Q
> JlIQLW2AWSbERIOWcGHJfuQP2ZA&e= >
> allocateSecurityParameterIndex(java.net.InetAddress,%20int) > Indeed,
> allocateSecurityParameterIndex is direction-agnostic; both > overloads
> are implemented internally by including the min and max > values in
> ALLOCSPI. For the variant where app developers provide a > specific SPI
> (which is also useful in testing), Android simply sets > both the min
> and max parameters to that exact value. Our > understanding > of
> #xfrm_alloc_spi is that min/max are required for ALLOCSPI, and >
> otherwise ENOENT will be returned. > Note that we also use the DADDR as
> a mandatory part of the tuple > because of the issue mentioned above:
> SPIs are only unique in > conjunction with a DADDR, regardless of
> direction, and accordingly, > that’s how Android is expecting the
> uniqueness requirement be > enforced. In this way, 5 duplicate SPIs can
> be used on 5 unique IP > addresses on the same machine; therefore, a
> strict "SPI only" > interpretation for ALLOCSPI (or SPI handling in
> general) is curious. > We feel that ALLOCSPI should really enforce the
> same uniqueness > requirements as the SAD. > Best, > Nathan and Yan >
> -Nathan > On Wed, Feb 18, 2026 at 12:42 AM Tobias Brunner >
> <[4]tobias@strongswan.org> wrote: > > > > Hi Yan, > > > > > For every
> inbound SA, we allocate SPIs before negotiation. For > > > outbound
> SAs, we allocate SPIs once requested by the peer. We > only > > >
> require the (SPI, destination address) combo to be unique. Thus, > we >
> > > may have an inbound and outbound SA sharing an SPI with > different
> > > > destinations, or multiple outbound SAs to different peers >
> sharing an > > > SPI. > > > > That should still be allowed when using
> the intended APIs (i.e. > ALLOCSPI > > for the inbound and NEWSA for
> the outbound SA). ALLOCSPI might > enforce > > a unique SPI without
> considering the address, as that's intended > for > > local, inbound
> SAs, where the kernel has full control (looking at > the > > the patch,
> it's certainly not ideal, as it goes through all > installed > > SAs to
> find a duplicate and it prevents an inbound SPI that > matches an > >
> existing outbound SPI - I guess that could be resolved by using >
> separate > > tables for in- and outbound SAs). But that must not
> prevent > installing > > outbound SAs with the same SPI to another peer
> using NEWSA, which > still > > uses a hash that includes the
> destination address (that must > always be > > the case because peers
> are free to allocate whatever SPI they > want). > > > > >> If so, why
> would you use ALLOCSPI and not just install the > outbound SA? Is it to
> avoid differences for in- and outbound SAs > (ALLOCSPI+UPDSA vs.
> NEWSA)?" > > > > > > Exactly—it is primarily for code symmetry. By
> using ALLOCSPI + > UPDSA > > > for both directions, we maintain a
> consistent larval lifecycle > and > > > make it easier to maintain. > >
> > > In my opinion, you are using the API incorrectly. ALLOCSPI is >
> intended > > to allocate a free local SPI for an inbound SA. That is,
> reserve > it > > before and while the details of the SA are negotiated
> with the > peer > > using IKE. This step isn't necessary for outbound
> SAs and forcing > such > > an allocation, after all the details are
> known, to the responder's > SPI > > (which I assume you do via min/max
> option) doesn't feel right. I > also > > don't think there are any
> benefits in that "consistent larval > lifecycle" > > (if you found any,
> please let us know). And the difference > between > > UPDSA and NEWSA
> is the nlmsg_type (there are some attributes that > are > > different
> for in- and outbound SAs, especially if you set the > direction > > in
> newer kernels, but that's the case regardless of the message > type). >
> > > > By the way, are you using the min/max option for inbound SAs as >
> well, > > with an SPI generated in userland? That would seem like a >
> violation of > > the intention of the API as well (i.e. letting the
> kernel control > the > > local SPIs). > > > > As the XFRM API basically
> mirrors PF_KEYv2 here, you can find more > about > > the two ways to
> install SAs in RFC 2367 (SADB_GETSPI/UPDATE vs. > SADB_ADD). > > > >
> Regards, > > Tobias > > > > -- > > -- > Best, > Yan > > References > >
> 1. mailto:nharold@google.com > 2.
> [4]https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.andro
> id.com_reference_android_net_IpSecManager-23allocateSecurityParameterIn
> dex-28java.net.InetAddress-29&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn
> 5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51r
> d5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=UtpwkuoswT-6aK0he6dSS-0dvZfIcRjbfj
> _Eu4A-c6E&e= > 3.
> [5]https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.andro
> id.com_reference_android_net_IpSecManager-23allocateSecurityParameterIn
> dex-28java.net.InetAddress&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn5Ln
> Vsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51rd5M
> 6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=fVmYoLIpyuMX3AtAkU7ejR1dno_8QtnhCKLMD
> 4BmURc&e=, int) > 4. mailto:tobias@strongswan.org
>
> References
>
> Visible links:
> 1. https://us-phishalarm-ewt.proofpoint.com/EWT/v1/CRVmXkqW!tG3Tv5d8inv1_6DXc1X1B4ctthRq2qCkR8nIF_n_SOJiXQ-SqG_LUk--J5LZEwV9jGdXABQriDruLYnPEg$
> 2. https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.android.com_reference_android_net_IpSecManager-23&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=9txNFXC-wFiRFV_QJlIQLW2AWSbERIOWcGHJfuQP2ZA&e=
> 3. https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.android.com_reference_android_net_IpSecManager-23&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=9txNFXC-wFiRFV_QJlIQLW2AWSbERIOWcGHJfuQP2ZA&e=
> 4. https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.android.com_reference_android_net_IpSecManager-23allocateSecurityParameterIndex-28java.net.InetAddress-29&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=UtpwkuoswT-6aK0he6dSS-0dvZfIcRjbfj_Eu4A-c6E&e=
> 5. https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.android.com_reference_android_net_IpSecManager-23allocateSecurityParameterIndex-28java.net.InetAddress&d=DwIDaQ&c=nKjWec2b6R0mOyPaz7xtfQ&r=r6Wzn5LnVsk7Tgc5x4l_c04I_Hr_8TYqFn-YFi_gjqI&m=JsnNMKKypJWISL5BbGkfA2yD1_H51rd5M6YO4NG3WpRdteRwP5OdfTJFNEK0Xiec&s=fVmYoLIpyuMX3AtAkU7ejR1dno_8QtnhCKLMD4BmURc&e=
>
> Hidden links:
> 7. https://aka.ms/GetOutlookForMac
[-- Attachment #2: 0001-xfrm-enforce-SPI-uniqueness-for-inbound-SAs-only.patch --]
[-- Type: text/x-diff, Size: 3671 bytes --]
From 47de0cbe52f1a872edfbeb9c2ad5f08c564fad43 Mon Sep 17 00:00:00 2001
From: Antony Antony <antony.antony@secunet.com>
Date: Wed, 25 Mar 2026 06:02:07 +0100
Subject: [PATCH] xfrm: enforce SPI uniqueness for inbound SAs only
Per RFC 4301 section 4.4.2.1, the SPI is selected by the receiving
end, which is interpreted as making SPI uniqueness an inbound-only
requirement.
Commit 94f39804d891 ("xfrm: Duplicate SPI Handling") introduced
xfrm_state_lookup_spi_proto() to fix duplicate SPI allocation for
inbound SAs with different destination addresses. However, it enforces
global uniqueness by (spi, proto) across all states regardless of
direction, which causes SPI allocation to fail for outbound SAs when
the same (spi, proto) is already in use by an inbound SA.
When x->dir == XFRM_DIR_IN, enforce SPI uniqueness via
xfrm_state_lookup_spi_proto() scoped to inbound SAs. SAs created via
PF_KEY, without direction, or with XFRM_DIR_OUT restore the
pre-94f39804d891 RFC 2401 lookup by (daddr, spi, proto).
Reported-by: Yan Yan <evitayan@google.com>
Fixes: 94f39804d891 ("xfrm: Duplicate SPI Handling")
Signed-off-by: Antony Antony <antony.antony@secunet.com>
---
net/xfrm/xfrm_state.c | 14 ++++++++++++--
net/xfrm/xfrm_user.c | 6 +++---
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 98b362d51836..66cff3e8dd65 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1698,7 +1698,9 @@ struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi,
}
EXPORT_SYMBOL(xfrm_state_lookup_byspi);
-static struct xfrm_state *xfrm_state_lookup_spi_proto(struct net *net, __be32 spi, u8 proto)
+static struct xfrm_state *xfrm_state_lookup_spi_proto(struct net *net,
+ __be32 spi, u8 proto,
+ u8 dir)
{
struct xfrm_state *x;
unsigned int i;
@@ -1707,6 +1709,8 @@ static struct xfrm_state *xfrm_state_lookup_spi_proto(struct net *net, __be32 sp
for (i = 0; i <= net->xfrm.state_hmask; i++) {
hlist_for_each_entry_rcu(x, &net->xfrm.state_byspi[i], byspi) {
if (x->id.spi == spi && x->id.proto == proto) {
+ if (x->dir != dir)
+ continue;
if (!xfrm_state_hold_rcu(x))
continue;
rcu_read_unlock();
@@ -2577,6 +2581,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
struct xfrm_state *x0;
int err = -ENOENT;
u32 range = high - low + 1;
+ u32 mark = x->mark.v & x->mark.m;
__be32 newspi = 0;
spin_lock_bh(&x->lock);
@@ -2598,7 +2603,12 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
newspi = htonl(spi);
spin_lock_bh(&net->xfrm.xfrm_state_lock);
- x0 = xfrm_state_lookup_spi_proto(net, newspi, x->id.proto);
+ if (x->dir == XFRM_SA_DIR_IN)
+ x0 = xfrm_state_lookup_spi_proto(net, newspi,
+ x->id.proto, x->dir);
+ else
+ x0 = xfrm_state_lookup(net, mark, &x->id.daddr, newspi,
+ x->id.proto, x->props.family);
if (!x0) {
x->id.spi = newspi;
h = xfrm_spi_hash(net, &x->id.daddr, newspi, x->id.proto, x->props.family);
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 403b5ecac2c5..98b90c747aad 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1873,13 +1873,13 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out_noput;
}
+ if (attrs[XFRMA_SA_DIR])
+ x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
+
err = xfrm_alloc_spi(x, p->min, p->max, extack);
if (err)
goto out;
- if (attrs[XFRMA_SA_DIR])
- x->dir = nla_get_u8(attrs[XFRMA_SA_DIR]);
-
resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
if (IS_ERR(resp_skb)) {
err = PTR_ERR(resp_skb);
--
2.39.5
^ permalink raw reply related
* Re: [PATCH 2/2 net-next v2] ipv4: handle devconf post-set actions on netlink updates
From: Fernando Fernandez Mancera @ 2026-03-31 10:45 UTC (permalink / raw)
To: Paolo Abeni, netdev; +Cc: horms, kuba, edumazet, dsahern, davem
In-Reply-To: <b3997b77-d76b-4297-bc67-a2c57c7da601@redhat.com>
On 3/31/26 12:36 PM, Paolo Abeni wrote:
> On 3/27/26 1:02 PM, Fernando Fernandez Mancera wrote:
>> When IPv4 device configuration parameters are updated via netlink, the
>> kernel currently only updates the value. This bypasses several
>> post-modification actions that occur when these same parameters are
>> updated via sysctl, such as flushing the routing cache or emitting
>> RTM_NEWNETCONF notifications.
>>
>> This patch addresses the inconsistency by calling the
>> devinet_conf_post_set() helper inside inet_set_link_af(). If a flush is
>> required, we defer it until the netlink attribute parsing loop
>> completes.
>
> IMHO the above deserve some additional self-test triggering the relevant
> code.
>
Hi Paolo,
sure, I could include a selftest for this.
>> This ensures consistent behavior and side-effects for devconf changes,
>> regardless of whether they are initiated via sysctl or netlink.
>>
>> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
>> ---
>> v2: handled forwarding notification and disabling LRO
>> ---
>> net/ipv4/devinet.c | 29 +++++++++++++++++++++++++++--
>> 1 file changed, 27 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
>> index 8300516fb38f..a35b72662e43 100644
>> --- a/net/ipv4/devinet.c
>> +++ b/net/ipv4/devinet.c
>> @@ -2161,6 +2161,20 @@ static bool devinet_conf_post_set(struct net *net, struct ipv4_devconf *cnf,
>> NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
>> ifindex, cnf);
>> break;
>> + case IPV4_DEVCONF_FORWARDING:
>> + if (new == 1) {
>
> AI reviews says:
>
> Does this check miss cases where forwarding is enabled with a value other
> than 1?
> The sysctl path allows enabling IP forwarding with any non-zero value. If
> a user sets IPV4_DEVCONF_FORWARDING to 2 via netlink, forwarding will be
> enabled but this check is bypassed, leaving Large Receive Offload (LRO)
> enabled. Could this cause LRO-coalesced packets to be forwarded without
> proper software segmentation?
>
If a user sets IPV4_DEVCONF_FORWARDING to 2 via netlink the value would
be rejected. There is netlink attribute validation and FORWARDING is set
as boolean with the range [0, 1].
This cannot happen.
>> + /* it is safe to use container_of() because forwarding case
>> + * is only used by the netlink path
>> + */
>> + struct in_device *idev = container_of(cnf, struct in_device, cnf);
>> +
>> + netif_disable_lro(idev->dev);
>
> AI review says:
>
> Is it safe to call netif_disable_lro() here without holding the device
> operations lock?
> While inet_set_link_af() runs under rtnl_lock(), netif_disable_lro()
> modifies device features and calls netdev_update_features(), which
> asserts that the device operations lock is held. Calling this without
> the lock might trigger an assertion or expose the device to data races
> with concurrent feature updates or BPF/XDP attachments.
> Should this use dev_disable_lro() instead, which acquires the required
> lock before disabling LRO?
It is not possible to call this without the holding the lock. The device
operations lock is held when handling IFLA_AF_SPEC at
net/core/rtnetlink.c. Calling netif_disable_lro() is the right thing to
do IMHO. Keep on mind that FORWARDING case here is only triggered by
netlink path and not sysctl.
Thanks,
Fernando.
>
> /P
>
^ permalink raw reply
* [PATCH iwl-next v1 2/2] igc: use napi_schedule_irqoff() instead of napi_schedule()
From: Daiki Harada @ 2026-03-31 10:39 UTC (permalink / raw)
To: intel-wired-lan, netdev, linux-kernel
Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Daiki Harada,
Kohei Enju
In-Reply-To: <20260331103924.36422-1-daiky0325@gmail.com>
Replace napi_schedule() with napi_schedule_irqoff()
in the interrupt handler path in igc driver
Tested on Intel Corporation Ethernet Controller I226-V.
Suggested-by: Kohei Enju <kohei@enjuk.jp>
Signed-off-by: Daiki Harada <daiky0325@gmail.com>
---
drivers/net/ethernet/intel/igc/igc_main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 72bc5128d8b8..712605886104 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -5688,7 +5688,7 @@ static irqreturn_t igc_msix_ring(int irq, void *data)
/* Write the ITR value calculated from the previous interrupt. */
igc_write_itr(q_vector);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -6059,7 +6059,7 @@ static irqreturn_t igc_intr_msi(int irq, void *data)
if (icr & IGC_ICR_TS)
igc_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -6105,7 +6105,7 @@ static irqreturn_t igc_intr(int irq, void *data)
if (icr & IGC_ICR_TS)
igc_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
--
2.53.0
^ permalink raw reply related
* [PATCH iwl-next v1 1/2] igb: use napi_schedule_irqoff() instead of napi_schedule()
From: Daiki Harada @ 2026-03-31 10:39 UTC (permalink / raw)
To: intel-wired-lan, netdev, linux-kernel
Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Daiki Harada,
Kohei Enju
In-Reply-To: <20260331103924.36422-1-daiky0325@gmail.com>
Replace napi_schedule() with napi_schedule_irqoff()
in the interrupt handler path in igb driver
Tested on QEMU with igb NIC emulation (-nic user,model=igb)
Suggested-by: Kohei Enju <kohei@enjuk.jp>
Signed-off-by: Daiki Harada <daiky0325@gmail.com>
---
drivers/net/ethernet/intel/igb/igb_main.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ee99fd8fd513..d7a6ae938cc5 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -7158,7 +7158,7 @@ static irqreturn_t igb_msix_ring(int irq, void *data)
/* Write the ITR value calculated from the previous interrupt. */
igb_write_itr(q_vector);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -8199,7 +8199,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
if (icr & E1000_ICR_TS)
igb_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -8245,7 +8245,7 @@ static irqreturn_t igb_intr(int irq, void *data)
if (icr & E1000_ICR_TS)
igb_tsync_interrupt(adapter);
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
--
2.53.0
^ permalink raw reply related
* [PATCH iwl-next v1 0/2] igb/igc: use napi_schedule_irqoff() instead of napi_schedule()
From: Daiki Harada @ 2026-03-31 10:39 UTC (permalink / raw)
To: intel-wired-lan, netdev, linux-kernel
Cc: Tony Nguyen, Przemek Kitszel, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Daiki Harada
This patch set is meant to replace the calls to napi_schedule with
napi_schedule_irqoff as this should help to reduce the interrupt overhead
slightly by removing the unneeded call to local_irq_save and
local_irq_restore.
In the interrupt path, callers of napi_schedule() are invoked with local
IRQ disabled, so napi_schedule_irqoff() can be used instead.
This series applies the same optimization that was done for other drivers
in [1] to the igb/igc driver.
Link: https://lore.kernel.org/intel-wired-lan/20150929215117.3388.48642.stgit@ahduyck-vm-fedora22/ [1]
Daiki Harada (2):
igb: use napi_schedule_irqoff() instead of napi_schedule()
igc: use napi_schedule_irqoff() instead of napi_schedule()
drivers/net/ethernet/intel/igb/igb_main.c | 6 +++---
drivers/net/ethernet/intel/igc/igc_main.c | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH 2/2 net-next v2] ipv4: handle devconf post-set actions on netlink updates
From: Paolo Abeni @ 2026-03-31 10:36 UTC (permalink / raw)
To: Fernando Fernandez Mancera, netdev; +Cc: horms, kuba, edumazet, dsahern, davem
In-Reply-To: <20260327120202.4761-2-fmancera@suse.de>
On 3/27/26 1:02 PM, Fernando Fernandez Mancera wrote:
> When IPv4 device configuration parameters are updated via netlink, the
> kernel currently only updates the value. This bypasses several
> post-modification actions that occur when these same parameters are
> updated via sysctl, such as flushing the routing cache or emitting
> RTM_NEWNETCONF notifications.
>
> This patch addresses the inconsistency by calling the
> devinet_conf_post_set() helper inside inet_set_link_af(). If a flush is
> required, we defer it until the netlink attribute parsing loop
> completes.
IMHO the above deserve some additional self-test triggering the relevant
code.
> This ensures consistent behavior and side-effects for devconf changes,
> regardless of whether they are initiated via sysctl or netlink.
>
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> v2: handled forwarding notification and disabling LRO
> ---
> net/ipv4/devinet.c | 29 +++++++++++++++++++++++++++--
> 1 file changed, 27 insertions(+), 2 deletions(-)
>
> diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
> index 8300516fb38f..a35b72662e43 100644
> --- a/net/ipv4/devinet.c
> +++ b/net/ipv4/devinet.c
> @@ -2161,6 +2161,20 @@ static bool devinet_conf_post_set(struct net *net, struct ipv4_devconf *cnf,
> NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
> ifindex, cnf);
> break;
> + case IPV4_DEVCONF_FORWARDING:
> + if (new == 1) {
AI reviews says:
Does this check miss cases where forwarding is enabled with a value other
than 1?
The sysctl path allows enabling IP forwarding with any non-zero value. If
a user sets IPV4_DEVCONF_FORWARDING to 2 via netlink, forwarding will be
enabled but this check is bypassed, leaving Large Receive Offload (LRO)
enabled. Could this cause LRO-coalesced packets to be forwarded without
proper software segmentation?
> + /* it is safe to use container_of() because forwarding case
> + * is only used by the netlink path
> + */
> + struct in_device *idev = container_of(cnf, struct in_device, cnf);
> +
> + netif_disable_lro(idev->dev);
AI review says:
Is it safe to call netif_disable_lro() here without holding the device
operations lock?
While inet_set_link_af() runs under rtnl_lock(), netif_disable_lro()
modifies device features and calls netdev_update_features(), which
asserts that the device operations lock is held. Calling this without
the lock might trigger an assertion or expose the device to data races
with concurrent feature updates or BPF/XDP attachments.
Should this use dev_disable_lro() instead, which acquires the required
lock before disabling LRO?
/P
^ permalink raw reply
* Re: [PATCH v2 5/7] phy: ti: gmii-sel: add support for J722S SoC family
From: Nora Schiffer @ 2026-03-31 10:35 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Nishanth Menon, Vignesh Raghavendra, Tero Kristo,
Siddharth Vadapalli, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Vinod Koul, Neil Armstrong, netdev, devicetree,
linux-kernel, linux-phy, linux-arm-kernel, linux
In-Reply-To: <20260330223741.pmrx25cslrlpbcea@skbuf>
On Tue, 2026-03-31 at 01:37 +0300, Vladimir Oltean wrote:
> Hi Nora,
>
> On Tue, Mar 24, 2026 at 01:29:41PM +0100, Nora Schiffer wrote:
> > The J722S gmii-sel is mostly identical to the AM64's, but additionally
> > supports SGMII.
> >
> > Signed-off-by: Nora Schiffer <nora.schiffer@ew.tq-group.com>
> > ---
> > drivers/phy/ti/phy-gmii-sel.c | 11 +++++++++++
> > 1 file changed, 11 insertions(+)
> >
> > diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c
> > index 6213c2b6005a5..4e242b1892334 100644
> > --- a/drivers/phy/ti/phy-gmii-sel.c
> > +++ b/drivers/phy/ti/phy-gmii-sel.c
> > @@ -251,6 +251,13 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
> > .regfields = phy_gmii_sel_fields_am654,
> > };
> >
> > +static const
> > +struct phy_gmii_sel_soc_data phy_gmii_sel_soc_j722s = {
> > + .use_of_data = true,
> > + .regfields = phy_gmii_sel_fields_am654,
> > + .extra_modes = BIT(PHY_INTERFACE_MODE_SGMII),
>
> I'm not familiar with the hardware, but "mostly identical to AM64, but
> additionally supports SGMII" does not explain why j722s does not inherit
> the features that am654 has (PHY_GMII_SEL_RGMII_ID_MODE and
> BIT(PHY_GMII_SEL_FIXED_TX_DELAY).
>
> The phy-gmii-sel from j722s does support RGMII, right? Because in lack
> of the PHY_GMII_SEL_RGMII_ID_MODE feature, phy_gmii_sel_mode() will just
> silently skip the regmap_field_write(regfield, rgmii_id) call, and
> return successfully despite an incomplete configuration.
>
> We have the phy_validate() call and phy_ops::validate() through which
> the PHY can report to the Ethernet controller which phy_interface_t it
> supports and which it doesn't. If the j722s doesn't support RGMII, maybe
> it should implement this method.
Thanks for noticing this, PHY_GMII_SEL_RGMII_ID_MODE and
PHY_GMII_SEL_FIXED_TX_DELAY are missing indeed - will fix in v3. I made the
mistake to rebase from an older kernel version where these flags didn't exist
yet and neglected to double check when the rebase went through without
conflicts. I assume I didn't notice any issues because our bootloader left the
controller in the correct state.
Best,
Nora
>
> > +};
> > +
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/
^ permalink raw reply
* [PATCH net-next] net: airoha: Set REG_RX_CPU_IDX() once in airoha_qdma_fill_rx_queue()
From: Lorenzo Bianconi @ 2026-03-31 10:33 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev
It is not necessary to update REG_RX_CPU_IDX register for each iteration
of the descriptor loop in airoha_qdma_fill_rx_queue routine.
Move REG_RX_CPU_IDX configuration out of the descriptor loop and rely on
the last queue head value updated in the descriptor loop.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 82e53c60f561f6314fbf201ba8bc8711e40edc68..e09442580f376b6810b6e4023794e93f63dac209 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -572,11 +572,12 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
WRITE_ONCE(desc->msg1, 0);
WRITE_ONCE(desc->msg2, 0);
WRITE_ONCE(desc->msg3, 0);
+ }
+ if (nframes)
airoha_qdma_rmw(qdma, REG_RX_CPU_IDX(qid),
RX_RING_CPU_IDX_MASK,
FIELD_PREP(RX_RING_CPU_IDX_MASK, q->head));
- }
return nframes;
}
---
base-commit: 93d04e76bcf1e81f36f5ea7ad620a07747f1527c
change-id: 20260331-airoha-cpu-idx-out-off-loop-41ea4b99404c
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* Re: (subset) [PATCH 05/10] leds: led-class: switch to using class_find_device_by_fwnode()
From: Lee Jones @ 2026-03-31 10:29 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vinod Koul, Neil Armstrong, Mark Brown,
Liam Girdwood, Lee Jones, Pavel Machek, Peter Rosin, Andrew Lunn,
Heiner Kallweit, Russell King, Moritz Fischer, Xu Yilun, Tom Rix,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Dmitry Torokhov
Cc: netdev, linux-kernel, linux-phy, linux-spi, linux-leds,
linux-fpga, driver-core
In-Reply-To: <20260322-remove-device-find-by-of-node-v1-5-b72eb22a1215@gmail.com>
On Sun, 22 Mar 2026 18:54:23 -0700, Dmitry Torokhov wrote:
> In preparation to class_find_device_by_of_node() going away switch to
> using class_find_device_by_fwnode().
Applied, thanks!
[05/10] leds: led-class: switch to using class_find_device_by_fwnode()
commit: b6de441f8ce22e3ead3b858342fe5652598a3572
--
Lee Jones [李琼斯]
^ 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