All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
@ 2026-06-10  9:18 wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 01/15] net: enetc: add trusted " wei.fang
                   ` (14 more replies)
  0 siblings, 15 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

This series enhances SR-IOV support for ENETC v1 and adds VF support
for ENETC v4. For ENETC v1, the trusted VF management and per-VF
configuration query via ndo_get_vf_config() are added. For ENETC v4,
preliminary VF support is introduced for i.MX94 and i.MX95 platforms.

On the PF side, the series introduces trust level management and MAC
address assignment for VFs, and implements ndo_get_vf_config() so that
userspace tools such as 'ip link show' can query the current per-VF
configuration. The PSI-to-VSI messaging infrastructure is extended to
handle link status and link speed queries from VFs, enabling DPDK-owned
VFs to obtain accurate link information from the PF.

On the VF side, MAC address filtering is implemented via the VSI-to-PSI
mailbox, supporting both unicast/multicast hash filters and promiscuous
mode control. Since the ndo_set_rx_mode callback is invoked with BH
disabled and is incompatible with the mailbox polling path,
ndo_set_rx_mode_async is used instead. Link status notifications are also
supported: the VF registers with the PF and receives PSI-to-VSI messages
on every PF link transition, keeping the VF carrier state in sync without
polling.

VF Function Level Reset is handled explicitly to restore the PF-managed
MAC promiscuous policy after each reset, since the hardware default after
FLR enables promiscuous mode regardless of the policy set by the PF
driver.

Several preparatory refactors are included to share infrastructure
between the PF and VF drivers, such as moving msg_task and mac_filter
from struct enetc_pf into the common struct enetc_si.

---
v2:
1. Remove duplicated macros from enetc4_pf.c
2. Check pf->num_vfs in enetc_pf_notify_vf_link_status()
3. Check whether msg_hdr->len is changed between the msg_size calculation
   and the memcpy()
4. Clear ENETC_VF_FLAG_UC_PROMISC and ENETC_VF_FLAG_MC_PROMISC bits of
   vf_state->flags in enetc_msg_psi_free()
5. Check pf->total_vfs before calling mutex_lock(&pf->msg_lock) in
   enetc4_psi_do_set_rx_mode()
6. Check ndev->reg_state in enetc_vf_msg_task()
7. Check the returned vale of vf_reg_link_status_notifier(), and fall
   back to the legacy netif_carrier_on() behavior
8. Add ENETC_LINK_STATUS_NOTIFIER_REGISTERED bit to priv->flags
9. Add netif_running() check in enetc_vf_msg_handle_link_status()
10. Refactor enetc_pf_send_msg()
11. Add sriov_enabled to struct enetc_pf
12. Improve enetc_msg_set_vf_mac_hash_filter() and
    enetc_msg_get_link_speed()
13. Add more comments to the driver
v1 link: https://lore.kernel.org/imx/20260605065550.3038579-1-wei.fang@oss.nxp.com/
---

Claudiu Manoil (1):
  net: enetc: add trusted VF support

Wei Fang (14):
  net: enetc: move msg_task and msg_int_name to struct enetc_si
  net: enetc: add link status message support to PF driver
  net: enetc: add link speed message support to PF driver
  net: enetc: use enetc_set_si_hw_addr() to set VF MAC address
  net: enetc: relocate enetc_pf_set_vf_mac() for common PF support
  net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
  net: enetc: move mac_filter from struct enetc_pf to struct enetc_si
  net: enetc: add MAC address filtering support for VFs of ENETC v4
  net: enetc: simplify and rename PSIIER enable/disable helpers
  net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
  net: enetc: add VF support for i.MX94 and i.MX95
  net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
  net: enetc: add PSI-to-VSI link status notification support for VF
  net: enetc: add ndo_get_vf_config() support

 drivers/net/ethernet/freescale/enetc/Kconfig  |   1 +
 drivers/net/ethernet/freescale/enetc/enetc.c  |  43 +-
 drivers/net/ethernet/freescale/enetc/enetc.h  |  11 +-
 .../net/ethernet/freescale/enetc/enetc4_hw.h  |   1 +
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  90 ++-
 .../ethernet/freescale/enetc/enetc_ethtool.c  |   6 +
 .../net/ethernet/freescale/enetc/enetc_hw.h   |  26 +
 .../ethernet/freescale/enetc/enetc_mailbox.h  |  90 +++
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 579 ++++++++++++++++--
 .../net/ethernet/freescale/enetc/enetc_pf.c   |  62 +-
 .../net/ethernet/freescale/enetc/enetc_pf.h   |  21 +-
 .../freescale/enetc/enetc_pf_common.c         |  96 +++
 .../freescale/enetc/enetc_pf_common.h         |  13 +
 .../net/ethernet/freescale/enetc/enetc_vf.c   | 355 ++++++++++-
 14 files changed, 1289 insertions(+), 105 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 01/15] net: enetc: add trusted VF support
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Claudiu Manoil <claudiu.manoil@nxp.com>

Some mailbox messages require a higher privilege level to be executed
on behalf of the requesting VF. Introduce a trusted VF flag
(ENETC_VF_FLAG_TRUSTED) and wire up the ndo_set_vf_trust callback via
enetc_pf_set_vf_trust(), which is shared between the enetc and enetc4
PF drivers.

The first message gated on trust is the VF primary MAC address change.
An untrusted VF that attempts to set its own MAC address will receive a
ENETC_MSG_CLASS_ID_PERMISSION_DENY response and the hardware will not
be programmed. To prevent a malicious VM from setting the VF address to
the MAC address of other VFs or PF, thereby eavesdropping on the traffic
of other SIs. Furthermore, a malicious VM that arbitrarily changes the
VF's MAC address can achieve MAC address spoofing and bypass security
policies.

Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  7 +++-
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 38 ++++++++++++++-----
 .../net/ethernet/freescale/enetc/enetc_pf.c   |  1 +
 .../net/ethernet/freescale/enetc/enetc_pf.h   |  1 +
 .../freescale/enetc/enetc_pf_common.c         | 23 +++++++++++
 .../freescale/enetc/enetc_pf_common.h         |  1 +
 6 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 4e771f852358..f7c532758e1c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -277,11 +277,15 @@ static const struct enetc_pf_ops enetc4_pf_ops = {
 static int enetc4_pf_struct_init(struct enetc_si *si)
 {
 	struct enetc_pf *pf = enetc_si_priv(si);
+	int err;
 
 	pf->si = si;
-	pf->total_vfs = pci_sriov_get_totalvfs(si->pdev);
 	pf->ops = &enetc4_pf_ops;
 
+	err = enetc_init_sriov_resources(pf);
+	if (err)
+		return err;
+
 	enetc4_get_port_caps(pf);
 	enetc4_get_psi_hw_features(si);
 
@@ -570,6 +574,7 @@ static const struct net_device_ops enetc4_ndev_ops = {
 	.ndo_eth_ioctl		= enetc_ioctl,
 	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
 	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
+	.ndo_set_vf_trust	= enetc_pf_set_vf_trust,
 };
 
 static struct phylink_pcs *
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index edc1277bb586..78114ab3e482 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -7,6 +7,8 @@
 					   ENETC_MSG_CLASS_ID_CMD_SUCCESS)
 #define ENETC_PF_MSG_NOTSUPP	FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
 					   ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT)
+#define ENETC_PF_MSG_PERM_DENY	FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+					   ENETC_MSG_CLASS_ID_PERMISSION_DENY)
 
 static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
 {
@@ -61,31 +63,49 @@ static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
 	struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
 	struct enetc_msg_mac_exact_filter *msg = vf_msg;
 	struct device *dev = &pf->si->pdev->dev;
+	u16 pf_msg = ENETC_PF_MSG_SUCCESS;
 	char *addr = msg->mac[0].addr;
 
+	mutex_lock(&vf_state->lock);
+
+	/* Untrusted VFs cannot set their MAC addresses by the mailbox
+	 * messages.
+	 */
+	if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+		pf_msg = ENETC_PF_MSG_PERM_DENY;
+		goto vf_state_unlock;
+	}
+
 	if (!is_valid_ether_addr(addr)) {
 		dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
 				    vf_id);
-		return (FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
-				   ENETC_MSG_CLASS_ID_MAC_FILTER) |
-			FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
-				   ENETC_MF_CLASS_CODE_INVALID_MAC));
+		pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+				    ENETC_MSG_CLASS_ID_MAC_FILTER) |
+			 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+				    ENETC_MF_CLASS_CODE_INVALID_MAC);
+		goto vf_state_unlock;
 	}
 
-	mutex_lock(&vf_state->lock);
+	/* PF has higher privileges. If PF has already modified the MAC
+	 * address for VF through .ndo_set_vf_mac() interface, VF is not
+	 * allowed to set its MAC address via mailbox messages, even if
+	 * it is trusted.
+	 */
 	if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
-		mutex_unlock(&vf_state->lock);
 		dev_err_ratelimited(dev,
 				    "VF%d attempted to override PF set MAC\n",
 				    vf_id);
-		return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
-				  ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED);
+		pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+				    ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED);
+		goto vf_state_unlock;
 	}
 
 	enetc_set_si_hw_addr(pf, vf_id + 1, addr);
+
+vf_state_unlock:
 	mutex_unlock(&vf_state->lock);
 
-	return ENETC_PF_MSG_SUCCESS;
+	return pf_msg;
 }
 
 static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 2d687bb8c3a0..8957e74314d1 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -540,6 +540,7 @@ static const struct net_device_ops enetc_ndev_ops = {
 	.ndo_set_rx_mode	= enetc_pf_set_rx_mode,
 	.ndo_vlan_rx_add_vid	= enetc_vlan_rx_add_vid,
 	.ndo_vlan_rx_kill_vid	= enetc_vlan_rx_del_vid,
+	.ndo_set_vf_trust	= enetc_pf_set_vf_trust,
 	.ndo_set_vf_mac		= enetc_pf_set_vf_mac,
 	.ndo_set_vf_vlan	= enetc_pf_set_vf_vlan,
 	.ndo_set_vf_spoofchk	= enetc_pf_set_vf_spoofchk,
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 285b7e5c48fd..eb977da2be71 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -9,6 +9,7 @@
 
 enum enetc_vf_flags {
 	ENETC_VF_FLAG_PF_SET_MAC	= BIT(0),
+	ENETC_VF_FLAG_TRUSTED		= BIT(1),
 };
 
 struct enetc_vf_state {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 6e5d2f869915..44c546b77d3f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -462,5 +462,28 @@ int enetc_init_sriov_resources(struct enetc_pf *pf)
 }
 EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
 
+int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_vf_state *vf_state;
+
+	if (vf >= pf->total_vfs)
+		return -EINVAL;
+
+	vf_state = &pf->vf_state[vf];
+	mutex_lock(&vf_state->lock);
+
+	if (setting)
+		vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
+	else
+		vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
+
+	mutex_unlock(&vf_state->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_set_vf_trust);
+
 MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index 57d2e0ebd2b0..5bf7c20aba42 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -17,6 +17,7 @@ void enetc_set_default_rss_key(struct enetc_pf *pf);
 int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid);
 int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid);
 int enetc_init_sriov_resources(struct enetc_pf *pf);
+int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting);
 
 static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 01/15] net: enetc: add trusted " wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

The ENETC PF currently uses msg_task and msg_int_name in struct enetc_pf
to handle VSI-to-PSI mailbox messages via a workqueue and a dedicated
interrupt.

PSI-to-VSI message support will be added to the VF driver, which will
require the same mechanism: a message interrupt and a workqueue handler.
Since struct enetc_si is the common structure shared between PF and VF,
move msg_task and msg_int_name from struct enetc_pf to struct enetc_si
to allow both drivers to use them without duplication.

Also relocate the ENETC_INT_NAME_MAX macro definition ahead of struct
enetc_si so it can be used for the msg_int_name array declaration.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.h  |  5 ++++-
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 19 ++++++++++---------
 .../net/ethernet/freescale/enetc/enetc_pf.h   |  3 ---
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 04a5dd5ea6c7..2cd035773aca 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -25,6 +25,7 @@
 #define ENETC_CBD_DATA_MEM_ALIGN 64
 
 #define ENETC_MADDR_HASH_TBL_SZ	64
+#define ENETC_INT_NAME_MAX	(IFNAMSIZ + 8)
 
 enum enetc_mac_addr_type {UC, MC, MADDR_TYPE};
 
@@ -328,6 +329,9 @@ struct enetc_si {
 	struct work_struct rx_mode_task;
 	struct dentry *debugfs_root;
 	struct enetc_msg_swbd msg; /* Only valid for VSI */
+
+	struct work_struct msg_task;
+	char msg_int_name[ENETC_INT_NAME_MAX];
 };
 
 #define ENETC_SI_ALIGN	32
@@ -369,7 +373,6 @@ static inline bool enetc_is_pseudo_mac(struct enetc_si *si)
 }
 
 #define ENETC_MAX_NUM_TXQS	8
-#define ENETC_INT_NAME_MAX	(IFNAMSIZ + 8)
 
 struct enetc_int_vector {
 	void __iomem *rbier;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index 78114ab3e482..a89a5a418a23 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -37,7 +37,7 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
 	struct enetc_pf *pf = enetc_si_priv(si);
 
 	enetc_msg_disable_mr_int(pf);
-	schedule_work(&pf->msg_task);
+	schedule_work(&si->msg_task);
 
 	return IRQ_HANDLED;
 }
@@ -223,12 +223,13 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 
 static void enetc_msg_task(struct work_struct *work)
 {
-	struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
-	u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
-	struct enetc_hw *hw = &pf->si->hw;
-	u32 mr_status;
+	struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
+	struct enetc_pf *pf = enetc_si_priv(si);
+	struct enetc_hw *hw = &si->hw;
+	u32 mr_status, mr_mask;
 	int i;
 
+	mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
 	mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
 		    (enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
 	if (!mr_status)
@@ -311,13 +312,13 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
 	}
 
 	/* initialize PSI mailbox */
-	INIT_WORK(&pf->msg_task, enetc_msg_task);
+	INIT_WORK(&si->msg_task, enetc_msg_task);
 
 	/* register message passing interrupt handler */
-	snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
+	snprintf(si->msg_int_name, sizeof(si->msg_int_name), "%s-vfmsg",
 		 si->ndev->name);
 	vector = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX);
-	err = request_irq(vector, enetc_msg_psi_msix, 0, pf->msg_int_name, si);
+	err = request_irq(vector, enetc_msg_psi_msix, 0, si->msg_int_name, si);
 	if (err) {
 		dev_err(&si->pdev->dev,
 			"PSI messaging: request_irq() failed!\n");
@@ -350,7 +351,7 @@ static void enetc_msg_psi_free(struct enetc_pf *pf)
 	/* de-register message passing interrupt handler */
 	free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
 
-	cancel_work_sync(&pf->msg_task);
+	cancel_work_sync(&si->msg_task);
 
 	/* MR interrupts may be re-enabled by workqueue */
 	enetc_msg_disable_mr_int(pf);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index eb977da2be71..07bb9aab89aa 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -43,10 +43,7 @@ struct enetc_pf {
 	struct enetc_vf_state *vf_state;
 
 	struct enetc_mac_filter mac_filter[MADDR_TYPE];
-
 	struct enetc_msg_swbd *rxmsg;
-	struct work_struct msg_task;
-	char msg_int_name[ENETC_INT_NAME_MAX];
 
 	char vlan_promisc_simap; /* bitmap of SIs in VLAN promisc mode */
 	DECLARE_BITMAP(vlan_ht_filter, ENETC_VLAN_HT_SIZE);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 01/15] net: enetc: add trusted " wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 04/15] net: enetc: add link speed " wei.fang
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Add link status message support to the PF driver using three command IDs
under message class 0x80 (ENETC_MSG_CLASS_ID_LINK_STATUS):

1. ENETC_MSG_GET_CURRENT_LINK_STATUS (cmd_id 0)
The VF queries the current PF link status synchronously. This command is
not used by the Linux VF driver but is intended for DPDK-owned VFs.

2. ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER (cmd_id 1)
The VF registers for link change notification. Upon registration, the PF
immediately notifies the VF of the current link status via a PSI-to-VSI
message, and continues to do so on every subsequent link state change.

3. ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER (cmd_id 2)
The VF unregisters from link change notification.

PSI-to-VSI notifications are sent via the ENETC_PSIMSGSR register. A new
msg_lock mutex is introduced in struct enetc_pf to protect concurrent
access between the phylink callbacks and the VSI-to-PSI message handler.
The bitmask link_status_ms_mask tracks which VFs have registered for
notifications and is cleared when SR-IOV is disabled.

Two functions are exported for use by PF drivers:
  enetc_pf_notify_vf_link_up()
  enetc_pf_notify_vf_link_down()

Through this mechanism, VFs can accurately perceive the link status and
report it to upper layers such as the kernel network stack, containers,
and virtual machines.

Note that currently only the ENETC v4 driver supports this feature, while
v1 does not.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |   2 +
 .../net/ethernet/freescale/enetc/enetc_hw.h   |   5 +
 .../ethernet/freescale/enetc/enetc_mailbox.h  |  18 ++
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 201 +++++++++++++++++-
 .../net/ethernet/freescale/enetc/enetc_pf.h   |   4 +
 .../freescale/enetc/enetc_pf_common.c         |   2 +
 .../freescale/enetc/enetc_pf_common.h         |   9 +
 7 files changed, 235 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index f7c532758e1c..4b69c19b2319 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -944,6 +944,7 @@ static void enetc4_pl_mac_link_up(struct phylink_config *config,
 	enetc4_set_rx_pause(pf, rx_pause);
 	enetc4_mac_tx_enable(pf);
 	enetc4_mac_rx_enable(pf);
+	enetc_pf_notify_vf_link_up(pf);
 }
 
 static void enetc4_pl_mac_link_down(struct phylink_config *config,
@@ -954,6 +955,7 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,
 
 	enetc4_mac_rx_graceful_stop(pf);
 	enetc4_mac_tx_graceful_stop(pf);
+	enetc_pf_notify_vf_link_down(pf);
 }
 
 static const struct phylink_mac_ops enetc_pl_mac_ops = {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index bf99b65d7598..7b66c5be1ccf 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -80,6 +80,11 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 #define ENETC_SIMSGSR_SET_MC(val) ((val) << 16)
 #define ENETC_SIMSGSR_GET_MC(val) ((val) >> 16)
 
+#define ENETC_PSIMSGSR		0x208
+/* n is VF index, which is less than 15 */
+#define  PSIMSGSR_MS(n)		BIT((n) + 1)
+#define  PSIMSGSR_MC		GENMASK(31, 16)
+
 /* SI statistics */
 #define ENETC_SIROCT	0x300
 #define ENETC_SIRFRM	0x308
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index d9677da38989..43b9ee2ab3e4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -107,6 +107,7 @@ enum enetc_msg_class_id {
 
 	/* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */
 	ENETC_MSG_CLASS_ID_MAC_FILTER		= 0x20,
+	ENETC_MSG_CLASS_ID_LINK_STATUS		= 0x80,
 	ENETC_MSG_CLASS_ID_IP_REVISION		= 0xf0,
 };
 
@@ -118,11 +119,23 @@ enum enetc_msg_ip_revision_cmd_id {
 	ENETC_MSG_GET_IP_MN			= 1,
 };
 
+enum enetc_msg_link_status_cmd_id {
+	ENETC_MSG_GET_CURRENT_LINK_STATUS,
+	ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER,
+	ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER,
+};
+
 /* Class-specific error return codes of MAC filter */
 enum enetc_mac_filter_class_code {
 	ENETC_MF_CLASS_CODE_INVALID_MAC,
 };
 
+/* Class-specific notifications/codes of link status */
+enum enetc_link_status_class_code {
+	ENETC_LINK_STATUS_CLASS_CODE_UP,
+	ENETC_LINK_STATUS_CLASS_CODE_DOWN,
+};
+
 struct enetc_msg_swbd {
 	void *vaddr;
 	dma_addr_t dma;
@@ -161,6 +174,11 @@ struct enetc_msg_mac_exact_filter {
 /* The generic message format applies to the following messages:
  * Get IP revision message, class_id 0xf0.
  * cmd_id 1: get IP minor revision
+ *
+ * Link status message, class id 0x80.
+ * cmd_id 0x0: get the current link status
+ * cmd_id 0x1: register link status change notification
+ * cmd_id 0x2: unregister link status change notification
  */
 struct enetc_msg_generic {
 	struct enetc_msg_header hdr;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index a89a5a418a23..eac71f90d80b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -136,6 +136,148 @@ static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
 	}
 }
 
+static u16 enetc_msg_get_link_status(struct enetc_pf *pf)
+{
+	struct net_device *ndev = pf->si->ndev;
+	u16 pf_msg;
+
+	pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+			    ENETC_MSG_CLASS_ID_LINK_STATUS);
+
+	if (netif_carrier_ok(ndev))
+		pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+				     ENETC_LINK_STATUS_CLASS_CODE_UP);
+	else
+		pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+				     ENETC_LINK_STATUS_CLASS_CODE_DOWN);
+
+	return pf_msg;
+}
+
+static int enetc_pf_send_msg(struct enetc_pf *pf, u32 msg_code, u16 ms_mask)
+{
+	struct enetc_hw *hw = &pf->si->hw;
+	u16 old_ms_mask = ms_mask;
+	u16 ms_status;
+	u32 val;
+
+	/* The MS bit is set, indicating that the corresponding VF has not
+	 * read the last message, PF cannot send new message to the VF. So
+	 * the bit corresponding to VF is cleared from ms_mask.
+	 */
+	ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
+	ms_mask &= ~ms_status;
+	if (!ms_mask)
+		return -EIO;
+
+	if (ms_mask != old_ms_mask)
+		dev_warn_ratelimited(&pf->si->pdev->dev,
+				     "PF cannot send message to VF(s) 0x%x\n",
+				     ms_mask ^ old_ms_mask);
+
+	/* Writing a 1 when the bit is already set is undefined.
+	 * Writing a 0 has no effect. Sending messages only to
+	 * VFs where the MR bit is not set.
+	 */
+	enetc_wr(hw, ENETC_PSIMSGSR,
+		 FIELD_PREP(PSIMSGSR_MC, msg_code) | ms_mask);
+
+	return read_poll_timeout(enetc_rd, val, !(val & ms_mask), 1000,
+				 200000, false, hw, ENETC_PSIMSGSR);
+}
+
+static void enetc_msg_notify_vf_link_status(struct enetc_pf *pf, u16 ms_mask,
+					    bool link_up)
+{
+	u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+				ENETC_MSG_CLASS_ID_LINK_STATUS);
+
+	if (link_up)
+		pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+				     ENETC_LINK_STATUS_CLASS_CODE_UP);
+	else
+		pf_msg |= FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
+				     ENETC_LINK_STATUS_CLASS_CODE_DOWN);
+
+	if (enetc_pf_send_msg(pf, pf_msg, ms_mask))
+		dev_err_ratelimited(&pf->si->pdev->dev,
+				    "PF notifies link status failed\n");
+}
+
+static void enetc_pf_reply_msg(struct enetc_hw *hw, int vf_id, u16 pf_msg)
+{
+	/* w1c to clear the corresponding VF MR bit */
+	enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(vf_id));
+	enetc_wr(hw, ENETC_PSIMSGRR, ENETC_SIMSGSR_SET_MC(pf_msg) |
+		 ENETC_PSIMR_BIT(vf_id));
+}
+
+static void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
+						    int vf_id)
+{
+	u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+				ENETC_MSG_CLASS_ID_CMD_SUCCESS);
+
+	mutex_lock(&pf->msg_lock);
+
+	enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
+
+	/* SR-IOV is being disabled if pf->sriov_enabled is false, so no
+	 * need to set link_status_ms_mask and notify the link status.
+	 */
+	if (!pf->sriov_enabled)
+		goto msg_unlock;
+
+	pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
+
+	/* Notify VF the current link status */
+	enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id),
+					netif_carrier_ok(pf->si->ndev));
+
+msg_unlock:
+	mutex_unlock(&pf->msg_lock);
+}
+
+static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
+						      int vf_id)
+{
+	u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+				ENETC_MSG_CLASS_ID_CMD_SUCCESS);
+
+	mutex_lock(&pf->msg_lock);
+
+	pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
+	enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
+
+	mutex_unlock(&pf->msg_lock);
+}
+
+static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
+					void *vf_msg)
+{
+	struct enetc_msg_header *msg_hdr = vf_msg;
+
+	switch (msg_hdr->cmd_id) {
+	case ENETC_MSG_GET_CURRENT_LINK_STATUS:
+		/* Currently, this message is intended only for
+		 * DPDK-owned VFs.
+		 */
+		return enetc_msg_get_link_status(pf);
+	case ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER:
+		enetc_msg_register_link_status_notifier(pf, vf_id);
+		return 0;
+	case ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER:
+		enetc_msg_unregister_link_status_notifier(pf, vf_id);
+		return 0;
+	default:
+		return ENETC_PF_MSG_NOTSUPP;
+	}
+}
+
+/* If *pf_msg is set to 0, it means that PF has responded to VF in
+ * enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(), which also
+ * clears the corresponding VF MR bit in PSIIDR.
+ */
 static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 				   u16 *pf_msg)
 {
@@ -211,6 +353,9 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 	case ENETC_MSG_CLASS_ID_IP_REVISION:
 		*pf_msg = enetc_msg_handle_ip_revision(pf, msg);
 		break;
+	case ENETC_MSG_CLASS_ID_LINK_STATUS:
+		*pf_msg = enetc_msg_handle_link_status(pf, vf_id, msg);
+		break;
 	default:
 		dev_err_ratelimited(dev,
 				    "Unsupported message class ID: 0x%x\n",
@@ -236,7 +381,6 @@ static void enetc_msg_task(struct work_struct *work)
 		goto out;
 
 	for (i = 0; i < pf->num_vfs; i++) {
-		u32 psimsgrr;
 		u16 msg_code;
 
 		if (!(ENETC_PSIMR_BIT(i) & mr_status))
@@ -244,12 +388,14 @@ static void enetc_msg_task(struct work_struct *work)
 
 		enetc_msg_handle_rxmsg(pf, i, &msg_code);
 
-		/* w1c to clear the corresponding VF MR bit */
-		enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));
+		/* If msg_code is 0, it means that PF has responded to VF
+		 * in enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(),
+		 * which also clears the corresponding VF MR bit in PSIIDR.
+		 */
+		if (!msg_code)
+			continue;
 
-		psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
-		psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
-		enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
+		enetc_pf_reply_msg(hw, i, msg_code);
 	}
 
 out:
@@ -367,6 +513,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 	int err;
 
 	if (!num_vfs) {
+		mutex_lock(&pf->msg_lock);
+		pf->sriov_enabled = false;
+		pf->link_status_ms_mask = 0;
+		mutex_unlock(&pf->msg_lock);
+
 		pci_disable_sriov(pdev);
 		enetc_msg_psi_free(pf);
 		pf->num_vfs = 0;
@@ -379,6 +530,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 			goto err_msg_psi;
 		}
 
+		/* As PCI SR-IOV is not enabled at the moment, there is no
+		 * concurrent access to sriov_enabled. So no need to use
+		 * msg_lock to protect sriov_enabld.
+		 */
+		pf->sriov_enabled = true;
 		err = pci_enable_sriov(pdev, num_vfs);
 		if (err) {
 			dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err);
@@ -389,6 +545,7 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 	return num_vfs;
 
 err_en_sriov:
+	pf->sriov_enabled = false;
 	enetc_msg_psi_free(pf);
 err_msg_psi:
 	pf->num_vfs = 0;
@@ -396,3 +553,35 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 	return err;
 }
 EXPORT_SYMBOL_GPL(enetc_sriov_configure);
+
+static void enetc_pf_notify_vf_link_status(struct enetc_pf *pf,
+					   bool link_up)
+{
+	u16 ms_mask;
+
+	if (!pf->num_vfs)
+		return;
+
+	mutex_lock(&pf->msg_lock);
+
+	ms_mask = pf->link_status_ms_mask;
+	if (!ms_mask)
+		goto msg_unlock;
+
+	enetc_msg_notify_vf_link_status(pf, ms_mask, link_up);
+
+msg_unlock:
+	mutex_unlock(&pf->msg_lock);
+}
+
+void enetc_pf_notify_vf_link_up(struct enetc_pf *pf)
+{
+	enetc_pf_notify_vf_link_status(pf, true);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_notify_vf_link_up);
+
+void enetc_pf_notify_vf_link_down(struct enetc_pf *pf)
+{
+	enetc_pf_notify_vf_link_status(pf, false);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_notify_vf_link_down);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 07bb9aab89aa..a5c13a4cc197 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -60,6 +60,10 @@ struct enetc_pf {
 	const struct enetc_pf_ops *ops;
 
 	int num_mfe;	/* number of mac address filter table entries */
+	/* Message lock, prevent concurrent access */
+	struct mutex msg_lock;
+	bool sriov_enabled;
+	u16 link_status_ms_mask;
 };
 
 #define phylink_to_enetc_pf(config) \
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 44c546b77d3f..e3bed5580d17 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -458,6 +458,8 @@ int enetc_init_sriov_resources(struct enetc_pf *pf)
 	for (int i = 0; i < pf->total_vfs; i++)
 		mutex_init(&pf->vf_state[i].lock);
 
+	mutex_init(&pf->msg_lock);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(enetc_init_sriov_resources);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index 5bf7c20aba42..bbe21c739cf7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -26,9 +26,18 @@ static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
 
 #if IS_ENABLED(CONFIG_PCI_IOV)
 int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs);
+void enetc_pf_notify_vf_link_up(struct enetc_pf *pf);
+void enetc_pf_notify_vf_link_down(struct enetc_pf *pf);
 #else
 static inline int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 {
 	return 0;
 }
+
+static inline void enetc_pf_notify_vf_link_up(struct enetc_pf *pf)
+{
+}
+
+static inline void enetc_pf_notify_vf_link_down(struct enetc_pf *pf)
+{}
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 04/15] net: enetc: add link speed message support to PF driver
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (2 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

When a VF is driven by DPDK, the VF relies on the PF to provide accurate
link speed information so that the VF-side user space application can
make correct forwarding and configuration decisions.

Therefore, add link speed message support for DPDK-owned VF. The PF will
reply the current link speed when it receives the get link speed message
from VF.

With this enhancement, VFs controlled by DPDK can obtain real-time link
speed information from the PF, improving overall link state visibility,
synchronization between PF/VF, and enabling more accurate performance
adjustments in VF-side applications.

Note that currently the link speed change notification is not supported.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../ethernet/freescale/enetc/enetc_mailbox.h  | 30 +++++++
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 85 +++++++++++++++++++
 2 files changed, 115 insertions(+)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index 43b9ee2ab3e4..46446330e222 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -108,6 +108,7 @@ enum enetc_msg_class_id {
 	/* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */
 	ENETC_MSG_CLASS_ID_MAC_FILTER		= 0x20,
 	ENETC_MSG_CLASS_ID_LINK_STATUS		= 0x80,
+	ENETC_MSG_CLASS_ID_LINK_SPEED		= 0x81,
 	ENETC_MSG_CLASS_ID_IP_REVISION		= 0xf0,
 };
 
@@ -125,6 +126,13 @@ enum enetc_msg_link_status_cmd_id {
 	ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER,
 };
 
+enum enetc_msg_link_speed_cmd_id {
+	ENETC_MSG_GET_CURRENT_LINK_SPEED,
+	/* The following command IDs are not currently supported */
+	ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER,
+	ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER,
+};
+
 /* Class-specific error return codes of MAC filter */
 enum enetc_mac_filter_class_code {
 	ENETC_MF_CLASS_CODE_INVALID_MAC,
@@ -136,6 +144,23 @@ enum enetc_link_status_class_code {
 	ENETC_LINK_STATUS_CLASS_CODE_DOWN,
 };
 
+/* Class-specific notifications/codes of link speed */
+enum enetc_link_speed_class_code {
+	ENETC_MSG_SPEED_UNKNOWN,
+	ENETC_MSG_SPEED_10M_HD,
+	ENETC_MSG_SPEED_10M_FD,
+	ENETC_MSG_SPEED_100M_HD,
+	ENETC_MSG_SPEED_100M_FD,
+	ENETC_MSG_SPEED_1000M,
+	ENETC_MSG_SPEED_2500M,
+	ENETC_MSG_SPEED_5G,
+	ENETC_MSG_SPEED_10G,
+	ENETC_MSG_SPEED_25G,
+	ENETC_MSG_SPEED_40G,
+	ENETC_MSG_SPEED_50G,
+	ENETC_MSG_SPEED_100G,
+};
+
 struct enetc_msg_swbd {
 	void *vaddr;
 	dma_addr_t dma;
@@ -179,6 +204,11 @@ struct enetc_msg_mac_exact_filter {
  * cmd_id 0x0: get the current link status
  * cmd_id 0x1: register link status change notification
  * cmd_id 0x2: unregister link status change notification
+ *
+ * Link speed message, class_id 0x81.
+ * cmd_id 0x0: get the current link speed.
+ * cmd_id 0x1: register link speed change notification, not supported yet
+ * cmd_id 0x2: unregister link speed change notification, not supported yet
  */
 struct enetc_msg_generic {
 	struct enetc_msg_header hdr;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index eac71f90d80b..b24903b23a61 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -9,6 +9,9 @@
 					   ENETC_MSG_CLASS_ID_CMD_NOT_SUPPORT)
 #define ENETC_PF_MSG_PERM_DENY	FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
 					   ENETC_MSG_CLASS_ID_PERMISSION_DENY)
+#define ENETC_PF_MSG_SPEED(s)	(FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+					    ENETC_MSG_CLASS_ID_LINK_SPEED) | \
+				 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, (s)))
 
 static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
 {
@@ -274,6 +277,84 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
 	}
 }
 
+static u16 enetc_msg_get_link_speed(struct enetc_pf *pf, int vf_id)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+	struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+	struct ethtool_link_ksettings link_info = {};
+
+	/* A malicious or malfunctioning VM could potentially spam these
+	 * messages in a tight loop causing global rtnl_lock contention,
+	 * which may severely starve other processes on the host that
+	 * require rtnl_lock for routine network configuration, resulting
+	 * in a system-wide control-plane denial of service. Therefore,
+	 * we expect the VF query for link speed to be trusted. There's no
+	 * need to consider the transition from trusted to untrusted here,
+	 * as this won't cause rntl_lock() to be called frequently.
+	 */
+	mutex_lock(&vf_state->lock);
+	if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+		mutex_unlock(&vf_state->lock);
+
+		return ENETC_PF_MSG_PERM_DENY;
+	}
+	mutex_unlock(&vf_state->lock);
+
+	rtnl_lock();
+	phylink_ethtool_ksettings_get(priv->phylink, &link_info);
+	rtnl_unlock();
+
+	switch (link_info.base.speed) {
+	case SPEED_10:
+		if (link_info.base.duplex == DUPLEX_HALF)
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_10M_HD);
+		else if (link_info.base.duplex == DUPLEX_FULL)
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_10M_FD);
+		else
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_UNKNOWN);
+	case SPEED_100:
+		if (link_info.base.duplex == DUPLEX_HALF)
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_100M_HD);
+		else if (link_info.base.duplex == DUPLEX_FULL)
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_100M_FD);
+		else
+			return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_UNKNOWN);
+	case SPEED_1000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_1000M);
+	case SPEED_2500:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_2500M);
+	case SPEED_5000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_5G);
+	case SPEED_10000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_10G);
+	case SPEED_25000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_25G);
+	case SPEED_40000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_40G);
+	case SPEED_50000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_50G);
+	case SPEED_100000:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_100G);
+	default:
+		return ENETC_PF_MSG_SPEED(ENETC_MSG_SPEED_UNKNOWN);
+	}
+}
+
+static u16 enetc_msg_handle_link_speed(struct enetc_pf *pf, int vf_id,
+				       void *vf_msg)
+{
+	struct enetc_msg_header *msg_hdr = vf_msg;
+
+	switch (msg_hdr->cmd_id) {
+	case ENETC_MSG_GET_CURRENT_LINK_SPEED:
+		return enetc_msg_get_link_speed(pf, vf_id);
+	case ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER:
+	case ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER:
+	default:
+		return ENETC_PF_MSG_NOTSUPP;
+	}
+}
+
 /* If *pf_msg is set to 0, it means that PF has responded to VF in
  * enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(), which also
  * clears the corresponding VF MR bit in PSIIDR.
@@ -356,6 +437,9 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 	case ENETC_MSG_CLASS_ID_LINK_STATUS:
 		*pf_msg = enetc_msg_handle_link_status(pf, vf_id, msg);
 		break;
+	case ENETC_MSG_CLASS_ID_LINK_SPEED:
+		*pf_msg = enetc_msg_handle_link_speed(pf, vf_id, msg);
+		break;
 	default:
 		dev_err_ratelimited(dev,
 				    "Unsupported message class ID: 0x%x\n",
@@ -540,6 +624,7 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
 			dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err);
 			goto err_en_sriov;
 		}
+
 	}
 
 	return num_vfs;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (3 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Prepare for moving enetc_pf_set_vf_mac() into the enetc-pf-common driver
by replacing enetc_pf_set_primary_mac_addr() with enetc_set_si_hw_addr().

This makes the VF primary MAC configuration path generic and allows
future enetc v4 PF driver to reuse the same interface.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc_pf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 8957e74314d1..4a16bc49312b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -255,7 +255,7 @@ static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
 
 	mutex_lock(&vf_state->lock);
 	vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
-	enetc_pf_set_primary_mac_addr(&priv->si->hw, vf + 1, mac);
+	enetc_set_si_hw_addr(pf, vf + 1, mac);
 	mutex_unlock(&vf_state->lock);
 
 	return 0;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (4 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Move enetc_pf_set_vf_mac() into enetc-pf-common driver as a generic
interface for both ENETC v1 and v4 PF driver to use.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc_pf.c   | 22 ------------------
 .../freescale/enetc/enetc_pf_common.c         | 23 +++++++++++++++++++
 .../freescale/enetc/enetc_pf_common.h         |  1 +
 3 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 4a16bc49312b..38fcc7b5fc6d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -239,28 +239,6 @@ static void enetc_set_loopback(struct net_device *ndev, bool en)
 	}
 }
 
-static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
-{
-	struct enetc_ndev_priv *priv = netdev_priv(ndev);
-	struct enetc_pf *pf = enetc_si_priv(priv->si);
-	struct enetc_vf_state *vf_state;
-
-	if (vf >= pf->total_vfs)
-		return -EINVAL;
-
-	if (!is_valid_ether_addr(mac))
-		return -EADDRNOTAVAIL;
-
-	vf_state = &pf->vf_state[vf];
-
-	mutex_lock(&vf_state->lock);
-	vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
-	enetc_set_si_hw_addr(pf, vf + 1, mac);
-	mutex_unlock(&vf_state->lock);
-
-	return 0;
-}
-
 static int enetc_pf_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan,
 				u8 qos, __be16 proto)
 {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index e3bed5580d17..85e1efa6a8ce 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -487,5 +487,28 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
 }
 EXPORT_SYMBOL_GPL(enetc_pf_set_vf_trust);
 
+int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_vf_state *vf_state;
+
+	if (vf >= pf->total_vfs)
+		return -EINVAL;
+
+	if (!is_valid_ether_addr(mac))
+		return -EADDRNOTAVAIL;
+
+	vf_state = &pf->vf_state[vf];
+
+	mutex_lock(&vf_state->lock);
+	vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
+	enetc_set_si_hw_addr(pf, vf + 1, mac);
+	mutex_unlock(&vf_state->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_set_vf_mac);
+
 MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index bbe21c739cf7..b2a2b06c794a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -18,6 +18,7 @@ int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid);
 int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid);
 int enetc_init_sriov_resources(struct enetc_pf *pf);
 int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting);
+int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac);
 
 static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (5 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Add .ndo_set_vf_mac() to the enetc v4 driver to configure the MAC
addresses of VFs.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc4_pf.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 4b69c19b2319..d310506a6fd4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -575,6 +575,7 @@ static const struct net_device_ops enetc4_ndev_ops = {
 	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
 	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
 	.ndo_set_vf_trust	= enetc_pf_set_vf_trust,
+	.ndo_set_vf_mac		= enetc_pf_set_vf_mac,
 };
 
 static struct phylink_pcs *
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (6 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

The mac_filter array currently resides in struct enetc_pf and is used to
track unicast and multicast MAC address filters for the PF. Since struct
enetc_si is the common structure shared between the PF and VF drivers,
move mac_filter into struct enetc_si to prepare for MAC filter support
in the VF driver.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.h     |  1 +
 drivers/net/ethernet/freescale/enetc/enetc4_pf.c |  5 +++--
 drivers/net/ethernet/freescale/enetc/enetc_pf.c  | 15 +++++++--------
 drivers/net/ethernet/freescale/enetc/enetc_pf.h  |  2 --
 4 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 2cd035773aca..24d9f89aee73 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -332,6 +332,7 @@ struct enetc_si {
 
 	struct work_struct msg_task;
 	char msg_int_name[ENETC_INT_NAME_MAX];
+	struct enetc_mac_filter mac_filter[MADDR_TYPE];
 };
 
 #define ENETC_SI_ALIGN	32
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index d310506a6fd4..77043fa01782 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -225,11 +225,12 @@ static void enetc4_pf_set_mac_hash_filter(struct enetc_pf *pf, int type)
 	struct net_device *ndev = pf->si->ndev;
 	struct enetc_mac_filter *mac_filter;
 	struct enetc_hw *hw = &pf->si->hw;
+	struct enetc_si *si = pf->si;
 	struct netdev_hw_addr *ha;
 
 	netif_addr_lock_bh(ndev);
 	if (type & ENETC_MAC_FILTER_TYPE_UC) {
-		mac_filter = &pf->mac_filter[UC];
+		mac_filter = &si->mac_filter[UC];
 		enetc_reset_mac_addr_filter(mac_filter);
 		netdev_for_each_uc_addr(ha, ndev)
 			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
@@ -239,7 +240,7 @@ static void enetc4_pf_set_mac_hash_filter(struct enetc_pf *pf, int type)
 	}
 
 	if (type & ENETC_MAC_FILTER_TYPE_MC) {
-		mac_filter = &pf->mac_filter[MC];
+		mac_filter = &si->mac_filter[MC];
 		enetc_reset_mac_addr_filter(mac_filter);
 		netdev_for_each_mc_addr(ha, ndev)
 			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 38fcc7b5fc6d..209a3503609f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -111,10 +111,9 @@ static void enetc_set_mac_ht_flt(struct enetc_si *si, int si_idx, int type,
 	}
 }
 
-static void enetc_sync_mac_filters(struct enetc_pf *pf)
+static void enetc_sync_mac_filters(struct enetc_si *si)
 {
-	struct enetc_mac_filter *f = pf->mac_filter;
-	struct enetc_si *si = pf->si;
+	struct enetc_mac_filter *f = si->mac_filter;
 	int i, pos;
 
 	pos = EMETC_MAC_ADDR_FILT_RES;
@@ -158,10 +157,10 @@ static void enetc_sync_mac_filters(struct enetc_pf *pf)
 static void enetc_pf_set_rx_mode(struct net_device *ndev)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
-	struct enetc_pf *pf = enetc_si_priv(priv->si);
-	struct enetc_hw *hw = &priv->si->hw;
 	bool uprom = false, mprom = false;
 	struct enetc_mac_filter *filter;
+	struct enetc_si *si = priv->si;
+	struct enetc_hw *hw = &si->hw;
 	struct netdev_hw_addr *ha;
 	u32 psipmr = 0;
 	bool em;
@@ -180,7 +179,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
 	/* first 2 filter entries belong to PF */
 	if (!uprom) {
 		/* Update unicast filters */
-		filter = &pf->mac_filter[UC];
+		filter = &si->mac_filter[UC];
 		enetc_reset_mac_addr_filter(filter);
 
 		em = (netdev_uc_count(ndev) == 1);
@@ -196,7 +195,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
 
 	if (!mprom) {
 		/* Update multicast filters */
-		filter = &pf->mac_filter[MC];
+		filter = &si->mac_filter[MC];
 		enetc_reset_mac_addr_filter(filter);
 
 		netdev_for_each_mc_addr(ha, ndev) {
@@ -209,7 +208,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
 
 	if (!uprom || !mprom)
 		/* update PF entries */
-		enetc_sync_mac_filters(pf);
+		enetc_sync_mac_filters(si);
 
 	psipmr |= enetc_port_rd(hw, ENETC_PSIPMR) &
 		  ~(ENETC_PSIPMR_SET_UP(0) | ENETC_PSIPMR_SET_MP(0));
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index a5c13a4cc197..22b98e89e393 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -41,8 +41,6 @@ struct enetc_pf {
 	int num_vfs; /* number of active VFs, after sriov_init */
 	int total_vfs; /* max number of VFs, set for PF at probe */
 	struct enetc_vf_state *vf_state;
-
-	struct enetc_mac_filter mac_filter[MADDR_TYPE];
 	struct enetc_msg_swbd *rxmsg;
 
 	char vlan_promisc_simap; /* bitmap of SIs in VLAN promisc mode */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (7 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

The ENETC v4 VF hardware supports MAC address filtering, but the
underlying hardware resources (PSIPMMR register and per-SI hash filter
tables) are owned and managed exclusively by the PF driver. Add
VSI-to-PSI mailbox message support so that VFs can request MAC filter
configuration from the PF.

Two new command IDs are introduced under the existing MAC filter message
class (0x20):

1. ENETC_MSG_SET_MAC_HASH_TABLE (cmd_id 3): allows a trusted VF to
program its unicast and/or multicast MAC hash filter table. The PF
validates that the hardware-supported 64-bit table size is requested
before applying the configuration via the per-SI hash filter registers.

2. ENETC_MSG_SET_MAC_PROMISC_MODE (cmd_id 5): allows a VF to enable or
disable unicast/multicast promiscuous mode, and optionally flush the
associated hash filter table. Enabling promiscuous mode requires the VF
to be marked as trusted, since it widens the traffic received by the VF.
Flushing the hash table without enabling promiscuous mode does not
require elevated privilege.

To accommodate independent per-type control, refactor
enetc4_pf_set_si_mac_promisc() to accept an enetc_mac_addr_type enum
and a single enable flag instead of two separate boolean parameters.
This allows callers to set unicast and multicast promiscuous modes in
separate steps.

The PSIPMMR register holds promiscuous mode bits for all SIs and is
modified by both the PF rx_mode workqueue (enetc4_psi_do_set_rx_mode)
and the VF message handler workqueue (enetc_msg_task). Since both
workqueues can run concurrently on SMP systems and
enetc4_pf_set_si_mac_promisc() performs a non-atomic read-modify-write,
protect all accesses to this register with pf->msg_lock to prevent lost
updates.

When a VF loses trusted status via ndo_set_vf_trust(), its unicast hash
filter is cleared and promiscuous mode is disabled to prevent it from
receiving traffic beyond its allowed scope.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  49 ++++--
 .../ethernet/freescale/enetc/enetc_mailbox.h  |  42 +++++
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 159 +++++++++++++++++-
 .../net/ethernet/freescale/enetc/enetc_pf.h   |   4 +
 .../freescale/enetc/enetc_pf_common.c         |  19 ++-
 .../net/ethernet/freescale/enetc/enetc_vf.c   |   6 +-
 6 files changed, 253 insertions(+), 26 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 77043fa01782..39257d364d4e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -12,11 +12,6 @@
 
 #define ENETC_SI_MAX_RING_NUM	8
 
-#define ENETC_MAC_FILTER_TYPE_UC	BIT(0)
-#define ENETC_MAC_FILTER_TYPE_MC	BIT(1)
-#define ENETC_MAC_FILTER_TYPE_ALL	(ENETC_MAC_FILTER_TYPE_UC | \
-					 ENETC_MAC_FILTER_TYPE_MC)
-
 static void enetc4_get_port_caps(struct enetc_pf *pf)
 {
 	struct enetc_hw *hw = &pf->si->hw;
@@ -76,19 +71,22 @@ static void enetc4_pf_get_si_primary_mac(struct enetc_hw *hw, int si,
 }
 
 static void enetc4_pf_set_si_mac_promisc(struct enetc_hw *hw, int si,
-					 bool uc_promisc, bool mc_promisc)
+					 enum enetc_mac_addr_type type,
+					 bool en)
 {
 	u32 val = enetc_port_rd(hw, ENETC4_PSIPMMR);
 
-	if (uc_promisc)
-		val |= PSIPMMR_SI_MAC_UP(si);
-	else
-		val &= ~PSIPMMR_SI_MAC_UP(si);
-
-	if (mc_promisc)
-		val |= PSIPMMR_SI_MAC_MP(si);
-	else
-		val &= ~PSIPMMR_SI_MAC_MP(si);
+	if (type == UC) {
+		if (en)
+			val |= PSIPMMR_SI_MAC_UP(si);
+		else
+			val &= ~PSIPMMR_SI_MAC_UP(si);
+	} else if (type == MC) {
+		if (en)
+			val |= PSIPMMR_SI_MAC_MP(si);
+		else
+			val &= ~PSIPMMR_SI_MAC_MP(si);
+	}
 
 	enetc_port_wr(hw, ENETC4_PSIPMMR, val);
 }
@@ -107,6 +105,16 @@ static void enetc4_pf_set_si_mc_hash_filter(struct enetc_hw *hw, int si,
 	enetc_port_wr(hw, ENETC4_PSIMMHFR1(si), upper_32_bits(hash));
 }
 
+static void enetc4_pf_set_si_mac_hash_filter(struct enetc_hw *hw, int si,
+					     enum enetc_mac_addr_type type,
+					     u64 hash)
+{
+	if (type == UC)
+		enetc4_pf_set_si_uc_hash_filter(hw, si, hash);
+	else if (type == MC)
+		enetc4_pf_set_si_mc_hash_filter(hw, si, hash);
+}
+
 static void enetc4_pf_set_loopback(struct net_device *ndev, bool en)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
@@ -273,6 +281,8 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type)
 static const struct enetc_pf_ops enetc4_pf_ops = {
 	.set_si_primary_mac = enetc4_pf_set_si_primary_mac,
 	.get_si_primary_mac = enetc4_pf_get_si_primary_mac,
+	.set_si_mac_promisc = enetc4_pf_set_si_mac_promisc,
+	.set_si_mac_hash_filter = enetc4_pf_set_si_mac_hash_filter,
 };
 
 static int enetc4_pf_struct_init(struct enetc_si *si)
@@ -517,7 +527,14 @@ static void enetc4_psi_do_set_rx_mode(struct work_struct *work)
 		type = ENETC_MAC_FILTER_TYPE_ALL;
 	}
 
-	enetc4_pf_set_si_mac_promisc(hw, 0, uc_promisc, mc_promisc);
+	if (pf->total_vfs)
+		mutex_lock(&pf->msg_lock);
+
+	enetc4_pf_set_si_mac_promisc(hw, 0, UC, uc_promisc);
+	enetc4_pf_set_si_mac_promisc(hw, 0, MC, mc_promisc);
+
+	if (pf->total_vfs)
+		mutex_unlock(&pf->msg_lock);
 
 	if (uc_promisc) {
 		enetc4_pf_set_si_uc_hash_filter(hw, 0, 0);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index 46446330e222..69657ea2b1c3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -91,6 +91,17 @@
 #define ENETC_PF_MSG_CLASS_CODE_U8		GENMASK(7, 0)
 #define ENETC_PF_MSG_CLASS_ID			GENMASK(15, 8)
 
+#define ENETC_MAC_HASH_TABLE_SIZE_64		0
+#define ENETC_MSG_MAC_HASH_SIZE			GENMASK(5, 0)
+#define ENETC_MSG_MAC_TYPE			GENMASK(7, 6)
+#define  ENETC_MAC_FILTER_TYPE_UC		BIT(0)
+#define  ENETC_MAC_FILTER_TYPE_MC		BIT(1)
+#define  ENETC_MAC_FILTER_TYPE_ALL		(ENETC_MAC_FILTER_TYPE_UC | \
+						 ENETC_MAC_FILTER_TYPE_MC)
+
+#define ENETC_MSG_MAC_FLUSH_MACS		BIT(0)
+#define ENETC_MSG_MAC_PROMISC_MODE		BIT(1)
+
 enum enetc_msg_class_id {
 	/* Class ID for PSI-to-VSI messages */
 	ENETC_MSG_CLASS_ID_CMD_SUCCESS		= 1,
@@ -114,6 +125,8 @@ enum enetc_msg_class_id {
 
 enum enetc_msg_mac_filter_cmd_id {
 	ENETC_MSG_SET_PRIMARY_MAC,
+	ENETC_MSG_SET_MAC_HASH_TABLE		= 3,
+	ENETC_MSG_SET_MAC_PROMISC_MODE		= 5,
 };
 
 enum enetc_msg_ip_revision_cmd_id {
@@ -136,6 +149,9 @@ enum enetc_msg_link_speed_cmd_id {
 /* Class-specific error return codes of MAC filter */
 enum enetc_mac_filter_class_code {
 	ENETC_MF_CLASS_CODE_INVALID_MAC,
+	ENETC_MF_CLASS_CODE_INVALID_TYPE	= 4,
+	/* Unicast Filter Is Denied */
+	ENETC_MF_CLASS_CODE_UCF_DENY		= 5,
 };
 
 /* Class-specific notifications/codes of link status */
@@ -196,6 +212,32 @@ struct enetc_msg_mac_exact_filter {
 	struct enetc_mac_addr mac[];
 };
 
+/* message format of class_id 0x20 for hash MAC filter.
+ * cmd_id 0x3: set MAC hash table
+ */
+struct enetc_msg_mac_hash_filter {
+	struct enetc_msg_header hdr;
+	/* bit 0 ~ 5: ENETC_MSG_MAC_HASH_SIZE
+	 * bit 6~7: ENETC_MSG_MAC_TYPE
+	 */
+	u8 sz_type;
+	u8 resv[3];
+	u32 hash_tbl[];
+};
+
+/* message format of class_id 0x20 for MAC promiscuous mode.
+ * cmd_id 0x5: set MAC promiscuous mode
+ */
+struct enetc_msg_mac_promisc_mode {
+	struct enetc_msg_header hdr;
+	/* bit 0: ENETC_MSG_MAC_FLUSH_MACS
+	 * bit 1: ENETC_MSG_MAC_PROMISC_MODE
+	 * bit 6~7: ENETC_MSG_MAC_TYPE
+	 */
+	u8 config;
+	u8 resv[15];
+};
+
 /* The generic message format applies to the following messages:
  * Get IP revision message, class_id 0xf0.
  * cmd_id 1: get IP minor revision
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index b24903b23a61..abf0f4d9aeac 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -12,6 +12,11 @@
 #define ENETC_PF_MSG_SPEED(s)	(FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
 					    ENETC_MSG_CLASS_ID_LINK_SPEED) | \
 				 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, (s)))
+#define ENETC_PF_MSG_INV_LEN	FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+					   ENETC_MSG_CLASS_ID_INVALID_MSG_LEN)
+#define ENETC_PF_MSG_MF(code)	(FIELD_PREP(ENETC_PF_MSG_CLASS_ID, \
+					    ENETC_MSG_CLASS_ID_MAC_FILTER) | \
+				 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, (code)))
 
 static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
 {
@@ -82,10 +87,7 @@ static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
 	if (!is_valid_ether_addr(addr)) {
 		dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
 				    vf_id);
-		pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
-				    ENETC_MSG_CLASS_ID_MAC_FILTER) |
-			 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE,
-				    ENETC_MF_CLASS_CODE_INVALID_MAC);
+		pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_MAC);
 		goto vf_state_unlock;
 	}
 
@@ -111,6 +113,139 @@ static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
 	return pf_msg;
 }
 
+static u16 enetc_msg_set_vf_mac_hash_filter(struct enetc_pf *pf, int vf_id,
+					    void *vf_msg)
+{
+	struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+	struct enetc_msg_mac_hash_filter *msg = vf_msg;
+	struct enetc_hw *hw = &pf->si->hw;
+	u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+	int si_id = vf_id + 1;
+	u64 uc_hash, mc_hash;
+	bool trusted;
+	int type;
+
+	if (!pf->ops->set_si_mac_hash_filter)
+		return ENETC_PF_MSG_NOTSUPP;
+
+	/* Currently, hardware only supports 64 bits table size */
+	if (FIELD_GET(ENETC_MSG_MAC_HASH_SIZE, msg->sz_type) !=
+	    ENETC_MAC_HASH_TABLE_SIZE_64)
+		return ENETC_PF_MSG_NOTSUPP;
+
+	mutex_lock(&vf_state->lock);
+
+	/* For an untrusted VF, unicast MAC hash filtering is not permitted.
+	 * For multicast, the MAC hash filter is strictly limited to a maximum
+	 * of 8 bits to satisfy its basic multicast communication requirements
+	 * while preventing potential network abuse.
+	 */
+	trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
+	type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->sz_type);
+	switch (type) {
+	case ENETC_MAC_FILTER_TYPE_UC:
+		if (!trusted) {
+			pf_msg = ENETC_PF_MSG_PERM_DENY;
+			goto vf_state_unlock;
+		}
+
+		uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+		pf->ops->set_si_mac_hash_filter(hw, si_id, UC, uc_hash);
+		break;
+	case ENETC_MAC_FILTER_TYPE_MC:
+		mc_hash = (u64)msg->hash_tbl[3] << 32 | msg->hash_tbl[2];
+		if (!trusted && hweight64(mc_hash) > 8) {
+			pf_msg = ENETC_PF_MSG_PERM_DENY;
+			goto vf_state_unlock;
+		}
+
+		pf->ops->set_si_mac_hash_filter(hw, si_id, MC, mc_hash);
+		break;
+	case ENETC_MAC_FILTER_TYPE_ALL:
+		if (!msg->hdr.len) {
+			pf_msg = ENETC_PF_MSG_INV_LEN;
+			goto vf_state_unlock;
+		}
+
+		uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+		mc_hash = (u64)msg->hash_tbl[3] << 32 | msg->hash_tbl[2];
+
+		if (!trusted && (hweight64(mc_hash) <= 8)) {
+			pf->ops->set_si_mac_hash_filter(hw, si_id, MC, mc_hash);
+			pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_UCF_DENY);
+			goto vf_state_unlock;
+		}
+
+		if (!trusted) {
+			pf_msg = ENETC_PF_MSG_PERM_DENY;
+			goto vf_state_unlock;
+		}
+
+		pf->ops->set_si_mac_hash_filter(hw, si_id, UC, uc_hash);
+		pf->ops->set_si_mac_hash_filter(hw, si_id, MC, mc_hash);
+		break;
+	default:
+		pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_TYPE);
+	}
+
+vf_state_unlock:
+	mutex_unlock(&vf_state->lock);
+
+	return pf_msg;
+}
+
+static u16 enetc_msg_set_vf_mac_promisc_mode(struct enetc_pf *pf, int vf_id,
+					     void *vf_msg)
+{
+	struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+	struct enetc_msg_mac_promisc_mode *msg = vf_msg;
+	u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+	struct enetc_hw *hw = &pf->si->hw;
+	bool promisc, flush_macs;
+	int si_id = vf_id + 1;
+	int type;
+
+	if (!pf->ops->set_si_mac_promisc)
+		return ENETC_PF_MSG_NOTSUPP;
+
+	flush_macs = !!(msg->config & ENETC_MSG_MAC_FLUSH_MACS);
+	if (flush_macs && !pf->ops->set_si_mac_hash_filter)
+		return ENETC_PF_MSG_NOTSUPP;
+
+	type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->config);
+	if (!type)
+		return ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_INVALID_TYPE);
+
+	mutex_lock(&vf_state->lock);
+
+	promisc = !!(msg->config & ENETC_MSG_MAC_PROMISC_MODE);
+	if (promisc && !(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+		pf_msg = ENETC_PF_MSG_PERM_DENY;
+		goto vf_state_unlock;
+	}
+
+	mutex_lock(&pf->msg_lock);
+
+	if (type & ENETC_MAC_FILTER_TYPE_UC)
+		pf->ops->set_si_mac_promisc(hw, si_id, UC, promisc);
+
+	if (type & ENETC_MAC_FILTER_TYPE_MC)
+		pf->ops->set_si_mac_promisc(hw, si_id, MC, promisc);
+
+	mutex_unlock(&pf->msg_lock);
+
+	if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
+		pf->ops->set_si_mac_hash_filter(hw, si_id, UC, 0);
+
+	if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
+		pf->ops->set_si_mac_hash_filter(hw, si_id, MC, 0);
+
+vf_state_unlock:
+	mutex_unlock(&vf_state->lock);
+
+	return pf_msg;
+}
+
 static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
 				       void *vf_msg)
 {
@@ -119,6 +254,10 @@ static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
 	switch (msg_hdr->cmd_id) {
 	case ENETC_MSG_SET_PRIMARY_MAC:
 		return enetc_msg_set_vf_primary_mac_addr(pf, vf_id, vf_msg);
+	case ENETC_MSG_SET_MAC_HASH_TABLE:
+		return enetc_msg_set_vf_mac_hash_filter(pf, vf_id, vf_msg);
+	case ENETC_MSG_SET_MAC_PROMISC_MODE:
+		return enetc_msg_set_vf_mac_promisc_mode(pf, vf_id, vf_msg);
 	default:
 		return ENETC_PF_MSG_NOTSUPP;
 	}
@@ -371,8 +510,7 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 	if (msg_size > ENETC_DEFAULT_MSG_SIZE) {
 		dev_err_ratelimited(dev,
 				    "Invalid message size: %u\n", msg_size);
-		*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
-				     ENETC_MSG_CLASS_ID_INVALID_MSG_LEN);
+		*pf_msg = ENETC_PF_MSG_INV_LEN;
 		return;
 	}
 
@@ -390,6 +528,14 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 	}
 
 	memcpy(msg, msg_swbd->vaddr, msg_size);
+	msg_hdr = (struct enetc_msg_header *)msg;
+
+	/* Check message length whether is changed */
+	if (ENETC_MSG_SIZE(msg_hdr->len) != msg_size) {
+		*pf_msg = ENETC_PF_MSG_INV_LEN;
+		goto free_msg;
+	}
+
 	if (!enetc_msg_check_crc16(msg, msg_size)) {
 		dev_err_ratelimited(dev, "VSI to PSI Message CRC16 error\n");
 		*pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
@@ -400,7 +546,6 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 
 	/* Default to not supported */
 	*pf_msg = ENETC_PF_MSG_NOTSUPP;
-	msg_hdr = (struct enetc_msg_header *)msg;
 
 	/* Currently, asynchronous actions are not supported */
 	if (FIELD_GET(ENETC_VF_MSG_COOKIE, msg_hdr->cookie)) {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 22b98e89e393..57591bd5afab 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -34,6 +34,10 @@ struct enetc_pf_ops {
 	struct phylink_pcs *(*create_pcs)(struct enetc_pf *pf, struct mii_bus *bus);
 	void (*destroy_pcs)(struct phylink_pcs *pcs);
 	int (*enable_psfp)(struct enetc_ndev_priv *priv);
+	void (*set_si_mac_promisc)(struct enetc_hw *hw, int si,
+				   enum enetc_mac_addr_type type, bool en);
+	void (*set_si_mac_hash_filter)(struct enetc_hw *hw, int si,
+				       enum enetc_mac_addr_type type, u64 hash);
 };
 
 struct enetc_pf {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 85e1efa6a8ce..f0ae69dcc59a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -468,6 +468,7 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_hw *hw = &pf->si->hw;
 	struct enetc_vf_state *vf_state;
 
 	if (vf >= pf->total_vfs)
@@ -476,11 +477,25 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
 	vf_state = &pf->vf_state[vf];
 	mutex_lock(&vf_state->lock);
 
-	if (setting)
+	if (setting) {
 		vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
-	else
+	} else {
 		vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
 
+		/* Clear unicast hash filter and disable MAC promiscuous modes
+		 * if the VF is untrusted.
+		 */
+		if (pf->ops->set_si_mac_hash_filter)
+			pf->ops->set_si_mac_hash_filter(hw, vf + 1, UC, 0);
+
+		mutex_lock(&pf->msg_lock);
+		if (pf->ops->set_si_mac_promisc) {
+			pf->ops->set_si_mac_promisc(hw, vf + 1, UC, false);
+			pf->ops->set_si_mac_promisc(hw, vf + 1, MC, false);
+		}
+		mutex_unlock(&pf->msg_lock);
+	}
+
 	mutex_unlock(&vf_state->lock);
 
 	return 0;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 9cdb0a4d6baf..e4d0e6c1a2e5 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -107,8 +107,12 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
 		case ENETC_MSG_CLASS_ID_CMD_TIMEOUT:
 			err = -ETIME;
 			break;
-		case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN:
 		case ENETC_MSG_CLASS_ID_MAC_FILTER:
+			if (FIELD_GET(ENETC_PF_MSG_CLASS_CODE, pf_msg) ==
+			    ENETC_MF_CLASS_CODE_UCF_DENY)
+				return -EACCES;
+			fallthrough;
+		case ENETC_MSG_CLASS_ID_INVALID_MSG_LEN:
 			err = -EINVAL;
 			break;
 		case ENETC_MSG_CLASS_ID_CMD_NOT_PERMITTED:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (8 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

The PSIIER register controls two categories of interrupt sources:
message-receive (MR) interrupts, which fire when a VF sends a mailbox
message to the PSI, and VF FLR interrupts, which fire when a VF
performs a Function Level Reset.

The current helpers enetc_msg_enable_mr_int() and
enetc_msg_disable_mr_int() use a read-modify-write sequence to update
only the MR bits in PSIIER, intending to preserve any other bits that
may be set. However, VF FLR interrupt support is not yet implemented,
so PSIIER only ever holds MR interrupt bits at this point. The
read-modify-write is therefore unnecessary overhead.

Simplify enetc_disable_psiier_interrupts() to write 0 directly to
PSIIER, disabling all interrupt sources at once, and simplify
enetc_enable_psiier_interrupts() to write the MR mask directly without
reading the current register value first.

Rename both helpers from the MR-specific names to names that reflect
their true scope, i.e. managing all PSIIER interrupt sources rather
than just the MR bits. This prepares the code for a future patch that
adds VF FLR interrupt support, at which point
enetc_enable_psiier_interrupts() will be extended to also set the
corresponding FLR bits.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 30 ++++++++-----------
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index abf0f4d9aeac..fddc69cb4b90 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -18,23 +18,17 @@
 					    ENETC_MSG_CLASS_ID_MAC_FILTER) | \
 				 FIELD_PREP(ENETC_PF_MSG_CLASS_CODE, (code)))
 
-static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
+static void enetc_disable_psiier_interrupts(struct enetc_pf *pf)
 {
 	struct enetc_hw *hw = &pf->si->hw;
-	u32 psiier;
 
-	psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
-
-	/* disable MR int source(s) */
-	enetc_wr(hw, ENETC_PSIIER, psiier);
+	enetc_wr(hw, ENETC_PSIIER, 0);
 }
 
-static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
+static void enetc_enable_psiier_interrupts(struct enetc_pf *pf)
 {
+	u32 psiier = ENETC_PSIMR_MASK(pf->num_vfs);
 	struct enetc_hw *hw = &pf->si->hw;
-	u32 psiier;
-
-	psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
 
 	enetc_wr(hw, ENETC_PSIIER, psiier);
 }
@@ -44,7 +38,7 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
 	struct enetc_si *si = (struct enetc_si *)data;
 	struct enetc_pf *pf = enetc_si_priv(si);
 
-	enetc_msg_disable_mr_int(pf);
+	enetc_disable_psiier_interrupts(pf);
 	schedule_work(&si->msg_task);
 
 	return IRQ_HANDLED;
@@ -628,7 +622,7 @@ static void enetc_msg_task(struct work_struct *work)
 	}
 
 out:
-	enetc_msg_enable_mr_int(pf);
+	enetc_enable_psiier_interrupts(pf);
 }
 
 /* Init */
@@ -703,8 +697,8 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
 	/* set one IRQ entry for PSI message receive notification (SI int) */
 	enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
 
-	/* enable MR interrupts */
-	enetc_msg_enable_mr_int(pf);
+	/* enable PSIIER interrupts */
+	enetc_enable_psiier_interrupts(pf);
 
 	return 0;
 
@@ -720,16 +714,16 @@ static void enetc_msg_psi_free(struct enetc_pf *pf)
 	struct enetc_si *si = pf->si;
 	int i;
 
-	/* disable MR interrupts */
-	enetc_msg_disable_mr_int(pf);
+	/* disable PSIIER interrupts */
+	enetc_disable_psiier_interrupts(pf);
 
 	/* de-register message passing interrupt handler */
 	free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
 
 	cancel_work_sync(&si->msg_task);
 
-	/* MR interrupts may be re-enabled by workqueue */
-	enetc_msg_disable_mr_int(pf);
+	/* PSIIER interrupts may be re-enabled by workqueue */
+	enetc_disable_psiier_interrupts(pf);
 
 	for (i = 0; i < pf->num_vfs; i++)
 		enetc_msg_free_mbx(si, i);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (9 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

On ENETC v4, a PCIe VF Function Level Reset (FLR) resets
PSIPMMR[SIn_MAC_UP] and PSIPMMR[SIn_MAC_MP] bits, which control the
unicast and multicast promiscuous mode for the corresponding SI. The
reset (default) value of these bits enables promiscuous mode, meaning
that after a VF FLR, the SI is left in promiscuous mode regardless of
the configuration set by the PF driver prior to the reset.

This is a potential security vulnerability: a malicious VM could
deliberately trigger a VF FLR to force promiscuous mode on its SI,
allowing it to capture network traffic not destined for that VF.

To mitigate this, make the following changes:

- Add ENETC_VF_FLAG_UC_PROMISC and ENETC_VF_FLAG_MC_PROMISC to
  enetc_vf_flags to track the PF-managed promiscuous mode state for each
  VF.

- Update enetc_msg_set_vf_mac_promisc_mode() to keep these flags in sync
  whenever a VF requests a promiscuous mode change via messaging.

- Update enetc_pf_set_vf_trust() to clear both promisc flags when a VF
  is untrusted, so that a subsequent FLR cannot restore promiscuous mode
  that the PF has already revoked.

- Add a vf_flr_handler callback to enetc_pf_ops. The ENETC v4
  implementation re-applies the tracked UC/MC promiscuous mode settings
  to the hardware after each FLR, ensuring the hardware state matches
  the PF-managed policy rather than the insecure reset default.

- Add enetc_vf_flr_handler() in enetc_msg.c to detect FLR events via the
  PSIIDR register and dispatch to the vf_flr_handler callback. Invoke it
  at the start of enetc_msg_task() before processing VF messages.

- Enable FLR interrupts in PSIIER only when a vf_flr_handler callback is
  registered, keeping ENETC v1 behavior unchanged.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc4_pf.c  | 21 +++++++
 .../net/ethernet/freescale/enetc/enetc_hw.h   | 12 ++++
 .../net/ethernet/freescale/enetc/enetc_msg.c  | 59 ++++++++++++++++++-
 .../net/ethernet/freescale/enetc/enetc_pf.h   |  3 +
 .../freescale/enetc/enetc_pf_common.c         |  4 +-
 5 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 39257d364d4e..6f7b4f4927e3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -278,11 +278,32 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type)
 		enetc4_pf_set_mac_hash_filter(pf, ENETC_MAC_FILTER_TYPE_MC);
 }
 
+static void enetc4_pf_vf_flr_handler(struct enetc_pf *pf, int vf_id)
+{
+	struct enetc_hw *hw = &pf->si->hw;
+	struct enetc_vf_state *vf_state;
+	bool uc_promisc, mc_promisc;
+
+	vf_state = &pf->vf_state[vf_id];
+	mutex_lock(&vf_state->lock);
+
+	uc_promisc = !!(vf_state->flags & ENETC_VF_FLAG_UC_PROMISC);
+	mc_promisc = !!(vf_state->flags & ENETC_VF_FLAG_MC_PROMISC);
+
+	mutex_lock(&pf->msg_lock);
+	enetc4_pf_set_si_mac_promisc(hw, vf_id + 1, UC, uc_promisc);
+	enetc4_pf_set_si_mac_promisc(hw, vf_id + 1, MC, mc_promisc);
+	mutex_unlock(&pf->msg_lock);
+
+	mutex_unlock(&vf_state->lock);
+}
+
 static const struct enetc_pf_ops enetc4_pf_ops = {
 	.set_si_primary_mac = enetc4_pf_set_si_primary_mac,
 	.get_si_primary_mac = enetc4_pf_get_si_primary_mac,
 	.set_si_mac_promisc = enetc4_pf_set_si_mac_promisc,
 	.set_si_mac_hash_filter = enetc4_pf_set_si_mac_hash_filter,
+	.vf_flr_handler = enetc4_pf_vf_flr_handler,
 };
 
 static int enetc4_pf_struct_init(struct enetc_si *si)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 7b66c5be1ccf..47de179e17c8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -110,6 +110,18 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 
 #define ENETC_PSIIER	0xa00
 #define ENETC_PSIIDR	0xa08
+
+/* VF FLR interrupt mask, n is the active number of VFs.
+ * It is available for ENETC_PSIIER and ENETC_PSIIDR registers.
+ */
+#define ENETC_VFFLR_MASK(n)	\
+	({ typeof(n) _n = (n); (_n) ? GENMASK(16 + (_n), 17) : 0; })
+
+/* VF FLR interrupt bit, n is VF index. It is available
+ * for ENETC_PSIIER and ENETC_PSIIDR registers.
+ */
+#define ENETC_VFFLR_BIT(n)	BIT(17 + (n))
+
 #define ENETC_SITXIDR	0xa18
 #define ENETC_SIRXIDR	0xa28
 #define ENETC_SIMSIVR	0xa30
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index fddc69cb4b90..e6ade6eeac32 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -30,6 +30,9 @@ static void enetc_enable_psiier_interrupts(struct enetc_pf *pf)
 	u32 psiier = ENETC_PSIMR_MASK(pf->num_vfs);
 	struct enetc_hw *hw = &pf->si->hw;
 
+	if (pf->ops->vf_flr_handler)
+		psiier |= ENETC_VFFLR_MASK(pf->num_vfs);
+
 	enetc_wr(hw, ENETC_PSIIER, psiier);
 }
 
@@ -220,11 +223,23 @@ static u16 enetc_msg_set_vf_mac_promisc_mode(struct enetc_pf *pf, int vf_id,
 
 	mutex_lock(&pf->msg_lock);
 
-	if (type & ENETC_MAC_FILTER_TYPE_UC)
+	if (type & ENETC_MAC_FILTER_TYPE_UC) {
+		if (promisc)
+			vf_state->flags |= ENETC_VF_FLAG_UC_PROMISC;
+		else
+			vf_state->flags &= ~ENETC_VF_FLAG_UC_PROMISC;
+
 		pf->ops->set_si_mac_promisc(hw, si_id, UC, promisc);
+	}
+
+	if (type & ENETC_MAC_FILTER_TYPE_MC) {
+		if (promisc)
+			vf_state->flags |= ENETC_VF_FLAG_MC_PROMISC;
+		else
+			vf_state->flags &= ~ENETC_VF_FLAG_MC_PROMISC;
 
-	if (type & ENETC_MAC_FILTER_TYPE_MC)
 		pf->ops->set_si_mac_promisc(hw, si_id, MC, promisc);
+	}
 
 	mutex_unlock(&pf->msg_lock);
 
@@ -589,6 +604,29 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id,
 	kfree(msg);
 }
 
+static void enetc_vf_flr_handler(struct enetc_pf *pf)
+{
+	u32 flr_mask = ENETC_VFFLR_MASK(pf->num_vfs);
+	struct enetc_hw *hw = &pf->si->hw;
+	u32 flr_status;
+
+	if (!pf->ops->vf_flr_handler)
+		return;
+
+	flr_status = enetc_rd(hw, ENETC_PSIIDR) & flr_mask;
+	if (!flr_status)
+		return;
+
+	for (int i = 0; i < pf->num_vfs; i++) {
+		if (!(ENETC_VFFLR_BIT(i) & flr_status))
+			continue;
+
+		/* Clear FLR interrupt status, W1C */
+		enetc_wr(hw, ENETC_PSIIDR, ENETC_VFFLR_BIT(i));
+		pf->ops->vf_flr_handler(pf, i);
+	}
+}
+
 static void enetc_msg_task(struct work_struct *work)
 {
 	struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
@@ -597,6 +635,8 @@ static void enetc_msg_task(struct work_struct *work)
 	u32 mr_status, mr_mask;
 	int i;
 
+	enetc_vf_flr_handler(pf);
+
 	mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
 	mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
 		    (enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
@@ -725,8 +765,21 @@ static void enetc_msg_psi_free(struct enetc_pf *pf)
 	/* PSIIER interrupts may be re-enabled by workqueue */
 	enetc_disable_psiier_interrupts(pf);
 
-	for (i = 0; i < pf->num_vfs; i++)
+	for (i = 0; i < pf->num_vfs; i++) {
+		struct enetc_vf_state *vf_state = &pf->vf_state[i];
+
 		enetc_msg_free_mbx(si, i);
+
+		/* VF may set these flags by mailbox messages, so need to
+		 * clear these flags when enetc_msg_psi_free() is called.
+		 * Flags set by PF are cleared, because these flags are
+		 * unrelated to whether SR-IOV is enabled or disabled.
+		 */
+		mutex_lock(&vf_state->lock);
+		vf_state->flags &= ~(ENETC_VF_FLAG_UC_PROMISC |
+				     ENETC_VF_FLAG_MC_PROMISC);
+		mutex_unlock(&vf_state->lock);
+	}
 }
 
 int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 57591bd5afab..378ca4464538 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -10,6 +10,8 @@
 enum enetc_vf_flags {
 	ENETC_VF_FLAG_PF_SET_MAC	= BIT(0),
 	ENETC_VF_FLAG_TRUSTED		= BIT(1),
+	ENETC_VF_FLAG_UC_PROMISC	= BIT(2),
+	ENETC_VF_FLAG_MC_PROMISC	= BIT(3),
 };
 
 struct enetc_vf_state {
@@ -38,6 +40,7 @@ struct enetc_pf_ops {
 				   enum enetc_mac_addr_type type, bool en);
 	void (*set_si_mac_hash_filter)(struct enetc_hw *hw, int si,
 				       enum enetc_mac_addr_type type, u64 hash);
+	void (*vf_flr_handler)(struct enetc_pf *pf, int vf_id);
 };
 
 struct enetc_pf {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index f0ae69dcc59a..52e7d2d2149b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -480,7 +480,9 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
 	if (setting) {
 		vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
 	} else {
-		vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
+		vf_state->flags &= ~(ENETC_VF_FLAG_TRUSTED |
+				     ENETC_VF_FLAG_UC_PROMISC |
+				     ENETC_VF_FLAG_MC_PROMISC);
 
 		/* Clear unicast hash filter and disable MAC promiscuous modes
 		 * if the VF is untrusted.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (10 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

This patch adds VF support for i.MX94 and i.MX95 platforms.  Compared to
the LS1028A ENETC, the VF device ID has been updated to 0xef00.

On i.MX95 (v4.1), each ENETC instance supports 2 VFs.

The i.MX94 (v4.3) has two types of ENETC with different VF capabilities:
 - standalone ENETC (same as i.MX95): does not support VFs
 - internal ENETC connected to the CPU port of NETC switch: supports 3
   VFs

The driver is updated to recognize these SoC-specific VF capabilities
and handle each ENETC instance accordingly.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/Kconfig  |  1 +
 drivers/net/ethernet/freescale/enetc/enetc.c  | 15 ++++++++++++++
 .../net/ethernet/freescale/enetc/enetc4_hw.h  |  1 +
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  4 ++++
 .../ethernet/freescale/enetc/enetc_ethtool.c  |  6 ++++++
 .../net/ethernet/freescale/enetc/enetc_vf.c   | 20 ++++++++++++++++++-
 6 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig
index db5c17a44613..f425f82a6213 100644
--- a/drivers/net/ethernet/freescale/enetc/Kconfig
+++ b/drivers/net/ethernet/freescale/enetc/Kconfig
@@ -69,6 +69,7 @@ config FSL_ENETC_VF
 	depends on PCI_MSI
 	select FSL_ENETC_CORE
 	select FSL_ENETC_MDIO
+	select NXP_NTMP
 	select PHYLINK
 	select DIMLIB
 	select CRC_ITU_T
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index aa8a87124b10..fdceaf36daa7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -3745,6 +3745,13 @@ static const struct enetc_drvdata enetc_vf_data = {
 	.eth_ops = &enetc_vf_ethtool_ops,
 };
 
+static const struct enetc_drvdata enetc4_vf_data = {
+	.sysclk_freq = ENETC_CLK_333M,
+	.tx_csum = true,
+	.max_frags = ENETC4_MAX_SKB_FRAGS,
+	.eth_ops = &enetc_vf_ethtool_ops,
+};
+
 static const struct enetc_platform_info enetc_info[] = {
 	{ .revision = ENETC_REV_1_0,
 	  .dev_id = ENETC_DEV_ID_PF,
@@ -3758,6 +3765,10 @@ static const struct enetc_platform_info enetc_info[] = {
 	  .dev_id = ENETC_DEV_ID_VF,
 	  .data = &enetc_vf_data,
 	},
+	{ .revision = ENETC_REV_4_1,
+	  .dev_id = NXP_ENETC_VF_DEV_ID,
+	  .data = &enetc4_vf_data,
+	},
 	{
 	  .revision = ENETC_REV_4_3,
 	  .dev_id = NXP_ENETC_PPM_DEV_ID,
@@ -3767,6 +3778,10 @@ static const struct enetc_platform_info enetc_info[] = {
 	  .dev_id = NXP_ENETC_PF_DEV_ID,
 	  .data = &enetc4_pf_data,
 	},
+	{ .revision = ENETC_REV_4_3,
+	  .dev_id = NXP_ENETC_VF_DEV_ID,
+	  .data = &enetc4_vf_data,
+	},
 };
 
 int enetc_get_driver_data(struct enetc_si *si)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index f18437556a0e..7595c7d80d0d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -12,6 +12,7 @@
 #define NXP_ENETC_VENDOR_ID		0x1131
 #define NXP_ENETC_PF_DEV_ID		0xe101
 #define NXP_ENETC_PPM_DEV_ID		0xe110
+#define NXP_ENETC_VF_DEV_ID		0xef00
 
 /**********************Station interface registers************************/
 /* Station interface LSO segmentation flag mask register 0/1 */
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 6f7b4f4927e3..c8059c67119c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -1211,6 +1211,9 @@ static void enetc4_pf_remove(struct pci_dev *pdev)
 	struct enetc_si *si = pci_get_drvdata(pdev);
 	struct enetc_pf *pf = enetc_si_priv(si);
 
+	if (pf->num_vfs)
+		enetc_sriov_configure(pdev, 0);
+
 	enetc_remove_debugfs(si);
 	enetc4_pf_netdev_destroy(si);
 	enetc4_pf_free(pf);
@@ -1228,6 +1231,7 @@ static struct pci_driver enetc4_pf_driver = {
 	.id_table = enetc4_pf_id_table,
 	.probe = enetc4_pf_probe,
 	.remove = enetc4_pf_remove,
+	.sriov_configure = enetc_sriov_configure,
 };
 module_pci_driver(enetc4_pf_driver);
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 71f376ef1be1..246852e453a0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -859,6 +859,9 @@ static int enetc_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	int i, j;
 
+	if (!is_enetc_rev1(priv->si))
+		return -EOPNOTSUPP;
+
 	switch (rxnfc->cmd) {
 	case ETHTOOL_GRXCLSRLCNT:
 		/* total number of entries */
@@ -903,6 +906,9 @@ static int enetc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc)
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	int err;
 
+	if (!is_enetc_rev1(priv->si))
+		return -EOPNOTSUPP;
+
 	switch (rxnfc->cmd) {
 	case ETHTOOL_SRXCLSRLINS:
 		if (rxnfc->fs.location >= priv->si->num_fs_entries)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index e4d0e6c1a2e5..4bc48a5f6869 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -285,6 +285,12 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
 		ndev->features |= NETIF_F_RXHASH;
 	}
 
+	if (si->drvdata->tx_csum)
+		priv->active_offloads |= ENETC_F_TXCSUM;
+
+	if (si->hw_features & ENETC_SI_F_LSO)
+		priv->active_offloads |= ENETC_F_LSO;
+
 	/* pick up primary MAC address from SI */
 	enetc_load_primary_mac_addr(&si->hw, ndev);
 }
@@ -296,6 +302,13 @@ static const struct enetc_si_ops enetc_vsi_ops = {
 	.teardown_cbdr = enetc_teardown_cbdr,
 };
 
+static const struct enetc_si_ops enetc4_vsi_ops = {
+	.get_rss_table = enetc4_get_rss_table,
+	.set_rss_table = enetc4_set_rss_table,
+	.setup_cbdr = enetc4_setup_cbdr,
+	.teardown_cbdr = enetc4_teardown_cbdr,
+};
+
 static int enetc_vf_probe(struct pci_dev *pdev,
 			  const struct pci_device_id *ent)
 {
@@ -311,7 +324,11 @@ static int enetc_vf_probe(struct pci_dev *pdev,
 
 	si = pci_get_drvdata(pdev);
 	enetc_vf_get_revision(si);
-	si->ops = &enetc_vsi_ops;
+	if (is_enetc_rev1(si))
+		si->ops = &enetc_vsi_ops;
+	else
+		si->ops = &enetc4_vsi_ops;
+
 	err = enetc_get_driver_data(si);
 	if (err) {
 		dev_err_probe(&pdev->dev, err,
@@ -406,6 +423,7 @@ static void enetc_vf_remove(struct pci_dev *pdev)
 
 static const struct pci_device_id enetc_vf_id_table[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_VF) },
+	{ PCI_DEVICE(NXP_ENETC_VENDOR_ID, NXP_ENETC_VF_DEV_ID) },
 	{ 0, } /* End of table. */
 };
 MODULE_DEVICE_TABLE(pci, enetc_vf_id_table);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (11 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
  2026-06-10  9:18 ` [PATCH v2 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

The ENETC VF communicates MAC filter changes to the PF driver via a VSI
mailbox interface. The message send path in enetc_msg_vsi_send() polls
for completion with a timeout up to 200ms, which requires a sleepable
context.

The legacy ndo_set_rx_mode callback is invoked with netif_addr_lock_bh
held and BH disabled, making it incompatible with the VSI messaging path.
Implement ndo_set_rx_mode_async instead, which runs from a workqueue with
rtnl_lock held in a fully sleepable context, and receives pre-snapshotted
unicast and multicast address lists from the networking core.

Add enetc_vf_set_mac_promisc() to send a MAC promiscuous mode message to
the PF. The message specifies the filter type (unicast, multicast, or
both) and whether to enable promiscuous mode and clear existing MAC hash
filter.

Add enetc_vf_set_mac_hash_filter() to send a 64-bit Bloom filter hash
table to the PF. Each filter type (UC or MC) contributes two u32 entries
representing the low and high halves of its 64-bit hash bitmap. The
function accepts pre-snapshotted address lists from the framework and
iterates them with netdev_hw_addr_list_for_each(). When IFF_PROMISC is
active, hash filter programming is skipped since promiscuous mode already
accepts all frames.

The ndo_set_rx_mode_async callback selects the appropriate filter
configuration based on the current netdev flags:

 - IFF_PROMISC: enable full promiscuous mode for both unicast and
   multicast
 - IFF_ALLMULTI: enable multicast promiscuous mode, disable unicast
   promiscuous mode, and apply a unicast hash filter
 - otherwise: disable all promiscuous modes and apply both
              unicast and multicast hash filters

Set IFF_UNICAST_FLT in priv_flags for ENETC v4 VFs so the network stack
does not fall back to full promiscuous mode unnecessarily when unicast
address filtering is supported by the hardware.

This feature applies to ENETC v4 hardware only. ENETC v1 (LS1028A) does
not support VF-PF MAC filter messaging and the callback returns early
for such devices.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc_vf.c   | 120 ++++++++++++++++++
 1 file changed, 120 insertions(+)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 4bc48a5f6869..dc9a0f74a2e3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -213,6 +213,122 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
 	}
 }
 
