* [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
@ 2026-08-31 2:54 wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
` (15 more replies)
0 siblings, 16 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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.
---
v3:
1. Add link_up flag to struct enetc_pf, refine
enetc_pf_notify_vf_link_status() and enetc_msg_notify_vf_link_status()
2. Link state is now encoded as bit flags in the 8-bit class code:
ENETC_CLASS_CODE_LINK_DOWN (bit 0) and ENETC_CLASS_CODE_TX_PAUSE_EN
(bit 1), so that convey the PF TX-PAUSE state so the VF can decide
whether to enable RX-ring congestion mode
3. Refactor the shared enetc_build_link_status_msg() helper, link state
cached in a new pf->link_up field instead of reading netif_carrier_ok()
each time.
4. Add mutex_lock(&pf->msg_lock) protection; reset link_status_ms_mask = 0
under the lock on the pci_enable_sriov() failure path
5. Uses pf->total_vfs (not num_vfs) to gate the notify path
6. Replace the hardcoded speed enum (10G/25G/40G/50G/100G) with a
formula-based scheme, capped at ENETC_MSG_SPEED_MAX = 0xff, so any
future high speed is supported without touching the enum/switch.
7. Refactored speed lookup into a new enetc_build_link_speed_msg() helper
8. Use bitmap_to_arr32() instead of memcpy()
9. Remove the ndev->state check, refine enetc_vf_wq_task_destroy()
10. VF MR interrupt enable moved to enetc_vf_probe(); explicit
enetc_vf_disable_mr_int() added in probe-error and remove paths
11. Remove set_si_mac_promisc(), set_si_mac_hash_filter() from struct
enetc_pf_ops, use the generic helpers enetc_set_si_uc/mc_promisc()
and enetc_set_si_uc/mc_hash_filter() directly
12. Remove get_si_mac_hash_filter() from struct enetc_pf_ops, use the
generic helper enetc_get_si_mc_hash_filter()
13. Introduce ENETC_VF_MC_HASH_BITS_MAX (8) constant instead of the
magic number 8
14. Add enetc_msg_clear_vf_config() to clear promisc/hash config on
SR-IOV teardown
15. enetc_vf_set_rx_mode() now returns int (was void), with proper error
propagation and goto out cleanup
16. VF MR-interrupt helpers now take struct enetc_si * and are no-ops on
ENETC v1 (is_enetc_rev1() guard); disable now writes 0 outright
17. Workqueue switched from create_singlethread_workqueue() to
alloc_ordered_workqueue(..., WQ_MEM_RECLAIM, ...)
18. Update commit messages
v2 link: https://lore.kernel.org/imx/20260610091844.3423693-1-wei.fang@oss.nxp.com/
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 | 57 +-
drivers/net/ethernet/freescale/enetc/enetc.h | 12 +-
.../ethernet/freescale/enetc/enetc4_debugfs.c | 51 +-
.../net/ethernet/freescale/enetc/enetc4_hw.h | 1 +
.../net/ethernet/freescale/enetc/enetc4_pf.c | 48 +-
.../ethernet/freescale/enetc/enetc_ethtool.c | 6 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 26 +
.../ethernet/freescale/enetc/enetc_mailbox.h | 104 ++-
.../net/ethernet/freescale/enetc/enetc_msg.c | 615 ++++++++++++++++--
.../net/ethernet/freescale/enetc/enetc_pf.c | 64 +-
.../net/ethernet/freescale/enetc/enetc_pf.h | 20 +-
.../freescale/enetc/enetc_pf_common.c | 145 ++++-
.../freescale/enetc/enetc_pf_common.h | 26 +
.../net/ethernet/freescale/enetc/enetc_vf.c | 385 ++++++++++-
15 files changed, 1443 insertions(+), 118 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH v3 net-next 01/15] net: enetc: add trusted VF support
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
` (14 subsequent siblings)
15 siblings, 2 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 9bb1004548ab..935a6a03b14f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -225,11 +225,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);
@@ -574,6 +578,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 55c07c528f22..a7bf4bfc25b7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -488,6 +488,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 56d23a8a11a0..6789f92d005e 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 d32a195a04c9..519fc90d2647 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -586,5 +586,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 8243ce0de57f..96a4dc63da57 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -22,6 +22,7 @@ void enetc_set_si_mc_promisc(struct enetc_si *si, int si_id, bool promisc);
void enetc_set_si_uc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc);
+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] 47+ messages in thread
* [PATCH v3 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
` (13 subsequent siblings)
15 siblings, 0 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 | 4 +++-
.../net/ethernet/freescale/enetc/enetc_msg.c | 19 ++++++++++---------
.../net/ethernet/freescale/enetc/enetc_pf.h | 3 ---
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index d1e9d9130057..bc713a7c3aa1 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};
@@ -333,6 +334,8 @@ struct enetc_si {
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
@@ -374,7 +377,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 6789f92d005e..142c911f1dfc 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -40,10 +40,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];
DECLARE_BITMAP(vlan_ht_filter, ENETC_VLAN_HT_SIZE);
DECLARE_BITMAP(active_vlans, VLAN_N_VID);
--
2.34.1
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 12:00 ` Andrew Lunn
` (2 more replies)
2026-08-31 2:54 ` [PATCH v3 net-next 04/15] net: enetc: add link speed " wei.fang
` (12 subsequent siblings)
15 siblings, 3 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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.
For link status message, the PSI-to-VSI message is 16 bits wide: the
upper 8 bits carry the message class ID, and the lower 8 bits carry the
class code. Bit 0 of the class code indicates the link state (1 = link
down, 0 = link up), and bit 1 indicates whether TX PAUSE is enabled on
the PF (1 = enabled, 0 = disabled).
The TX PAUSE state is included because VF RX BD rings support congestion
mode, but whether the hardware can actually send PAUSE frames depends on
whether TX PAUSE is enabled on the PF. By conveying the PF TX PAUSE state
in the link status message, the VF can determine whether to enable
congestion mode on its RX BD rings.
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 | 26 ++-
.../net/ethernet/freescale/enetc/enetc_msg.c | 214 +++++++++++++++++-
.../net/ethernet/freescale/enetc/enetc_pf.h | 6 +
.../freescale/enetc/enetc_pf_common.c | 2 +
.../freescale/enetc/enetc_pf_common.h | 10 +
7 files changed, 256 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 935a6a03b14f..17fd9ee27942 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -899,6 +899,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,
@@ -907,6 +908,7 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,
{
struct enetc_pf *pf = phylink_to_enetc_pf(config);
+ enetc_pf_notify_vf_link_down(pf);
enetc4_mac_rx_graceful_stop(pf);
enetc4_mac_tx_graceful_stop(pf);
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 16da732dc5de..f97602714118 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..846998f07989 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -66,8 +66,8 @@
* 2) PSI_TX_control: PSIMSGSR[MC] - for PSI to VSI notification messages
* (async mode)
*
- * Note that for some GET messages, there is no COOKIE field, and the CLASS
- * CODE field is expanded to 8 bits.
+ * Note that for some PSI-to-VSI messages, there is no COOKIE field, and the
+ * CLASS CODE field is expanded to 8 bits.
*/
#ifndef __ENETC_MAILBOX_H
@@ -87,7 +87,11 @@
/* The fileds of PSI-to-VSI message, the message is only 16-bit */
#define ENETC_PF_MSG_COOKIE GENMASK(3, 0)
#define ENETC_PF_MSG_CLASS_CODE GENMASK(7, 4)
-/* Extend the class code to 8-bit for GET messages without COOKIE */
+/* Extend the class code to 8-bit for PSI-to-VSI messages without COOKIE
+ * The class code for the following messages is 8-bit.
+ * 1. Get IP revision messages
+ * 2. Link status messages
+ */
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
@@ -107,6 +111,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 +123,21 @@ 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 */
+#define ENETC_CLASS_CODE_LINK_DOWN BIT(0)
+#define ENETC_CLASS_CODE_TX_PAUSE_EN BIT(1)
+
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
@@ -161,6 +176,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..e21414acdc0d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -136,6 +136,154 @@ static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_msg)
}
}
+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 u16 enetc_build_link_status_msg(struct enetc_ndev_priv *priv,
+ bool link_up)
+{
+ u8 status = 0;
+
+ if (link_up) {
+ spin_lock(&priv->si->gen_lock);
+ if (test_bit(ENETC_RXBDR_CM, &priv->flags))
+ status |= ENETC_CLASS_CODE_TX_PAUSE_EN;
+ spin_unlock(&priv->si->gen_lock);
+ } else {
+ status |= ENETC_CLASS_CODE_LINK_DOWN;
+ }
+
+ return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_STATUS) |
+ FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, status);
+}
+
+static void enetc_msg_get_link_status(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ u16 pf_msg;
+
+ mutex_lock(&pf->msg_lock);
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ enetc_pf_reply_msg(&pf->si->hw, vf_id, pf_msg);
+ mutex_unlock(&pf->msg_lock);
+}
+
+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. To
+ * avoid sending messages to such a VF, the bit corresponding to VF
+ * is cleared from ms_mask. Because the MS bit can only be written
+ * as 1, writing a 0 has no effect. Writing a 1 when the bit is
+ * already set is undefined.
+ */
+ 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);
+
+ 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)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ u16 pf_msg;
+
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ 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_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));
+
+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.
+ */
+ enetc_msg_get_link_status(pf, vf_id);
+ break;
+ case ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_register_link_status_notifier(pf, vf_id);
+ break;
+ case ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER:
+ enetc_msg_unregister_link_status_notifier(pf, vf_id);
+ break;
+ default:
+ return ENETC_PF_MSG_NOTSUPP;
+ }
+
+ return 0;
+}
+
+/* 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 +359,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 +387,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 +394,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 +519,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 +536,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_enabled.
+ */
+ 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 +551,15 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
return num_vfs;
err_en_sriov:
+ /* 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.
+ */
+ mutex_lock(&pf->msg_lock);
+ pf->sriov_enabled = false;
+ pf->link_status_ms_mask = 0;
+ mutex_unlock(&pf->msg_lock);
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
@@ -396,3 +567,34 @@ 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)
+{
+ /* pf->msg_lock is initialized when pf->total_vfs is not 0 */
+ if (!pf->total_vfs)
+ return;
+
+ mutex_lock(&pf->msg_lock);
+
+ pf->link_up = link_up;
+ if (!pf->link_status_ms_mask)
+ goto msg_unlock;
+
+ enetc_msg_notify_vf_link_status(pf, pf->link_status_ms_mask);
+
+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 142c911f1dfc..fb48a06e6c42 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -54,6 +54,12 @@ struct enetc_pf {
struct enetc_port_caps caps;
const struct enetc_pf_ops *ops;
+
+ /* Message lock, prevent concurrent access */
+ struct mutex msg_lock;
+ bool sriov_enabled;
+ bool link_up;
+ 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 519fc90d2647..345cb940aaa3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -582,6 +582,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 96a4dc63da57..693aee3c64b8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -31,9 +31,19 @@ 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] 47+ messages in thread
* [PATCH v3 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (2 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
` (11 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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.
The PSI-to-VSI message is 16 bits and the high 8 bits are the message
class ID. For link speed message, the low bits are the speed code, so
the message supports up to 255 speed values (ENETC_MSG_SPEED_MAX = 0xff).
Instead of enumerating every speed value greater than 5Gbps explicitly,
introduce a formula-based approach:
speed_code = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G
This removes the explicit enum entries for 10G, 25G, 40G,50G, 100G and
so on. Making it easy to support any future high speed without modifying
the enum or the switch statement.
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 | 36 ++++++++
.../net/ethernet/freescale/enetc/enetc_msg.c | 91 +++++++++++++++++++
2 files changed, 127 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index 846998f07989..bd669543e96c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -91,6 +91,7 @@
* The class code for the following messages is 8-bit.
* 1. Get IP revision messages
* 2. Link status messages
+ * 3. Link speed messages
*/
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
@@ -112,6 +113,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,
};
@@ -129,6 +131,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,
@@ -138,6 +147,28 @@ enum enetc_mac_filter_class_code {
#define ENETC_CLASS_CODE_LINK_DOWN BIT(0)
#define ENETC_CLASS_CODE_TX_PAUSE_EN BIT(1)
+/* 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,
+ /* Do not add enumeration values for any speed greater than
+ * 5Gbps. For any speed greater than 5Gbps, its speed class
+ * code should follow the formula below.
+ *
+ * SPEED = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G
+ *
+ * The unit of link_speed should be Mbps, the max SPEED
+ * should <= ENETC_MSG_SPEED_MAX.
+ */
+ ENETC_MSG_SPEED_MAX = 0xff,
+};
+
struct enetc_msg_swbd {
void *vaddr;
dma_addr_t dma;
@@ -181,6 +212,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 e21414acdc0d..c3ae4c024f34 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -280,6 +280,93 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
return 0;
}
+static u16 enetc_build_link_speed_msg(int speed, int duplex)
+{
+ u32 speed_code = ENETC_MSG_SPEED_UNKNOWN;
+
+ switch (speed) {
+ case SPEED_10:
+ if (duplex == DUPLEX_HALF)
+ speed_code = ENETC_MSG_SPEED_10M_HD;
+ else if (duplex == DUPLEX_FULL)
+ speed_code = ENETC_MSG_SPEED_10M_FD;
+ break;
+ case SPEED_100:
+ if (duplex == DUPLEX_HALF)
+ speed_code = ENETC_MSG_SPEED_100M_HD;
+ else if (duplex == DUPLEX_FULL)
+ speed_code = ENETC_MSG_SPEED_100M_FD;
+ break;
+ case SPEED_1000:
+ speed_code = ENETC_MSG_SPEED_1000M;
+ break;
+ case SPEED_2500:
+ speed_code = ENETC_MSG_SPEED_2500M;
+ break;
+ case SPEED_5000:
+ speed_code = ENETC_MSG_SPEED_5G;
+ break;
+ default:
+ if (speed < SPEED_5000)
+ break;
+
+ speed_code = (speed - SPEED_5000) / SPEED_1000 +
+ ENETC_MSG_SPEED_5G;
+ if (speed_code > ENETC_MSG_SPEED_MAX)
+ speed_code = ENETC_MSG_SPEED_UNKNOWN;
+ }
+
+ return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_LINK_SPEED) |
+ FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, speed_code);
+}
+
+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 rtnl_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();
+
+ return enetc_build_link_speed_msg(link_info.base.speed,
+ link_info.base.duplex);
+}
+
+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.
@@ -362,6 +449,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",
@@ -546,6 +636,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] 47+ messages in thread
* [PATCH v3 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (3 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
` (10 subsequent siblings)
15 siblings, 0 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 a7bf4bfc25b7..b74d965e403e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -207,7 +207,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] 47+ messages in thread
* [PATCH v3 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (4 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
` (9 subsequent siblings)
15 siblings, 0 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 b74d965e403e..c467dc05510e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -191,28 +191,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 345cb940aaa3..7a11370d2b8e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -611,5 +611,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 693aee3c64b8..1d35e906fc3a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -23,6 +23,7 @@ void enetc_set_si_uc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc);
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] 47+ messages in thread
* [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (5 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-08-31 2:54 ` [PATCH v3 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
` (8 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 17fd9ee27942..eeb70feeb773 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -579,6 +579,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] 47+ messages in thread
* [PATCH v3 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (6 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
` (7 subsequent siblings)
15 siblings, 0 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 | 2 ++
.../net/ethernet/freescale/enetc/enetc4_pf.c | 6 +++---
drivers/net/ethernet/freescale/enetc/enetc_pf.c | 17 ++++++++---------
drivers/net/ethernet/freescale/enetc/enetc_pf.h | 2 --
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index bc713a7c3aa1..8d7c1790b672 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -336,6 +336,8 @@ struct enetc_si {
struct enetc_msg_swbd msg; /* Only valid for VSI */
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 eeb70feeb773..363ec562934e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -137,7 +137,7 @@ static int enetc4_pf_add_maft_entries(struct enetc_pf *pf,
static void enetc4_pf_set_uc_hash_filter(struct enetc_pf *pf,
struct netdev_hw_addr_list *uc)
{
- struct enetc_mac_filter *mac_filter = &pf->mac_filter[UC];
+ struct enetc_mac_filter *mac_filter = &pf->si->mac_filter[UC];
struct netdev_hw_addr *ha;
u64 hash;
@@ -172,7 +172,7 @@ static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf,
err = enetc4_pf_add_maft_entries(pf, uc);
if (!err) {
- enetc_reset_mac_addr_filter(&pf->mac_filter[UC]);
+ enetc_reset_mac_addr_filter(&si->mac_filter[UC]);
enetc_set_si_uc_hash_filter(si, 0, 0);
}
@@ -182,7 +182,7 @@ static int enetc4_pf_set_uc_exact_filter(struct enetc_pf *pf,
static void enetc4_pf_set_mc_hash_filter(struct enetc_pf *pf,
struct netdev_hw_addr_list *mc)
{
- struct enetc_mac_filter *mac_filter = &pf->mac_filter[MC];
+ struct enetc_mac_filter *mac_filter = &pf->si->mac_filter[MC];
struct netdev_hw_addr *ha;
u64 hash;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index c467dc05510e..523c71324780 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -60,10 +60,9 @@ static void enetc_add_mac_addr_em_filter(struct enetc_mac_filter *filter,
filter->mac_addr_cnt++;
}
-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;
@@ -115,9 +114,9 @@ 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);
bool uprom = false, mprom = false;
struct enetc_mac_filter *filter;
+ struct enetc_si *si = priv->si;
struct netdev_hw_addr *ha;
bool em;
@@ -133,7 +132,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);
@@ -149,7 +148,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) {
@@ -162,10 +161,10 @@ 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);
- enetc_set_si_uc_promisc(priv->si, 0, uprom);
- enetc_set_si_mc_promisc(priv->si, 0, mprom);
+ enetc_set_si_uc_promisc(si, 0, uprom);
+ enetc_set_si_mc_promisc(si, 0, mprom);
}
static void enetc_set_loopback(struct net_device *ndev, bool en)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index fb48a06e6c42..06dc47164dc5 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -38,8 +38,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;
DECLARE_BITMAP(vlan_ht_filter, ENETC_VLAN_HT_SIZE);
--
2.34.1
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (7 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
` (6 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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.
The PSIPMMR register holds promiscuous mode bits for all SIs and is
modified by both enetc4_pf_set_rx_mode() and the VF message handler
workqueue (enetc_msg_task). Since both functions can run concurrently
on SMP systems and enetc_set_si_uc/mc_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>
---
.../ethernet/freescale/enetc/enetc4_debugfs.c | 51 +++--
.../net/ethernet/freescale/enetc/enetc4_pf.c | 7 +-
.../ethernet/freescale/enetc/enetc_mailbox.h | 42 ++++
.../net/ethernet/freescale/enetc/enetc_msg.c | 181 +++++++++++++++++-
.../net/ethernet/freescale/enetc/enetc_pf.h | 1 +
.../freescale/enetc/enetc_pf_common.c | 68 ++++++-
.../freescale/enetc/enetc_pf_common.h | 12 ++
.../net/ethernet/freescale/enetc/enetc_vf.c | 6 +-
8 files changed, 332 insertions(+), 36 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c b/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
index 5029038bf99f..91af78ba4f74 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
@@ -6,24 +6,48 @@
#include <linux/seq_file.h>
#include <linux/string_choices.h>
-#include "enetc_pf.h"
+#include "enetc_pf_common.h"
#include "enetc4_debugfs.h"
-static void enetc_show_si_mac_hash_filter(struct seq_file *s, int i)
+static void enetc_vf_state_lock(struct enetc_pf *pf, int vf_id)
{
- struct enetc_si *si = s->private;
- struct enetc_hw *hw = &si->hw;
+ struct enetc_vf_state *vf_state;
+
+ if (vf_id < 0)
+ return;
+
+ vf_state = &pf->vf_state[vf_id];
+ mutex_lock(&vf_state->lock);
+}
+
+static void enetc_vf_state_unlock(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_vf_state *vf_state;
+
+ if (vf_id < 0)
+ return;
+
+ vf_state = &pf->vf_state[vf_id];
+ mutex_unlock(&vf_state->lock);
+}
+
+static void enetc_show_si_mac_hash_filter(struct seq_file *s, int si_id)
+{
+ struct enetc_pf *pf = enetc_si_priv(s->private);
+ struct enetc_hw *hw = &pf->si->hw;
u32 hash_h, hash_l;
- hash_l = enetc_port_rd(hw, ENETC4_PSIUMHFR0(i));
- hash_h = enetc_port_rd(hw, ENETC4_PSIUMHFR1(i));
+ enetc_vf_state_lock(pf, si_id - 1);
+ hash_l = enetc_port_rd(hw, ENETC4_PSIUMHFR0(si_id));
+ hash_h = enetc_port_rd(hw, ENETC4_PSIUMHFR1(si_id));
seq_printf(s, "SI %d unicast MAC hash filter: 0x%08x%08x\n",
- i, hash_h, hash_l);
+ si_id, hash_h, hash_l);
- hash_l = enetc_port_rd(hw, ENETC4_PSIMMHFR0(i));
- hash_h = enetc_port_rd(hw, ENETC4_PSIMMHFR1(i));
+ hash_l = enetc_port_rd(hw, ENETC4_PSIMMHFR0(si_id));
+ hash_h = enetc_port_rd(hw, ENETC4_PSIMMHFR1(si_id));
seq_printf(s, "SI %d multicast MAC hash filter: 0x%08x%08x\n",
- i, hash_h, hash_l);
+ si_id, hash_h, hash_l);
+ enetc_vf_state_unlock(pf, si_id - 1);
}
static int enetc_mac_filter_show(struct seq_file *s, void *data)
@@ -37,7 +61,11 @@ static int enetc_mac_filter_show(struct seq_file *s, void *data)
int err = 0;
int i;
+ /* Prevent concurrent access from causing PSIPMMR to be modified */
+ enetc_pf_msg_lock(pf);
val = enetc_port_rd(hw, ENETC4_PSIPMMR);
+ enetc_pf_msg_unlock(pf);
+
for (i = 0; i < num_si; i++) {
seq_printf(s, "SI %d Unicast Promiscuous mode: %s\n", i,
str_enabled_disabled(PSIPMMR_SI_MAC_UP(i) & val));
@@ -45,13 +73,12 @@ static int enetc_mac_filter_show(struct seq_file *s, void *data)
str_enabled_disabled(PSIPMMR_SI_MAC_MP(i) & val));
}
+ rtnl_lock();
/* MAC hash filter table */
for (i = 0; i < num_si; i++)
enetc_show_si_mac_hash_filter(s, i);
user = &pf->si->ntmp_user;
- rtnl_lock();
-
if (bitmap_empty(user->maft_eid_bitmap, user->maft_num_entries))
goto unlock_rtnl;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 363ec562934e..a4ffe1100bd7 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;
@@ -528,8 +523,10 @@ static int enetc4_pf_set_rx_mode(struct net_device *ndev,
type = ENETC_MAC_FILTER_TYPE_ALL;
}
+ enetc_pf_msg_lock(pf);
enetc_set_si_uc_promisc(si, 0, uc_promisc);
enetc_set_si_mc_promisc(si, 0, mc_promisc);
+ enetc_pf_msg_unlock(pf);
if (uc_promisc) {
enetc_set_si_uc_hash_filter(si, 0, 0);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index bd669543e96c..200651e6ce85 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -96,6 +96,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,
@@ -119,6 +130,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 {
@@ -141,6 +154,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 */
@@ -204,6 +220,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 c3ae4c024f34..d58fbaeaf46c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -9,6 +9,11 @@
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_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)
{
@@ -79,10 +84,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;
}
@@ -108,6 +110,132 @@ 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;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+ struct enetc_si *si = pf->si;
+ int si_id = vf_id + 1;
+ u64 uc_hash, mc_hash;
+ bool trusted;
+ int type;
+
+ /* 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];
+ enetc_set_si_uc_hash_filter(si, si_id, uc_hash);
+ break;
+ case ENETC_MAC_FILTER_TYPE_MC:
+ mc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+ if (!trusted &&
+ hweight64(mc_hash) > ENETC_VF_MC_HASH_BITS_MAX) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ enetc_set_si_mc_hash_filter(si, si_id, 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) <= ENETC_VF_MC_HASH_BITS_MAX)) {
+ enetc_set_si_mc_hash_filter(si, si_id, 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;
+ }
+
+ enetc_set_si_uc_hash_filter(si, si_id, uc_hash);
+ enetc_set_si_mc_hash_filter(si, si_id, 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_si *si = pf->si;
+ bool promisc, flush_macs;
+ int si_id = vf_id + 1;
+ int type;
+
+ flush_macs = !!(msg->config & ENETC_MSG_MAC_FLUSH_MACS);
+ 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)
+ enetc_set_si_uc_promisc(si, si_id, promisc);
+
+ if (type & ENETC_MAC_FILTER_TYPE_MC)
+ enetc_set_si_mc_promisc(si, si_id, promisc);
+
+ mutex_unlock(&pf->msg_lock);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
+ enetc_set_si_mc_hash_filter(si, si_id, 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)
{
@@ -116,6 +244,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;
}
@@ -383,8 +515,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;
}
@@ -402,6 +533,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,
@@ -412,7 +551,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)) {
@@ -582,6 +720,31 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
return err;
}
+static void enetc_msg_clear_vf_config(struct enetc_pf *pf, int vf_id)
+{
+ struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+ struct enetc_si *si = pf->si;
+ int si_id = vf_id + 1;
+
+ /* For ENETC v1, we only support setting the VF's MAC address via
+ * VSI-to-PSI messages, so there is no configuration to clear.
+ */
+ if (is_enetc_rev1(si))
+ return;
+
+ mutex_lock(&vf_state->lock);
+
+ mutex_lock(&pf->msg_lock);
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
+ mutex_unlock(&pf->msg_lock);
+
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+ enetc_set_si_mc_hash_filter(si, si_id, 0);
+
+ mutex_unlock(&vf_state->lock);
+}
+
static void enetc_msg_psi_free(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
@@ -598,8 +761,10 @@ static void enetc_msg_psi_free(struct enetc_pf *pf)
/* MR interrupts may be re-enabled by workqueue */
enetc_msg_disable_mr_int(pf);
- for (i = 0; i < pf->num_vfs; i++)
+ for (i = 0; i < pf->num_vfs; i++) {
enetc_msg_free_mbx(si, i);
+ enetc_msg_clear_vf_config(pf, i);
+ }
}
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 06dc47164dc5..12e67f611f77 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -6,6 +6,7 @@
#define ENETC_PF_NUM_RINGS 8
#define ENETC_VLAN_HT_SIZE 64
+#define ENETC_VF_MC_HASH_BITS_MAX 8 /* For untrusted VFs */
enum enetc_vf_flags {
ENETC_VF_FLAG_PF_SET_MAC = BIT(0),
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 7a11370d2b8e..8007dce90195 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -151,26 +151,44 @@ void enetc_set_si_uc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
}
EXPORT_SYMBOL_GPL(enetc_set_si_uc_hash_filter);
-void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
+static void enetc_get_psimmhfr_offsets(struct enetc_si *si, int si_id,
+ int *psimmhfr0, int *psimmhfr1)
{
- int psimmhfr0_off, psimmhfr1_off;
- struct enetc_hw *hw = &si->hw;
-
if (is_enetc_rev1(si)) {
bool err = si->errata & ENETC_ERR_UCMCSWP;
- psimmhfr0_off = ENETC_PSIMMHFR0(si_id, err);
- psimmhfr1_off = ENETC_PSIMMHFR1(si_id);
+ *psimmhfr0 = ENETC_PSIMMHFR0(si_id, err);
+ *psimmhfr1 = ENETC_PSIMMHFR1(si_id);
} else {
- psimmhfr0_off = ENETC4_PSIMMHFR0(si_id);
- psimmhfr1_off = ENETC4_PSIMMHFR1(si_id);
+ *psimmhfr0 = ENETC4_PSIMMHFR0(si_id);
+ *psimmhfr1 = ENETC4_PSIMMHFR1(si_id);
}
+}
+void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash)
+{
+ int psimmhfr0_off, psimmhfr1_off;
+ struct enetc_hw *hw = &si->hw;
+
+ enetc_get_psimmhfr_offsets(si, si_id, &psimmhfr0_off, &psimmhfr1_off);
enetc_port_wr(hw, psimmhfr0_off, lower_32_bits(hash));
enetc_port_wr(hw, psimmhfr1_off, upper_32_bits(hash));
}
EXPORT_SYMBOL_GPL(enetc_set_si_mc_hash_filter);
+static u64 enetc_get_si_mc_hash_filter(struct enetc_si *si, int si_id)
+{
+ int psimmhfr0_off, psimmhfr1_off;
+ struct enetc_hw *hw = &si->hw;
+ u32 hash_h, hash_l;
+
+ enetc_get_psimmhfr_offsets(si, si_id, &psimmhfr0_off, &psimmhfr1_off);
+ hash_l = enetc_port_rd(hw, psimmhfr0_off);
+ hash_h = enetc_port_rd(hw, psimmhfr1_off);
+
+ return ((u64)hash_h << 32) | hash_l;
+}
+
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc)
{
struct enetc_hw *hw = &si->hw;
@@ -593,6 +611,8 @@ 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;
+ struct enetc_si *si = priv->si;
+ int si_id = vf + 1;
if (vf >= pf->total_vfs)
return -EINVAL;
@@ -600,11 +620,39 @@ 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 {
+ u64 hash;
+
vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
+ /* For ENETC v1, we only support setting the VF's MAC address
+ * via VSI-to-PSI messages. Unicast and multicast promiscuous
+ * mode and hash filters are not supported, so there is no need
+ * to clear these configurations.
+ */
+ if (is_enetc_rev1(si))
+ goto vf_state_unlock;
+
+ /* Disable unicast and multicast promiscuous modes */
+ mutex_lock(&pf->msg_lock);
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
+ mutex_unlock(&pf->msg_lock);
+
+ /* Clear unicast hash filter */
+ enetc_set_si_uc_hash_filter(si, si_id, 0);
+
+ /* Clear multicast hash filter if its set bits exceed
+ * ENETC_VF_MC_HASH_BITS_MAX.
+ */
+ hash = enetc_get_si_mc_hash_filter(si, si_id);
+ if (hweight64(hash) > ENETC_VF_MC_HASH_BITS_MAX)
+ enetc_set_si_mc_hash_filter(si, si_id, 0);
+ }
+
+vf_state_unlock:
mutex_unlock(&vf_state->lock);
return 0;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index 1d35e906fc3a..91a9c339245a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -30,6 +30,18 @@ static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
return enetc_global_rd(hw, ENETC_G_EIPBRR0) & EIPBRR0_REVISION;
}
+static inline void enetc_pf_msg_lock(struct enetc_pf *pf)
+{
+ if (pf->total_vfs)
+ mutex_lock(&pf->msg_lock);
+}
+
+static inline void enetc_pf_msg_unlock(struct enetc_pf *pf)
+{
+ if (pf->total_vfs)
+ mutex_unlock(&pf->msg_lock);
+}
+
#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);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 7dcb4a0246f5..a60af40d8546 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] 47+ messages in thread
* [PATCH v3 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (8 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
` (5 subsequent siblings)
15 siblings, 0 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 d58fbaeaf46c..4aabeb23a386 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -15,23 +15,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);
}
@@ -41,7 +35,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;
@@ -633,7 +627,7 @@ static void enetc_msg_task(struct work_struct *work)
}
out:
- enetc_msg_enable_mr_int(pf);
+ enetc_enable_psiier_interrupts(pf);
}
/* Init */
@@ -708,8 +702,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;
@@ -750,16 +744,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] 47+ messages in thread
* [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (9 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
` (4 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, imx, netdev, linux-kernel
From: Wei Fang <wei.fang@nxp.com>
On ENETC v4, when VF performs a PCI FLR, it 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 | 20 ++++++++
.../net/ethernet/freescale/enetc/enetc_hw.h | 12 +++++
.../net/ethernet/freescale/enetc/enetc_msg.c | 50 +++++++++++++++++++
.../net/ethernet/freescale/enetc/enetc_pf.h | 3 ++
.../freescale/enetc/enetc_pf_common.c | 4 +-
5 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index a4ffe1100bd7..c421c0e7355b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -212,9 +212,29 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type,
enetc4_pf_set_mc_hash_filter(pf, mc);
}
+static void enetc4_pf_vf_flr_handler(struct enetc_pf *pf, int vf_id)
+{
+ 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);
+ enetc_set_si_uc_promisc(pf->si, vf_id + 1, uc_promisc);
+ enetc_set_si_mc_promisc(pf->si, vf_id + 1, 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,
+ .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 f97602714118..c18ad8b9b071 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 4aabeb23a386..55c23d4a73a8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -27,6 +27,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);
}
@@ -208,6 +211,20 @@ static u16 enetc_msg_set_vf_mac_promisc_mode(struct enetc_pf *pf, int vf_id,
goto vf_state_unlock;
}
+ 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;
+ }
+
+ 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;
+ }
+
mutex_lock(&pf->msg_lock);
if (type & ENETC_MAC_FILTER_TYPE_UC)
@@ -594,6 +611,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);
@@ -602,6 +642,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);
@@ -728,6 +770,14 @@ static void enetc_msg_clear_vf_config(struct enetc_pf *pf, int vf_id)
mutex_lock(&vf_state->lock);
+ /* VF may set these flags by mailbox messages, so need to clear these
+ * flags when enetc_msg_psi_free() is called. PF-set flags (TRUSTED,
+ * PF_SET_MAC) are not cleared, because these flags are unrelated to
+ * whether SR-IOV is enabled or disabled.
+ */
+ vf_state->flags &= ~(ENETC_VF_FLAG_UC_PROMISC |
+ ENETC_VF_FLAG_MC_PROMISC);
+
mutex_lock(&pf->msg_lock);
enetc_set_si_uc_promisc(si, si_id, false);
enetc_set_si_mc_promisc(si, si_id, false);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 12e67f611f77..6bf4105ee0e3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -11,6 +11,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 {
@@ -32,6 +34,7 @@ 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 (*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 8007dce90195..10134d7a1f70 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -625,7 +625,9 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
} else {
u64 hash;
- vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
+ vf_state->flags &= ~(ENETC_VF_FLAG_TRUSTED |
+ ENETC_VF_FLAG_UC_PROMISC |
+ ENETC_VF_FLAG_MC_PROMISC);
/* For ENETC v1, we only support setting the VF's MAC address
* via VSI-to-PSI messages. Unicast and multicast promiscuous
--
2.34.1
^ permalink raw reply related [flat|nested] 47+ messages in thread
* [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (10 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
` (3 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 80f0082f6c63..803c5c541a5c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -3794,6 +3794,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,
@@ -3807,6 +3814,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,
@@ -3816,6 +3827,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 09025e7a2a3a..e23d8d82d2ba 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 c421c0e7355b..a945a120c553 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -1121,6 +1121,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);
@@ -1138,6 +1141,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 07b7832f2427..7965dfd06f5f 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 a60af40d8546..322705202d49 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,
@@ -413,6 +430,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] 47+ messages in thread
* [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (11 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
` (2 subsequent siblings)
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 | 140 ++++++++++++++++++
1 file changed, 140 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 322705202d49..4e717afba7f7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -213,6 +213,142 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
}
}
+static int enetc_vf_set_mac_promisc(struct enetc_si *si, int type, bool en)
+{
+ struct enetc_msg_mac_promisc_mode *msg;
+ struct device *dev = &si->pdev->dev;
+ 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(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(si, &msg_swbd);
+}
+
+static int enetc_vf_set_mac_hash_filter(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ struct enetc_msg_mac_hash_filter *msg;
+ struct enetc_mac_filter *mac_filter;
+ struct device *dev = &si->pdev->dev;
+ struct net_device *ndev = si->ndev;
+ struct enetc_msg_swbd msg_swbd;
+ struct netdev_hw_addr *ha;
+ u32 msg_size, tbl_cnt;
+ int mac_filter_type;
+ 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(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);
+
+ bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
+ ENETC_MADDR_HASH_TBL_SZ);
+ i += 2;
+ }
+
+ 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);
+
+ bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
+ ENETC_MADDR_HASH_TBL_SZ);
+ }
+
+ 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 int 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;
+ int err;
+
+ /* For ENETC v1, we cannot return -EOPNOTSUPP or any other error,
+ * otherwise ndev->rx_mode_retry_timer will try to set rx_mode
+ * multiple times, which is pointless.
+ */
+ if (is_enetc_rev1(si))
+ return 0;
+
+ if (ndev->flags & IFF_PROMISC) {
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ true);
+ } else if (ndev->flags & IFF_ALLMULTI) {
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
+ false);
+ if (err)
+ goto out;
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
+ true);
+ } else {
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ false);
+ }
+
+ if (err)
+ goto out;
+
+ err = enetc_vf_set_mac_hash_filter(si, uc, mc);
+
+out:
+ /* If the error code is -EOPNOTSUPP or -EACCES or -EPERM, return 0
+ * directly to avoid meaningless retries.
+ */
+ if (err == -EOPNOTSUPP || err == -EACCES || err == -EPERM)
+ return 0;
+
+ return err;
+}
+
/* Probing/ Init */
static const struct net_device_ops enetc_ndev_ops = {
.ndo_open = enetc_open,
@@ -225,6 +361,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 +417,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] 47+ messages in thread
* [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (12 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
2026-09-03 2:56 ` [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support Jakub Kicinski
15 siblings, 2 replies; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 | 42 +++-
drivers/net/ethernet/freescale/enetc/enetc.h | 6 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 9 +
.../net/ethernet/freescale/enetc/enetc_vf.c | 219 ++++++++++++++++++
4 files changed, 275 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 803c5c541a5c..fb5df740650e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2935,11 +2935,31 @@ 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)
+ goto carrier_on;
+
+ /* For phy-less VFs on ENETC v4, attempt to register a link
+ * status notifier with the PF via the VSI-to-PSI messaging
+ * channel. If registration succeeds, the PF will immediately
+ * send the current link status and broadcast future link
+ * transitions; carrier state is then managed in
+ * enetc_vf_msg_handle_link_status(). If registration fails,
+ * fall back to the LS1028A behaviour and assert carrier
+ * unconditionally via netif_carrier_on().
+ */
+ if (!si->ops->vf_reg_link_status_notifier(si))
+ return 0;
+
+ dev_warn(&ndev->dev,
+ "Link status notifier registration failed\n");
+
+carrier_on:
netif_carrier_on(ndev);
return 0;
}
@@ -3011,6 +3031,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;
@@ -3051,8 +3072,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:
@@ -3093,6 +3121,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);
@@ -3100,6 +3129,17 @@ 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))
+ goto carrier_off;
+
+ if (!si->ops->vf_unreg_link_status_notifier(si))
+ goto carrier_off;
+
+ dev_warn(&ndev->dev,
+ "Link status notifier unregistration failed\n");
+carrier_off:
netif_carrier_off(ndev);
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 8d7c1790b672..52bd502976ee 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 */
@@ -334,6 +338,7 @@ struct enetc_si {
struct dentry *debugfs_root;
struct enetc_msg_swbd msg; /* Only valid for VSI */
+ struct workqueue_struct *workqueue;
struct work_struct msg_task;
char msg_int_name[ENETC_INT_NAME_MAX];
@@ -429,6 +434,7 @@ enum enetc_flags_bit {
ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS = 0,
ENETC_TX_DOWN,
ENETC_RXBDR_CM,
+ 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 c18ad8b9b071..2c9d9042eb0b 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 4e717afba7f7..a4d0089ef1a9 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)
{
@@ -435,6 +481,128 @@ 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_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ enetc_wr(&si->hw, ENETC_VSIIER, VSIIER_MRIE);
+}
+
+static void enetc_vf_disable_mr_int(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ enetc_wr(&si->hw, ENETC_VSIIER, 0);
+}
+
+static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 status)
+{
+ bool tx_pause = !!(status & ENETC_CLASS_CODE_TX_PAUSE_EN);
+ bool link_down = !!(status & ENETC_CLASS_CODE_LINK_DOWN);
+ struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
+ struct net_device *ndev = si->ndev;
+
+ rtnl_lock();
+ if (!netif_running(ndev))
+ goto unlock_rtnl;
+
+ if (link_down) {
+ if (netif_carrier_ok(ndev)) {
+ netif_carrier_off(ndev);
+ netdev_info(ndev, "Link is Down\n");
+ }
+
+ goto unlock_rtnl;
+ }
+
+ /* Link is up */
+ enetc_set_congestion_mode(priv, tx_pause);
+
+ if (!netif_carrier_ok(ndev)) {
+ netif_carrier_on(ndev);
+ netdev_info(ndev, "Link is Up, tx pause %s\n",
+ tx_pause ? "on" : "off");
+ }
+
+unlock_rtnl:
+ rtnl_unlock();
+}
+
+static void enetc_vf_msg_task(struct work_struct *work)
+{
+ struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
+ struct enetc_hw *hw = &si->hw;
+ u8 class_id, class_code;
+ u16 pf_msg;
+
+ /* W1C to clear the message received interrupt event */
+ enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR);
+
+ /* Reading VSIMSGRR retrieves the message data and acknowledges to
+ * the PF that the message was received and another message can be
+ * sent.
+ */
+ pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR));
+ class_id = FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg);
+
+ switch (class_id) {
+ case ENETC_MSG_CLASS_ID_LINK_STATUS:
+ class_code = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg);
+ 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(si);
+}
+
+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);
+ 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);
+
+ return 0;
+}
+
+static void enetc_vf_free_msg_msix(struct enetc_si *si)
+{
+ if (is_enetc_rev1(si))
+ return;
+
+ 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,
@@ -447,8 +615,38 @@ 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)
+{
+ if (is_enetc_rev1(si))
+ return 0;
+
+ si->workqueue = alloc_ordered_workqueue("enetc-%s-wq", WQ_MEM_RECLAIM,
+ pci_name(si->pdev));
+ 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);
+
+ /* The MR interrupt may be re-enabled by si->msg_task */
+ enetc_vf_disable_mr_int(si);
+
+ destroy_workqueue(si->workqueue);
+}
+
static int enetc_vf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -520,15 +718,33 @@ 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;
+ /* Enable message received interrupt */
+ enetc_vf_enable_mr_int(si);
netif_carrier_off(ndev);
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:
@@ -554,8 +770,11 @@ static void enetc_vf_remove(struct pci_dev *pdev)
struct enetc_msg_swbd msg;
priv = netdev_priv(si->ndev);
+ enetc_vf_disable_mr_int(si);
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] 47+ messages in thread
* [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (13 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
@ 2026-08-31 2:54 ` wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-03 2:56 ` [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support Jakub Kicinski
15 siblings, 1 reply; 47+ messages in thread
From: wei.fang @ 2026-08-31 2:54 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux
Cc: wei.fang, 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 a945a120c553..b4d76505bc03 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -597,6 +597,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 523c71324780..d77a07cece28 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -195,6 +195,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;
@@ -207,6 +208,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;
}
@@ -214,6 +226,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)
@@ -223,6 +236,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;
}
@@ -476,6 +499,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 6bf4105ee0e3..25e869d54365 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -13,11 +13,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 10134d7a1f70..264294a0cc23 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -684,5 +684,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 91a9c339245a..f36f45450733 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -24,6 +24,8 @@ void enetc_set_si_mc_hash_filter(struct enetc_si *si, int si_id, u64 hash);
void enetc_set_si_vlan_promisc(struct enetc_si *si, int si_id, bool promisc);
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] 47+ messages in thread
* Re: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-08-31 12:00 ` Andrew Lunn
2026-09-01 2:31 ` Wei Fang
2026-09-01 3:23 ` sashiko-bot
2026-09-03 23:44 ` netdev-bot+sashiko
2 siblings, 1 reply; 47+ messages in thread
From: Andrew Lunn @ 2026-08-31 12:00 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
On Mon, Aug 31, 2026 at 10:54:29AM +0800, wei.fang@oss.nxp.com wrote:
> 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.
>
> For link status message, the PSI-to-VSI message is 16 bits wide: the
> upper 8 bits carry the message class ID, and the lower 8 bits carry the
> class code. Bit 0 of the class code indicates the link state (1 = link
> down, 0 = link up), and bit 1 indicates whether TX PAUSE is enabled on
> the PF (1 = enabled, 0 = disabled).
>
> The TX PAUSE state is included because VF RX BD rings support congestion
> mode, but whether the hardware can actually send PAUSE frames depends on
> whether TX PAUSE is enabled on the PF. By conveying the PF TX PAUSE state
> in the link status message, the VF can determine whether to enable
> congestion mode on its RX BD rings.
I see you have hit the 15 patch limit. Do you have a patch in the next
series implementing ethtool get and set pause?
Andrew
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-08-31 12:00 ` Andrew Lunn
@ 2026-09-01 2:31 ` Wei Fang
2026-09-01 3:05 ` Andrew Lunn
0 siblings, 1 reply; 47+ messages in thread
From: Wei Fang @ 2026-09-01 2:31 UTC (permalink / raw)
To: Andrew Lunn, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, olteanv@gmail.com,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, linux@armlinux.org.uk,
imx@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
> > 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.
> >
> > For link status message, the PSI-to-VSI message is 16 bits wide: the
> > upper 8 bits carry the message class ID, and the lower 8 bits carry the
> > class code. Bit 0 of the class code indicates the link state (1 = link
> > down, 0 = link up), and bit 1 indicates whether TX PAUSE is enabled on
> > the PF (1 = enabled, 0 = disabled).
> >
> > The TX PAUSE state is included because VF RX BD rings support congestion
> > mode, but whether the hardware can actually send PAUSE frames depends on
> > whether TX PAUSE is enabled on the PF. By conveying the PF TX PAUSE state
> > in the link status message, the VF can determine whether to enable
> > congestion mode on its RX BD rings.
>
> I see you have hit the 15 patch limit. Do you have a patch in the next
> series implementing ethtool get and set pause?
>
No, we do not have the patch to support ethtool get and set pause for VF.
The link is controlled by the PF, including TX PAUSE-related configurations
(ON/OFF/refresh threshold and PAUSE Quanta). The VF can only passively
decide whether to enable congestion mode based on the actual link status.
Adding an ethtool set pause interface to the VF is meaningless because it
cannot control the link or configure TX PAUSE, , or rather, it shouldn't control
these.
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-01 2:31 ` Wei Fang
@ 2026-09-01 3:05 ` Andrew Lunn
2026-09-01 3:40 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: Andrew Lunn @ 2026-09-01 3:05 UTC (permalink / raw)
To: Wei Fang
Cc: Wei Fang (OSS), Claudiu Manoil, Vladimir Oltean, Clark Wang,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
On Tue, Sep 01, 2026 at 02:31:21AM +0000, Wei Fang wrote:
> > > 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.
> > >
> > > For link status message, the PSI-to-VSI message is 16 bits wide: the
> > > upper 8 bits carry the message class ID, and the lower 8 bits carry the
> > > class code. Bit 0 of the class code indicates the link state (1 = link
> > > down, 0 = link up), and bit 1 indicates whether TX PAUSE is enabled on
> > > the PF (1 = enabled, 0 = disabled).
> > >
> > > The TX PAUSE state is included because VF RX BD rings support congestion
> > > mode, but whether the hardware can actually send PAUSE frames depends on
> > > whether TX PAUSE is enabled on the PF. By conveying the PF TX PAUSE state
> > > in the link status message, the VF can determine whether to enable
> > > congestion mode on its RX BD rings.
> >
> > I see you have hit the 15 patch limit. Do you have a patch in the next
> > series implementing ethtool get and set pause?
> >
>
> No, we do not have the patch to support ethtool get and set pause for VF.
> The link is controlled by the PF, including TX PAUSE-related configurations
> (ON/OFF/refresh threshold and PAUSE Quanta). The VF can only passively
> decide whether to enable congestion mode based on the actual link status.
> Adding an ethtool set pause interface to the VF is meaningless because it
> cannot control the link or configure TX PAUSE, , or rather, it shouldn't control
> these.
Well, get will return what is actually happening. That could be
useful.
And from what you described, it sounds like the VF has the option to
not enable congestion mode, i.e. not send pause frames. So you can
implement a limited set, returning -EOPNOTSUPP under many conditions,
but still allow some control. But it might not be worth it.
Andrew
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
2026-08-31 2:54 ` [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
@ 2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:59 ` Wei Fang (OSS)
0 siblings, 1 reply; 47+ messages in thread
From: sashiko-bot @ 2026-09-01 3:23 UTC (permalink / raw)
To: wei.fang; +Cc: Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Asymmetrical MAC address register access between set and get functions for the PF (si == 0), causing the bootloader's MAC address to be ignored and overwritten.
--
commit 7332ce3a436cebfb9b0a304898ec0b592d1b0ff9
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 17fd9ee27942f..eeb70feeb7737 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -579,6 +579,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 while reviewing the MAC address handling in
this file, I noticed an asymmetrical register access between the set and get
functions for the PF (when si == 0).
In enetc4_pf_get_si_primary_mac(), the code unconditionally reads from
PSIPMAR0:
upper = __raw_readl(hw->port + ENETC4_PSIPMAR0(si));
lower = __raw_readl(hw->port + ENETC4_PSIPMAR1(si));
However, the set function enetc4_pf_set_si_primary_mac() correctly writes
to ENETC4_PMAR0 for si == 0.
Does this cause the PF (si == 0) to fail to read the primary MAC address
configured by the bootloader or firmware during enetc4_pf_init()?
Since enetc_setup_mac_addresses() calls enetc_get_si_hw_addr(), wouldn't this
read from the wrong register, assume no MAC is present, and generate a random
MAC address that overwrites the bootloader's MAC address?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang@oss.nxp.com?part=7
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 01/15] net: enetc: add trusted VF support
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
@ 2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:13 ` Wei Fang (OSS)
2026-09-03 23:44 ` netdev-bot+sashiko
1 sibling, 1 reply; 47+ messages in thread
From: sashiko-bot @ 2026-09-01 3:23 UTC (permalink / raw)
To: wei.fang; +Cc: Frank.Li, imx
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The patch breaks standard Linux SR-IOV semantics by unconditionally preventing untrusted VFs from setting their MAC addresses, and preventing trusted VFs from overriding administratively set MAC addresses.
--
commit d231761f64378426562111f6fe937bf009f559c2
Author: Claudiu Manoil <claudiu.manoil@nxp.com>
net: enetc: add trusted VF support
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.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index edc1277bb5860..78114ab3e4820 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -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;
> + }
[Severity: High]
Does this code break standard Linux SR-IOV semantics?
By unconditionally returning ENETC_PF_MSG_PERM_DENY for untrusted VFs, VMs and
container frameworks that rely on the standard behavior may fail to initialize
networking. Specifically, untrusted VMs that generate and set their own MAC
(when the host has not enforced one) will receive a permission denied response
and fail. Standard drivers typically allow untrusted VFs to set their MAC if
no administrative MAC was assigned by the host.
> +
> 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);
[Severity: High]
Is this restriction defeating the established kernel purpose of the VF_TRUST
flag?
If a VF is trusted, standard SR-IOV semantics typically allow it to override
administrative restrictions like a host-assigned MAC address. This block
prevents even trusted VFs from overriding a PF-assigned MAC address, causing
a trusted VF to fail with a command not permitted error if it tries to change
a host-assigned MAC.
> - 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;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang@oss.nxp.com?part=1
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-08-31 12:00 ` Andrew Lunn
@ 2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:46 ` Wei Fang (OSS)
2026-09-03 23:44 ` netdev-bot+sashiko
2 siblings, 1 reply; 47+ messages in thread
From: sashiko-bot @ 2026-09-01 3:23 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] Denial of Service via Synchronous Polling of Untrusted VF
- [High] Logic Error and TOCTOU Race Permanently Dropping Link State Notifications
--
commit 0897c02e67549130fea1a46e3080e9af30c83727
Author: Wei Fang <wei.fang@nxp.com>
net: enetc: add link status message support to PF driver
Add link status message support to the PF driver using three command IDs
under message class 0x80 (ENETC_MSG_CLASS_ID_LINK_STATUS).
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index a89a5a418a23d..e21414acdc0d0 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. To
> + * avoid sending messages to such a VF, the bit corresponding to VF
> + * is cleared from ms_mask. Because the MS bit can only be written
> + * as 1, writing a 0 has no effect. Writing a 1 when the bit is
> + * already set is undefined.
> + */
> + ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> + ms_mask &= ~ms_status;
> + if (!ms_mask)
> + return -EIO;
[Severity: High]
Does this logic introduce a time-of-check to time-of-use race condition?
If a VF acknowledges a previous message by clearing its MS bit immediately
after ms_status is read, the mask will remain zero and this early return
will prevent the link state from being updated.
Because the early return prevents the shared MC payload from being updated
in hardware, and there is no retry logic, will the VF receive the old link
state when it eventually reads the register and be left permanently out of
sync with the PF?
> +
> + 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);
> +
> + 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);
[Severity: High]
Can an untrusted VF use this synchronous polling to intentionally stall the
PF's execution thread?
This blocks for up to 200ms waiting for the VF to acknowledge the message.
Since this is reachable by untrusted VFs (e.g., running in a VM), can a
malicious VF ignore the message and tie up the PF thread?
[ ... ]
> +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));
[Severity: High]
Does calling enetc_msg_notify_vf_link_status() here block other mailbox
operations?
Because enetc_msg_notify_vf_link_status() eventually calls
enetc_pf_send_msg() which can poll for 200ms, and we are holding
pf->msg_lock, will a slow or malicious VF block all other VF mailbox
operations and link state processing for this PF?
> +
> +msg_unlock:
> + mutex_unlock(&pf->msg_lock);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang@oss.nxp.com?part=3
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
2026-08-31 2:54 ` [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
@ 2026-09-01 3:23 ` sashiko-bot
2026-09-03 23:44 ` netdev-bot+sashiko
1 sibling, 0 replies; 47+ messages in thread
From: sashiko-bot @ 2026-09-01 3:23 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] Race condition in `enetc_vf_probe` causes permanent loss of carrier state if the interface is brought UP immediately by userspace.
--
commit 14cc725fe4ebcb6942ed2c3904878d85a508f30f
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. It implements
a dedicated MSI-X vector and workqueue to parse notifications and
update the carrier state accordingly.
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
[ ... ]
> @@ -520,15 +718,33 @@ 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;
>
> + /* Enable message received interrupt */
> + enetc_vf_enable_mr_int(si);
> netif_carrier_off(ndev);
[Severity: High]
This isn't a bug introduced by this patch, but this is a pre-existing
issue that appears to be exacerbated by the new asynchronous message
handling.
Could this sequence in enetc_vf_probe() cause a permanent loss of the
carrier state if the interface is brought up immediately by userspace?
Once register_netdev() exposes the device to userspace, the rtnl_lock
is dropped. If userspace reacts to the registration and brings the
interface up immediately, enetc_open() will run and successfully register
the link status notifier.
The PF then replies with the initial link status, queuing the msg_task
workqueue which calls netif_carrier_on().
Concurrently, enetc_vf_probe() resumes and unconditionally calls
netif_carrier_off(). If the workqueue wins the race, netif_carrier_off()
will overwrite the valid on state with off, severing the link without any
notification until the next physical link flap or a manual restart.
Should netif_carrier_off() be called before register_netdev() to prevent
this condition?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang@oss.nxp.com?part=14
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-01 3:05 ` Andrew Lunn
@ 2026-09-01 3:40 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-01 3:40 UTC (permalink / raw)
To: Andrew Lunn
Cc: Wei Fang (OSS), Claudiu Manoil, Vladimir Oltean, Clark Wang,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Best Regards,
Wei Fang
> On Tue, Sep 01, 2026 at 02:31:21AM +0000, Wei Fang wrote:
> > > > 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.
> > > >
> > > > For link status message, the PSI-to-VSI message is 16 bits wide: the
> > > > upper 8 bits carry the message class ID, and the lower 8 bits carry the
> > > > class code. Bit 0 of the class code indicates the link state (1 = link
> > > > down, 0 = link up), and bit 1 indicates whether TX PAUSE is enabled on
> > > > the PF (1 = enabled, 0 = disabled).
> > > >
> > > > The TX PAUSE state is included because VF RX BD rings support congestion
> > > > mode, but whether the hardware can actually send PAUSE frames depends
> on
> > > > whether TX PAUSE is enabled on the PF. By conveying the PF TX PAUSE
> state
> > > > in the link status message, the VF can determine whether to enable
> > > > congestion mode on its RX BD rings.
> > >
> > > I see you have hit the 15 patch limit. Do you have a patch in the next
> > > series implementing ethtool get and set pause?
> > >
> >
> > No, we do not have the patch to support ethtool get and set pause for VF.
> > The link is controlled by the PF, including TX PAUSE-related configurations
> > (ON/OFF/refresh threshold and PAUSE Quanta). The VF can only passively
> > decide whether to enable congestion mode based on the actual link status.
> > Adding an ethtool set pause interface to the VF is meaningless because it
> > cannot control the link or configure TX PAUSE, , or rather, it shouldn't control
> > these.
>
> Well, get will return what is actually happening. That could be
> useful.
You are right, I will consider adding a get interface, but that should be done
after this series is received. Thanks.
>
> And from what you described, it sounds like the VF has the option to
> not enable congestion mode, i.e. not send pause frames. So you can
> implement a limited set, returning -EOPNOTSUPP under many conditions,
> but still allow some control. But it might not be worth it.
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 01/15] net: enetc: add trusted VF support
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-01 6:13 ` Wei Fang (OSS)
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang (OSS) @ 2026-09-01 6:13 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: Frank.Li@kernel.org, imx@lists.linux.dev, Netdev
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > index edc1277bb5860..78114ab3e4820 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> [ ... ]
> > @@ -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;
> > + }
>
> [Severity: High]
> Does this code break standard Linux SR-IOV semantics?
>
> By unconditionally returning ENETC_PF_MSG_PERM_DENY for untrusted VFs,
> VMs and
> container frameworks that rely on the standard behavior may fail to initialize
> networking. Specifically, untrusted VMs that generate and set their own MAC
> (when the host has not enforced one) will receive a permission denied response
> and fail. Standard drivers typically allow untrusted VFs to set their MAC if
> no administrative MAC was assigned by the host.
This is a false positive. The PF allocates the MAC address for each VF by default.
The VF can get the MAC address from its SIPMAR0/1 registers. So untrusted VF
still can use the network.
The current patch just prevents the untrusted VF from setting its own MAC
address through the mailbox channel.
>
> > +
> > 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);
>
> [Severity: High]
> Is this restriction defeating the established kernel purpose of the VF_TRUST
> flag?
>
> If a VF is trusted, standard SR-IOV semantics typically allow it to override
> administrative restrictions like a host-assigned MAC address. This block
> prevents even trusted VFs from overriding a PF-assigned MAC address, causing
> a trusted VF to fail with a command not permitted error if it tries to change
> a host-assigned MAC.
This is also a false positive. This part is inherited from LS1028A, and is not
introduced by this patch. This patch simply adds some comments explaining
the intention behind doing so. For trusted VFs, the PF still has the highest
privileges. If a user assigns an address to a VF via the ndo_set_vf_mac()
interface, even a trusted VF cannot overwrite the MAC address set by the PF.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-01 6:46 ` Wei Fang (OSS)
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang (OSS) @ 2026-09-01 6:46 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: Frank.Li@kernel.org, imx@lists.linux.dev, Netdev
> > +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. To
> > + * avoid sending messages to such a VF, the bit corresponding to VF
> > + * is cleared from ms_mask. Because the MS bit can only be written
> > + * as 1, writing a 0 has no effect. Writing a 1 when the bit is
> > + * already set is undefined.
> > + */
> > + ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> > + ms_mask &= ~ms_status;
> > + if (!ms_mask)
> > + return -EIO;
>
> [Severity: High]
> Does this logic introduce a time-of-check to time-of-use race condition?
>
> If a VF acknowledges a previous message by clearing its MS bit immediately
> after ms_status is read, the mask will remain zero and this early return
> will prevent the link state from being updated.
>
> Because the early return prevents the shared MC payload from being updated
> in hardware, and there is no retry logic, will the VF receive the old link
> state when it eventually reads the register and be left permanently out of
> sync with the PF?
This issue stems from the hardware limitation, the software cannot guarantee
that the hardware won't clear the MS bit after the check is complete. However,
this problem is highly unlikely to occur, as mailbox messages are infrequent, and
VF has ample time to process them. If this situation does arise, it indicates that
VF itself is already malfunctioning, making retries pointless. Therefore, I believe
that adding a retry mechanism to address this theoretical issue is unnecessary,
as it would only complicate the code logic and introduce new problems.
>
> > +
> > + 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);
> > +
> > + 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);
>
> [Severity: High]
> Can an untrusted VF use this synchronous polling to intentionally stall the
> PF's execution thread?
>
> This blocks for up to 200ms waiting for the VF to acknowledge the message.
> Since this is reachable by untrusted VFs (e.g., running in a VM), can a
> malicious VF ignore the message and tie up the PF thread?
The `enetc_pf_send_msg()` function is currently only used to notify link
status, and messages only occur when the link status changes, making its
frequency extremely low. Even if new messages are added in the future,
they will still be very infrequent. I don't believe that malicious VF
intentionally blocking for 200ms would have any impact, so there's nothing
to fix.
>
> [ ... ]
> > +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));
>
> [Severity: High]
> Does calling enetc_msg_notify_vf_link_status() here block other mailbox
> operations?
>
> Because enetc_msg_notify_vf_link_status() eventually calls
> enetc_pf_send_msg() which can poll for 200ms, and we are holding
> pf->msg_lock, will a slow or malicious VF block all other VF mailbox
> operations and link state processing for this PF?
>
Mailbox messages are infrequent, and link status messages are only
generated when the link status changes. Therefore, this theoretical
problem is not worth fixing. Even if it does occur, it won't cause any
issues other VFs will simply experience timeout errors.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-01 6:59 ` Wei Fang (OSS)
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang (OSS) @ 2026-09-01 6:59 UTC (permalink / raw)
To: sashiko-reviews@lists.linux.dev
Cc: Frank.Li@kernel.org, imx@lists.linux.dev, Netdev
> Pre-existing issues:
> - [High] Asymmetrical MAC address register access between set and get
> functions for the PF (si == 0), causing the bootloader's MAC address to be
> ignored and overwritten.
> --
>
> commit 7332ce3a436cebfb9b0a304898ec0b592d1b0ff9
> 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 17fd9ee27942f..eeb70feeb7737 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> > @@ -579,6 +579,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 while reviewing the MAC address handling in
> this file, I noticed an asymmetrical register access between the set and get
> functions for the PF (when si == 0).
>
> In enetc4_pf_get_si_primary_mac(), the code unconditionally reads from
> PSIPMAR0:
>
> upper = __raw_readl(hw->port + ENETC4_PSIPMAR0(si));
> lower = __raw_readl(hw->port + ENETC4_PSIPMAR1(si));
>
> However, the set function enetc4_pf_set_si_primary_mac() correctly writes
> to ENETC4_PMAR0 for si == 0.
>
> Does this cause the PF (si == 0) to fail to read the primary MAC address
> configured by the bootloader or firmware during enetc4_pf_init()?
>
> Since enetc_setup_mac_addresses() calls enetc_get_si_hw_addr(), wouldn't this
> read from the wrong register, assume no MAC is present, and generate a
> random
> MAC address that overwrites the bootloader's MAC address?
>
This is a false positive. For the PF (SI 0), PSIPMAR0/1(0) are read only
registers, the MAC address of PF should be configured by PMAR0/1
registers. The values in PMAR0/1 are reflected in PSIPMAR0/1(0), so
the driver can get the MAC address from PSIPMAR0/1(0).
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (14 preceding siblings ...)
2026-08-31 2:54 ` [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
@ 2026-09-03 2:56 ` Jakub Kicinski
2026-09-03 3:24 ` Wei Fang (OSS)
15 siblings, 1 reply; 47+ messages in thread
From: Jakub Kicinski @ 2026-09-03 2:56 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, pabeni, linux, wei.fang, imx,
netdev, linux-kernel
On Mon, 31 Aug 2026 10:54:26 +0800 wei.fang@oss.nxp.com wrote:
> 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.
You only replied to some of the AI feedback, should we assume that
feedback on patches 12/13/14 is legit and expect v4?
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
2026-09-03 2:56 ` [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support Jakub Kicinski
@ 2026-09-03 3:24 ` Wei Fang (OSS)
2026-09-03 23:22 ` Jakub Kicinski
0 siblings, 1 reply; 47+ messages in thread
From: Wei Fang (OSS) @ 2026-09-03 3:24 UTC (permalink / raw)
To: Jakub Kicinski, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, linux@armlinux.org.uk,
Wei Fang, imx@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
> On Mon, 31 Aug 2026 10:54:26 +0800 wei.fang@oss.nxp.com wrote:
> > 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.
>
> You only replied to some of the AI feedback, should we assume that
> feedback on patches 12/13/14 is legit and expect v4?
I have not seen any other feedback on patches 12/13/14. Except for a
pre-existing issue on patch 14, but I do not think the issue needs to be
addressed.
Sashiko review link: https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
2026-09-03 3:24 ` Wei Fang (OSS)
@ 2026-09-03 23:22 ` Jakub Kicinski
2026-09-04 2:02 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: Jakub Kicinski @ 2026-09-03 23:22 UTC (permalink / raw)
To: Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, linux@armlinux.org.uk,
Wei Fang, imx@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
On Thu, 3 Sep 2026 03:24:24 +0000 Wei Fang (OSS) wrote:
> > On Mon, 31 Aug 2026 10:54:26 +0800 wei.fang@oss.nxp.com wrote:
> > > 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.
> >
> > You only replied to some of the AI feedback, should we assume that
> > feedback on patches 12/13/14 is legit and expect v4?
>
> I have not seen any other feedback on patches 12/13/14. Except for a
> pre-existing issue on patch 14, but I do not think the issue needs to be
> addressed.
>
> Sashiko review link: https://sashiko.dev/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
Please TAL at:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-7-wei.fang@oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 01/15] net: enetc: add trusted VF support
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 6:29 ` Wei Fang
1 sibling, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 2 · Low: 2
- [High] The patch breaks standard Linux SR-IOV semantics by
unconditionally preventing untrusted VFs from setting their MAC…
- [Medium] ENETC_VF_FLAG_TRUSTED is stored in pf->vf_state[], which is
devm_kcalloc()'ed once per PF at probe (enetc_init_sriov_resources(),…
- [Medium] Trust revocation leaves hardware state behind:
enetc_pf_set_vf_trust() clears only software flag bits.
- [Low] (Baseline verdict: false positive - retained only because the
concern is flagged requires_validation.) Claim: the commit describes a…
- [Low] (Baseline verdict: false positive - retained only because the
concern is flagged requires_validation.) Claim: both PF ops tables…
--
net: enetc: add trusted VF support
This adds a per-VF ENETC_VF_FLAG_TRUSTED flag stored in pf->vf_state[] and
wires up ndo_set_vf_trust through the new shared helper
enetc_pf_set_vf_trust() for both the enetc and enetc4 PF drivers. The VF
primary MAC address mailbox message is the first message gated on that
flag, and untrusted VFs now get ENETC_MSG_CLASS_ID_PERMISSION_DENY instead
of having the hardware programmed.
A couple of questions about the trust flag lifetime and about the
changelog below.
The changelog says:
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.
[Severity: Low]
Before this patch any VF address that passed is_valid_ether_addr() and the
ENETC_VF_FLAG_PF_SET_MAC check was programmed into the SI primary MAC
registers, so the description matches the pre-patch behaviour. Was a Fixes
tag considered here, given the wording describes an exploitable condition?
This is likely just a maintainer judgement call rather than a code problem:
the default-deny change is documented in the changelog itself, and the VF
side already maps the deny class to -EACCES in enetc_msg_vsi_send() with an
explicit comment, so the deny path looks like a coordinated design point.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index 9bb1004548ab..935a6a03b14f 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
[ ... ]
> @@ -574,6 +578,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,
> };
> 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);
[Severity: High]
This gate looks like it inverts the usual meaning of the trust
attribute, and it changes behaviour for existing VF users.
In ixgbe/i40e/ice the rule is: a VF may program its own MAC as long as
the PF has not administratively assigned one, and "trusted" is exactly
the knob that lets a VF go further and override the PF-assigned address.
Here both halves are reversed. The new check in
enetc_msg_set_vf_primary_mac_addr() returns ENETC_PF_MSG_PERM_DENY for
any VF without ENETC_VF_FLAG_TRUSTED, even when no PF-assigned address
exists (ENETC_VF_FLAG_PF_SET_MAC clear), and the following
ENETC_VF_FLAG_PF_SET_MAC test still refuses the request for a trusted
VF - the new comment states this explicitly ("even if it is trusted").
So trust neither grants the override it is supposed to grant, nor is it
irrelevant to the plain "VF picks its own MAC" case.
Since vf_state[] is devm_kcalloc()'ed in enetc_init_sriov_resources()
and flags starts at 0, every VF is untrusted at creation. That means
after this patch a guest that generates its own address and calls
ndo_set_mac_address on the VF gets the deny class back, which
enetc_msg_vsi_send() turns into -EACCES, and interface bring-up fails
unless the host has first run "ip link set ... vf N trust on". That is
a visible change for setups that work today, and there is no Fixes tag
or mention of the compatibility impact in the changelog.
Was the ixgbe-style rule considered instead, i.e. allow the mailbox MAC
change whenever ENETC_VF_FLAG_PF_SET_MAC is clear, and consult
ENETC_VF_FLAG_TRUSTED only to decide whether a VF may override a
PF-assigned address? If the stricter default-deny is deliberate for
this hardware, could the changelog spell out that untrusted VFs can no
longer set any MAC at all and that trust is now required for what used
to work by default?
[ ... ]
> 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);
^^^^^^^^^^^^^^^^^^^^^^
Note for the two questions further down: this is where the trusted VF's
chosen address is committed to the per-SI primary MAC registers, and the
driver keeps no copy of the address that was there before.
> +
> +vf_state_unlock:
> mutex_unlock(&vf_state->lock);
>
> - return ENETC_PF_MSG_SUCCESS;
> + return pf_msg;
> }
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> index 55c07c528f22..a7bf4bfc25b7 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> @@ -488,6 +488,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,
[Severity: Low]
At this commit neither enetc_ndev_ops nor enetc4_ndev_ops provides
.ndo_get_vf_config, so rtnl_fill_vfinfo() emits no VF info and the trust
bit set here cannot be read back with "ip link show".
This does get resolved inside the same series: the later commit "net:
enetc: add ndo_get_vf_config() support" adds enetc_pf_get_vf_config() with
ivi->trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
and registers it in both ops tables. Would it be worth ordering the getter
before or together with the setter so that no intermediate commit has a
write-only trust attribute?
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> index d32a195a04c9..519fc90d2647 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> @@ -586,5 +586,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;
[Severity: Medium]
Can ENETC_VF_FLAG_TRUSTED survive an SR-IOV teardown and be inherited by a
different VF instance later?
pf->vf_state[] is devm_kcalloc()'ed once per PF in
enetc_init_sriov_resources(), and the disable path in
enetc_sriov_configure() does not touch it:
if (!num_vfs) {
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
} else {
So for:
echo 2 > sriov_numvfs
ip link set eth0 vf 0 trust on
echo 0 > sriov_numvfs
echo 2 > sriov_numvfs
vf_state[0].flags still has ENETC_VF_FLAG_TRUSTED set, and the newly
created VF0 (possibly assigned to a different guest) passes the new gate in
enetc_msg_set_vf_primary_mac_addr() straight away and gets
enetc_set_si_hw_addr(pf, vf_id + 1, addr) executed for whatever address it
asks for.
Related: the bounds check here uses pf->total_vfs, the hardware maximum,
rather than pf->num_vfs, so trust can also be recorded for VF slots that do
not exist yet and takes effect as soon as they are created. Was that
intentional?
Later in the series enetc_msg_clear_vf_config() clears only the UC/MC
promiscuous bits and carries a comment saying TRUSTED and PF_SET_MAC are
intentionally preserved, and it returns early for ENETC v1, so the
carry-over is still present at the end of the series. Would clearing the
trust bit for all VFs on SR-IOV disable, as ixgbe and i40e effectively do
by reallocating their per-VF state, be preferable?
> + else
> + vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
> +
> + mutex_unlock(&vf_state->lock);
[Severity: Medium]
Does revoking trust leave the hardware MAC filter behind?
While trusted, the VF can install any valid unicast address, including the
PF's or another VSI's, through enetc_msg_set_vf_primary_mac_addr() ->
enetc_set_si_hw_addr(pf, vf_id + 1, addr). This path only clears software
flag bits, so after "ip link set ... vf N trust off" the SI primary MAC
filter still holds the address the VF picked, and the now-untrusted VF keeps
receiving frames for it.
There is also no saved copy of the address programmed at probe, so the
original value cannot be restored.
At the end of the series the untrust branch clears UC/MC promiscuous mode
and the hash filters (and skips even that for ENETC v1), but still never
re-programs or clears the primary MAC. ixgbe_ndo_set_vf_trust() and
i40e_ndo_set_vf_trust() force a VF reset on a trust change so the VF has to
re-negotiate its filters; would something equivalent be appropriate here?
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(enetc_pf_set_vf_trust);
> +
> MODULE_DESCRIPTION("NXP ENETC PF common functionality driver");
> MODULE_LICENSE("Dual BSD/GPL");
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-08-31 12:00 ` Andrew Lunn
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 7:16 ` Wei Fang
2 siblings, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 2 · Medium: 1 · Low: 1
- [High] A link-state notification that cannot be delivered is silently
discarded, leaving the VF with a permanently stale link/TX-PAUSE view.
- [High] enetc_pf_send_msg() performs a sleeping 200 ms poll
(read_poll_timeout(..., 1000, 200000, ...), enetc_msg.c:204) whose…
- [Medium] Per-VF link-change notifier registration (bit in
pf->link_status_ms_mask) has no teardown path tied to VF lifetime.
- [Low] enetc_pf_send_msg() logs "PF cannot send message to VF(s) 0x%x"
(enetc_msg.c:197-199) with the value `ms_mask ^ old_ms_mask`, which is…
--
net: enetc: add link status message support to PF driver
This adds a link status message class (0x80) to the ENETC PF mailbox so a
VF can query the current PF link state, register for link change
notifications, or unregister from them. Registered VFs are notified from
the PF phylink callbacks via the ENETC_PSIMSGSR register, with a new
pf->msg_lock mutex and a pf->link_status_ms_mask bitmask tracking which VFs
want notifications.
A few questions below about the notification delivery path.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index 935a6a03b14f..17fd9ee27942 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -899,6 +899,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,
> @@ -907,6 +908,7 @@ static void enetc4_pl_mac_link_down(struct phylink_config *config,
> {
> struct enetc_pf *pf = phylink_to_enetc_pf(config);
>
> + enetc_pf_notify_vf_link_down(pf);
> enetc4_mac_rx_graceful_stop(pf);
> enetc4_mac_tx_graceful_stop(pf);
> }
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> index 16da732dc5de..f97602714118 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)
> +
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index a89a5a418a23..e21414acdc0d 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> @@ -136,6 +136,154 @@ static u16 enetc_msg_handle_ip_revision(struct enetc_pf *pf, void *vf_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. To
> + * avoid sending messages to such a VF, the bit corresponding to VF
> + * is cleared from ms_mask. Because the MS bit can only be written
> + * as 1, writing a 0 has no effect. Writing a 1 when the bit is
> + * already set is undefined.
> + */
> + ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> + ms_mask &= ~ms_status;
> + if (!ms_mask)
> + return -EIO;
[Severity: High]
Can a link state change be lost permanently here?
The return happens before the enetc_wr(hw, ENETC_PSIMSGSR, ...) below, so
when the target VF still has an unread message the new link state is never
published, and enetc_msg_notify_vf_link_status() only prints
"PF notifies link status failed". Nothing records that the VF is out of
date, and notifications are generated only on the phylink transitions in
enetc4_pl_mac_link_up()/enetc4_pl_mac_link_down().
Single-VF sequence:
link up -> PSIMSGSR written with "link up", the poll below times out
after 200 ms because the VF has not read VSIMSGRR yet
link down -> ms_status still has MS(0) set, ms_mask becomes 0,
-EIO is returned and "link down" is never written
The VF then consumes the stale "link up" message and keeps carrier on plus
a stale TX PAUSE derived congestion-mode setting until the next PF link
transition. The same drop can hit the initial notification issued from
enetc_msg_register_link_status_notifier(), which has already replied
ENETC_MSG_CLASS_ID_CMD_SUCCESS to the VF.
Would it make sense to record a pending state and re-send it once the MS
bit is observed clear, rather than dropping the transition?
> +
> + 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);
[Severity: Low]
The printed value is a PSIMSGSR MS-bit mask, not a VF index mask, since
#define PSIMSGSR_MS(n) BIT((n) + 1)
so a stuck VF0 is reported as 0x2 and VF1 as 0x4. Should the value be
shifted back to VF numbering before printing, or should the message say
that it is an MS mask?
> +
> + 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);
> +}
[Severity: High]
This waits up to 200 ms for a bit that only the VF can clear, and every
caller holds pf->msg_lock across it. Can an untrusted guest use that to
stall the host?
The VF-initiated path has no ENETC_VF_FLAG_TRUSTED check (unlike the MAC
filter handlers) and no rate limiting:
enetc_msg_task()
enetc_msg_handle_rxmsg()
enetc_msg_handle_link_status() /* cmd_id from the VF */
enetc_msg_register_link_status_notifier()
mutex_lock(&pf->msg_lock);
enetc_msg_notify_vf_link_status()
enetc_pf_send_msg() /* sleeps up to 200 ms */
enetc_msg_task() handles all VFs sequentially in one work item with the MR
interrupts disabled, so a VF that registers, drains its mailbox and
re-registers in a loop keeps pf->msg_lock and the mailbox work item busy
and denies mailbox service to the sibling VFs.
The same pf->msg_lock is taken by enetc_pf_notify_vf_link_status() from
enetc4_pl_mac_link_up()/enetc4_pl_mac_link_down(), and phylink runs those
from its resolve work, which is flushed synchronously from ndo_open/
ndo_stop with rtnl_lock held:
drivers/net/phy/phylink.c:phylink_run_resolve_and_disable() {
...
flush_work(&pl->resolve);
...
}
so the guest-induced stall propagates into the host PF link handling and
rtnl-held paths. In the other direction, one unresponsive registered VF
adds up to 200 ms to every PF link transition, because the poll condition
!(val & ms_mask) waits for all bits of the aggregate mask.
Is the synchronous acknowledgement wait needed at all here? Link state is
idempotent, so a fire-and-forget write with a deferred retry, or at least
dropping pf->msg_lock before waiting, would keep the PF link state machine
independent of VF behaviour. The commit message does not mention that a PF
link transition can now block on VF acknowledgement.
> +
> +static void enetc_msg_notify_vf_link_status(struct enetc_pf *pf, u16 ms_mask)
> +{
> + struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
> + u16 pf_msg;
> +
> + pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
> + 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_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);
[Severity: Medium]
What clears this bit if the VF goes away without unregistering?
The bit is set here and cleared only by a cooperative
ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER in
enetc_msg_unregister_link_status_notifier(), or globally in
enetc_sriov_configure() when SR-IOV is disabled. A VM destroy, a guest
crash/reboot, or a killed DPDK application leaves the bit set.
After that, every PF link transition reaches enetc_pf_send_msg() with the
stale mask and either writes the MS bit for a VF that will never read it
and then blocks the full 200 ms with pf->msg_lock held from the phylink
callbacks, or, once the MS bit is stuck set, filters that VF out and (when
it is the only registered VF) returns -EIO, logging "PF notifies link
status failed" indefinitely. Since the poll waits for all bits of the mask,
one dead VF also delays notifications to the live ones.
The FLR handler added later in the series, enetc4_pf_vf_flr_handler(),
only restores the promiscuous mode bits and does not touch
link_status_ms_mask, and enetc_sriov_configure() resets only the software
mask without bringing PSIMSGSR back to a known state. Should the per-VF
bit be cleared on FLR / VF teardown as well?
> +
> + /* Notify VF the current link status */
> + enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id));
> +
> +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.
> + */
> + enetc_msg_get_link_status(pf, vf_id);
> + break;
> + case ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER:
> + enetc_msg_register_link_status_notifier(pf, vf_id);
> + break;
> + case ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER:
> + enetc_msg_unregister_link_status_notifier(pf, vf_id);
> + break;
> + default:
> + return ENETC_PF_MSG_NOTSUPP;
> + }
> +
> + return 0;
> +}
[ ... ]
> @@ -367,6 +519,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;
[ ... ]
> @@ -396,3 +567,34 @@ 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)
> +{
> + /* pf->msg_lock is initialized when pf->total_vfs is not 0 */
> + if (!pf->total_vfs)
> + return;
> +
> + mutex_lock(&pf->msg_lock);
> +
> + pf->link_up = link_up;
> + if (!pf->link_status_ms_mask)
> + goto msg_unlock;
> +
> + enetc_msg_notify_vf_link_status(pf, pf->link_status_ms_mask);
> +
> +msg_unlock:
> + mutex_unlock(&pf->msg_lock);
> +}
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-08-31 2:54 ` [PATCH v3 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 7:52 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 2 · Low: 1
- [High] Global rtnl_lock() is acquired from inside the PF mailbox work
item to answer a VF-originated request.
- [Medium] The new link-speed query is silently restricted to trusted
VFs, which neither the commit message nor the in-tree ABI description…
- [Medium] enetc_msg_get_link_speed() answers
ENETC_MSG_GET_CURRENT_LINK_SPEED (documented in enetc_mailbox.h as…
- [Low] The patch contains a stray hunk in enetc_sriov_configure()
(drivers/net/ethernet/freescale/enetc/enetc_msg.c) that only inserts a…
--
net: enetc: add link speed message support to PF driver
This adds a new PSI-to-VSI mailbox class (0x81) so a VF can ask the PF for
the current link speed, encoding speeds up to 5Gbps as explicit enum values
and higher speeds via a formula. The PF answers the query from
phylink_ethtool_ksettings_get() and only for VFs marked trusted.
A few questions below.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
> index 846998f07989..bd669543e96c 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
[ ... ]
> @@ -181,6 +212,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
> */
[Severity: Medium]
This documents cmd_id 0x0 as returning the current link speed, but can the
reply describe a speed the port is not running at?
enetc_msg_get_link_speed() takes the value straight from
phylink_ethtool_ksettings_get() with no link-state gating. For a fixed
link, phylink copies the configured speed/duplex unconditionally:
drivers/net/phy/phylink.c:phylink_ethtool_ksettings_get() {
...
case MLO_AN_FIXED:
phylink_get_fixed_state(pl, &link_state);
phylink_get_ksettings(&link_state, kset);
break;
...
}
So with the carrier down, or before the PF netdev has ever been opened,
enetc_build_link_speed_msg() still encodes a valid speed class code rather
than ENETC_MSG_SPEED_UNKNOWN. The MLO_AN_INBAND case reads the PCS live,
which also need not match what the MAC was last programmed with.
Would it be more consistent to derive the answer from the state the PF
already tracks, the way the sibling link-status message does?
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_get_link_status() {
...
pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
...
}
priv->speed is written next to PCR_PSPEED in enetc4_set_port_speed()
(including the 10Mbps fallback on SPEED_UNKNOWN), so the programmed rate is
already recorded. As written, a VF can be told 1G/2.5G while the same PF
reports the link as down over class 0x80.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index e21414acdc0d..c3ae4c024f34 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> @@ -280,6 +280,93 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
> return 0;
> }
>
> +static u16 enetc_build_link_speed_msg(int speed, int duplex)
> +{
> + u32 speed_code = ENETC_MSG_SPEED_UNKNOWN;
> +
> + switch (speed) {
[ ... ]
> + return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
> + ENETC_MSG_CLASS_ID_LINK_SPEED) |
> + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, speed_code);
> +}
> +
> +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 rtnl_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);
[Severity: Medium]
Should the trusted-VF requirement be described in the changelog and in the
mailbox ABI comment in enetc_mailbox.h?
ENETC_VF_FLAG_TRUSTED is only ever set from enetc_pf_set_vf_trust()
(ndo_set_vf_trust, i.e. "ip link set <pf> vf N trust on"), and pf->vf_state
is allocated with devm_kcalloc() in enetc_init_sriov_resources(), so flags
is zero by default. A default setup therefore gets
ENETC_PF_MSG_PERM_DENY / -EACCES back from enetc_msg_vsi_send().
The commit message says without qualification:
"The PF will reply the current link speed when it receives the get link
speed message from VF."
"VFs controlled by DPDK can obtain real-time link speed information from
the PF"
The ABI comment added to struct enetc_msg_generic also describes class 0x81
cmd 0x0 with the same wording as the 0x80 status query, and
enetc_msg_get_link_status() answers any VF with no trust check. Two paired
link-info queries with different permission models is surprising for a
DPDK VF implementer reading only the header.
> +
> + rtnl_lock();
> + phylink_ethtool_ksettings_get(priv->phylink, &link_info);
> + rtnl_unlock();
[Severity: High]
Can blocking on rtnl here stall mailbox service for the other VFs?
This runs from the single work item that services every VF with the PSIIER
MR interrupts masked:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_task() {
...
for (i = 0; i < pf->num_vfs; i++) {
...
enetc_msg_handle_rxmsg(pf, i, &msg_code);
...
}
out:
enetc_enable_psiier_interrupts(pf);
}
rtnl hold times elsewhere (ndo_open with PCS/PHY negotiation, netdev
unregistration, another driver probing, netlink dumps) are unbounded, while
the VF side has a hard 200ms budget:
drivers/net/ethernet/freescale/enetc/enetc_vf.c:enetc_msg_vsi_send() {
...
err = read_poll_timeout(enetc_rd, vsimsgsr,
!(vsimsgsr & ENETC_VSIMSGSR_MB),
1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
if (err) {
dev_err(dev, "VSI mailbox timeout\n");
...
}
So one iteration waiting on rtnl can push unrelated requests from other VFs
past that timeout, with a hardware mailbox transaction left outstanding.
For a host-assigned VF, enetc_msg_vsi_set_primary_mac_addr() is issued from
ndo_set_mac_address with rtnl already held, i.e. the VF waits for a PF reply
that needs the very lock the VF holds.
There is a second effect on teardown: enetc_msg_psi_free() does
cancel_work_sync(&si->msg_task), and it is called from
enetc_sriov_configure() (pci_driver::sriov_configure, invoked from
sriov_numvfs_store() under the PCI device_lock) and from
enetc_pf_remove()/enetc4_pf_remove(). Does SR-IOV disable/removal now wait
for rtnl while holding device_lock, for a time a busy VF can extend?
Every other handler in this file avoids rtnl. enetc_msg_get_link_status()
answers from the cached pf->link_up under pf->msg_lock, and speed/duplex are
already handed to enetc4_pl_mac_link_up() where they could be cached the
same way. Would caching the speed there and replying from the cache work
instead?
[ ... ]
> @@ -546,6 +636,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;
> }
> +
> }
[Severity: Low]
This isn't a bug, but this hunk only adds a blank line before a close brace
in enetc_sriov_configure(), which is unrelated to link speed messages and
trips checkpatch ("Blank lines aren't necessary before a close brace"). It
is still present at the end of the series, so nothing later removes it.
Could it be dropped?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
2026-08-31 2:54 ` [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
0 siblings, 0 replies; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 3 · Low: 1
- [Medium] Commit message mischaracterizes the privilege model of
ENETC_MSG_SET_MAC_HASH_TABLE.
- [Medium] The patch lets a VF arm PF-owned hardware filtering state
(PSIPMMR unicast/multicast promiscuous bits and the per-SI…
- [Medium] pf->msg_lock is now used for two incompatible purposes: (a) as
the mailbox transaction lock, held across a sleeping hardware poll -…
- [Low] Concern that the patch adds the PF-side mailbox protocol
(ENETC_MSG_SET_MAC_HASH_TABLE, ENETC_MSG_SET_MAC_PROMISC_MODE) without…
--
net: enetc: add MAC address filtering support for VFs of ENETC v4
This adds two VSI-to-PSI mailbox commands so an ENETC v4 VF can ask the PF
to program its per-SI MAC hash filter tables and to change its unicast and
multicast promiscuous mode bits in PSIPMMR. The PF-owned PSIPMMR
read-modify-write is now serialized with pf->msg_lock, and losing trusted
status clears part of the VF's filtering state.
A few questions below.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index 363ec562934e..a4ffe1100bd7 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
[ ... ]
> @@ -528,8 +523,10 @@ static int enetc4_pf_set_rx_mode(struct net_device *ndev,
> type = ENETC_MAC_FILTER_TYPE_ALL;
> }
>
> + enetc_pf_msg_lock(pf);
> enetc_set_si_uc_promisc(si, 0, uc_promisc);
> enetc_set_si_mc_promisc(si, 0, mc_promisc);
> + enetc_pf_msg_unlock(pf);
[Severity: Medium]
Is pf->msg_lock the right lock to reuse for the PSIPMMR read-modify-write
here? It looks like msg_lock is also the mailbox transaction lock, and it
is held across a sleeping hardware poll:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:
enetc_msg_register_link_status_notifier() {
mutex_lock(&pf->msg_lock);
...
enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id));
msg_unlock:
mutex_unlock(&pf->msg_lock);
}
which ends in:
enetc_pf_send_msg() {
...
return read_poll_timeout(enetc_rd, val, !(val & ms_mask), 1000,
200000, false, hw, ENETC_PSIMSGSR);
}
enetc_pf_notify_vf_link_status() takes the same mutex around the same poll.
enetc4_pf_set_rx_mode() is the ndo_set_rx_mode_async callback, which runs
with rtnl held (netif_rx_mode_run() asserts it for a driver that sets none
of request_ops_lock/queue_mgmt_ops/net_shaper_ops, and netdev_work_proc()
takes rtnl_lock() unconditionally). Can a VF that never drains its mailbox
reply then keep the PF's address-list/promiscuity update, and the global
rtnl mutex, waiting up to 200 ms per poll? The same helper is now taken by
the mac_filter debugfs reader in enetc_mac_filter_show().
Would a small dedicated lock for the PSIPMMR read-modify-write, separate
from the mailbox transaction mutex, avoid coupling a couple of register
writes to a guest-controlled sleeping poll?
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index c3ae4c024f34..d58fbaeaf46c 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -108,6 +110,132 @@ 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;
> + u16 pf_msg = ENETC_PF_MSG_SUCCESS;
> + struct enetc_si *si = pf->si;
> + int si_id = vf_id + 1;
> + u64 uc_hash, mc_hash;
> + bool trusted;
> + int type;
> +
> + /* 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];
> + enetc_set_si_uc_hash_filter(si, si_id, uc_hash);
> + break;
> + case ENETC_MAC_FILTER_TYPE_MC:
> + mc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
> + if (!trusted &&
> + hweight64(mc_hash) > ENETC_VF_MC_HASH_BITS_MAX) {
> + pf_msg = ENETC_PF_MSG_PERM_DENY;
> + goto vf_state_unlock;
> + }
> +
> + enetc_set_si_mc_hash_filter(si, si_id, mc_hash);
> + break;
[Severity: Medium]
Does the changelog match the policy implemented here? It says:
"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."
The ENETC_MAC_FILTER_TYPE_MC case does not test ENETC_VF_FLAG_TRUSTED at
all. An untrusted VF gets its multicast hash filter programmed as long as
no more than ENETC_VF_MC_HASH_BITS_MAX of the 64 buckets are set.
The TYPE_ALL case below does the same: for an untrusted VF it applies the
multicast half of the request and then reports UCF_DENY.
Neither the new ENETC_VF_MC_HASH_BITS_MAX constant nor the "up to 8 buckets
for untrusted VFs" rule appears in the changelog. Could the commit message
describe this rule, and where the value 8 comes from?
> + 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) <= ENETC_VF_MC_HASH_BITS_MAX)) {
> + enetc_set_si_mc_hash_filter(si, si_id, mc_hash);
> + pf_msg = ENETC_PF_MSG_MF(ENETC_MF_CLASS_CODE_UCF_DENY);
> + goto vf_state_unlock;
> + }
[ ... ]
> +static u16 enetc_msg_set_vf_mac_promisc_mode(struct enetc_pf *pf, int vf_id,
> + void *vf_msg)
> +{
[ ... ]
> + mutex_lock(&pf->msg_lock);
> +
> + if (type & ENETC_MAC_FILTER_TYPE_UC)
> + enetc_set_si_uc_promisc(si, si_id, promisc);
> +
> + if (type & ENETC_MAC_FILTER_TYPE_MC)
> + enetc_set_si_mc_promisc(si, si_id, promisc);
> +
> + mutex_unlock(&pf->msg_lock);
[Severity: Medium]
This arms PSIPMMR_SI_MAC_UP/MP for the VF's SI, and the hash filter paths
above write PSIUMHFR/PSIMMHFR for that SI. Those registers live in the PF
port register space, so a VF-level reset does not clear them.
What clears them when a single VF goes away while SR-IOV stays enabled, for
example on VF driver unbind or guest reboot? The only disarm paths added
here are enetc_pf_set_vf_trust(ndev, vf, false) and
enetc_msg_clear_vf_config(), and the latter only runs from
enetc_msg_psi_free(), i.e. full SR-IOV teardown.
If neither runs, does the SI stay in promiscuous mode with the previously
programmed hash buckets, so that the next driver bound to that VF inherits
filtering scope it never asked for? An untrusted VF can also leave up to
ENETC_VF_MC_HASH_BITS_MAX multicast buckets armed this way.
The later patch "net: enetc: restore VF MAC promiscuous mode after FLR for
ENETC v4" adds enetc4_pf_vf_flr_handler(), but that re-applies the tracked
ENETC_VF_FLAG_UC_PROMISC/MC_PROMISC state instead of clearing it and does
not touch the hash filter tables, so the gap seems to remain at the end of
the series.
[ ... ]
> @@ -582,6 +720,31 @@ static int enetc_msg_psi_init(struct enetc_pf *pf)
> return err;
> }
>
> +static void enetc_msg_clear_vf_config(struct enetc_pf *pf, int vf_id)
> +{
> + struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
> + struct enetc_si *si = pf->si;
> + int si_id = vf_id + 1;
> +
> + /* For ENETC v1, we only support setting the VF's MAC address via
> + * VSI-to-PSI messages, so there is no configuration to clear.
> + */
> + if (is_enetc_rev1(si))
> + return;
> +
> + mutex_lock(&vf_state->lock);
> +
> + mutex_lock(&pf->msg_lock);
> + enetc_set_si_uc_promisc(si, si_id, false);
> + enetc_set_si_mc_promisc(si, si_id, false);
> + mutex_unlock(&pf->msg_lock);
> +
> + enetc_set_si_uc_hash_filter(si, si_id, 0);
> + enetc_set_si_mc_hash_filter(si, si_id, 0);
> +
> + mutex_unlock(&vf_state->lock);
> +}
> +
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> index 06dc47164dc5..12e67f611f77 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> @@ -6,6 +6,7 @@
>
> #define ENETC_PF_NUM_RINGS 8
> #define ENETC_VLAN_HT_SIZE 64
> +#define ENETC_VF_MC_HASH_BITS_MAX 8 /* For untrusted VFs */
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> index 7a11370d2b8e..8007dce90195 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
[ ... ]
> @@ -600,11 +620,39 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
> + /* Disable unicast and multicast promiscuous modes */
> + mutex_lock(&pf->msg_lock);
> + enetc_set_si_uc_promisc(si, si_id, false);
> + enetc_set_si_mc_promisc(si, si_id, false);
> + mutex_unlock(&pf->msg_lock);
> +
> + /* Clear unicast hash filter */
> + enetc_set_si_uc_hash_filter(si, si_id, 0);
> +
> + /* Clear multicast hash filter if its set bits exceed
> + * ENETC_VF_MC_HASH_BITS_MAX.
> + */
> + hash = enetc_get_si_mc_hash_filter(si, si_id);
> + if (hweight64(hash) > ENETC_VF_MC_HASH_BITS_MAX)
> + enetc_set_si_mc_hash_filter(si, si_id, 0);
> + }
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
> index 1d35e906fc3a..91a9c339245a 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
> @@ -30,6 +30,18 @@ static inline u16 enetc_get_ip_revision(struct enetc_hw *hw)
> return enetc_global_rd(hw, ENETC_G_EIPBRR0) & EIPBRR0_REVISION;
> }
>
> +static inline void enetc_pf_msg_lock(struct enetc_pf *pf)
> +{
> + if (pf->total_vfs)
> + mutex_lock(&pf->msg_lock);
> +}
> +
> +static inline void enetc_pf_msg_unlock(struct enetc_pf *pf)
> +{
> + if (pf->total_vfs)
> + mutex_unlock(&pf->msg_lock);
> +}
> +
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-08-31 2:54 ` [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 8:40 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 0
- [Medium] The promiscuous-mode revocation after a VF FLR is fully
asynchronous: enetc_msg_psi_msix() (enetc_msg.c:36-45) masks PSIIER…
--
net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
On ENETC v4 a VF FLR resets PSIPMMR[SIn_MAC_UP] and PSIPMMR[SIn_MAC_MP]
to their default, which enables unicast and multicast promiscuous mode
for that SI. The patch tracks the PF-managed promiscuous state in
enetc_vf_flags and adds a vf_flr_handler callback that re-applies it from
enetc_msg_task() after each FLR.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index a4ffe1100bd7..c421c0e7355b 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -212,9 +212,29 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type,
> enetc4_pf_set_mc_hash_filter(pf, mc);
> }
>
> +static void enetc4_pf_vf_flr_handler(struct enetc_pf *pf, int vf_id)
> +{
> + 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);
> + enetc_set_si_uc_promisc(pf->si, vf_id + 1, uc_promisc);
> + enetc_set_si_mc_promisc(pf->si, vf_id + 1, 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,
> + .vf_flr_handler = enetc4_pf_vf_flr_handler,
> };
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index 4aabeb23a386..55c23d4a73a8 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> @@ -27,6 +27,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);
> }
[ ... ]
> @@ -594,6 +611,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);
> @@ -602,6 +642,8 @@ static void enetc_msg_task(struct work_struct *work)
> u32 mr_status, mr_mask;
> int i;
>
> + enetc_vf_flr_handler(pf);
> +
[Severity: Medium]
How long can the SI stay in the hardware promiscuous state before this
restore runs?
The hardware puts the SI into UC/MC promiscuous mode at the instant of the
FLR, but the policy is only re-applied from the work item:
enetc_msg_psi_msix() {
enetc_disable_psiier_interrupts(pf);
schedule_work(&si->msg_task);
return IRQ_HANDLED;
}
enetc_msg_task() -> enetc_vf_flr_handler() -> pf->ops->vf_flr_handler()
-> enetc4_pf_vf_flr_handler()
Since the guest owning the VF triggers the FLR itself (vfio-pci reset, or a
driver bind path reaching pcie_flr()), it knows exactly when that window
opens and only needs to re-arm an Rx BD ring to receive frames destined for
other SIs until enetc4_pf_vf_flr_handler() clears the bits in PSIPMMR.
The scan also happens once, at the top of enetc_msg_task():
enetc_vf_flr_handler(pf);
mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
and enetc_vf_flr_handler() takes a single PSIIDR snapshot with an early
return:
flr_status = enetc_rd(hw, ENETC_PSIIDR) & flr_mask;
if (!flr_status)
return;
If an FLR lands while msg_task is already part-way through the VF message
loop, is the restore then delayed until the whole in-flight batch finishes
and the re-queued work runs? The FLR bits are not re-checked before
enetc_enable_psiier_interrupts(pf) at the end of the work item.
Would it be feasible to clear PSIPMMR[SIn_MAC_UP]/[SIn_MAC_MP] directly in
enetc_msg_psi_msix() for the VFs whose FLR bits are set, and leave the full
policy re-apply in the work item? As written the handler sleeps on
vf_state->lock and pf->msg_lock, so it cannot run from the hardirq.
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95
2026-08-31 2:54 ` [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 9:05 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 2 · Low: 0
- [High] An untrusted VF can repeatedly force the PF mailbox worker to
wait up to 200 ms while holding pf->msg_lock.
- [Medium] Commit message claims driver-side recognition of per-instance
VF capabilities that the patch does not implement, and is internally…
- [Medium] Blocking mailbox wait under rtnl_lock vs. a PF mailbox worker
that itself takes rtnl_lock.
--
net: enetc: add VF support for i.MX94 and i.MX95
This adds the ENETC v4 VF PCI device ID 0xef00 to the VF driver, adds VF
driver data and enetc4 SI ops for revisions 4.1 and 4.3, and hooks
.sriov_configure into the enetc4 PF driver so VFs can be created on i.MX94
and i.MX95. It also restricts the rxnfc ethtool callbacks to revision 1.
There are no Link: tags in the commit header.
A few questions below.
[Severity: Medium]
The commit message says:
> 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
Are these two statements consistent? The first says each i.MX95 instance
supports 2 VFs, the second says the i.MX94 standalone ENETC is the same as
i.MX95 and supports no VFs.
The message then says:
> The driver is updated to recognize these SoC-specific VF capabilities
> and handle each ENETC instance accordingly.
Where does that recognition happen? The only PF-side change here is the
unconditional .sriov_configure in enetc4_pf_driver, which applies to every
entry of enetc4_pf_id_table (both the standalone PF 0xe101 and the PPM
0xe110), plus the VF teardown in enetc4_pf_remove(). There is no new field
in struct enetc_drvdata and no revision or device-ID test bounding the VF
count; enetc_init_sriov_resources() in enetc_pf_common.c still derives it
purely from the PCI capability:
pf->total_vfs = pci_sriov_get_totalvfs(pf->si->pdev);
and that function is not touched by this patch. So if the i.MX94
standalone instance advertises a non-zero TotalVFs, a write to
sysfs sriov_numvfs would still be accepted on the instance the message
declares unsupported. Is the gating code missing from the patch, or should
the message be reworded?
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index c421c0e7355b..a945a120c553 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
[ ... ]
> @@ -1138,6 +1141,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);
[Severity: Medium]
Enabling .sriov_configure here, together with the new 0xef00 VF ID, makes
the PSI/VSI mailbox path live on ENETC v4 for the first time. Can the PF
mailbox worker and a VF mailbox sender deadlock against each other on
rtnl_lock until the poll times out?
The PF worker path takes rtnl_lock while serving a VF request:
enetc_msg_task() -> enetc_msg_handle_rxmsg() -> enetc_msg_handle_link_speed()
-> enetc_msg_get_link_speed() {
rtnl_lock();
phylink_ethtool_ksettings_get(priv->phylink, &link_info);
rtnl_unlock();
}
The VF side runs from ndo_set_mac_address, so rtnl is already held, and it
then waits up to 200 ms for the PF to answer:
enetc_vf_set_mac_addr() -> enetc_msg_vsi_set_primary_mac_addr()
-> enetc_msg_vsi_send() {
err = read_poll_timeout(enetc_rd, vsimsgsr,
!(vsimsgsr & ENETC_VSIMSGSR_MB),
1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
}
If the single worker is servicing a trusted VF's link-speed request while a
kernel-bound VF issues its own request, the worker blocks in rtnl_lock()
held by that VF's sender, so the VF's message cannot be consumed. The VF
operation then fails with "VSI mailbox timeout" after holding rtnl for
200 ms, and the still-unconsumed message makes the following VF sends fail
with "VSI mailbox is busy" (-EIO) until the worker drains it.
The trigger needs a mixed configuration: one trusted VF issuing link-speed
queries (for example a vfio/DPDK-owned VF, since untrusted requests return
PERM_DENY before rtnl_lock) plus a second kernel-bound ENETC v4 VF on the
same host. The later patch adding ndo_set_rx_mode_async for the ENETC v4
VF introduces a second rtnl-held 200 ms mailbox waiter on the same path.
[Severity: High]
With SR-IOV now available on ENETC v4, can an untrusted VF repeatedly stall
the PF mailbox worker for 200 ms at a time while pf->msg_lock is held?
enetc_msg_handle_link_status() dispatches
ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER to
enetc_msg_register_link_status_notifier() with no ENETC_VF_FLAG_TRUSTED
check, unlike enetc_msg_get_link_speed() which does test the flag. That
function holds pf->msg_lock across the send:
enetc_msg_register_link_status_notifier() {
mutex_lock(&pf->msg_lock);
...
enetc_msg_notify_vf_link_status(pf, PSIMSGSR_MS(vf_id));
msg_unlock:
mutex_unlock(&pf->msg_lock);
}
and the send waits for the VF to consume the notification:
enetc_pf_send_msg() {
...
return read_poll_timeout(enetc_rd, val, !(val & ms_mask), 1000,
200000, false, hw, ENETC_PSIMSGSR);
}
A guest can loop: acknowledge the previous notification so its MS bit is
clear (otherwise the next enetc_pf_send_msg() short-circuits with -EIO),
re-register, and then never read the new notification. Each iteration
occupies the single VF mailbox work item enetc_msg_task() for 200 ms with
PSIIER interrupts disabled and pf->msg_lock held.
Does that starve the other VFs? Their enetc_msg_vsi_send() has the same
200 ms timeout, so their ndo_set_mac_address and rx-mode requests would
fail, and the PF link path enetc4_pl_mac_link_up()/enetc4_pl_mac_link_down()
-> enetc_pf_notify_vf_link_up()/_down() -> mutex_lock(&pf->msg_lock) would
stall for up to 200 ms per transition. Would a trust check on the
registration path, or a non-blocking notification, be preferable here?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-08-31 2:54 ` [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 9:53 ` Wei Fang
0 siblings, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 1 · Low: 2
- [High] For the default (untrusted) ENETC v4 VF the PF handlers reject
exactly the messages the new callback sends, and…
- [Medium] enetc_vf_set_rx_mode() narrows the SI's promiscuous
configuration before the replacement MAC hash filters are installed,…
- [Low] The commit message describes the change as a plain Rx-mode
offload implementation for ENETC v4 VFs and never mentions that every…
- [Low] The single enetc_ndev_ops table used by all ENETC VFs now
installs '.ndo_set_rx_mode_async = enetc_vf_set_rx_mode'…
--
net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
This adds ndo_set_rx_mode_async to the ENETC VF driver so MAC filter
updates can be sent to the PF through the sleepable VSI mailbox path, and
sets IFF_UNICAST_FLT for ENETC v4 VFs. Two helpers are added, one for the
MAC promiscuous mode message and one for the 64-bit hash filter message.
A few questions about the interaction with the PF-side trust checks and
about the ordering of the two mailbox messages.
First, on the commit message itself:
> 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.
[Severity: Low]
Could the commit message mention that all of these messages are subject to
the PF-side ENETC_VF_FLAG_TRUSTED check in enetc_msg.c?
For an untrusted VF (the default), enetc_msg_set_vf_mac_promisc_mode()
denies any promisc-enable and enetc_msg_set_vf_mac_hash_filter() denies
unicast hash programming, so the described IFF_PROMISC and IFF_ALLMULTI
behaviour does not take effect, and the justification for IFF_UNICAST_FLT
("unicast address filtering is supported by the hardware") only holds for
trusted VFs.
The message also documents only the ENETC v1 early return, not the new
policy at the out: label that maps -EOPNOTSUPP, -EACCES and -EPERM to 0.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index 322705202d49..4e717afba7f7 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> @@ -213,6 +213,142 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
> }
> }
>
> +static int enetc_vf_set_mac_promisc(struct enetc_si *si, int type, bool en)
> +{
> + struct enetc_msg_mac_promisc_mode *msg;
> + struct device *dev = &si->pdev->dev;
> + 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(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(si, &msg_swbd);
> +}
> +
> +static int enetc_vf_set_mac_hash_filter(struct enetc_si *si,
> + struct netdev_hw_addr_list *uc,
> + struct netdev_hw_addr_list *mc)
> +{
> + struct enetc_msg_mac_hash_filter *msg;
> + struct enetc_mac_filter *mac_filter;
> + struct device *dev = &si->pdev->dev;
> + struct net_device *ndev = si->ndev;
> + struct enetc_msg_swbd msg_swbd;
> + struct netdev_hw_addr *ha;
> + u32 msg_size, tbl_cnt;
> + int mac_filter_type;
> + 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;
> + }
> +
[ ... ]
> + 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);
> +
> + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
> + ENETC_MADDR_HASH_TBL_SZ);
> + i += 2;
> + }
> +
> + 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);
> +
> + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
> + ENETC_MADDR_HASH_TBL_SZ);
> + }
> +
> + 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 int 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;
> + int err;
> +
> + /* For ENETC v1, we cannot return -EOPNOTSUPP or any other error,
> + * otherwise ndev->rx_mode_retry_timer will try to set rx_mode
> + * multiple times, which is pointless.
> + */
> + if (is_enetc_rev1(si))
> + return 0;
> +
> + if (ndev->flags & IFF_PROMISC) {
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> + true);
> + } else if (ndev->flags & IFF_ALLMULTI) {
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
> + false);
> + if (err)
> + goto out;
> +
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
> + true);
> + } else {
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> + false);
> + }
> +
> + if (err)
> + goto out;
> +
> + err = enetc_vf_set_mac_hash_filter(si, uc, mc);
> +
[Severity: Medium]
Is the order of these two mailbox transactions the right way round?
enetc_vf_set_mac_promisc() sends FLUSH_MACS together with en, and the PF
handler zeroes both hash filter registers when FLUSH_MACS is set:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_promisc_mode() {
...
if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
enetc_set_si_uc_hash_filter(si, si_id, 0);
if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
enetc_set_si_mc_hash_filter(si, si_id, 0);
...
}
So while promiscuous mode is active the SI hash filters are known to be
empty (enetc_vf_set_mac_hash_filter() also returns early for IFF_PROMISC).
On the way out of promiscuous mode this function completes the
promisc-disable round trip first and only then installs the replacement
hash filters.
Each enetc_msg_vsi_send() is a blocking round trip polled with
read_poll_timeout(..., 1000, 200000, ...). Between the two sends the SI has
promiscuity cleared and empty hash filters - can that drop all multicast
and secondary unicast frames for the duration, two round trips in the
IFF_ALLMULTI branch?
And if the hash filter send then fails (-EIO "VSI mailbox is busy", -ETIME
on mailbox timeout, -EBUSY for a busy PSI), the promisc-off write is
already committed and is not undone; netif_rx_mode_schedule_retry() gives
up after NETIF_RX_MODE_RETRY_MAX attempts:
net/core/dev_addr_lists.c:netif_rx_mode_schedule_retry() {
if (dev->rx_mode_retry_count >= NETIF_RX_MODE_RETRY_MAX) {
netdev_err(dev, "rx_mode retry limit reached, giving up\n");
return;
}
...
}
Would programming the new (superset) filters before narrowing the
promiscuous bits avoid both the window and the stuck state? The PF side
already does something similar in enetc4_pf_set_uc_exact_filter() with its
temporary unicast hash filter.
> +out:
> + /* If the error code is -EOPNOTSUPP or -EACCES or -EPERM, return 0
> + * directly to avoid meaningless retries.
> + */
> + if (err == -EOPNOTSUPP || err == -EACCES || err == -EPERM)
> + return 0;
> +
> + return err;
> +}
> +
[Severity: High]
For an untrusted VF - the default, since pf->vf_state is allocated with
devm_kcalloc() and ENETC_VF_FLAG_TRUSTED is only set by
enetc_pf_set_vf_trust() - the PF handlers reject exactly the messages this
callback sends.
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_promisc_mode() {
...
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;
}
...
}
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_hash_filter() {
...
case ENETC_MAC_FILTER_TYPE_UC:
if (!trusted) {
pf_msg = ENETC_PF_MSG_PERM_DENY;
goto vf_state_unlock;
}
...
case ENETC_MAC_FILTER_TYPE_ALL:
...
if (!trusted &&
(hweight64(mc_hash) <= ENETC_VF_MC_HASH_BITS_MAX)) {
enetc_set_si_mc_hash_filter(si, si_id, 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;
}
...
}
enetc_msg_vsi_send() maps PERMISSION_DENY and UCF_DENY to -EACCES and
returns before the dev_err(), so there is not even a log line.
In the IFF_ALLMULTI branch, does the MC promisc-enable message therefore
always fail on an untrusted VF, making "if (err) goto out;" skip
enetc_vf_set_mac_hash_filter() completely?
Even when it is reached, enetc_vf_set_mac_hash_filter() selects
mac_filter_type = ENETC_MAC_FILTER_TYPE_UC under IFF_ALLMULTI, so the mc
snapshot handed in by the core is never converted into an MC hash table.
With MC promiscuous mode denied as well, does the multicast hash filter
stay frozen at its previous contents - so groups joined after the VF is
enslaved to a bridge or gets a macvlan on top (both call
dev_set_allmulti()), such as IPv6 solicited-node addresses or mDNS, are
silently dropped by hardware?
In the default branch the TYPE_ALL message is refused outright once more
than ENETC_VF_MC_HASH_BITS_MAX (8) multicast hash bits are set, so
PSIMMHFR0/1 keeps its stale value. Would it be worth falling back to the
MC-only message, which the PF does accept from an untrusted VF?
The out: block then turns -EACCES, -EPERM and -EOPNOTSUPP into 0, and the
core treats that as "installed":
net/core/dev_addr_lists.c:netif_rx_mode_run() {
...
if (err)
netif_rx_mode_schedule_retry(dev);
else
dev->rx_mode_retry_count = 0;
...
}
Can this leave the SI hash filters permanently out of sync with the address
list the stack believes is programmed, with no retry and no message? Since
this patch also sets IFF_UNICAST_FLT, the core's unicast promiscuous
fallback is suppressed too.
Two related points: the promisc-disable message carries FLUSH_MACS = en =
false, so a denied hash filter update can also leave hardware more
permissive than requested (removed multicast groups keep being received).
And when trust is granted later, enetc_pf_set_vf_trust() only flips the
PF-side flag - nothing replays or re-requests the receive mode
configuration that the VF already reported as successfully installed, so
does a configuration denied while untrusted stay unapplied indefinitely?
> /* Probing/ Init */
> static const struct net_device_ops enetc_ndev_ops = {
> .ndo_open = enetc_open,
> @@ -225,6 +361,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,
> };
[Severity: Low]
This ops table is shared by ENETC v1 and v4 VFs, but the callback returns 0
immediately for v1 without programming anything. Core code uses callback
presence as the rx-filtering capability test:
net/core/dev_ioctl.c:dev_ifsioc() {
...
case SIOCADDMULTI:
if ((!ops->ndo_set_rx_mode && !ops->ndo_set_rx_mode_async) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
...
}
Does SIOCADDMULTI/SIOCDELMULTI on an ENETC v1 VF now return success instead
of -EINVAL while no hardware filter is touched, and __dev_set_rx_mode()
queue rx-mode work that can never do anything? Could the revision test be
applied where the ops are selected, as the probe path already does for
si->ops, so the capability is only advertised where it is implemented?
>
> static void enetc_vf_get_revision(struct enetc_si *si)
> @@ -280,6 +417,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;
> +
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
2026-08-31 2:54 ` [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
2026-09-01 3:23 ` sashiko-bot
@ 2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 10:47 ` Wei Fang
1 sibling, 1 reply; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 7 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 6 · Low: 1
- [Medium] The PSI-to-VSI link-status notification protocol has no
coalescing, retry or re-query path, so the newest link state can be…
- [Medium] The VF treats a CMD_SUCCESS reply to
ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER as a guarantee that the PF…
- [Medium] The PF-side link-status subscription (bit in
pf->link_status_ms_mask) is acquired with no guaranteed release, and…
- [Medium] Teardown ordering bug: enetc_vf_remove()
(drivers/net/ethernet/freescale/enetc/enetc_vf.c:773-774) calls…
- [Medium] Head-of-line blocking / lock inversion between rtnl_lock and
the VSI-PSI mailbox.
- [Medium] enetc_vf_probe() enables the message-received interrupt and
then calls netif_carrier_off(ndev) *after* register_netdev() with no…
- [Low] Claim: the commit message omits that
enetc_vf_msg_handle_link_status() also reprograms hardware…
--
net: enetc: add PSI-to-VSI link status notification support for VF
This adds two enetc_si_ops hooks so that a phy-less ENETC v4 VF registers a
link status notifier with the PF over the VSI-to-PSI mailbox on open and
unregisters it on close. Incoming PSI-to-VSI notifications are taken by a
dedicated MSI-X vector and processed in a work item that updates the VF
carrier.
I had a few questions about the new paths below.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index 803c5c541a5c..fb5df740650e 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
> @@ -2935,11 +2935,31 @@ 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)
> + goto carrier_on;
> +
> + /* For phy-less VFs on ENETC v4, attempt to register a link
> + * status notifier with the PF via the VSI-to-PSI messaging
> + * channel. If registration succeeds, the PF will immediately
> + * send the current link status and broadcast future link
> + * transitions; carrier state is then managed in
> + * enetc_vf_msg_handle_link_status(). If registration fails,
> + * fall back to the LS1028A behaviour and assert carrier
> + * unconditionally via netif_carrier_on().
> + */
> + if (!si->ops->vf_reg_link_status_notifier(si))
> + return 0;
[Severity: Medium]
Here a CMD_SUCCESS reply to ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER is
taken as a promise that the PF registered the VF and will push the current
link status, so this path returns without calling netif_carrier_on() and
without any timeout fallback.
Does the PF side keep that promise? enetc_msg_register_link_status_notifier()
writes the SUCCESS reply first and only then checks sriov_enabled:
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;
so both pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id) and the immediate
enetc_msg_notify_vf_link_status() are skipped after the VF has already been
told SUCCESS.
That window is entered by enetc_sriov_configure(pdev, 0) (echo 0 >
sriov_numvfs) and by the pci_enable_sriov() error path, both before the VFs
are torn down.
Even with sriov_enabled true, the immediate notification can fail inside
enetc_pf_send_msg() (-EIO when the VF MS bit is still set, or the 200 ms
timeout) and is only reported with dev_err_ratelimited("PF notifies link
status failed").
In these cases, is the VF left administratively up with carrier off, with no
traffic, until a manual down/up?
> +
> + dev_warn(&ndev->dev,
> + "Link status notifier registration failed\n");
> +
> +carrier_on:
> netif_carrier_on(ndev);
> return 0;
> }
[ ... ]
> @@ -3051,8 +3072,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:
[ ... ]
> @@ -3100,6 +3129,17 @@ 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))
> + goto carrier_off;
> +
> + if (!si->ops->vf_unreg_link_status_notifier(si))
> + goto carrier_off;
> +
> + dev_warn(&ndev->dev,
> + "Link status notifier unregistration failed\n");
> +carrier_off:
> netif_carrier_off(ndev);
> }
[Severity: Medium]
Both new unregister call sites, here and in the enetc_open() unwind, are
gated on ENETC_LINK_STATUS_NOTIFIER_REGISTERED, but that flag is only set
when the whole mailbox round trip returned 0:
enetc_vf_reg_link_status_notifier()
err = enetc_msg_link_status_notifier(si, true);
if (!err)
set_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED, &priv->flags);
On the PF side, enetc_msg_register_link_status_notifier() replies first and
sets pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id) afterwards.
So if enetc_msg_vsi_send() hits its 200 ms read_poll_timeout (-ETIME) or
-EIO, the PF is registered while the VF flag stays clear, and this code
takes the goto carrier_off path so UNREGISTER is never sent. Can the
PF-side subscription be leaked this way?
A failed unregister is likewise only warned about and leaves the flag set,
and enetc_vf_remove() relies solely on unregister_netdev() -> enetc_close(),
so after close or unbind the PF keeps notifying a VF that can no longer
acknowledge, paying the full 200 ms poll under pf->msg_lock which also
serialises mailbox service for the other VFs.
enetc4_pf_vf_flr_handler() restores promisc state but does not clear the
reset VF's bit in link_status_ms_mask either. Would a forced unregister in
enetc_vf_remove(), plus clearing link_status_ms_mask on FLR, be appropriate?
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index 4e717afba7f7..a4d0089ef1a9 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
[ ... ]
> @@ -435,6 +481,128 @@ 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_si *si)
> +{
> + if (is_enetc_rev1(si))
> + return;
> +
> + enetc_wr(&si->hw, ENETC_VSIIER, VSIIER_MRIE);
> +}
> +
> +static void enetc_vf_disable_mr_int(struct enetc_si *si)
> +{
> + if (is_enetc_rev1(si))
> + return;
> +
> + enetc_wr(&si->hw, ENETC_VSIIER, 0);
> +}
> +
> +static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 status)
> +{
> + bool tx_pause = !!(status & ENETC_CLASS_CODE_TX_PAUSE_EN);
> + bool link_down = !!(status & ENETC_CLASS_CODE_LINK_DOWN);
> + struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
> + struct net_device *ndev = si->ndev;
> +
> + rtnl_lock();
[Severity: Medium]
Is rtnl_lock() needed here? netif_carrier_on()/netif_carrier_off() do not
require it, and enetc_set_congestion_mode() already serialises on
si->gen_lock.
Taking rtnl in this work item ties PF message acknowledgement to rtnl
availability. Every VSI-to-PSI sender runs with rtnl held and busy-polls the
PF for up to 200 ms inside enetc_msg_vsi_send(): the two new notifier ops
called from enetc_phylink_connect() and enetc_close(), plus
enetc_vf_set_mac_addr() and the rx-mode async worker.
There is a single si->msg_task on an ordered workqueue and MRIE stays masked
from enetc_vf_msg_msix_handler() until it completes, so while one invocation
is parked on rtnl_lock() no further PF message can be acked:
VF work item VF rtnl holder (ip link set <vf> down)
enetc_vf_msg_task()
enetc_vf_msg_handle_link_status()
rtnl_lock() <- blocks enetc_close()
vf_unreg_link_status_notifier()
enetc_msg_vsi_send() 200 ms poll
The PF in turn holds pf->msg_lock while polling for that same VF's ack.
Does this resolve only when the 200 ms timeouts fire, leaving "VSI mailbox
timeout" and "Link status notifier unregistration failed", a leaked PF
registration and a lost link transition?
> + if (!netif_running(ndev))
> + goto unlock_rtnl;
> +
> + if (link_down) {
> + if (netif_carrier_ok(ndev)) {
> + netif_carrier_off(ndev);
> + netdev_info(ndev, "Link is Down\n");
> + }
> +
> + goto unlock_rtnl;
> + }
> +
> + /* Link is up */
> + enetc_set_congestion_mode(priv, tx_pause);
[Severity: Low]
This isn't a bug, but the commit message says only that the work item
"parses the notification and updates the carrier state via
netif_carrier_on() or netif_carrier_off() accordingly". It does not mention
that the same notification reprograms hardware, updating the congestion
mode bit in ENETC_RBMR for every VF Rx BD ring from the PF TX pause bit.
Could that be spelled out in the changelog?
> +
> + if (!netif_carrier_ok(ndev)) {
> + netif_carrier_on(ndev);
> + netdev_info(ndev, "Link is Up, tx pause %s\n",
> + tx_pause ? "on" : "off");
> + }
> +
> +unlock_rtnl:
> + rtnl_unlock();
> +}
> +
> +static void enetc_vf_msg_task(struct work_struct *work)
> +{
> + struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
> + struct enetc_hw *hw = &si->hw;
> + u8 class_id, class_code;
> + u16 pf_msg;
> +
> + /* W1C to clear the message received interrupt event */
> + enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR);
> +
> + /* Reading VSIMSGRR retrieves the message data and acknowledges to
> + * the PF that the message was received and another message can be
> + * sent.
> + */
> + pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR));
[Severity: Medium]
Only the single 16-bit value VSIMSGRR currently holds is ever consumed, and
the VF never sends ENETC_MSG_GET_CURRENT_LINK_STATUS even though the
protocol defines it and the PF already handles it.
On the PF side, enetc_pf_send_msg() drops every VF whose MS bit is still
set:
ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
ms_mask &= ~ms_status;
if (!ms_mask)
return -EIO;
and enetc_msg_notify_vf_link_status() only logs "PF notifies link status
failed" without storing the pending state or retrying once the MS bit
clears.
If the VF has not read VSIMSGRR within the PF's 200 ms poll, the ack being
deferred to this work item with MRIE masked meanwhile, and the link then
changes again, the second transition is skipped for that VF and never
resent.
Does the VF carrier then stay at the older state, including carrier off
which blocks all VF transmit, until some later PF transition happens to
succeed?
> + class_id = FIELD_GET(ENETC_PF_MSG_CLASS_ID, pf_msg);
> +
> + switch (class_id) {
> + case ENETC_MSG_CLASS_ID_LINK_STATUS:
> + class_code = FIELD_GET(ENETC_PF_MSG_CLASS_CODE_U8, pf_msg);
> + 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(si);
> +}
> +
> +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);
> + queue_work(si->workqueue, &si->msg_task);
> +
> + return IRQ_HANDLED;
> +}
> +
[ ... ]
> @@ -447,8 +615,38 @@ static const struct enetc_si_ops enetc4_vsi_ops = {
[ ... ]
> +static void enetc_vf_wq_task_destroy(struct enetc_si *si)
> +{
> + if (!si->workqueue)
> + return;
> +
> + disable_work_sync(&si->msg_task);
> +
> + /* The MR interrupt may be re-enabled by si->msg_task */
> + enetc_vf_disable_mr_int(si);
> +
> + destroy_workqueue(si->workqueue);
> +}
> +
> static int enetc_vf_probe(struct pci_dev *pdev,
> const struct pci_device_id *ent)
> {
> @@ -520,15 +718,33 @@ 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;
>
> + /* Enable message received interrupt */
> + enetc_vf_enable_mr_int(si);
> netif_carrier_off(ndev);
[Severity: Medium]
This netif_carrier_off() runs after register_netdev() has published the
interface and dropped rtnl, with no lock held, while the new work item
writes the same carrier bit under rtnl. If the probe thread is preempted
after register_netdev(), can this happen?
probe udev / ifup
register_netdev()
enetc_open()
enetc_phylink_connect()
vf_reg_link_status_notifier()
PF sends current link status
enetc_vf_msg_task()
netif_carrier_on() (rtnl held)
netif_carrier_off() <- reverts it
Since the carrier is now driven only by PF transition notifications, with no
periodic refresh and no VF-side status query, the interface would stay up
with carrier off until the next physical PF link change.
Would moving netif_carrier_off() and enetc_vf_enable_mr_int() before
register_netdev() avoid this? The netif_running() check in
enetc_vf_msg_handle_link_status() should make the earlier interrupt enable
harmless.
>
> 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:
> @@ -554,8 +770,11 @@ static void enetc_vf_remove(struct pci_dev *pdev)
> struct enetc_msg_swbd msg;
>
> priv = netdev_priv(si->ndev);
> + enetc_vf_disable_mr_int(si);
> unregister_netdev(si->ndev);
[Severity: Medium]
Is this ordering intentional? enetc_vf_disable_mr_int() masks VSIIER while
the VF is still present in pf->link_status_ms_mask, because the
UNREGISTER_LINK_CHANGE_NOTIFIER message is only sent later from
enetc_close(), reached via unregister_netdev() -> dev_close().
A PF link transition landing in that window writes PSIMSGSR and polls up to
200 ms under pf->msg_lock, but nothing can read ENETC_VSIMSGRR any more:
the MR interrupt is masked, and enetc_vf_wq_task_destroy() then calls
disable_work_sync() and masks MRIE again, so enetc_vf_msg_task() never runs.
Per enetc_pf_send_msg()'s own comment, reading VSIMSGRR is the only way to
clear the MS bit, "the MS bit can only be written as 1, writing a 0 has no
effect".
Does the MS bit then stay asserted across an unbind, so that on the next
probe and open enetc_pf_send_msg()
ms_mask &= ~ms_status;
if (!ms_mask)
return -EIO;
drops the initial link status notification for the freshly registered VF,
whose carrier stays off because enetc_phylink_connect() skips
netif_carrier_on() on registration success?
The early disable also looks redundant, since enetc_vf_wq_task_destroy()
already performs the final MRIE disable after disable_work_sync().
>
> + enetc_vf_free_msg_msix(si);
> + enetc_vf_wq_task_destroy(si);
> enetc_free_msix(priv);
>
> enetc_free_si_resources(priv);
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support
2026-08-31 2:54 ` [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
@ 2026-09-03 23:44 ` netdev-bot+sashiko
0 siblings, 0 replies; 47+ messages in thread
From: netdev-bot+sashiko @ 2026-09-03 23:44 UTC (permalink / raw)
To: wei.fang
Cc: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, wei.fang,
imx, netdev, linux-kernel
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 2 · Low: 0
- [Medium] enetc4_ndev_ops
(drivers/net/ethernet/freescale/enetc/enetc4_pf.c:600) registers…
- [Medium] enetc_pf_get_vf_config() reports the VF VLAN from the new
software shadow (vf_state->vid/qos, enetc_pf_common.c:705-709) instead…
--
net: enetc: add ndo_get_vf_config() support
This adds enetc_pf_get_vf_config() so 'ip link show' can query the per-VF
MAC, VLAN, spoofchk and trust state from the PF. struct enetc_vf_state
gains tpid/qos/vid fields plus an ENETC_VF_FLAG_SPOOFCHK flag, which the
existing VLAN and spoofchk setters now update, and the new callback is
registered in both the ENETC rev1 and the ENETC v4 ops tables.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index a945a120c553..b4d76505bc03 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> @@ -597,6 +597,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,
> };
[Severity: Medium]
Is registering enetc_pf_get_vf_config() in enetc4_ndev_ops correct given
that this table has neither .ndo_set_vf_spoofchk nor .ndo_set_vf_vlan?
Those two setters are the only writers of ENETC_VF_FLAG_SPOOFCHK and of
vf_state->vid/qos/tpid, and they are installed only in enetc_ndev_ops in
enetc_pf.c. On an ENETC v4 PF the shadow state therefore stays at its
zero-initialized value forever.
rtnl_fill_vfinfo() presets the sentinel before calling the driver:
net/core/rtnetlink.c:rtnl_fill_vfinfo() {
...
ivi.spoofchk = -1;
...
/* The default value for VF link state is "auto"
* IFLA_VF_LINK_STATE_AUTO which equals zero
*/
ivi.linkstate = 0;
...
if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
return 0;
...
}
enetc_pf_get_vf_config() then overwrites that with an affirmative value:
ivi->spoofchk = !!(vf_state->flags & ENETC_VF_FLAG_SPOOFCHK);
so 'ip link show' on an ENETC v4 PF prints "spoof checking off" instead of
omitting the value, for a feature this revision cannot control: attempts to
change it return -EOPNOTSUPP because no ndo_set_vf_spoofchk is installed,
and the enetc4 anti-spoofing bits PSICFGR0_VASE/PSICFGR0_ASE (via
PSICFGR0_ANTI_SPOOFING in enetc4_hw.h) have no users at all -
enetc4_psicfgr0_val_construct() never touches them.
Would it make sense to only report spoofchk/VLAN where the matching
setters exist, e.g. by leaving ivi->spoofchk untouched when the revision
has no spoofchk support?
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> index 523c71324780..d77a07cece28 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> @@ -207,6 +208,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;
> }
[ ... ]
> @@ -223,6 +236,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;
> }
[ ... ]
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> index 10134d7a1f70..264294a0cc23 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> @@ -684,5 +684,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)
> +{
[ ... ]
> + 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);
> + }
[Severity: Medium]
Can this report a stale VLAN configuration after a PF driver unbind and
rebind?
The MAC is read back from hardware via enetc_get_si_hw_addr(), but the VLAN
comes from the new software shadow instead of ENETC_PSIVLANR(vf + 1), which
is where EN/QOS/VID actually live. enetc_set_isol_vlan() is the only writer
of that register:
drivers/net/ethernet/freescale/enetc/enetc_pf.c:enetc_set_isol_vlan() {
u32 val = 0;
if (vlan)
val = ENETC_PSIVLAN_EN | ENETC_PSIVLAN_SET_QOS(qos) | vlan;
enetc_port_wr(hw, ENETC_PSIVLANR(si), val);
}
Probe-time port setup rewrites PSICFGR0 for every VF, but nothing clears
PSIVLANR:
drivers/net/ethernet/freescale/enetc/enetc_pf.c:enetc_port_si_configure() {
...
for (i = 0; i < pf->total_vfs; i++)
enetc_port_wr(hw, ENETC_PSICFGR0(i + 1), val);
...
}
So after 'ip link set <pf> vf N vlan 100' followed by unbind/rebind of the
PF driver, pf->vf_state is freshly zeroed while the hardware still holds
ENETC_PSIVLAN_EN | vid | qos for that VF SI, and this getter reports vlan 0.
Would reading PSIVLANR (and the ASE bit in PSICFGR0) directly in the getter
avoid the divergence and also remove the need for the new shadow fields?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831025441.635045-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
2026-09-03 23:22 ` Jakub Kicinski
@ 2026-09-04 2:02 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 2:02 UTC (permalink / raw)
To: Jakub Kicinski, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, linux@armlinux.org.uk,
imx@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
> > > You only replied to some of the AI feedback, should we assume that
> > > feedback on patches 12/13/14 is legit and expect v4?
> >
> > I have not seen any other feedback on patches 12/13/14. Except for a
> > pre-existing issue on patch 14, but I do not think the issue needs to
> > be addressed.
> >
> > Sashiko review link:
> > https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fsash
> >
> iko.dev%2F%23%2Fpatchset%2F20260831025441.635045-1-wei.fang%2540oss.
> nx
> >
> p.com&data=05%7C02%7Cwei.fang%40nxp.com%7Cdaedc3330cdd4e7b25e70
> 8df0a12
> >
> 3c4f%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C63924074558397
> 7688%7
> >
> CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAw
> MCIsIlA
> >
> iOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=p
> FJgf
> > %2FatLEXFRvgYlZaSDj03LonRvB7Xq8Ykd4eDaYU%3D&reserved=0
>
> Please TAL at:
>
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnetdev-ai
> .bots.linux.dev%2Fsashiko%2F%23%2Fpatchset%2F20260831025441.635045-7-
> wei.fang%40oss.nxp.com&data=05%7C02%7Cwei.fang%40nxp.com%7Cdaedc3
> 330cdd4e7b25e708df0a123c4f%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0
> %7C0%7C639240745583994975%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1
> hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUI
> joyfQ%3D%3D%7C0%7C%7C%7C&sdata=np8ORiHSmuEzsBU4lfjqOi9hsVW661f
> %2B7%2BSNCj1utb8%3D&reserved=0
I also checked netdev-ai.bots as well yesterday, but it showed "Embargoed until
9/7/2026". I have received the review comments email today, and I will confirm
them one by one. Thank you.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 01/15] net: enetc: add trusted VF support
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 6:29 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 6:29 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> This adds a per-VF ENETC_VF_FLAG_TRUSTED flag stored in pf->vf_state[] and
> wires up ndo_set_vf_trust through the new shared helper
> enetc_pf_set_vf_trust() for both the enetc and enetc4 PF drivers. The VF
> primary MAC address mailbox message is the first message gated on that
> flag, and untrusted VFs now get ENETC_MSG_CLASS_ID_PERMISSION_DENY
> instead
> of having the hardware programmed.
>
> A couple of questions about the trust flag lifetime and about the
> changelog below.
>
> The changelog says:
>
> 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.
>
> [Severity: Low]
> Before this patch any VF address that passed is_valid_ether_addr() and the
> ENETC_VF_FLAG_PF_SET_MAC check was programmed into the SI primary
> MAC
> registers, so the description matches the pre-patch behaviour. Was a Fixes
> tag considered here, given the wording describes an exploitable condition?
No need a fixes tag, the intention of ndo_set_vf_trust() is a new feature not a
bug fix. ENETC v1 is only used on LS1028A, but due to the hardware issue (MDIO
issue), the VFs must run on the same OS with PF, so for ENETC v1, VF cannot be
assigned to VM.
> > 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);
>
> [Severity: High]
> This gate looks like it inverts the usual meaning of the trust
> attribute, and it changes behaviour for existing VF users.
>
> In ixgbe/i40e/ice the rule is: a VF may program its own MAC as long as
> the PF has not administratively assigned one, and "trusted" is exactly
> the knob that lets a VF go further and override the PF-assigned address.
> Here both halves are reversed. The new check in
> enetc_msg_set_vf_primary_mac_addr() returns ENETC_PF_MSG_PERM_DENY
> for
> any VF without ENETC_VF_FLAG_TRUSTED, even when no PF-assigned address
> exists (ENETC_VF_FLAG_PF_SET_MAC clear), and the following
> ENETC_VF_FLAG_PF_SET_MAC test still refuses the request for a trusted
> VF - the new comment states this explicitly ("even if it is trusted").
> So trust neither grants the override it is supposed to grant, nor is it
> irrelevant to the plain "VF picks its own MAC" case.
>
> Since vf_state[] is devm_kcalloc()'ed in enetc_init_sriov_resources()
> and flags starts at 0, every VF is untrusted at creation. That means
> after this patch a guest that generates its own address and calls
> ndo_set_mac_address on the VF gets the deny class back, which
> enetc_msg_vsi_send() turns into -EACCES, and interface bring-up fails
> unless the host has first run "ip link set ... vf N trust on". That is
> a visible change for setups that work today, and there is no Fixes tag
> or mention of the compatibility impact in the changelog.
>
> Was the ixgbe-style rule considered instead, i.e. allow the mailbox MAC
> change whenever ENETC_VF_FLAG_PF_SET_MAC is clear, and consult
> ENETC_VF_FLAG_TRUSTED only to decide whether a VF may override a
> PF-assigned address? If the stricter default-deny is deliberate for
> this hardware, could the changelog spell out that untrusted VFs can no
> longer set any MAC at all and that trust is now required for what used
> to work by default?
This behavior is intentional, and the compatibility impact is minimal. A few
points to clarify:
1. No bring-up regression. The PF driver assigns a MAC address to every VF
at probe time, so each VF reads a valid address from its own SIPMAR0/1
registers via enetc_load_primary_mac_addr(). Interface bring-up (.ndo_open)
does not send any set-MAC mailbox message, so it is unaffected. Only an
explicit ndo_set_mac_address (a VF trying to change its MAC via mailbox)
is now gated on trust.
2. The default-deny is deliberate, not an inverted trust rule. It prevents a
malicious VM from setting its MAC to that of another VF/PF to eavesdrop
on other SIs' traffic, or from spoofing a MAC to bypass security policies. So
the two gates are complementary: untrusted VFs cannot self-assign a MAC
at all, and even a trusted VF cannot override a PF-assigned MAC. This is a
stricter model than ixgbe/i40e/ice by design, which suits the ENETC SI
security model.
3. Impact on existing SoCs is negligible. Due to the ENETC v1 MDIO issue,
ENETC v1 VFs cannot be assigned to a VM, and the v1 mailbox path is
effectively unusable downstream. This change is primarily aimed at the
upcoming ENETC v4 VF support, so it does not change behavior for any
real existing deployment.
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> > index 55c07c528f22..a7bf4bfc25b7 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> > @@ -488,6 +488,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,
>
> [Severity: Low]
> At this commit neither enetc_ndev_ops nor enetc4_ndev_ops provides
> .ndo_get_vf_config, so rtnl_fill_vfinfo() emits no VF info and the trust
> bit set here cannot be read back with "ip link show".
>
> This does get resolved inside the same series: the later commit "net:
> enetc: add ndo_get_vf_config() support" adds enetc_pf_get_vf_config() with
>
> ivi->trusted = !!(vf_state->flags & ENETC_VF_FLAG_TRUSTED);
>
> and registers it in both ops tables. Would it be worth ordering the getter
> before or together with the setter so that no intermediate commit has a
> write-only trust attribute?
Since these two patches are in the same series, there is no need to put them
together.
>
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> > index d32a195a04c9..519fc90d2647 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
> > @@ -586,5 +586,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;
>
> [Severity: Medium]
> Can ENETC_VF_FLAG_TRUSTED survive an SR-IOV teardown and be inherited
> by a
> different VF instance later?
>
> pf->vf_state[] is devm_kcalloc()'ed once per PF in
> enetc_init_sriov_resources(), and the disable path in
> enetc_sriov_configure() does not touch it:
>
> if (!num_vfs) {
> pci_disable_sriov(pdev);
> enetc_msg_psi_free(pf);
> pf->num_vfs = 0;
> } else {
>
> So for:
>
> echo 2 > sriov_numvfs
> ip link set eth0 vf 0 trust on
> echo 0 > sriov_numvfs
> echo 2 > sriov_numvfs
>
> vf_state[0].flags still has ENETC_VF_FLAG_TRUSTED set, and the newly
> created VF0 (possibly assigned to a different guest) passes the new gate in
> enetc_msg_set_vf_primary_mac_addr() straight away and gets
> enetc_set_si_hw_addr(pf, vf_id + 1, addr) executed for whatever address it
> asks for.
>
> Related: the bounds check here uses pf->total_vfs, the hardware maximum,
> rather than pf->num_vfs, so trust can also be recorded for VF slots that do
> not exist yet and takes effect as soon as they are created. Was that
> intentional?
>
> Later in the series enetc_msg_clear_vf_config() clears only the UC/MC
> promiscuous bits and carries a comment saying TRUSTED and PF_SET_MAC are
> intentionally preserved, and it returns early for ENETC v1, so the
> carry-over is still present at the end of the series. Would clearing the
> trust bit for all VFs on SR-IOV disable, as ixgbe and i40e effectively do
> by reallocating their per-VF state, be preferable?
This is intentional by design. The ENETC_VF_FLAG_TRUSTED flag is decoupled
from whether SR-IOV is enabled - it can be set even before SR-IOV is enabled.
This is deliberate: when SR-IOV is later enabled, a trusted VF can apply certain
configurations right at init time, instead of having to wait for the user to enable
the trust flag on the PF side first.
The persistence across teardown is therefore expected, not a leak. Trust is a
PF/host-side administrative policy tied to the VF index, and the host always
remains in control. If an admin is concerned about a VF slot being reused by
a different guest, the correct action is to explicitly set that VF back to untrusted
via "ip link set ... vf N trust off" after disabling SR-IOV.
The total_vfs bounds check is consistent with this model - it intentionally
allows trust to be pre-configured for VF slots before they are instantiated,
and the array is allocated for total_vfs, so there is no out-of-bounds access.
So no fix is needed here.
>
> > + else
> > + vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
> > +
> > + mutex_unlock(&vf_state->lock);
>
> [Severity: Medium]
> Does revoking trust leave the hardware MAC filter behind?
>
> While trusted, the VF can install any valid unicast address, including the
> PF's or another VSI's, through enetc_msg_set_vf_primary_mac_addr() ->
> enetc_set_si_hw_addr(pf, vf_id + 1, addr). This path only clears software
> flag bits, so after "ip link set ... vf N trust off" the SI primary MAC
> filter still holds the address the VF picked, and the now-untrusted VF keeps
> receiving frames for it.
>
> There is also no saved copy of the address programmed at probe, so the
> original value cannot be restored.
>
> At the end of the series the untrust branch clears UC/MC promiscuous mode
> and the hash filters (and skips even that for ENETC v1), but still never
> re-programs or clears the primary MAC. ixgbe_ndo_set_vf_trust() and
> i40e_ndo_set_vf_trust() force a VF reset on a trust change so the VF has to
> re-negotiate its filters; would something equivalent be appropriate here?
>
No fix is needed here. This follows the same intentional model as the trust
gate itself.
Once a VF is set to untrusted, it can no longer change its MAC at all - any
subsequent ndo_set_mac_address from the VF is immediately rejected with
PERM_DENY. So the MAC left in the SI primary filter is simply the address the
host authorized while the VF was trusted; it is not something the now-untrusted
VF can keep modifying.
The host also retains a definitive way to reclaim or reset that address:
ip link set ... vf N mac <addr> (ndo_set_vf_mac) reprograms PSIPMAR0/1 and
sets ENETC_VF_FLAG_PF_SET_MAC, after which even a trusted VF can no longer
override it. So there is no need to save the probe-time address or to force a VF
reset - the host can deterministically take over the primary MAC through the
existing PF interface.
Unlike ixgbe/i40e, ENETC does not need a VF reset to regain control, because
the primary MAC is a single per-SI register the PF owns directly, rather than
something re-negotiated through a VF<->PF protocol. Clearing only the software
trust flag on trust off is therefore sufficient and intended.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 7:16 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 7:16 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > +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. To
> > + * avoid sending messages to such a VF, the bit corresponding to VF
> > + * is cleared from ms_mask. Because the MS bit can only be written
> > + * as 1, writing a 0 has no effect. Writing a 1 when the bit is
> > + * already set is undefined.
> > + */
> > + ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> > + ms_mask &= ~ms_status;
> > + if (!ms_mask)
> > + return -EIO;
>
> [Severity: High]
> Can a link state change be lost permanently here?
>
> The return happens before the enetc_wr(hw, ENETC_PSIMSGSR, ...) below, so
> when the target VF still has an unread message the new link state is never
> published, and enetc_msg_notify_vf_link_status() only prints
> "PF notifies link status failed". Nothing records that the VF is out of
> date, and notifications are generated only on the phylink transitions in
> enetc4_pl_mac_link_up()/enetc4_pl_mac_link_down().
>
> Single-VF sequence:
>
> link up -> PSIMSGSR written with "link up", the poll below times out
> after 200 ms because the VF has not read VSIMSGRR yet
> link down -> ms_status still has MS(0) set, ms_mask becomes 0,
> -EIO is returned and "link down" is never written
>
> The VF then consumes the stale "link up" message and keeps carrier on plus
> a stale TX PAUSE derived congestion-mode setting until the next PF link
> transition. The same drop can hit the initial notification issued from
> enetc_msg_register_link_status_notifier(), which has already replied
> ENETC_MSG_CLASS_ID_CMD_SUCCESS to the VF.
>
> Would it make sense to record a pending state and re-send it once the MS
> bit is observed clear, rather than dropping the transition?
The scenario is theoretically possible, but it does not require a fix.
To lose a transition, a VF would have to leave its previous PF message unread
for longer than the 200 ms send window, and a reverse link change would have
to occur within that same window. That does not happen in practice:
The PHY state machine polls link status once per second, so consecutive link
changes are at least ~1 s apart - more than enough time for the VF to consume
the message and clear its MS bit. In real usage, link transitions are infrequent
to begin with.
The PF also waits up to 200 ms for the VF to read the message. A VF that fails
to read a single message within that window is already malfunctioning (e.g. a
stalled), and I don't think the PF should compensate for a broken VF.
So no fix is needed. Recording a pending state to re-send later is possible, but
since every notification already carries the current state and the next transition
corrects any stale value, the extra complexity is not justified.
>
> > +
> > + 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);
>
> [Severity: Low]
> The printed value is a PSIMSGSR MS-bit mask, not a VF index mask, since
>
> #define PSIMSGSR_MS(n) BIT((n) + 1)
>
> so a stuck VF0 is reported as 0x2 and VF1 as 0x4. Should the value be
> shifted back to VF numbering before printing, or should the message say
> that it is an MS mask?
No fix is needed; it stays consistent with the registers, making debugging
easier.
>
> > +
> > + 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);
> > +}
>
> [Severity: High]
> This waits up to 200 ms for a bit that only the VF can clear, and every
> caller holds pf->msg_lock across it. Can an untrusted guest use that to
> stall the host?
>
> The VF-initiated path has no ENETC_VF_FLAG_TRUSTED check (unlike the MAC
> filter handlers) and no rate limiting:
>
> enetc_msg_task()
> enetc_msg_handle_rxmsg()
> enetc_msg_handle_link_status() /* cmd_id from the VF */
> enetc_msg_register_link_status_notifier()
> mutex_lock(&pf->msg_lock);
> enetc_msg_notify_vf_link_status()
> enetc_pf_send_msg() /* sleeps up to 200 ms */
>
> enetc_msg_task() handles all VFs sequentially in one work item with the MR
> interrupts disabled, so a VF that registers, drains its mailbox and
> re-registers in a loop keeps pf->msg_lock and the mailbox work item busy
> and denies mailbox service to the sibling VFs.
>
> The same pf->msg_lock is taken by enetc_pf_notify_vf_link_status() from
> enetc4_pl_mac_link_up()/enetc4_pl_mac_link_down(), and phylink runs those
> from its resolve work, which is flushed synchronously from ndo_open/
> ndo_stop with rtnl_lock held:
>
> drivers/net/phy/phylink.c:phylink_run_resolve_and_disable() {
> ...
> flush_work(&pl->resolve);
> ...
> }
>
> so the guest-induced stall propagates into the host PF link handling and
> rtnl-held paths. In the other direction, one unresponsive registered VF
> adds up to 200 ms to every PF link transition, because the poll condition
> !(val & ms_mask) waits for all bits of the aggregate mask.
>
> Is the synchronous acknowledgement wait needed at all here? Link state is
> idempotent, so a fire-and-forget write with a deferred retry, or at least
> dropping pf->msg_lock before waiting, would keep the PF link state machine
> independent of VF behaviour. The commit message does not mention that a PF
> link transition can now block on VF acknowledgement.
This is a potential issue, I will improve this.
>
> > +
> > +static void enetc_msg_notify_vf_link_status(struct enetc_pf *pf, u16
> ms_mask)
> > +{
> > + struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
> > + u16 pf_msg;
> > +
> > + pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
> > + 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_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);
>
> [Severity: Medium]
> What clears this bit if the VF goes away without unregistering?
>
> The bit is set here and cleared only by a cooperative
> ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER in
> enetc_msg_unregister_link_status_notifier(), or globally in
> enetc_sriov_configure() when SR-IOV is disabled. A VM destroy, a guest
> crash/reboot, or a killed DPDK application leaves the bit set.
>
> After that, every PF link transition reaches enetc_pf_send_msg() with the
> stale mask and either writes the MS bit for a VF that will never read it
> and then blocks the full 200 ms with pf->msg_lock held from the phylink
> callbacks, or, once the MS bit is stuck set, filters that VF out and (when
> it is the only registered VF) returns -EIO, logging "PF notifies link
> status failed" indefinitely. Since the poll waits for all bits of the mask,
> one dead VF also delays notifications to the live ones.
>
> The FLR handler added later in the series, enetc4_pf_vf_flr_handler(),
> only restores the promiscuous mode bits and does not touch
> link_status_ms_mask, and enetc_sriov_configure() resets only the software
> mask without bringing PSIMSGSR back to a known state. Should the per-VF
> bit be cleared on FLR / VF teardown as well?
link_status_ms_mask is PF-side software state that records which VFs
have subscribed to link-status notifications. Its lifetime is tied to the VF
driver's register/unregister requests, not to the VF hardware.
FLR resets the VF hardware; it does not mean the VF has stopped wanting
link notifications. A VF can go through an FLR while still being a valid,
registered subscriber, without re-registering. Clearing its bit in the FLR
handler would silently stop delivering link updates to a VF that is still
alive and still subscribed - that would introduce a bug, not fix one.
So the FLR handler should only restore hardware-related state (e.g.
promiscuous filters), and must not touch link_status_ms_mask. Genuine
teardown (VM destroy / VF unbind / SR-IOV disable) already clears the
whole mask in enetc_sriov_configure(), and a graceful VF close sends
an explicit unregister. Clearing the bit on FLR is therefore neither correct
nor necessary.
As for the "VF goes away without unregistering" case, a follow-up
improvement will remove the 200 ms synchronous wait in
enetc_pf_send_msg() (moving to a fire-and-forget notification). Once that
is in place, a dead VF that never reads its message can no longer block the
PF link state machine: the pre-send MS check simply skips that VF, and
notifications to the live VFs are unaffected. So this concern will be mitigated
by that change.
>
--
pw-bot: cr
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 7:52 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 7:52 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > @@ -181,6 +212,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
> > */
>
> [Severity: Medium]
>
> This documents cmd_id 0x0 as returning the current link speed, but can the
> reply describe a speed the port is not running at?
>
> enetc_msg_get_link_speed() takes the value straight from
> phylink_ethtool_ksettings_get() with no link-state gating. For a fixed
> link, phylink copies the configured speed/duplex unconditionally:
>
> drivers/net/phy/phylink.c:phylink_ethtool_ksettings_get() {
> ...
> case MLO_AN_FIXED:
> phylink_get_fixed_state(pl, &link_state);
> phylink_get_ksettings(&link_state, kset);
> break;
> ...
> }
>
> So with the carrier down, or before the PF netdev has ever been opened,
> enetc_build_link_speed_msg() still encodes a valid speed class code rather
> than ENETC_MSG_SPEED_UNKNOWN. The MLO_AN_INBAND case reads the
> PCS live,
> which also need not match what the MAC was last programmed with.
>
> Would it be more consistent to derive the answer from the state the PF
> already tracks, the way the sibling link-status message does?
No fix is needed here. Message class 0x81 (link speed) and class 0x80 (link
status) are intentionally separate, and each returns the correct thing.
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_get_link_status()
> {
> ...
> pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
> ...
> }
>
> priv->speed is written next to PCR_PSPEED in enetc4_set_port_speed()
> (including the 10Mbps fallback on SPEED_UNKNOWN), so the programmed rate
> is
> already recorded. As written, a VF can be told 1G/2.5G while the same PF
> reports the link as down over class 0x80.
GET_CURRENT_LINK_SPEED is meant to expose the same value userspace
would see via ethtool - the port's configured/negotiated speed/duplex. That
is a property that remains meaningful even when the carrier is down (for a
fixed link, ethtool likewise reports the configured speed regardless of link state).
This message never claims the link is currently up; whether the link is up is
answered by class 0x80, which the DPDK VF is expected to query separately
>
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > index e21414acdc0d..c3ae4c024f34 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > @@ -280,6 +280,93 @@ static u16 enetc_msg_handle_link_status(struct
> enetc_pf *pf, int vf_id,
> > return 0;
> > }
> >
> > +static u16 enetc_build_link_speed_msg(int speed, int duplex)
> > +{
> > + u32 speed_code = ENETC_MSG_SPEED_UNKNOWN;
> > +
> > + switch (speed) {
>
> [ ... ]
>
> > + return FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
> > + ENETC_MSG_CLASS_ID_LINK_SPEED) |
> > + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, speed_code);
> > +}
> > +
> > +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 rtnl_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);
>
> [Severity: Medium]
>
> Should the trusted-VF requirement be described in the changelog and in the
> mailbox ABI comment in enetc_mailbox.h?
>
> ENETC_VF_FLAG_TRUSTED is only ever set from enetc_pf_set_vf_trust()
> (ndo_set_vf_trust, i.e. "ip link set <pf> vf N trust on"), and pf->vf_state
> is allocated with devm_kcalloc() in enetc_init_sriov_resources(), so flags
> is zero by default. A default setup therefore gets
> ENETC_PF_MSG_PERM_DENY / -EACCES back from enetc_msg_vsi_send().
>
> The commit message says without qualification:
>
> "The PF will reply the current link speed when it receives the get link
> speed message from VF."
>
> "VFs controlled by DPDK can obtain real-time link speed information from
> the PF"
>
> The ABI comment added to struct enetc_msg_generic also describes class 0x81
> cmd 0x0 with the same wording as the 0x80 status query, and
> enetc_msg_get_link_status() answers any VF with no trust check. Two paired
> link-info queries with different permission models is surprising for a
> DPDK VF implementer reading only the header.
The asymmetry is intentional and technically justified, but you're right that it
should be documented. I'll update the changelog to state that the get-link-speed
query requires the VF to be trusted, and annotate the class 0x81 cmd 0x0 entry
in enetc_mailbox.h to note the trusted-VF requirement, distinguishing it from the
ungated 0x80 status query.
>
> > +
> > + rtnl_lock();
> > + phylink_ethtool_ksettings_get(priv->phylink, &link_info);
> > + rtnl_unlock();
>
> [Severity: High]
>
> Can blocking on rtnl here stall mailbox service for the other VFs?
>
> This runs from the single work item that services every VF with the PSIIER
> MR interrupts masked:
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_task() {
> ...
> for (i = 0; i < pf->num_vfs; i++) {
> ...
> enetc_msg_handle_rxmsg(pf, i, &msg_code);
> ...
> }
> out:
> enetc_enable_psiier_interrupts(pf);
> }
>
> rtnl hold times elsewhere (ndo_open with PCS/PHY negotiation, netdev
> unregistration, another driver probing, netlink dumps) are unbounded, while
> the VF side has a hard 200ms budget:
>
> drivers/net/ethernet/freescale/enetc/enetc_vf.c:enetc_msg_vsi_send() {
> ...
> err = read_poll_timeout(enetc_rd, vsimsgsr,
> !(vsimsgsr & ENETC_VSIMSGSR_MB),
> 1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
> if (err) {
> dev_err(dev, "VSI mailbox timeout\n");
> ...
> }
>
> So one iteration waiting on rtnl can push unrelated requests from other VFs
> past that timeout, with a hardware mailbox transaction left outstanding.
> For a host-assigned VF, enetc_msg_vsi_set_primary_mac_addr() is issued from
> ndo_set_mac_address with rtnl already held, i.e. the VF waits for a PF reply
> that needs the very lock the VF holds.
>
> There is a second effect on teardown: enetc_msg_psi_free() does
> cancel_work_sync(&si->msg_task), and it is called from
> enetc_sriov_configure() (pci_driver::sriov_configure, invoked from
> sriov_numvfs_store() under the PCI device_lock) and from
> enetc_pf_remove()/enetc4_pf_remove(). Does SR-IOV disable/removal now
> wait
> for rtnl while holding device_lock, for a time a busy VF can extend?
>
> Every other handler in this file avoids rtnl. enetc_msg_get_link_status()
> answers from the cached pf->link_up under pf->msg_lock, and speed/duplex are
> already handed to enetc4_pl_mac_link_up() where they could be cached the
> same way. Would caching the speed there and replying from the cache work
> instead?
this does not need a fix. Using phylink_ethtool_ksettings_get() here is deliberate
rather than an oversight, and the practical impact is negligible.
The speed passed into enetc4_pl_mac_link_up() is not always the actual link speed.
When there is a PCS after the MAC and rate matching is in effect, the speed given
to the callback is the PCS/interface rate, not the external PHY link speed. Only
phylink_ethtool_ksettings_get() reflects the real media/PHY link speed. So caching
the callback speed and replying from it could hand the VF the PCS rate instead of
the actual PHY link speed - exactly what DPDK needs to get right. Querying phylink
guarantees the VF sees the same link speed that ethtool reports on the PF.
In real usage a DPDK VF does not poll link speed in a tight loop - it queries it
occasionally (typically around link-change events), not on a hot path. Combined
with the existing trusted-VF gate, the chance of this rtnl_lock() acquisition
meaningfully contending with host control-plane operations is negligible.
There is no measurable stall in practice.
Given that the cached value can be semantically wrong for PCS/rate-matching
setups, and the rtnl acquisition is rare and already restricted to trusted VFs,
keeping the phylink query is the correct trade-off. No change is needed.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 8:40 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 8:40 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > static void enetc_msg_task(struct work_struct *work)
> > {
> > struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
> > @@ -602,6 +642,8 @@ static void enetc_msg_task(struct work_struct *work)
> > u32 mr_status, mr_mask;
> > int i;
> >
> > + enetc_vf_flr_handler(pf);
> > +
>
> [Severity: Medium]
> How long can the SI stay in the hardware promiscuous state before this
> restore runs?
>
> The hardware puts the SI into UC/MC promiscuous mode at the instant of the
> FLR, but the policy is only re-applied from the work item:
>
> enetc_msg_psi_msix() {
> enetc_disable_psiier_interrupts(pf);
> schedule_work(&si->msg_task);
>
> return IRQ_HANDLED;
> }
>
> enetc_msg_task() -> enetc_vf_flr_handler() -> pf->ops->vf_flr_handler()
> -> enetc4_pf_vf_flr_handler()
>
> Since the guest owning the VF triggers the FLR itself (vfio-pci reset, or a
> driver bind path reaching pcie_flr()), it knows exactly when that window
> opens and only needs to re-arm an Rx BD ring to receive frames destined for
> other SIs until enetc4_pf_vf_flr_handler() clears the bits in PSIPMMR.
>
> The scan also happens once, at the top of enetc_msg_task():
>
> enetc_vf_flr_handler(pf);
>
> mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
>
> and enetc_vf_flr_handler() takes a single PSIIDR snapshot with an early
> return:
>
> flr_status = enetc_rd(hw, ENETC_PSIIDR) & flr_mask;
> if (!flr_status)
> return;
>
> If an FLR lands while msg_task is already part-way through the VF message
> loop, is the restore then delayed until the whole in-flight batch finishes
> and the re-queued work runs? The FLR bits are not re-checked before
> enetc_enable_psiier_interrupts(pf) at the end of the work item.
>
> Would it be feasible to clear PSIPMMR[SIn_MAC_UP]/[SIn_MAC_MP] directly in
> enetc_msg_psi_msix() for the VFs whose FLR bits are set, and leave the full
> policy re-apply in the work item? As written the handler sleeps on
> vf_state->lock and pf->msg_lock, so it cannot run from the hardirq.
>
I don't think this window is worth fixing - the risk is very low and the proposed
change adds complexity for little gain.
First, the exposure is theoretical rather than practically useful. A VF FLR resets
the entire VF function, not just PSIPMMR - the Rx BD rings, SI enable state and
DMA configuration are all reset too. So immediately after the FLR the VF has no
armed Rx ring and cannot receive any frame, promiscuous or not. Before it can
capture anything it must first re-initialize and re-arm an Rx ring, which is not
instantaneous. The promiscuous state is only the reset default and is corrected
by the PF asynchronously; there is no guarantee the VF can bring up a ring and
line it up with that short window to actually sniff another SI's traffic. It cannot
reliably exploit the timing.
Second, under normal conditions the detect-to-restore latency is very short
(interrupt latency plus a workqueue wakeup).
Third, moving the promiscuous-mode clear into enetc_msg_psi_msix() would
not give a real guarantee anyway: the guest owns the FLR trigger, so it can simply
issue another FLR before the workqueue runs and return the SI to the reset-default
promiscuous state. So the hardirq clear does not close the window in any
meaningful sense.
Finally, it would add real complexity. enetc4_pf_vf_flr_handler() accesses
PSIPMMR under vf_state->lock and pf->msg_lock, which are mutexes and
cannot be taken in hardirq context. Doing the clear in the ISR would require
converting the PSIPMMR synchronization to an irq-safe spinlock, which touches
every path that writes PSIPMMR (set_rx_mode, the promisc message handler,
trust-off, clear_vf_config). That is a non-trivial change to the locking model for
a window that is not practically exploitable.
So I'd keep the current design: clear/re-apply the policy from the work item.
No change needed.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 9:05 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 9:05 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > @@ -1138,6 +1141,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);
>
> [Severity: Medium]
> Enabling .sriov_configure here, together with the new 0xef00 VF ID, makes
> the PSI/VSI mailbox path live on ENETC v4 for the first time. Can the PF
> mailbox worker and a VF mailbox sender deadlock against each other on
> rtnl_lock until the poll times out?
>
> The PF worker path takes rtnl_lock while serving a VF request:
>
> enetc_msg_task() -> enetc_msg_handle_rxmsg() ->
> enetc_msg_handle_link_speed()
> -> enetc_msg_get_link_speed() {
> rtnl_lock();
> phylink_ethtool_ksettings_get(priv->phylink, &link_info);
> rtnl_unlock();
> }
>
> The VF side runs from ndo_set_mac_address, so rtnl is already held, and it
> then waits up to 200 ms for the PF to answer:
>
> enetc_vf_set_mac_addr() -> enetc_msg_vsi_set_primary_mac_addr()
> -> enetc_msg_vsi_send() {
> err = read_poll_timeout(enetc_rd, vsimsgsr,
> !(vsimsgsr & ENETC_VSIMSGSR_MB),
> 1000, 200000, false, &si->hw, ENETC_VSIMSGSR);
> }
>
> If the single worker is servicing a trusted VF's link-speed request while a
> kernel-bound VF issues its own request, the worker blocks in rtnl_lock()
> held by that VF's sender, so the VF's message cannot be consumed. The VF
> operation then fails with "VSI mailbox timeout" after holding rtnl for
> 200 ms, and the still-unconsumed message makes the following VF sends fail
> with "VSI mailbox is busy" (-EIO) until the worker drains it.
>
> The trigger needs a mixed configuration: one trusted VF issuing link-speed
> queries (for example a vfio/DPDK-owned VF, since untrusted requests return
> PERM_DENY before rtnl_lock) plus a second kernel-bound ENETC v4 VF on the
> same host. The later patch adding ndo_set_rx_mode_async for the ENETC v4
> VF introduces a second rtnl-held 200 ms mailbox waiter on the same path.
>
The rtnl_lock() in enetc_msg_get_link_speed() is intentional, as discussed
earlier: for a MAC sitting behind a PCS with rate matching, the speed cached
at mac_link_up() is the PCS/interface rate, not the external PHY link speed,
so querying phylink is what gives the VF the correct value. In practice a DPDK
VF does not poll link speed in a tight loop, so the chance of this rtnl acquisition
overlapping another VF's rtnl-held mailbox send is small.
If the concerned situation does occur, the worst case is that one of the two VFs
gets a mailbox timeout error and retries, which is acceptable. So no change is
needed here.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 9:53 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 9:53 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > + 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);
> > +
> > + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
> > + ENETC_MADDR_HASH_TBL_SZ);
> > + i += 2;
> > + }
> > +
> > + 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);
> > +
> > + bitmap_to_arr32(&msg->hash_tbl[i], mac_filter->mac_hash_table,
> > + ENETC_MADDR_HASH_TBL_SZ);
> > + }
> > +
> > + 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 int 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;
> > + int err;
> > +
> > + /* For ENETC v1, we cannot return -EOPNOTSUPP or any other error,
> > + * otherwise ndev->rx_mode_retry_timer will try to set rx_mode
> > + * multiple times, which is pointless.
> > + */
> > + if (is_enetc_rev1(si))
> > + return 0;
> > +
> > + if (ndev->flags & IFF_PROMISC) {
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> > + true);
> > + } else if (ndev->flags & IFF_ALLMULTI) {
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
> > + false);
> > + if (err)
> > + goto out;
> > +
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
> > + true);
> > + } else {
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> > + false);
> > + }
> > +
> > + if (err)
> > + goto out;
> > +
> > + err = enetc_vf_set_mac_hash_filter(si, uc, mc);
> > +
>
> [Severity: Medium]
> Is the order of these two mailbox transactions the right way round?
>
> enetc_vf_set_mac_promisc() sends FLUSH_MACS together with en, and the PF
> handler zeroes both hash filter registers when FLUSH_MACS is set:
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_pr
> omisc_mode() {
> ...
> if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
> enetc_set_si_uc_hash_filter(si, si_id, 0);
>
> if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
> enetc_set_si_mc_hash_filter(si, si_id, 0);
> ...
> }
>
> So while promiscuous mode is active the SI hash filters are known to be
> empty (enetc_vf_set_mac_hash_filter() also returns early for IFF_PROMISC).
> On the way out of promiscuous mode this function completes the
> promisc-disable round trip first and only then installs the replacement
> hash filters.
>
> Each enetc_msg_vsi_send() is a blocking round trip polled with
> read_poll_timeout(..., 1000, 200000, ...). Between the two sends the SI has
> promiscuity cleared and empty hash filters - can that drop all multicast
> and secondary unicast frames for the duration, two round trips in the
> IFF_ALLMULTI branch?
>
> And if the hash filter send then fails (-EIO "VSI mailbox is busy", -ETIME
> on mailbox timeout, -EBUSY for a busy PSI), the promisc-off write is
> already committed and is not undone; netif_rx_mode_schedule_retry() gives
> up after NETIF_RX_MODE_RETRY_MAX attempts:
>
> net/core/dev_addr_lists.c:netif_rx_mode_schedule_retry() {
> if (dev->rx_mode_retry_count >= NETIF_RX_MODE_RETRY_MAX) {
> netdev_err(dev, "rx_mode retry limit reached, giving up\n");
> return;
> }
> ...
> }
>
> Would programming the new (superset) filters before narrowing the
> promiscuous bits avoid both the window and the stuck state? The PF side
> already does something similar in enetc4_pf_set_uc_exact_filter() with its
> temporary unicast hash filter.
>
I don't think this needs to be changed.
First, the window is acceptable. It lasts one mailbox round trip. rx_mode
changes are infrequent management events, so a brief loss during a
reconfiguration is tolerable.
Second, the retry mechanism exists precisely to cover a failed configuration.
The only errors the hash filter send can return here are -EIO (mailbox busy),
-ETIME (mailbox timeout) and -EBUSY (PSI busy) - all of which mean the PF
is not servicing mailbox messages. The core retries with backoff over roughly
15 seconds before giving up. If the PF cannot process a single message in that
time, the PF side is already broken, and reordering the two sends would not
rescue the configuration either - it would just fail at a different point.
> > +out:
> > + /* If the error code is -EOPNOTSUPP or -EACCES or -EPERM, return 0
> > + * directly to avoid meaningless retries.
> > + */
> > + if (err == -EOPNOTSUPP || err == -EACCES || err == -EPERM)
> > + return 0;
> > +
> > + return err;
> > +}
> > +
>
> [Severity: High]
> For an untrusted VF - the default, since pf->vf_state is allocated with
> devm_kcalloc() and ENETC_VF_FLAG_TRUSTED is only set by
> enetc_pf_set_vf_trust() - the PF handlers reject exactly the messages this
> callback sends.
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_pr
> omisc_mode() {
> ...
> 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;
> }
> ...
> }
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_mac_ha
> sh_filter() {
> ...
> case ENETC_MAC_FILTER_TYPE_UC:
> if (!trusted) {
> pf_msg = ENETC_PF_MSG_PERM_DENY;
> goto vf_state_unlock;
> }
> ...
> case ENETC_MAC_FILTER_TYPE_ALL:
> ...
> if (!trusted &&
> (hweight64(mc_hash) <= ENETC_VF_MC_HASH_BITS_MAX)) {
> enetc_set_si_mc_hash_filter(si, si_id, 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;
> }
> ...
> }
>
> enetc_msg_vsi_send() maps PERMISSION_DENY and UCF_DENY to -EACCES and
> returns before the dev_err(), so there is not even a log line.
>
> In the IFF_ALLMULTI branch, does the MC promisc-enable message therefore
> always fail on an untrusted VF, making "if (err) goto out;" skip
> enetc_vf_set_mac_hash_filter() completely?
>
> Even when it is reached, enetc_vf_set_mac_hash_filter() selects
> mac_filter_type = ENETC_MAC_FILTER_TYPE_UC under IFF_ALLMULTI, so the
> mc
> snapshot handed in by the core is never converted into an MC hash table.
> With MC promiscuous mode denied as well, does the multicast hash filter
> stay frozen at its previous contents - so groups joined after the VF is
> enslaved to a bridge or gets a macvlan on top (both call
> dev_set_allmulti()), such as IPv6 solicited-node addresses or mDNS, are
> silently dropped by hardware?
>
> In the default branch the TYPE_ALL message is refused outright once more
> than ENETC_VF_MC_HASH_BITS_MAX (8) multicast hash bits are set, so
> PSIMMHFR0/1 keeps its stale value. Would it be worth falling back to the
> MC-only message, which the PF does accept from an untrusted VF?
>
> The out: block then turns -EACCES, -EPERM and -EOPNOTSUPP into 0, and the
> core treats that as "installed":
>
> net/core/dev_addr_lists.c:netif_rx_mode_run() {
> ...
> if (err)
> netif_rx_mode_schedule_retry(dev);
> else
> dev->rx_mode_retry_count = 0;
> ...
> }
>
> Can this leave the SI hash filters permanently out of sync with the address
> list the stack believes is programmed, with no retry and no message? Since
> this patch also sets IFF_UNICAST_FLT, the core's unicast promiscuous
> fallback is suppressed too.
>
> Two related points: the promisc-disable message carries FLUSH_MACS = en =
> false, so a denied hash filter update can also leave hardware more
> permissive than requested (removed multicast groups keep being received).
> And when trust is granted later, enetc_pf_set_vf_trust() only flips the
> PF-side flag - nothing replays or re-requests the receive mode
> configuration that the VF already reported as successfully installed, so
> does a configuration denied while untrusted stay unapplied indefinitely?
I think the current handling is fine for an untrusted VF.
First, an untrusted VF not getting allmulti is the security policy working as
intended. IFF_ALLMULTI means "receive all multicast", which is exactly
what we do not want to grant to an untrusted VF. If a VF genuinely needs
this (for example to be enslaved to a bridge or to carry a macvlan), the
administrator should mark it trusted with ip link set <pf> vf N trust on.
Second, returning 0 for a denied request is a limitation of the
ndo_set_rx_mode_async() interface. The interface only defines two
outcomes: return 0 for success, or a negative errno to request a retry
via the core backoff. There is no way to express "this was permanently
denied by policy, do not retry". A permission denial is a stable failure,
so returning an error would just make the core retry four times with
backoff and then log "rx_mode retry limit reached, giving up", which
is misleading and pointless. Returning 0 is the better of the two options
the interface offers.
^ permalink raw reply [flat|nested] 47+ messages in thread
* RE: [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
2026-09-03 23:44 ` netdev-bot+sashiko
@ 2026-09-04 10:47 ` Wei Fang
0 siblings, 0 replies; 47+ messages in thread
From: Wei Fang @ 2026-09-04 10:47 UTC (permalink / raw)
To: netdev-bot+sashiko@kernel.org, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew@lunn.ch,
olteanv@gmail.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux@armlinux.org.uk, imx@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> > @@ -2935,11 +2935,31 @@ 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)
> > + goto carrier_on;
> > +
> > + /* For phy-less VFs on ENETC v4, attempt to register a link
> > + * status notifier with the PF via the VSI-to-PSI messaging
> > + * channel. If registration succeeds, the PF will immediately
> > + * send the current link status and broadcast future link
> > + * transitions; carrier state is then managed in
> > + * enetc_vf_msg_handle_link_status(). If registration fails,
> > + * fall back to the LS1028A behaviour and assert carrier
> > + * unconditionally via netif_carrier_on().
> > + */
> > + if (!si->ops->vf_reg_link_status_notifier(si))
> > + return 0;
>
> [Severity: Medium]
>
> Here a CMD_SUCCESS reply to
> ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER is
> taken as a promise that the PF registered the VF and will push the current
> link status, so this path returns without calling netif_carrier_on() and
> without any timeout fallback.
>
> Does the PF side keep that promise? enetc_msg_register_link_status_notifier()
> writes the SUCCESS reply first and only then checks sriov_enabled:
>
> 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;
>
> so both pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id) and the immediate
> enetc_msg_notify_vf_link_status() are skipped after the VF has already been
> told SUCCESS.
>
> That window is entered by enetc_sriov_configure(pdev, 0) (echo 0 >
> sriov_numvfs) and by the pci_enable_sriov() error path, both before the VFs
> are torn down.
The VF is being disabled, no need to notify the link status.
>
> Even with sriov_enabled true, the immediate notification can fail inside
> enetc_pf_send_msg() (-EIO when the VF MS bit is still set, or the 200 ms
> timeout) and is only reported with dev_err_ratelimited("PF notifies link
> status failed").
>
> In these cases, is the VF left administratively up with carrier off, with no
> traffic, until a manual down/up?
For the -EIO and timeout cases, both can only mean the VF is not consuming
its mailbox messages.
-EIO is returned only when the VF's MS bit is still set, i.e. it has not read the
previous PSI-to-VSI message, and the 200 ms timeout means it did not read
the new one within that window. Note that at registration time the VF's MS
bit should be clear: the VF's bit is not in link_status_ms_mask before it
registers, so the PF has never sent it an asynchronous notification. A healthy
VF therefore cannot hit -EIO on this path - a set MS bit at that point implies
either a stale unread message from a previous driver instance or a VF that is
stuck.
So in these cases the VF is already malfunctioning. If it does not process
mailbox messages at all, it cannot work correctly regardless of the carrier
state, and I don't think the PF should try to compensate for that. No change
is needed here.
>
> > +
> > + dev_warn(&ndev->dev,
> > + "Link status notifier registration failed\n");
> > +
> > +carrier_on:
> > netif_carrier_on(ndev);
> > return 0;
> > }
>
> [ ... ]
> > @@ -3100,6 +3129,17 @@ 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))
> > + goto carrier_off;
> > +
> > + if (!si->ops->vf_unreg_link_status_notifier(si))
> > + goto carrier_off;
> > +
> > + dev_warn(&ndev->dev,
> > + "Link status notifier unregistration failed\n");
> > +carrier_off:
> > netif_carrier_off(ndev);
> > }
>
> [Severity: Medium]
>
> Both new unregister call sites, here and in the enetc_open() unwind, are
> gated on ENETC_LINK_STATUS_NOTIFIER_REGISTERED, but that flag is only set
> when the whole mailbox round trip returned 0:
>
> enetc_vf_reg_link_status_notifier()
> err = enetc_msg_link_status_notifier(si, true);
> if (!err)
> set_bit(ENETC_LINK_STATUS_NOTIFIER_REGISTERED, &priv->flags);
>
> On the PF side, enetc_msg_register_link_status_notifier() replies first and
> sets pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id) afterwards.
>
> So if enetc_msg_vsi_send() hits its 200 ms read_poll_timeout (-ETIME) or
> -EIO, the PF is registered while the VF flag stays clear, and this code
> takes the goto carrier_off path so UNREGISTER is never sent. Can the
> PF-side subscription be leaked this way?
>
> A failed unregister is likewise only warned about and leaves the flag set,
> and enetc_vf_remove() relies solely on unregister_netdev() -> enetc_close(),
> so after close or unbind the PF keeps notifying a VF that can no longer
> acknowledge, paying the full 200 ms poll under pf->msg_lock which also
> serialises mailbox service for the other VFs.
>
> enetc4_pf_vf_flr_handler() restores promisc state but does not clear the
> reset VF's bit in link_status_ms_mask either. Would a forced unregister in
> enetc_vf_remove(), plus clearing link_status_ms_mask on FLR, be appropriate?
The ordering mismatch is real: if enetc_msg_vsi_send() hits -ETIME or -EIO, the
PF may have registered the VF while the VF-side
ENETC_LINK_STATUS_NOTIFIER_REGISTERED flag stays clear, so no UNREGISTER
is sent. But the consequences are much smaller than described.
After enetc_close() the VF still consumes and acknowledges PF messages. The
MR interrupt is enabled in enetc_vf_probe() and only disabled in enetc_vf_remove(),
not on close, and enetc_vf_msg_task() does not check the REGISTERED flag
- it always reads VSIMSGRR, which acknowledges the message and clears the
MS bit. So the PF's enetc_pf_send_msg() gets its acknowledgement promptly
and does not pay the 200 ms poll, and mailbox service for the other VFs is not
affected. enetc_vf_msg_handle_link_status() also bails out early when the netdev
is not running, so a stale subscription after close is harmless.
A leftover subscription only matters after enetc_vf_remove(), once the MR
interrupt is disabled and the VF no longer reads its mailbox. That is the same
situation as the earlier "VF goes away without unregistering" case, and it will
be mitigated by the follow-up change that removes the 200 ms synchronous
wait in enetc_pf_send_msg(): with a fire-and-forget notification, a VF that
never reads its message can no longer block the PF, since the pre-send MS
check simply skips it.
As for clearing link_status_ms_mask on FLR, I don't think that is right, for the
same reason as before: it is PF-side software state tracking which VFs
subscribed, and a VF can legitimately go through an FLR while remaining a
registered subscriber. Clearing it there would stop delivering link updates to
a VF that is still alive and still subscribed.
> > +static void enetc_vf_msg_handle_link_status(struct enetc_si *si, u8 status)
> > +{
> > + bool tx_pause = !!(status & ENETC_CLASS_CODE_TX_PAUSE_EN);
> > + bool link_down = !!(status & ENETC_CLASS_CODE_LINK_DOWN);
> > + struct enetc_ndev_priv *priv = netdev_priv(si->ndev);
> > + struct net_device *ndev = si->ndev;
> > +
> > + rtnl_lock();
>
> [Severity: Medium]
>
> Is rtnl_lock() needed here? netif_carrier_on()/netif_carrier_off() do not
> require it, and enetc_set_congestion_mode() already serialises on
> si->gen_lock.
rtnl_lock() is not redundant here - it protects the netif_running() check.
> > +static void enetc_vf_msg_task(struct work_struct *work)
> > +{
> > + struct enetc_si *si = container_of(work, struct enetc_si, msg_task);
> > + struct enetc_hw *hw = &si->hw;
> > + u8 class_id, class_code;
> > + u16 pf_msg;
> > +
> > + /* W1C to clear the message received interrupt event */
> > + enetc_wr(hw, ENETC_VSIIDR, VSIIDR_MR);
> > +
> > + /* Reading VSIMSGRR retrieves the message data and acknowledges to
> > + * the PF that the message was received and another message can be
> > + * sent.
> > + */
> > + pf_msg = FIELD_GET(VSIMSGRR_MC, enetc_rd(hw, ENETC_VSIMSGRR));
>
> [Severity: Medium]
>
> Only the single 16-bit value VSIMSGRR currently holds is ever consumed, and
> the VF never sends ENETC_MSG_GET_CURRENT_LINK_STATUS even though the
> protocol defines it and the PF already handles it.
>
> On the PF side, enetc_pf_send_msg() drops every VF whose MS bit is still
> set:
>
> ms_status = enetc_rd(hw, ENETC_PSIMSGSR) & 0xffff;
> ms_mask &= ~ms_status;
> if (!ms_mask)
> return -EIO;
>
> and enetc_msg_notify_vf_link_status() only logs "PF notifies link status
> failed" without storing the pending state or retrying once the MS bit
> clears.
>
> If the VF has not read VSIMSGRR within the PF's 200 ms poll, the ack being
> deferred to this work item with MRIE masked meanwhile, and the link then
> changes again, the second transition is skipped for that VF and never
> resent.
>
> Does the VF carrier then stay at the older state, including carrier off
> which blocks all VF transmit, until some later PF transition happens to
> succeed?
This is the same underlying situation as the earlier link-status notification
question, and I don't think it needs a fix.
To lose a transition, the VF must fail to read VSIMSGRR within the PF's 200
ms poll window. On receiving the MR interrupt the VF only masks MRIE and
queues a work item, so under normal scheduling the acknowledgement
happens far sooner than 200 ms. A VF that cannot complete that within
200 ms is already malfunctioning - for example a stalled or suspended
guest - and I don't think the PF should be expected to compensate for that.
The link change cadence also makes this hard to hit: the PHY state machine
polls link status once per second, so consecutive transitions are normally at
least ~1 s apart, which is ample time for a healthy VF to consume the previous
message and clear its MS bit. In real usage link transitions are infrequent to
begin with.
So no change is needed here.
> > static int enetc_vf_probe(struct pci_dev *pdev,
> > const struct pci_device_id *ent)
> > {
> > @@ -520,15 +718,33 @@ 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;
> >
> > + /* Enable message received interrupt */
> > + enetc_vf_enable_mr_int(si);
> > netif_carrier_off(ndev);
>
> [Severity: Medium]
>
> This netif_carrier_off() runs after register_netdev() has published the
> interface and dropped rtnl, with no lock held, while the new work item
> writes the same carrier bit under rtnl. If the probe thread is preempted
> after register_netdev(), can this happen?
>
> probe udev / ifup
> register_netdev()
> enetc_open()
> enetc_phylink_connect()
> vf_reg_link_status_notifier()
> PF sends current link status
> enetc_vf_msg_task()
> netif_carrier_on() (rtnl held)
> netif_carrier_off() <- reverts it
>
> Since the carrier is now driven only by PF transition notifications, with no
> periodic refresh and no VF-side status query, the interface would stay up
> with carrier off until the next physical PF link change.
>
> Would moving netif_carrier_off() and enetc_vf_enable_mr_int() before
> register_netdev() avoid this? The netif_running() check in
> enetc_vf_msg_handle_link_status() should make the earlier interrupt enable
> harmless.
I think this is a theoretical concern rather than a practical one.
enetc_vf_enable_mr_int() and netif_carrier_off() are adjacent
statements in the same execution path, so the interval between
them is just a register write returning. The interrupt handler, on
the other hand, only masks MRIE and calls queue_work() - the
carrier is not touched there. Before netif_carrier_on() can run,
the work item has to be woken and scheduled, read VSIMSGRR,
acquire rtnl_lock and pass the netif_running() check. That chain
is considerably longer than the gap between those two adjacent
statements.
^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2026-09-04 10:47 UTC | newest]
Thread overview: 47+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 2:54 [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 01/15] net: enetc: add trusted " wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:13 ` Wei Fang (OSS)
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 6:29 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-08-31 12:00 ` Andrew Lunn
2026-09-01 2:31 ` Wei Fang
2026-09-01 3:05 ` Andrew Lunn
2026-09-01 3:40 ` Wei Fang
2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:46 ` Wei Fang (OSS)
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 7:16 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 04/15] net: enetc: add link speed " wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 7:52 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-09-01 6:59 ` Wei Fang (OSS)
2026-08-31 2:54 ` [PATCH v3 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-08-31 2:54 ` [PATCH v3 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
2026-08-31 2:54 ` [PATCH v3 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 8:40 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 9:05 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 9:53 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
2026-09-01 3:23 ` sashiko-bot
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-04 10:47 ` Wei Fang
2026-08-31 2:54 ` [PATCH v3 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
2026-09-03 23:44 ` netdev-bot+sashiko
2026-09-03 2:56 ` [PATCH v3 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support Jakub Kicinski
2026-09-03 3:24 ` Wei Fang (OSS)
2026-09-03 23:22 ` Jakub Kicinski
2026-09-04 2:02 ` Wei Fang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).