+static int enetc_vf_set_mac_promisc(struct enetc_ndev_priv *priv,
+				    int type, bool en)
+{
+	struct enetc_msg_mac_promisc_mode *msg;
+	struct enetc_msg_swbd msg_swbd;
+
+	if (!(type & ENETC_MAC_FILTER_TYPE_ALL))
+		return -EINVAL;
+
+	msg_swbd.size = ALIGN(sizeof(*msg), ENETC_MSG_ALIGN);
+	msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size,
+					    &msg_swbd.dma, GFP_KERNEL);
+	if (!msg_swbd.vaddr)
+		return -ENOMEM;
+
+	msg = (struct enetc_msg_mac_promisc_mode *)msg_swbd.vaddr;
+	msg->config = FIELD_PREP(ENETC_MSG_MAC_TYPE,
+				 type & ENETC_MAC_FILTER_TYPE_ALL);
+	msg->config |= FIELD_PREP(ENETC_MSG_MAC_PROMISC_MODE, en);
+	msg->config |= FIELD_PREP(ENETC_MSG_MAC_FLUSH_MACS, en);
+	enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
+				  ENETC_MSG_SET_MAC_PROMISC_MODE, 0, 0);
+
+	return enetc_msg_vsi_send(priv->si, &msg_swbd);
+}
+
+static int enetc_vf_set_mac_hash_filter(struct enetc_ndev_priv *priv,
+					struct netdev_hw_addr_list *uc,
+					struct netdev_hw_addr_list *mc)
+{
+	struct enetc_msg_mac_hash_filter *msg;
+	struct net_device *ndev = priv->ndev;
+	struct enetc_mac_filter *mac_filter;
+	struct enetc_msg_swbd msg_swbd;
+	struct enetc_si *si = priv->si;
+	struct netdev_hw_addr *ha;
+	u32 msg_size, tbl_cnt;
+	int mac_filter_type;
+	u64 hash_val;
+	int i = 0;
+
+	if (ndev->flags & IFF_PROMISC)
+		return 0;
+
+	if (ndev->flags & IFF_ALLMULTI) {
+		tbl_cnt = 2;
+		mac_filter_type = ENETC_MAC_FILTER_TYPE_UC;
+	} else {
+		tbl_cnt = 4;
+		mac_filter_type = ENETC_MAC_FILTER_TYPE_ALL;
+	}
+
+	msg_size = struct_size(msg, hash_tbl, tbl_cnt);
+	msg_swbd.size = ALIGN(msg_size, ENETC_MSG_ALIGN);
+	msg_swbd.vaddr = dma_alloc_coherent(priv->dev, msg_swbd.size,
+					    &msg_swbd.dma, GFP_KERNEL);
+	if (!msg_swbd.vaddr)
+		return -ENOMEM;
+
+	msg = (struct enetc_msg_mac_hash_filter *)msg_swbd.vaddr;
+	msg->sz_type = FIELD_PREP(ENETC_MSG_MAC_TYPE, mac_filter_type);
+	msg->sz_type |= FIELD_PREP(ENETC_MSG_MAC_HASH_SIZE,
+				   ENETC_MAC_HASH_TABLE_SIZE_64);
+
+	if (mac_filter_type & ENETC_MAC_FILTER_TYPE_UC) {
+		mac_filter = &si->mac_filter[UC];
+		enetc_reset_mac_addr_filter(mac_filter);
+		netdev_hw_addr_list_for_each(ha, uc)
+			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
+
+		memcpy(&hash_val, mac_filter->mac_hash_table,
+		       sizeof(hash_val));
+		msg->hash_tbl[i++] = hash_val & GENMASK(31, 0);
+		msg->hash_tbl[i++] = hash_val >> 32;
+	}
+
+	if (mac_filter_type & ENETC_MAC_FILTER_TYPE_MC) {
+		mac_filter = &si->mac_filter[MC];
+		enetc_reset_mac_addr_filter(mac_filter);
+		netdev_hw_addr_list_for_each(ha, mc)
+			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
+
+		memcpy(&hash_val, mac_filter->mac_hash_table,
+		       sizeof(hash_val));
+		msg->hash_tbl[i++] = hash_val & GENMASK(31, 0);
+		msg->hash_tbl[i++] = hash_val >> 32;
+	}
+
+	enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
+				  ENETC_MSG_SET_MAC_HASH_TABLE, 0, 0);
+
+	return enetc_msg_vsi_send(si, &msg_swbd);
+}
+
+static void enetc_vf_set_rx_mode(struct net_device *ndev,
+				 struct netdev_hw_addr_list *uc,
+				 struct netdev_hw_addr_list *mc)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_si *si = priv->si;
+
+	if (is_enetc_rev1(si))
+		return;
+
+	if (ndev->flags & IFF_PROMISC) {
+		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_ALL, true);
+	} else if (ndev->flags & IFF_ALLMULTI) {
+		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_MC, true);
+		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_UC, false);
+	} else {
+		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_ALL, false);
+	}
+
+	enetc_vf_set_mac_hash_filter(priv, uc, mc);
+}
+
 /* Probing/ Init */
 static const struct net_device_ops enetc_ndev_ops = {
 	.ndo_open		= enetc_open,
@@ -225,6 +341,7 @@ static const struct net_device_ops enetc_ndev_ops = {
 	.ndo_setup_tc		= enetc_vf_setup_tc,
 	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
 	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
+	.ndo_set_rx_mode_async	= enetc_vf_set_rx_mode,
 };
 
 static void enetc_vf_get_revision(struct enetc_si *si)
@@ -280,6 +397,9 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
 	ndev->vlan_features = NETIF_F_SG | NETIF_F_HW_CSUM |
 			      NETIF_F_TSO | NETIF_F_TSO6;
 
+	if (!is_enetc_rev1(si))
+		ndev->priv_flags |= IFF_UNICAST_FLT;
+
 	if (si->num_rss) {
 		ndev->hw_features |= NETIF_F_RXHASH;
 		ndev->features |= NETIF_F_RXHASH;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (12 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-06-10  9:18 ` wei.fang
  2026-06-11  9:47   ` sashiko-bot
  2026-06-10  9:18 ` [PATCH v2 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
  14 siblings, 1 reply; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Add infrastructure for ENETC v4 VFs to track PF link status changes via
the PSI-to-VSI messaging channel. Two new ops,
vf_reg_link_status_notifier and vf_unreg_link_status_notifier, are added
to enetc_si_ops and wired into enetc_phylink_connect() and enetc_close()
for the phy-less path. The feature is populated only in enetc4_vsi_ops;
rev1 hardware is not affected.

On enetc_open(), the VF sends a REGISTER_LINK_CHANGE_NOTIFIER message to
the PF through the VSI-to-PSI messaging channel. The PF records the VF
in link_status_ms_mask, and immediately sends the current link status so
that the VF carrier quickly reflects the link status as soon as the
interface comes up. On every subsequent PF link transition the PF
broadcasts a 16-bit notification to all registered VFs.

On the VF side, a dedicated MSI-X vector handles incoming PSI-to-VSI
messages. The interrupt handler schedules a work item which parses the
notification and updates the carrier state via netif_carrier_on() or
netif_carrier_off() accordingly.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 drivers/net/ethernet/freescale/enetc/enetc.c  |  28 ++-
 drivers/net/ethernet/freescale/enetc/enetc.h  |   5 +
 .../net/ethernet/freescale/enetc/enetc_hw.h   |   9 +
 .../net/ethernet/freescale/enetc/enetc_vf.c   | 209 +++++++++++++++++-
 4 files changed, 249 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index fdceaf36daa7..fd5885b0fa81 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2892,11 +2892,20 @@ static void enetc_clear_interrupts(struct enetc_ndev_priv *priv)
 static int enetc_phylink_connect(struct net_device *ndev)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_si *si = priv->si;
 	struct ethtool_keee edata;
 	int err;
 
 	if (!priv->phylink) {
 		/* phy-less mode */
+		if (si->ops->vf_reg_link_status_notifier) {
+			if (!si->ops->vf_reg_link_status_notifier(si))
+				return 0;
+
+			dev_warn(&ndev->dev,
+				 "Link status notifier registration failed\n");
+		}
+
 		netif_carrier_on(ndev);
 		return 0;
 	}
@@ -2968,6 +2977,7 @@ int enetc_open(struct net_device *ndev)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	struct enetc_bdr_resource *tx_res, *rx_res;
+	struct enetc_si *si = priv->si;
 	bool extended;
 	int err;
 
@@ -3008,8 +3018,15 @@ int enetc_open(struct net_device *ndev)
 err_alloc_rx:
 	enetc_free_tx_resources(tx_res, priv->num_tx_rings);
 err_alloc_tx:
-	if (priv->phylink)
+	if (priv->phylink) {
 		phylink_disconnect_phy(priv->phylink);
+	} else if (si->ops->vf_unreg_link_status_notifier &&
+		   test_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED,
+			    &priv->flags)) {
+		if (si->ops->vf_unreg_link_status_notifier(si))
+			dev_warn(&ndev->dev,
+				 "Link status notifier unregistration failed\n");
+	}
 err_phy_connect:
 	enetc_free_irqs(priv);
 err_setup_irqs:
@@ -3050,6 +3067,7 @@ EXPORT_SYMBOL_GPL(enetc_stop);
 int enetc_close(struct net_device *ndev)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_si *si = priv->si;
 
 	enetc_stop(ndev);
 
@@ -3057,6 +3075,14 @@ int enetc_close(struct net_device *ndev)
 		phylink_stop(priv->phylink);
 		phylink_disconnect_phy(priv->phylink);
 	} else {
+		if (si->ops->vf_unreg_link_status_notifier &&
+		    test_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED,
+			     &priv->flags)) {
+			if (si->ops->vf_unreg_link_status_notifier(si))
+				dev_warn(&ndev->dev,
+					 "Link status notifier unregistration failed\n");
+		}
+
 		netif_carrier_off(ndev);
 	}
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 24d9f89aee73..a8c845e5f863 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -300,6 +300,10 @@ struct enetc_si_ops {
 	int (*set_rss_table)(struct enetc_si *si, const u32 *table, int count);
 	int (*setup_cbdr)(struct enetc_si *si);
 	void (*teardown_cbdr)(struct enetc_si *si);
+
+	/* VSI-specific hooks */
+	int (*vf_reg_link_status_notifier)(struct enetc_si *si);
+	int (*vf_unreg_link_status_notifier)(struct enetc_si *si);
 };
 
 /* PCI IEP device data */
@@ -423,6 +427,7 @@ enum enetc_active_offloads {
 enum enetc_flags_bit {
 	ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS = 0,
 	ENETC_TX_DOWN,
+	ENETC_LINK_STATUS_NOTIFIER_REGISTERED,
 };
 
 /* interrupt coalescing modes */
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 47de179e17c8..78282dbbcfa3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -85,6 +85,9 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 #define  PSIMSGSR_MS(n)		BIT((n) + 1)
 #define  PSIMSGSR_MC		GENMASK(31, 16)
 
+#define ENETC_VSIMSGRR		0x208
+#define  VSIMSGRR_MC		GENMASK(31, 16)
+
 /* SI statistics */
 #define ENETC_SIROCT	0x300
 #define ENETC_SIRFRM	0x308
@@ -108,6 +111,12 @@ static inline u32 enetc_vsi_set_msize(u32 size)
 #define ENETC_SICAPR0	0x900
 #define ENETC_SICAPR1	0x904
 
+#define ENETC_VSIIER	0xa00
+#define  VSIIER_MRIE	BIT(9)
+
+#define ENETC_VSIIDR	0xa08
+#define  VSIIDR_MR	BIT(9)
+
 #define ENETC_PSIIER	0xa00
 #define ENETC_PSIIDR	0xa08
 
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index dc9a0f74a2e3..ba7686fb32ce 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -135,6 +135,52 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
 	return err;
 }
 
+static int enetc_msg_link_status_notifier(struct enetc_si *si, bool reg)
+{
+	struct device *dev = &si->pdev->dev;
+	struct enetc_msg_swbd msg_swbd;
+	u8 cmd_id;
+
+	msg_swbd.size = ALIGN(sizeof(struct enetc_msg_generic),
+			      ENETC_MSG_ALIGN);
+	msg_swbd.vaddr = dma_alloc_coherent(dev, msg_swbd.size,
+					    &msg_swbd.dma, GFP_KERNEL);
+	if (!msg_swbd.vaddr)
+		return -ENOMEM;
+
+	cmd_id = reg ? ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER :
+		       ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER;
+	enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_LINK_STATUS,
+				  cmd_id, 0, 0);
+
+	return enetc_msg_vsi_send(si, &msg_swbd);
+}
+
+static int enetc_vf_reg_link_status_notifier(struct enetc_si *si)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
+	int err;
+
+	err = enetc_msg_link_status_notifier(si, true);
+	if (!err)
+		set_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED, &priv->flags);
+
+	return err;
+}
+
+static int enetc_vf_unreg_link_status_notifier(struct enetc_si *si)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
+	int err;
+
+	err = enetc_msg_link_status_notifier(si, false);
+	if (!err)
+		clear_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED,
+			  &priv->flags);
+
+	return err;
+}
+
 static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
 					      struct sockaddr *saddr)
 {
@@ -415,6 +461,122 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
 	enetc_load_primary_mac_addr(&si->hw, ndev);
 }
 
+static void enetc_vf_enable_mr_int(struct enetc_hw *hw)
+{
+	u32 val = enetc_rd(hw, ENETC_VSIIER) | VSIIER_MRIE;
+
+	enetc_wr(hw, ENETC_VSIIER, val);
+}
+
+static void enetc_vf_disable_mr_int(struct enetc_hw *hw)
+{
+	u32 val = enetc_rd(hw, ENETC_VSIIER) & (~VSIIER_MRIE);
+
+	enetc_wr(hw, ENETC_VSIIER, val);
+}
+
+static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 class_code)
+{
+	struct net_device *ndev = si->ndev;
+
+	switch (class_code) {
+	case ENETC_LINK_STATUS_CLASS_CODE_UP:
+		if (netif_running(ndev) && !netif_carrier_ok(ndev)) {
+			netif_carrier_on(ndev);
+			netdev_info(ndev, "Link is Up\n");
+		}
+		break;
+	case ENETC_LINK_STATUS_CLASS_CODE_DOWN:
+		if (netif_carrier_ok(ndev)) {
+			netif_carrier_off(ndev);
+			netdev_info(ndev, "Link is Down\n");
+		}
+		break;
+	}
+}
+
+static void enetc_vf_msg_task(struct work_struct *work)
+{
+	struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
+	struct net_device *ndev = si->ndev;
+	struct enetc_hw *hw = &si->hw;
+	u8 class_id, class_code;
+	u16 pf_msg;
+
+	pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR));
+	/* W1C to clear the message received interrupt event */
+	enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR);
+
+	/* If ndev is in the NETREG_UNREGISTERED state, return directly
+	 * without processing the message or enabling MR interrupt. Any
+	 * future caller that reuses this work item after the device
+	 * returns to a registered state should enable MR interrupt again.
+	 */
+	if (ndev->reg_state == NETREG_UNREGISTERED)
+		return;
+
+	class_id = FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg);
+	class_code = FIELD_GET(ENETC_PF_MSG_CLASS_CODE, pf_msg);
+
+	switch (class_id) {
+	case ENETC_MSG_CLASS_ID_LINK_STATUS:
+		enetc_vf_msg_handle_link_status(si, class_code);
+		break;
+	default:
+		dev_err(&si->pdev->dev,
+			"Unsupported Message Class ID (0x%02x) from PF\n",
+			class_id);
+	}
+
+	enetc_vf_enable_mr_int(hw);
+}
+
+static irqreturn_t enetc_vf_msg_msix_handler(int irq, void *data)
+{
+	struct enetc_si *si = (struct enetc_si *)data;
+
+	enetc_vf_disable_mr_int(&si->hw);
+	queue_work(si->workqueue, &si->msg_task);
+
+	return IRQ_HANDLED;
+}
+
+static int enetc_vf_register_msg_msix(struct enetc_si *si)
+{
+	int irq, err;
+
+	if (is_enetc_rev1(si))
+		return 0;
+
+	snprintf(si->msg_int_name, sizeof(si->msg_int_name), "%s-pfmsg",
+		 pci_name(si->pdev));
+	irq = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX);
+	err = request_irq(irq, enetc_vf_msg_msix_handler, 0,
+			  si->msg_int_name, si);
+	if (err) {
+		dev_err(&si->pdev->dev,
+			"VF messaging: request_irq() failed!\n");
+		return err;
+	}
+
+	/* set one IRQ entry for PSI-to-VSI messaging */
+	enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
+
+	/* Enable message received interrupt */
+	enetc_vf_enable_mr_int(&si->hw);
+
+	return 0;
+}
+
+static void enetc_vf_free_msg_msix(struct enetc_si *si)
+{
+	if (is_enetc_rev1(si))
+		return;
+
+	enetc_vf_disable_mr_int(&si->hw);
+	free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
+}
+
 static const struct enetc_si_ops enetc_vsi_ops = {
 	.get_rss_table = enetc_get_rss_table,
 	.set_rss_table = enetc_set_rss_table,
@@ -427,8 +589,36 @@ static const struct enetc_si_ops enetc4_vsi_ops = {
 	.set_rss_table = enetc4_set_rss_table,
 	.setup_cbdr = enetc4_setup_cbdr,
 	.teardown_cbdr = enetc4_teardown_cbdr,
+	.vf_reg_link_status_notifier = enetc_vf_reg_link_status_notifier,
+	.vf_unreg_link_status_notifier = enetc_vf_unreg_link_status_notifier,
 };
 
+static int enetc_vf_wq_task_init(struct enetc_si *si)
+{
+	char wq_name[24];
+
+	if (is_enetc_rev1(si))
+		return 0;
+
+	snprintf(wq_name, sizeof(wq_name), "enetc-%s", pci_name(si->pdev));
+	si->workqueue = create_singlethread_workqueue(wq_name);
+	if (!si->workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&si->msg_task, enetc_vf_msg_task);
+
+	return 0;
+}
+
+static void enetc_vf_wq_task_destroy(struct enetc_si *si)
+{
+	if (!si->workqueue)
+		return;
+
+	disable_work_sync(&si->msg_task);
+	destroy_workqueue(si->workqueue);
+}
+
 static int enetc_vf_probe(struct pci_dev *pdev,
 			  const struct pci_device_id *ent)
 {
@@ -493,6 +683,18 @@ static int enetc_vf_probe(struct pci_dev *pdev,
 		goto err_alloc_msix;
 	}
 
+	err = enetc_vf_wq_task_init(si);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to init workqueue\n");
+		goto err_wq_init;
+	}
+
+	err = enetc_vf_register_msg_msix(si);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register msg irq\n");
+		goto err_register_msg_msix;
+	}
+
 	err = register_netdev(ndev);
 	if (err)
 		goto err_reg_netdev;
@@ -502,6 +704,10 @@ static int enetc_vf_probe(struct pci_dev *pdev,
 	return 0;
 
 err_reg_netdev:
+	enetc_vf_free_msg_msix(si);
+err_register_msg_msix:
+	enetc_vf_wq_task_destroy(si);
+err_wq_init:
 	enetc_free_msix(priv);
 err_config_si:
 err_alloc_msix:
@@ -528,7 +734,8 @@ static void enetc_vf_remove(struct pci_dev *pdev)
 
 	priv = netdev_priv(si->ndev);
 	unregister_netdev(si->ndev);
-
+	enetc_vf_free_msg_msix(si);
+	enetc_vf_wq_task_destroy(si);
 	enetc_free_msix(priv);
 
 	enetc_free_si_resources(priv);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 net-next 15/15] net: enetc: add ndo_get_vf_config() support
  2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
                   ` (13 preceding siblings ...)
  2026-06-10  9:18 ` [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
@ 2026-06-10  9:18 ` wei.fang
  14 siblings, 0 replies; 24+ messages in thread
From: wei.fang @ 2026-06-10  9:18 UTC (permalink / raw)
  To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
	davem, edumazet, kuba, pabeni, linux, wei.fang
  Cc: imx, netdev, linux-kernel

From: Wei Fang <wei.fang@nxp.com>

Without ndo_get_vf_config(), userspace tools such as 'ip link show'
cannot query the current VF configuration from the PF.

To support this, extend struct enetc_vf_state to track the per-VF VLAN
and spoofchk settings, and update the corresponding setter callbacks to
persist their state when the hardware is programmed.

enetc_pf_get_vf_config() reads back the persisted state and reports MAC
address, VLAN parameters, spoofchk, and trust state through struct
ifla_vf_info.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc4_pf.c  |  1 +
 .../net/ethernet/freescale/enetc/enetc_pf.c   | 24 ++++++++++++++
 .../net/ethernet/freescale/enetc/enetc_pf.h   |  4 +++
 .../freescale/enetc/enetc_pf_common.c         | 31 +++++++++++++++++++
 .../freescale/enetc/enetc_pf_common.h         |  2 ++
 5 files changed, 62 insertions(+)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index c8059c67119c..4faff9e62749 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -615,6 +615,7 @@ static const struct net_device_ops enetc4_ndev_ops = {
 	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
 	.ndo_set_vf_trust	= enetc_pf_set_vf_trust,
 	.ndo_set_vf_mac		= enetc_pf_set_vf_mac,
+	.ndo_get_vf_config	= enetc_pf_get_vf_config,
 };
 
 static struct phylink_pcs *
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index 209a3503609f..f11d401ba4c9 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -243,6 +243,7 @@ static int enetc_pf_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan,
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_vf_state *vf_state;
 
 	if (priv->si->errata & ENETC_ERR_VLAN_ISOL)
 		return -EOPNOTSUPP;
@@ -255,6 +256,17 @@ static int enetc_pf_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan,
 		return -EPROTONOSUPPORT;
 
 	enetc_set_isol_vlan(&priv->si->hw, vf + 1, vlan, qos);
+
+	vf_state = &pf->vf_state[vf];
+	mutex_lock(&vf_state->lock);
+	/* Currently only C-tags is supported, so tpid is always 0,
+	 * which indicates ETH_P_8021Q.
+	 */
+	vf_state->tpid = 0;
+	vf_state->qos = qos;
+	vf_state->vid = vlan;
+	mutex_unlock(&vf_state->lock);
+
 	return 0;
 }
 
@@ -262,6 +274,7 @@ static int enetc_pf_set_vf_spoofchk(struct net_device *ndev, int vf, bool en)
 {
 	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_vf_state *vf_state;
 	u32 cfgr;
 
 	if (vf >= pf->total_vfs)
@@ -271,6 +284,16 @@ static int enetc_pf_set_vf_spoofchk(struct net_device *ndev, int vf, bool en)
 	cfgr = (cfgr & ~ENETC_PSICFGR0_ASE) | (en ? ENETC_PSICFGR0_ASE : 0);
 	enetc_port_wr(&priv->si->hw, ENETC_PSICFGR0(vf + 1), cfgr);
 
+	vf_state = &pf->vf_state[vf];
+	mutex_lock(&vf_state->lock);
+
+	if (en)
+		vf_state->flags |= ENETC_VF_FLAG_SPOOFCHK;
+	else
+		vf_state->flags &= ~ENETC_VF_FLAG_SPOOFCHK;
+
+	mutex_unlock(&vf_state->lock);
+
 	return 0;
 }
 
@@ -528,6 +551,7 @@ static const struct net_device_ops enetc_ndev_ops = {
 	.ndo_xdp_xmit		= enetc_xdp_xmit,
 	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
 	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
+	.ndo_get_vf_config	= enetc_pf_get_vf_config,
 };
 
 static struct phylink_pcs *
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 378ca4464538..55057073416c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -12,11 +12,15 @@ enum enetc_vf_flags {
 	ENETC_VF_FLAG_TRUSTED		= BIT(1),
 	ENETC_VF_FLAG_UC_PROMISC	= BIT(2),
 	ENETC_VF_FLAG_MC_PROMISC	= BIT(3),
+	ENETC_VF_FLAG_SPOOFCHK		= BIT(4),
 };
 
 struct enetc_vf_state {
 	struct mutex lock; /* Prevent concurrent access */
 	enum enetc_vf_flags flags;
+	u8 tpid; /* SI-based VLAN TPID (0: 0x8100, 1: 0x88a8) */
+	u8 qos; /* SI-based VLAN QOS (priority) bits */
+	u16 vid; /* SI-based VLAN ID */
 };
 
 struct enetc_port_caps {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 52e7d2d2149b..597e020a0128 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -527,5 +527,36 @@ int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
 }
 EXPORT_SYMBOL_GPL(enetc_pf_set_vf_mac);
 
+int enetc_pf_get_vf_config(struct net_device *ndev, int vf,
+			   struct ifla_vf_info *ivi)
+{
+	struct enetc_ndev_priv *priv = netdev_priv(ndev);
+	struct enetc_pf *pf = enetc_si_priv(priv->si);
+	struct enetc_vf_state *vf_state;
+
+	if (vf >= pf->total_vfs)
+		return -EINVAL;
+
+	vf_state = &pf->vf_state[vf];
+	mutex_lock(&vf_state->lock);
+
+	ivi->vf = vf;
+	ivi->spoofchk = !!(vf_state->flags & ENETC_VF_FLAG_SPOOFCHK);
+	ivi->trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
+	enetc_get_si_hw_addr(pf, vf + 1, ivi->mac);
+
+	if (vf_state->vid) {
+		ivi->vlan = vf_state->vid;
+		ivi->qos = vf_state->qos;
+		ivi->vlan_proto = vf_state->tpid ? htons(ETH_P_8021AD) :
+						   htons(ETH_P_8021Q);
+	}
+
+	mutex_unlock(&vf_state->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(enetc_pf_get_vf_config);
+
 MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index b2a2b06c794a..0e487ea9c0a5 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -19,6 +19,8 @@ int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid);
 int enetc_init_sriov_resources(struct enetc_pf *pf);
 int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting);
 int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac);
+int enetc_pf_get_vf_config(struct net_device *ndev, int vf,
+			   struct ifla_vf_info *ivi);
 
 static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si
  2026-06-10  9:18 ` [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Silent 32-bit truncation of 64-bit MAC hash table bitmap
--

commit d6f9f9bacee81eb61d86d4e3d383dcddb4c66cbb
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: move mac_filter from struct enetc_pf to struct enetc_si

The mac_filter array currently resides in struct enetc_pf and is used to
track unicast and multicast MAC address filters for the PF. Since struct
enetc_si is the common structure shared between the PF and VF drivers,
move mac_filter into struct enetc_si to prepare for MAC filter support
in the VF driver.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -225,11 +225,12 @@ static void enetc4_pf_set_mac_hash_filter(struct enetc_pf *pf, int type)
>  	struct net_device *ndev = pf->si->ndev;
>  	struct enetc_mac_filter *mac_filter;
>  	struct enetc_hw *hw = &pf->si->hw;
> +	struct enetc_si *si = pf->si;
>  	struct netdev_hw_addr *ha;
>  
>  	netif_addr_lock_bh(ndev);
>  	if (type & ENETC_MAC_FILTER_TYPE_UC) {
> -		mac_filter = &pf->mac_filter[UC];
> +		mac_filter = &si->mac_filter[UC];
>  		enetc_reset_mac_addr_filter(mac_filter);
>  		netdev_for_each_uc_addr(ha, ndev)
>  			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);

[Severity: High]
This isn't a bug introduced by this patch, but is it safe to dereference
the mac_hash_table bitmap directly?

The mac_hash_table is declared as a 64-bit bitmap. On 32-bit architectures,
an unsigned long is 32 bits, making the bitmap an array of two unsigned
longs.

When the code dereferences this bitmap in enetc4_pf_set_mac_hash_filter():

    enetc4_pf_set_si_uc_hash_filter(hw, 0, *mac_filter->mac_hash_table);

It reads only the first unsigned long, capturing just the lower 32 bits.
When this is implicitly promoted to u64 for enetc4_pf_set_si_uc_hash_filter(),
the upper 32 bits will always be zero.

This same truncation also happens in enetc4_pf_set_uc_exact_filter() and
in enetc_sync_mac_filters() when it passes *f->mac_hash_table to
enetc_set_mac_ht_flt().

Furthermore, the enetc_set_mac_ht_flt() function itself takes an unsigned
long:

    static void enetc_set_mac_ht_flt(struct enetc_si *si, int si_idx, int type,
                                     unsigned long hash)

Because hash is truncated to 32 bits on 32-bit systems, upper_32_bits(hash)
will always evaluate to 0, leaving the upper MAC hash hardware registers
unprogrammed.

Could this lead to incorrect multicast and unicast MAC filtering on 32-bit
systems?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=8

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 04/15] net: enetc: add link speed message support to PF driver
  2026-06-10  9:18 ` [PATCH v2 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Pre-existing out-of-bounds heap read in VF mailbox handlers due to unvalidated message length.
--

commit 6b55ba2c40a6c04cf0db8ebb97921067e4376ee1
Author: Wei Fang <wei.fang@nxp.com>
net: enetc: add link speed message support to PF driver

This commit adds link speed message support for DPDK-owned VFs. The PF
driver replies with the current link speed when it receives a get link
speed message from a VF, improving link state visibility for the VF.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index eac71f90d80bd..b24903b23a616 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -274,6 +277,84 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
>  	}
>  }
>  
> +static u16 enetc_msg_get_link_speed(struct enetc_pf *pf, int vf_id)
> +{
[ ... ]
> +}
> +
> +static u16 enetc_msg_handle_link_speed(struct enetc_pf *pf, int vf_id,
> +				       void *vf_msg)
> +{
> +	struct enetc_msg_header *msg_hdr = vf_msg;

[Severity: High]
This isn't a bug introduced by this patch, but could unvalidated message
lengths lead to out-of-bounds heap reads in mailbox handlers?

In existing handlers, the PF driver calculates the allocation size based on
msg_hdr->len (e.g., msg_size = 16 + len) and allocates a buffer. If a trusted
VF sends a message with an artificially small length like 0, only 16 bytes
are allocated.

However, handlers cast this buffer to larger structures and read fields
outside the bounds:

enetc_msg_set_vf_primary_mac_addr() {
    ...
    char *addr = msg->mac[0].addr;
    ...
    if (!is_valid_ether_addr(addr)) {
    ...
        enetc_set_si_hw_addr(pf, vf_id + 1, addr);
    ...
}

Since msg->mac[0].addr starts at offset 20, reading it from a 16-byte
buffer causes an out-of-bounds heap read, which could leak sensitive kernel
memory into the device MAC address registers or crash the host kernel. Should
message lengths be validated against the expected structure size before
processing the messages?

> +
> +	switch (msg_hdr->cmd_id) {
> +	case ENETC_MSG_GET_CURRENT_LINK_SPEED:
> +		return enetc_msg_get_link_speed(pf, vf_id);
> +	case ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER:
> +	case ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER:
> +	default:
> +		return ENETC_PF_MSG_NOTSUPP;
> +	}
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=4

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
  2026-06-10  9:18 ` [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The v4 driver's MAC address programming functions (`enetc4_pf_set_si_primary_mac` and `enetc4_pf_get_si_primary_mac`) use `__raw_writel` and `__raw_readl` coupled with LE-unaligned helpers, which silently breaks the MAC address configuration on Big Endian architectures.
--

commit fb7fbde8e308b4c501381b6a15d58e74e9aff89a
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
    
Add .ndo_set_vf_mac() to the enetc v4 driver to configure the MAC
addresses of VFs.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index 4b69c19b23194..d310506a6fd4b 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -575,6 +575,7 @@ static const struct net_device_ops enetc4_ndev_ops = {
>  	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
>  	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
>  	.ndo_set_vf_trust	= enetc_pf_set_vf_trust,
> +	.ndo_set_vf_mac		= enetc_pf_set_vf_mac,

[Severity: High]
This is a pre-existing issue, but I'd like to ask about the endianness handling
in the underlying v4 hardware accessors used for MAC configuration.

Looking at enetc4_pf_set_si_primary_mac() and enetc4_pf_get_si_primary_mac()
in drivers/net/ethernet/freescale/enetc/enetc4_pf.c, they use __raw_writel()
and __raw_readl():

    __raw_writel(lower, hw->port + ENETC4_PSIPMAR1(si));
    ...
    lower = __raw_readl(hw->port + ENETC4_PSIPMAR1(si));

Does this silently break MAC address configuration on Big Endian architectures?

get_unaligned_le32() reads the first 4 bytes of the MAC address and returns
them as a host-endian 32-bit integer. __raw_writel() then writes this value
directly to memory without byte swapping. On a Big Endian system, this causes
the bytes to be written in reverse order to the little-endian PCIe device.

For the remaining 16 bits, get_unaligned_le16() returns a host-endian 16-bit
value which is passed into __raw_writel(). The value is zero-extended to
32 bits. On a Big Endian system, writing this directly via __raw_writel()
places the non-zero MAC bytes at offset +2 and +3 instead of offset +0 and +1,
effectively programming 00:00 into the device's MAC address.

The read function enetc4_pf_get_si_primary_mac() is also broken on Big Endian:
__raw_readl() places the MAC bytes into the upper 16 bits of the 32-bit return
value. When assigned to a u16 variable, the value is truncated and the MAC
address is discarded, resulting in 00:00.

Should the driver use standard writel() / readl() or the driver's
enetc_port_wr() to ensure correct host-to-little-endian conversion?

>  };
>  
>  static struct phylink_pcs *

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=7

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
  2026-06-10  9:18 ` [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing message length validation causes an out-of-bounds heap read when an untrusted VF configures multicast MAC hash filters.
- [Medium] Revoking a VF's trusted status fails to clear its multicast hash filter, allowing it to retain permissive multicast filtering.
--

commit 77c31d3d766f02fd9aa7d5c04ff2e5bdf2b6e12e
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: add MAC address filtering support for VFs of ENETC v4

This commit adds VSI-to-PSI mailbox message support to allow VFs to request 
MAC filter configurations from the PF, including hash tables and promiscuous 
modes. It updates message handling, trust checks, and hardware register 
access to enforce permissions securely.

> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -111,6 +113,139 @@ static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
[ ... ]
> +static u16 enetc_msg_set_vf_mac_hash_filter(struct enetc_pf *pf, int vf_id,
> +					    void *vf_msg)
> +{
[ ... ]
> +	switch (type) {
> +	case ENETC_MAC_FILTER_TYPE_UC:
> +		if (!trusted) {
> +			pf_msg = ENETC_PF_MSG_PERM_DENY;
> +			goto vf_state_unlock;
> +		}
> +
> +		uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
> +		pf->ops->set_si_mac_hash_filter(hw, si_id, UC, uc_hash);
> +		break;
> +	case ENETC_MAC_FILTER_TYPE_MC:
> +		mc_hash = (u64)msg->hash_tbl[3] << 32 | msg->hash_tbl[2];
> +		if (!trusted && hweight64(mc_hash) > 8) {

[Severity: High]
Does this path miss a message length validation?

If msg->hdr.len == 0, the allocated buffer size is 32 bytes (via
ENETC_MSG_SIZE(0)). The hash_tbl array starts at offset 20. Reading
msg->hash_tbl[3] accesses bytes 32-35, which is an out-of-bounds heap read
past the 32-byte allocation.

The ENETC_MAC_FILTER_TYPE_ALL case explicitly checks !msg->hdr.len
before accessing hash_tbl[3]. Should the ENETC_MAC_FILTER_TYPE_MC case
have the same check to prevent untrusted VFs from triggering KASAN faults
or leaking adjacent heap memory?

> +			pf_msg = ENETC_PF_MSG_PERM_DENY;
> +			goto vf_state_unlock;
> +		}
> +
> +		pf->ops->set_si_mac_hash_filter(hw, si_id, MC, mc_hash);
> +		break;
[ ... ]
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
[ ... ]
> @@ -476,11 +477,25 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
>  	vf_state = &pf->vf_state[vf];
>  	mutex_lock(&vf_state->lock);
>  
> -	if (setting)
> +	if (setting) {
>  		vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
> -	else
> +	} else {
>  		vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
>  
> +		/* Clear unicast hash filter and disable MAC promiscuous modes
> +		 * if the VF is untrusted.
> +		 */
> +		if (pf->ops->set_si_mac_hash_filter)
> +			pf->ops->set_si_mac_hash_filter(hw, vf + 1, UC, 0);

[Severity: Medium]
Should this trust revocation path also clear the multicast hash filter?

Untrusted VFs are strictly restricted to an 8-bit multicast hash filter
(hweight64(mc_hash) <= 8), while trusted VFs can set a permissive 64-bit
filter.

When ndo_set_vf_trust() clears the trusted flag, the unicast filter is
correctly zeroed, but the hardware's multicast filter is left intact.
Doesn't this allow a newly untrusted VF to retain its previously configured
permissive 64-bit multicast filter, bypassing the intended security policy?

> +
> +		mutex_lock(&pf->msg_lock);
> +		if (pf->ops->set_si_mac_promisc) {
> +			pf->ops->set_si_mac_promisc(hw, vf + 1, UC, false);
> +			pf->ops->set_si_mac_promisc(hw, vf + 1, MC, false);
> +		}
> +		mutex_unlock(&pf->msg_lock);
> +	}
> +
>  	mutex_unlock(&vf_state->lock);
>  
>  	return 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=9

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver
  2026-06-10  9:18 ` [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] An untrusted or unresponsive Virtual Function (VF) can stall the Physical Function's (PF) shared mailbox workqueue and block phylink state changes by forcing a 200ms timeout per request.
- [High] Dropping link state notifications for slow VFs causes permanent state desynchronization.
- [High] VF link status registration races with phylink carrier updates, causing permanent link state desynchronization.
- [Medium] The error path for `pci_enable_sriov()` modifies `pf->sriov_enabled` without holding `msg_lock` and fails to clear `pf->link_status_ms_mask`, leaking state.
--

commit 36849ca6cc467beaae907837bf9648f05856b5c8
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: add link status message support to PF driver

This commit adds link status message support to the PF driver using three
command IDs. It enables VFs to accurately perceive the link status and
report it to upper layers by registering for link change notifications.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index a89a5a418a23d..eac71f90d80bd 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> +static int enetc_pf_send_msg(struct enetc_pf *pf, u32 msg_code, u16 ms_mask)
> +{
> +	struct enetc_hw *hw = &pf->si->hw;
> +	u16 old_ms_mask = ms_mask;
> +	u16 ms_status;
> +	u32 val;
> +
> +	/* The MS bit is set, indicating that the corresponding VF has not
> +	 * read the last message, PF cannot send new message to the VF. So
> +	 * the bit corresponding to VF is cleared from ms_mask.
> +	 */
> +	ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> +	ms_mask &= ~ms_status;
> +	if (!ms_mask)
> +		return -EIO;

[Severity: High]
Does this clear busy VFs from the message mask and silently drop the
notification completely? If the new state is dropped entirely, when the
slow VF eventually reads the register, it will receive the outdated link
state. This seems like it could leave the VF operating with the wrong
link status until the next physical link bounce, permanently breaking
network connectivity.

> +
> +	if (ms_mask != old_ms_mask)
> +		dev_warn_ratelimited(&pf->si->pdev->dev,
> +				     "PF cannot send message to VF(s) 0x%x\n",
> +				     ms_mask ^ old_ms_mask);
> +
> +	/* Writing a 1 when the bit is already set is undefined.
> +	 * Writing a 0 has no effect. Sending messages only to
> +	 * VFs where the MR bit is not set.
> +	 */
> +	enetc_wr(hw, ENETC_PSIMSGSR,
> +		 FIELD_PREP(PSIMSGSR_MC, msg_code) | ms_mask);
> +
> +	return read_poll_timeout(enetc_rd, val, !(val & ms_mask), 1000,
> +				 200000, false, hw, ENETC_PSIMSGSR);
> +}
[ ... ]
> +static void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
> +						    int vf_id)
> +{
> +	u16 pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
> +				ENETC_MSG_CLASS_ID_CMD_SUCCESS);
> +
> +	mutex_lock(&pf->msg_lock);
> +
> +	enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
> +
> +	/* SR-IOV is being disabled if pf->sriov_enabled is false, so no
> +	 * need to set link_status_ms_mask and notify the link status.
> +	 */
> +	if (!pf->sriov_enabled)
> +		goto msg_unlock;
> +
> +	pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
> +
> +	/* Notify VF the current link status */
> +	enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id),
> +					netif_carrier_ok(pf->si->ndev));

[Severity: High]
Is there a race condition with phylink carrier updates here? 

The PF broadcasts link state changes to registered VFs inside the
mac_link_up/down callbacks. However, the phylink core updates the netdev
carrier state (netif_carrier_on/off) only after these callbacks return.

If a VF registers for notifications exactly in the window between the
mac_link_up broadcast and the netif_carrier_on update, it misses the
broadcast. Because netif_carrier_ok() still reflects the old DOWN state,
the VF would be sent the DOWN state while the physical link is actually UP,
permanently desynchronizing it until the next link toggle.

[Severity: High]
Does this allow an untrusted VF to stall the PF's shared mailbox workqueue
and block phylink state changes?

The PF handles the register message in the shared enetc_msg_task workqueue.
It acknowledges the message, allowing the VF to send another immediately,
and then synchronously sends a link status notification via
enetc_pf_send_msg() while holding msg_lock.

Since enetc_pf_send_msg() polls for up to 200ms waiting for the VF to clear
its notification bit, a malicious VF could deliberately ignore the
notification and stall the workqueue thread for the full 200ms. Because
msg_lock is held, this also blocks enetc_pf_notify_vf_link_up/down from
executing in the host's phylink state machine, starving other VFs and
stalling physical link transitions.

> +
> +msg_unlock:
> +	mutex_unlock(&pf->msg_lock);
> +}
[ ... ]
> @@ -389,6 +545,7 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
>  	return num_vfs;
>  
>  err_en_sriov:
> +	pf->sriov_enabled = false;

[Severity: Medium]
Could this error path leave leaked state and cause data races?

If pci_enable_sriov() fails after partially creating VFs, a VF driver that
successfully bound to one of the created VFs could have sent a registration
message, setting its bit in link_status_ms_mask.

Here, pf->sriov_enabled is set to false without acquiring msg_lock, and
link_status_ms_mask is not cleared. Could this leaked bit cause future
physical link toggles to mistakenly attempt to notify an unregistered VF,
resulting in a 200ms delay timeout on the link toggle?

>  	enetc_msg_psi_free(pf);
>  err_msg_psi:
>  	pf->num_vfs = 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=3

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
  2026-06-10  9:18 ` [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Untrusted VFs retain expansive multicast hash filters when trust is revoked.
- [Low] Comment logic inversion regarding PF-set flags in `enetc_msg_psi_free`.
--

commit 39a4c53c34896221262cf925fccff9665bc34780
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4

This commit addresses a security vulnerability on ENETC v4 where a VF FLR
resets the MAC promiscuous mode to enabled. The patch tracks the PF-managed
promiscuous state, updates it on VF requests and trust revocation, and adds
an FLR handler to restore the tracked state after an FLR.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -725,8 +765,21 @@ static void enetc_msg_psi_free(struct enetc_pf *pf)
>  	/* PSIIER interrupts may be re-enabled by workqueue */
>  	enetc_disable_psiier_interrupts(pf);
>  
> -	for (i = 0; i < pf->num_vfs; i++)
> +	for (i = 0; i < pf->num_vfs; i++) {
> +		struct enetc_vf_state *vf_state = &pf->vf_state[i];
> +
>  		enetc_msg_free_mbx(si, i);
> +
> +		/* VF may set these flags by mailbox messages, so need to
> +		 * clear these flags when enetc_msg_psi_free() is called.
> +		 * Flags set by PF are cleared, because these flags are
> +		 * unrelated to whether SR-IOV is enabled or disabled.
> +		 */

[Severity: Low]
This isn't a bug, but should the comment say "Flags set by PF are NOT
cleared"?

The bitwise AND operation below exclusively clears the promiscuous flags
set by the VF while preserving flags set by the PF, such as
ENETC_VF_FLAG_TRUSTED.

> +		mutex_lock(&vf_state->lock);
> +		vf_state->flags &= ~(ENETC_VF_FLAG_UC_PROMISC |
> +				     ENETC_VF_FLAG_MC_PROMISC);
> +		mutex_unlock(&vf_state->lock);
> +	}
>  }

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
[ ... ]
> @@ -480,7 +480,9 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
>  	if (setting) {
>  		vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
>  	} else {
> -		vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
> +		vf_state->flags &= ~(ENETC_VF_FLAG_TRUSTED |
> +				     ENETC_VF_FLAG_UC_PROMISC |
> +				     ENETC_VF_FLAG_MC_PROMISC);
>  
>  		/* Clear unicast hash filter and disable MAC promiscuous modes
>  		 * if the VF is untrusted.
>  		 */

[Severity: High]
Does this leave untrusted VFs with an expansive multicast hash filter when
trust is revoked?

When a VF is trusted, it can set a broad 64-bit multicast hash filter,
while untrusted VFs are restricted to 8 bits. When an administrator revokes
trust, the PF clears the unicast hash filter and disables MAC promiscuous
modes, but it doesn't appear to clear the multicast hash filter here.

Could a previously trusted VF retain its expansive multicast hash filter
after trust is revoked, bypassing the security policy?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=11

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
  2026-06-10  9:18 ` [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: Frank.Li, imx

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Function signature mismatch for ndo_set_rx_mode_async causing CFI panics and silently dropping errors
- [Medium] Endianness logic bug in MAC hash filter conversion
--

commit 7154dd069b56705c2fb84b8fb83bdfde897299bf
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF

This commit implements ndo_set_rx_mode_async for the ENETC VF driver, allowing
MAC filter changes to be communicated to the PF over a VSI mailbox interface.
It manages promiscuous modes and Bloom filter hash tables for unicast and
multicast traffic.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index 4bc48a5f6869c..dc9a0f74a2e34 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
[ ... ]
> +static int enetc_vf_set_mac_hash_filter(struct enetc_ndev_priv *priv,
> +					struct netdev_hw_addr_list *uc,
> +					struct netdev_hw_addr_list *mc)
> +{
[ ... ]
> +	if (mac_filter_type & ENETC_MAC_FILTER_TYPE_UC) {
> +		mac_filter = &si->mac_filter[UC];
> +		enetc_reset_mac_addr_filter(mac_filter);
> +		netdev_hw_addr_list_for_each(ha, uc)
> +			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
> +
> +		memcpy(&hash_val, mac_filter->mac_hash_table,
> +		       sizeof(hash_val));
> +		msg->hash_tbl[i++] = hash_val & GENMASK(31, 0);
> +		msg->hash_tbl[i++] = hash_val >> 32;
> +	}

[Severity: Medium]
Will this memcpy() correctly handle bitmaps on 32-bit big-endian
architectures?

A DECLARE_BITMAP is an array of 32-bit words, where word 0 (bits 0-31) is
stored at lower addresses and word 1 (bits 32-63) is at higher addresses.

When memcpy() copies these 8 bytes into a u64 variable on a big-endian
system, word 0 becomes the most significant 32 bits and word 1 becomes the
least significant 32 bits. The subsequent bitwise operations
(hash_val & GENMASK(31, 0) and hash_val >> 32) then extract the halves in
reverse order compared to 64-bit or little-endian systems, which silently
swaps the hardware filter configuration.

Should this use bitmap_to_arr32() instead to safely convert the bitmap to
a u32 array across all architectures?

> +
> +	if (mac_filter_type & ENETC_MAC_FILTER_TYPE_MC) {
> +		mac_filter = &si->mac_filter[MC];
> +		enetc_reset_mac_addr_filter(mac_filter);
> +		netdev_hw_addr_list_for_each(ha, mc)
> +			enetc_add_mac_addr_ht_filter(mac_filter, ha->addr);
> +
> +		memcpy(&hash_val, mac_filter->mac_hash_table,
> +		       sizeof(hash_val));
> +		msg->hash_tbl[i++] = hash_val & GENMASK(31, 0);
> +		msg->hash_tbl[i++] = hash_val >> 32;
> +	}
> +
> +	enetc_msg_fill_common_hdr(&msg_swbd, ENETC_MSG_CLASS_ID_MAC_FILTER,
> +				  ENETC_MSG_SET_MAC_HASH_TABLE, 0, 0);
> +
> +	return enetc_msg_vsi_send(si, &msg_swbd);
> +}
> +
> +static void enetc_vf_set_rx_mode(struct net_device *ndev,
> +				 struct netdev_hw_addr_list *uc,
> +				 struct netdev_hw_addr_list *mc)
> +{

[Severity: High]
Does this function signature cause a Control Flow Integrity (CFI) panic?

The ndo_set_rx_mode_async callback expects a function returning an int,
but enetc_vf_set_rx_mode() is defined with a void return type. This
mismatch will trigger a CFI kernel panic on architectures with CFI enabled.

Additionally, because it returns void, the caller __dev_set_rx_mode_async()
will read uninitialized garbage from the return register. If that garbage is
non-zero, could it cause continuous polling retries in the network stack?

[ ... ]
> +	if (ndev->flags & IFF_PROMISC) {
> +		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_ALL, true);
> +	} else if (ndev->flags & IFF_ALLMULTI) {
> +		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_MC, true);
> +		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_UC, false);
> +	} else {
> +		enetc_vf_set_mac_promisc(priv, ENETC_MAC_FILTER_TYPE_ALL, false);
> +	}
> +
> +	enetc_vf_set_mac_hash_filter(priv, uc, mc);
> +}

[Severity: High]
Are the error codes from these mailbox commands inadvertently being dropped?

Because this function is defined as void and doesn't return the results of
enetc_vf_set_mac_promisc() or enetc_vf_set_mac_hash_filter(), any hardware
configuration errors are discarded instead of propagated to the network
stack, which breaks the framework's retry mechanism.

[ ... ]
> @@ -225,6 +341,7 @@ static const struct net_device_ops enetc_ndev_ops = {
>  	.ndo_setup_tc		= enetc_vf_setup_tc,
>  	.ndo_hwtstamp_get	= enetc_hwtstamp_get,
>  	.ndo_hwtstamp_set	= enetc_hwtstamp_set,
> +	.ndo_set_rx_mode_async	= enetc_vf_set_rx_mode,
>  };

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=13

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
  2026-06-10  9:18 ` [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
@ 2026-06-11  9:47   ` sashiko-bot
  0 siblings, 0 replies; 24+ messages in thread
From: sashiko-bot @ 2026-06-11  9:47 UTC (permalink / raw)
  To: wei.fang; +Cc: imx, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Race condition in VF message workqueue teardown leading to unhandled hardware interrupts.
--

commit 56c5e3ce01133d9549230bbfb6889bc307567cb5
Author: Wei Fang <wei.fang@nxp.com>

net: enetc: add PSI-to-VSI link status notification support for VF
    
This commit adds infrastructure for ENETC v4 VFs to track PF link status
changes via the PSI-to-VSI messaging channel. A dedicated MSI-X vector
handles incoming messages and schedules a work item to update the carrier
state.

> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index dc9a0f74a2e34..ba7686fb32ce9 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
[ ... ]
> @@ -415,6 +461,122 @@ static void enetc_vf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
[ ... ]
> +static void enetc_vf_msg_task(struct work_struct *work)
> +{
[ ... ]
> +	/* If ndev is in the NETREG_UNREGISTERED state, return directly
> +	 * without processing the message or enabling MR interrupt. Any
> +	 * future caller that reuses this work item after the device
> +	 * returns to a registered state should enable MR interrupt again.
> +	 */
> +	if (ndev->reg_state == NETREG_UNREGISTERED)
> +		return;
> +
[ ... ]
> +	enetc_vf_enable_mr_int(hw);
> +}

[Severity: High]
Can this task re-enable hardware interrupts after the OS IRQ handler has
been freed?

If this work item executes during a probe failure or races with
unregister_netdev() during remove, the ndev->reg_state check might not
protect it. During a probe failure, the state is NETREG_UNINITIALIZED, and
during remove, this check can race with the state change.

[ ... ]
> @@ -493,6 +683,18 @@ static int enetc_vf_probe(struct pci_dev *pdev,
[ ... ]
>  err_reg_netdev:
> +	enetc_vf_free_msg_msix(si);
> +err_register_msg_msix:
> +	enetc_vf_wq_task_destroy(si);
> +err_wq_init:
>  	enetc_free_msix(priv);

[Severity: High]
Can this teardown order cause an unhandled hardware interrupt?

If enetc_vf_free_msg_msix() frees the OS IRQ before enetc_vf_wq_task_destroy()
drains and disables the workqueue, the work task could run and unmask the
hardware interrupt while the handler is already gone.

Should enetc_vf_wq_task_destroy() be called before freeing the IRQ?

[ ... ]
> @@ -528,7 +734,8 @@ static void enetc_vf_remove(struct pci_dev *pdev)
>  
>  	priv = netdev_priv(si->ndev);
>  	unregister_netdev(si->ndev);
> -
> +	enetc_vf_free_msg_msix(si);
> +	enetc_vf_wq_task_destroy(si);

[Severity: High]
Similar to the probe error path, does freeing the IRQ before destroying
the workqueue allow the work item to execute and re-enable the hardware
interrupt after the handler is unregistered?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610091844.3423693-1-wei.fang@oss.nxp.com?part=14

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2026-06-11  9:47 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-10  9:18 [PATCH v2 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 01/15] net: enetc: add trusted " wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 04/15] net: enetc: add link speed " wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
2026-06-10  9:18 ` [PATCH v2 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
2026-06-11  9:47   ` sashiko-bot
2026-06-10  9:18 ` [PATCH v2 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.