* [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support
@ 2026-09-09 10:07 wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 01/15] net: enetc: add trusted " wei.fang
` (14 more replies)
0 siblings, 15 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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.
---
v4:
1. Use the existing si->gen_lock instead of pf->msg_lock
2. Link status notification sending is now deferred to an ordered
workqueue, instead of sending inline under a mutex
3. enetc_pf_send_link_status_msg() with a retry loop (up to 200ms)
polling the MS bits, and a per-VF msg_fail_cnt that clears a VF's
link_status_ms_mask bit after 10 consecutive failures
4. Remove a stray blank line in enetc_sriov_configure
5. enetc_vf_set_mac_promisc() now takes separate promisc and flush_macs
booleans
6. Refactor enetc_vf_set_mac_hash_filter(), now it takes explicit uc/mc
presence, builds mac_filter_type/tbl_cnt incrementally, and no longer
reads ndev->flags directly
7. Restructure rx_mode logic into three helpers:
enetc_vf_enable_iff_promisc(), enetc_vf_disable_iff_promisc(),
enetc_vf_enable_iff_allmulti(), with detailed per-step error logging
8. Add a separate enetc4_ndev_ops for ENETC v4 VFs
9. Disable promisc and clears MAC filters on VF removal
10. enetc_phylink_connect() moved to after Tx/Rx resource allocation
11. Remove the ENETC_LINK_STATUS_NOTIFIER_REGISTERED flag
12. MR interrupt handler now explicitly writes VSIIDR_MR and reads
VSIMSGRR to ACK the PF
13. netif_carrier_off() and enetc_vf_enable_mr_int() are moved before
register_netdev()
14. Guard spoofchk reporting with is_enetc_rev1()
15. Refine commit messages
v3 link: https://lore.kernel.org/imx/20260831025441.635045-1-wei.fang@oss.nxp.com/
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 | 63 +-
drivers/net/ethernet/freescale/enetc/enetc.h | 11 +-
.../ethernet/freescale/enetc/enetc4_debugfs.c | 51 +-
.../net/ethernet/freescale/enetc/enetc4_hw.h | 1 +
.../net/ethernet/freescale/enetc/enetc4_pf.c | 85 ++-
.../ethernet/freescale/enetc/enetc_ethtool.c | 6 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 26 +
.../ethernet/freescale/enetc/enetc_mailbox.h | 109 ++-
.../net/ethernet/freescale/enetc/enetc_msg.c | 643 ++++++++++++++++--
.../net/ethernet/freescale/enetc/enetc_pf.c | 64 +-
.../net/ethernet/freescale/enetc/enetc_pf.h | 21 +-
.../freescale/enetc/enetc_pf_common.c | 146 +++-
.../freescale/enetc/enetc_pf_common.h | 19 +
.../net/ethernet/freescale/enetc/enetc_vf.c | 449 +++++++++++-
15 files changed, 1568 insertions(+), 127 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 01/15] net: enetc: add trusted VF support
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-09 10:07 ` [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
` (13 subsequent siblings)
14 siblings, 1 reply; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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.
Trust is a PF/host-side policy bound to the VF index, decoupled from
whether SR-IOV is enabled. It may be set on a slot before SR-IOV is
enabled so a trusted VF can apply configuration right at init time,
and it is intentionally preserved across an SR-IOV disable/enable
cycle. The bounds check uses pf->total_vfs, the hardware maximum and
the size of pf->vf_state[], so pre-configuring an uninstantiated slot
is in-bounds. If a slot may be reassigned to another guest, the admin
clears trust with "ip link set <dev> vf <N> trust off".
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. This prevents a malicious VM from setting the VF address to
the MAC address of other VFs or the PF and eavesdropping on the traffic
of other SIs, and it stops a malicious VM from arbitrarily changing the
VF MAC address to achieve MAC address spoofing and bypass security
policies. This does not regress VF bring-up. The PF programs a valid
primary MAC into every VF slot at probe, and the VF loads it from its
own SIPMAR0/1 registers.
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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 01/15] net: enetc: add trusted " wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-11 20:14 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
` (12 subsequent siblings)
14 siblings, 1 reply; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 01/15] net: enetc: add trusted " wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 20:15 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 04/15] net: enetc: add link speed " wei.fang
` (11 subsequent siblings)
14 siblings, 2 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 a mechanism for VFs to learn the PF link state, using message class
0x80 (ENETC_MSG_CLASS_ID_LINK_STATUS) with three VSI-to-PSI commands:
1. ENETC_MSG_GET_CURRENT_LINK_STATUS: the VF queries the current PF link
status synchronously. This is intended for DPDK-owned VFs and is not
used by the Linux VF driver.
2. ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER: the VF registers for link
change notification. The PF then reports the current link status and
notifies the VF on every later link change.
3. ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER: the VF unregisters.
The PSI-to-VSI notification is 16 bits wide: the upper 8 bits carry the
class ID and the lower 8 bits the class code. Bit 0 of the class code
indicates the link state (1 = down, 0 = up) and bit 1 indicates whether
TX PAUSE is enabled on the PF. The TX PAUSE state is included so a VF
can decide whether to enable congestion mode on its RX BD rings, which
only works when the PF can actually send PAUSE frames.
Notifications are sent through the ENETC_PSIMSGSR register. Since sending
a notification may take a long time, as it polls the per-VF message
status bits, the actual transmission is deferred to an ordered workqueue
rather than running in the phylink link_up/link_down callbacks. The
link_status_ms_mask tracks the VFs registered for notification and is
cleared when SR-IOV is disabled.
Export enetc_pf_notify_vf_link_up() and enetc_pf_notify_vf_link_down()
for the PF phylink callbacks. Through this, VFs can perceive the link
status and report it to upper layers such as the kernel network stack,
containers and virtual machines.
Currently only the ENETC v4 driver supports this feature; v1 does not.
And the SR-IOV feature of ENETC v4 will be added by subsequent patches.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/enetc.h | 1 +
.../net/ethernet/freescale/enetc/enetc4_pf.c | 39 +++
.../net/ethernet/freescale/enetc/enetc_hw.h | 5 +
.../ethernet/freescale/enetc/enetc_mailbox.h | 26 +-
.../net/ethernet/freescale/enetc/enetc_msg.c | 264 +++++++++++++++++-
.../net/ethernet/freescale/enetc/enetc_pf.h | 7 +
.../freescale/enetc/enetc_pf_common.h | 15 +
7 files changed, 347 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index bc713a7c3aa1..0eff25b0c81b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -334,6 +334,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];
};
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 935a6a03b14f..e20989b6f8b4 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);
}
@@ -966,6 +968,36 @@ static void enetc4_link_deinit(struct enetc_ndev_priv *priv)
enetc_mdiobus_destroy(pf);
}
+static void enetc4_pf_link_status_task(struct work_struct *work)
+{
+ struct enetc_pf *pf = container_of(work, struct enetc_pf,
+ link_status_task);
+
+ enetc_pf_send_link_status_msg(pf);
+}
+
+static int enetc4_pf_wq_task_init(struct enetc_si *si)
+{
+ struct enetc_pf *pf = enetc_si_priv(si);
+
+ si->workqueue = alloc_ordered_workqueue("enetc-%s-wq", 0,
+ pci_name(si->pdev));
+ if (!si->workqueue)
+ return -ENOMEM;
+
+ INIT_WORK(&pf->link_status_task, enetc4_pf_link_status_task);
+
+ return 0;
+}
+
+static void enetc4_pf_wq_task_destroy(struct enetc_si *si)
+{
+ struct enetc_pf *pf = enetc_si_priv(si);
+
+ disable_work_sync(&pf->link_status_task);
+ destroy_workqueue(si->workqueue);
+}
+
static int enetc4_pf_netdev_create(struct enetc_si *si)
{
struct device *dev = &si->pdev->dev;
@@ -1006,6 +1038,10 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
if (err)
goto err_link_init;
+ err = enetc4_pf_wq_task_init(si);
+ if (err)
+ goto err_wq_init;
+
err = register_netdev(ndev);
if (err) {
dev_err(dev, "Failed to register netdev\n");
@@ -1015,6 +1051,8 @@ static int enetc4_pf_netdev_create(struct enetc_si *si)
return 0;
err_reg_netdev:
+ enetc4_pf_wq_task_destroy(si);
+err_wq_init:
enetc4_link_deinit(priv);
err_link_init:
enetc_free_msix(priv);
@@ -1032,6 +1070,7 @@ static void enetc4_pf_netdev_destroy(struct enetc_si *si)
struct net_device *ndev = si->ndev;
unregister_netdev(ndev);
+ enetc4_pf_wq_task_destroy(si);
enetc4_link_deinit(priv);
enetc_free_msix(priv);
free_netdev(ndev);
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..79dbaf72fcff 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -136,6 +136,104 @@ 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) {
+ if (test_bit(ENETC_RXBDR_CM, &priv->flags))
+ status |= ENETC_CLASS_CODE_TX_PAUSE_EN;
+ } 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);
+ struct enetc_si *si = pf->si;
+ u16 pf_msg;
+
+ spin_lock(&si->gen_lock);
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ enetc_pf_reply_msg(&si->hw, vf_id, pf_msg);
+ spin_unlock(&si->gen_lock);
+}
+
+static void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ struct enetc_si *si = pf->si;
+
+ spin_lock(&si->gen_lock);
+ enetc_pf_reply_msg(&si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
+
+ /* 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) {
+ spin_unlock(&si->gen_lock);
+ return;
+ }
+
+ pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
+ spin_unlock(&si->gen_lock);
+
+ /* Notify VF the current link status */
+ queue_work(si->workqueue, &pf->link_status_task);
+}
+
+static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
+ int vf_id)
+{
+ spin_lock(&pf->si->gen_lock);
+ pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
+ enetc_pf_reply_msg(&pf->si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
+ spin_unlock(&pf->si->gen_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 +309,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 +337,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 +344,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:
@@ -340,6 +442,22 @@ 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;
+
+ /* 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;
+
+ spin_lock(&si->gen_lock);
+ vf_state->msg_fail_cnt = 0;
+ spin_unlock(&si->gen_lock);
+}
+
static void enetc_msg_psi_free(struct enetc_pf *pf)
{
struct enetc_si *si = pf->si;
@@ -356,8 +474,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)
@@ -367,6 +487,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
int err;
if (!num_vfs) {
+ spin_lock(&si->gen_lock);
+ pf->sriov_enabled = false;
+ pf->link_status_ms_mask = 0;
+ spin_unlock(&si->gen_lock);
+
pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
@@ -379,6 +504,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
+ * gen_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 +519,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.
+ */
+ spin_lock(&si->gen_lock);
+ pf->sriov_enabled = false;
+ pf->link_status_ms_mask = 0;
+ spin_unlock(&si->gen_lock);
enetc_msg_psi_free(pf);
err_msg_psi:
pf->num_vfs = 0;
@@ -396,3 +535,114 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
return err;
}
EXPORT_SYMBOL_GPL(enetc_sriov_configure);
+
+void enetc_pf_send_link_status_msg(struct enetc_pf *pf)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
+ u16 pf_msg, ms_mask, new_ms_msk, ms_status;
+ struct enetc_si *si = pf->si;
+ int retry_num = 0;
+
+retry:
+ spin_lock(&si->gen_lock);
+ ms_mask = pf->link_status_ms_mask;
+ /* VFs have unregistered link status notification, return directly */
+ if (!ms_mask)
+ goto unlock;
+
+ /* 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(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
+ if ((ms_mask & ms_status) && retry_num++ < 200) {
+ spin_unlock(&si->gen_lock);
+ /* Wait VFs to handle the last message */
+ usleep_range(1000, 1020);
+ goto retry;
+ }
+
+ /* None of the relevant VFs have processed the previous message, and
+ * the PF has tried 200 times. This situation indicates that VF has
+ * malfunctioned.
+ */
+ new_ms_msk = ms_mask & (~ms_status);
+ if (!new_ms_msk) {
+ dev_err_ratelimited(&si->pdev->dev,
+ "All registered VFs (MS: 0x%x) are busy\n",
+ ms_mask);
+ goto ms_status_check;
+ }
+
+ if (new_ms_msk != ms_mask)
+ dev_warn_ratelimited(&si->pdev->dev,
+ "Failed to notify link status to VFs (MS: 0x%x)\n",
+ ms_mask ^ new_ms_msk);
+
+ pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
+ enetc_wr(&si->hw, ENETC_PSIMSGSR,
+ FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
+
+ms_status_check:
+ /* If the PF fails to send messages to the corresponding VF for 10
+ * consecutive times, clear that VF's bit in link_status_ms_mask.
+ */
+ for (int i = 0; i < pf->num_vfs; i++) {
+ struct enetc_vf_state *vf_state = &pf->vf_state[i];
+
+ if (!(PSIMSGSR_MS(i) & ms_mask))
+ continue;
+
+ if (!(PSIMSGSR_MS(i) & ms_status)) {
+ vf_state->msg_fail_cnt = 0;
+ continue;
+ }
+
+ if (vf_state->msg_fail_cnt++ < 10)
+ continue;
+
+ vf_state->msg_fail_cnt = 0;
+ pf->link_status_ms_mask &= ~PSIMSGSR_MS(i);
+ dev_warn_ratelimited(&si->pdev->dev,
+ "Clear VF%d's link status MS bit\n", i);
+ }
+
+unlock:
+ spin_unlock(&si->gen_lock);
+}
+EXPORT_SYMBOL_GPL(enetc_pf_send_link_status_msg);
+
+static void enetc_pf_notify_vf_link_status(struct enetc_pf *pf,
+ bool link_up)
+{
+ struct enetc_si *si = pf->si;
+
+ /* Currently we do not add link status message support for ENETC v1 */
+ if (!pf->total_vfs || is_enetc_rev1(si))
+ return;
+
+ spin_lock(&si->gen_lock);
+ pf->link_up = link_up;
+ if (!pf->link_status_ms_mask) {
+ spin_unlock(&si->gen_lock);
+ return;
+ }
+ spin_unlock(&si->gen_lock);
+
+ queue_work(si->workqueue, &pf->link_status_task);
+}
+
+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..b3ad498f91a0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -15,6 +15,8 @@ enum enetc_vf_flags {
struct enetc_vf_state {
struct mutex lock; /* Prevent concurrent access */
enum enetc_vf_flags flags;
+ /* Number of consecutive failures to send PF-to-VF messages */
+ int msg_fail_cnt;
};
struct enetc_port_caps {
@@ -54,6 +56,11 @@ struct enetc_pf {
struct enetc_port_caps caps;
const struct enetc_pf_ops *ops;
+
+ struct work_struct link_status_task;
+ 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.h b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
index 96a4dc63da57..bcf113efc2ef 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.h
@@ -31,9 +31,24 @@ 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_send_link_status_msg(struct enetc_pf *pf);
+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_send_link_status_msg(struct enetc_pf *pf)
+{
+}
+
+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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (2 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 20:16 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
` (10 subsequent siblings)
14 siblings, 2 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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, its user space application needs accurate
link speed information to make correct forwarding and configuration
decisions. Add link speed message support so the PF replies with the
current link speed when it receives a get-link-speed message from a VF.
Use a new message class 0x81 (ENETC_MSG_CLASS_ID_LINK_SPEED). The
PSI-to-VSI message is 16 bits: the high 8 bits are the class ID and the
low 8 bits are the speed code, so up to 255 speed values are supported
(ENETC_MSG_SPEED_MAX = 0xff). Instead of enumerating every speed above
5Gbps, use a formula so future high speeds need no enum or switch
changes:
speed_code = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G
The speed is read via phylink_ethtool_ksettings_get() rather than the
speed passed to the mac_link_up() callback. When the MAC has a PCS
layer, mac_link_up() reports the PCS link speed, which may differ from
the external PHY link speed; phylink_ethtool_ksettings_get() returns the
actual external link speed.
Unlike the link status message (class 0x80), the get-link-speed message
is only permitted for trusted VFs. Reading the speed requires the PF to
take rtnl_lock(), so an untrusted VF spamming this query could cause
rtnl_lock contention and starve routine network configuration on the
host. An untrusted VF therefore receives a permission-deny response; a
VF can be marked trusted via 'ip link set <pf> vf N trust on'.
Note that link speed change notification is not supported yet.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../ethernet/freescale/enetc/enetc_mailbox.h | 41 +++++++++
.../net/ethernet/freescale/enetc/enetc_msg.c | 90 +++++++++++++++++++
2 files changed, 131 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index 846998f07989..832d2f6ddcd8 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,16 @@ 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. Unlike the link status
+ * query (class 0x80), this query is only permitted for trusted VFs;
+ * an untrusted VF receives a permission-deny response. This is
+ * because the PF must take rtnl_lock() to read the link speed, so
+ * restricting it to trusted VFs avoids rtnl_lock contention on the
+ * host from a misbehaving VF.
+ * 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 79dbaf72fcff..08a9ffdd3eb0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -230,6 +230,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.
@@ -312,6 +399,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",
--
2.34.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (3 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
` (9 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (4 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
` (8 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 519fc90d2647..1b2ca2b31a80 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -609,5 +609,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 bcf113efc2ef..7d7e67a12278 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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (5 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
` (7 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 e20989b6f8b4..a44d50b7fd2a 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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (6 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
` (6 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 0eff25b0c81b..0501cc2fc304 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -337,6 +337,8 @@ struct enetc_si {
struct workqueue_struct *workqueue;
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 a44d50b7fd2a..eedcaf2d7bc9 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 b3ad498f91a0..88a558649c68 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -40,8 +40,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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (7 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-09 10:07 ` [PATCH v4 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
` (5 subsequent siblings)
14 siblings, 1 reply; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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>
ENETC v4 VF hardware supports MAC address filtering, but the underlying
resources (the PSIPMMR register and per-SI hash filter tables) are owned
by the PF. Add VSI-to-PSI mailbox messages so a VF can request MAC
filter configuration from the PF, using two new command IDs under the
existing MAC filter class (0x20):
1. ENETC_MSG_SET_MAC_HASH_TABLE (cmd_id 3): program the unicast and/or
multicast MAC hash filter table. Unicast filtering is only allowed for
a trusted VF, since it could be used to receive traffic destined for
other SIs. Multicast filtering is allowed even for an untrusted VF,
but limited to ENETC_VF_MC_HASH_BITS_MAX (8) buckets, enough for basic
operation such as IPv6 neighbor discovery and mDNS; a trusted VF may
use all 64 buckets.
2. ENETC_MSG_SET_MAC_PROMISC_MODE (cmd_id 5): enable or disable unicast/
multicast promiscuous mode, and optionally flush the hash filter
table. Enabling promiscuous mode requires a trusted VF; flushing the
table alone does not.
The PSIPMMR register is a shared resource accessed by both
enetc4_pf_set_rx_mode() and the VF message handler via a non-atomic
read-modify-write, so protect these accesses with si->gen_lock to avoid
lost updates on SMP.
When a VF loses trusted status via ndo_set_vf_trust(), clear its unicast
hash filter and disable promiscuous mode so it cannot receive 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 | 160 +++++++++++++++++-
.../net/ethernet/freescale/enetc/enetc_pf.h | 1 +
.../freescale/enetc/enetc_pf_common.c | 68 ++++++--
.../net/ethernet/freescale/enetc/enetc_vf.c | 6 +-
7 files changed, 300 insertions(+), 35 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c b/drivers/net/ethernet/freescale/enetc/enetc4_debugfs.c
index 5029038bf99f..c73722e2285f 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 */
+ spin_lock(&pf->si->gen_lock);
val = enetc_port_rd(hw, ENETC4_PSIPMMR);
+ spin_unlock(&pf->si->gen_lock);
+
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 eedcaf2d7bc9..abe1e8dafe24 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;
}
+ spin_lock(&si->gen_lock);
enetc_set_si_uc_promisc(si, 0, uc_promisc);
enetc_set_si_mc_promisc(si, 0, mc_promisc);
+ spin_unlock(&si->gen_lock);
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 832d2f6ddcd8..6fa66c863748 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 08a9ffdd3eb0..22f5485e968a 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,130 @@ 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;
+ }
+
+ spin_lock(&si->gen_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);
+ spin_unlock(&si->gen_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 +242,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;
}
@@ -333,8 +463,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;
}
@@ -352,6 +481,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,
@@ -362,7 +499,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)) {
@@ -536,6 +672,7 @@ 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.
@@ -543,9 +680,18 @@ static void enetc_msg_clear_vf_config(struct enetc_pf *pf, int vf_id)
if (is_enetc_rev1(si))
return;
+ mutex_lock(&vf_state->lock);
+
spin_lock(&si->gen_lock);
vf_state->msg_fail_cnt = 0;
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
spin_unlock(&si->gen_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)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index 88a558649c68..c5eda6c8eaf0 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 1b2ca2b31a80..9dbb0a1417ea 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;
@@ -591,6 +609,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;
@@ -598,11 +618,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 */
+ spin_lock(&si->gen_lock);
+ enetc_set_si_uc_promisc(si, si_id, false);
+ enetc_set_si_mc_promisc(si, si_id, false);
+ spin_unlock(&si->gen_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_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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (8 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
` (4 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 22f5485e968a..aa37a0123274 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;
@@ -581,7 +575,7 @@ static void enetc_msg_task(struct work_struct *work)
}
out:
- enetc_msg_enable_mr_int(pf);
+ enetc_enable_psiier_interrupts(pf);
}
/* Init */
@@ -656,8 +650,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;
@@ -699,16 +693,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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (9 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 20:17 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
` (3 subsequent siblings)
14 siblings, 2 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 abe1e8dafe24..6a9b1e079ae2 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);
+
+ spin_lock(&pf->si->gen_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);
+ spin_unlock(&pf->si->gen_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 aa37a0123274..ff99937815c0 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;
+ }
+
spin_lock(&si->gen_lock);
if (type & ENETC_MAC_FILTER_TYPE_UC)
enetc_set_si_uc_promisc(si, si_id, promisc);
@@ -542,6 +559,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);
@@ -550,6 +590,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);
@@ -676,6 +718,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);
+
spin_lock(&si->gen_lock);
vf_state->msg_fail_cnt = 0;
enetc_set_si_uc_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 c5eda6c8eaf0..1a9fa3714c3c 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 {
@@ -34,6 +36,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 9dbb0a1417ea..86958f1e9cd3 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -623,7 +623,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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (10 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
` (2 subsequent siblings)
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 VF support for the i.MX94 and i.MX95 platforms. Compared to the
LS1028A ENETC, the VF device ID is updated to 0xef00, so add it to the
VF driver's PCI device ID table. Also add the enetc4 SI ops and VF
driver data, and wire up .sriov_configure for the enetc4 PF driver so
that VFs can be created and torn down on these SoCs.
The number of VFs is not decided by the driver. It is determined by
each ENETC instance's SR-IOV hardware capability, reported through the
PCI SR-IOV TotalVFs field and configured by the SoC integration and
device tree. The driver derives it from pci_sriov_get_totalvfs() in
enetc_init_sriov_resources(); an instance that advertises zero VFs
simply cannot enable SR-IOV. So there is no driver-side revision or
device-ID check bounding the VF count.
For reference, the per-instance VF capability on these SoCs is:
- i.MX95 (v4.1): each ENETC instance supports 2 VFs.
- i.MX94 (v4.3) has two kinds of ENETC:
- standalone ENETC, the same instance type as on i.MX95, but on
i.MX94 it advertises no VFs;
- internal ENETC connected to the CPU port of the NETC switch,
which supports 3 VFs.
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 6a9b1e079ae2..24c1fd869c2c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -1158,6 +1158,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);
@@ -1175,6 +1178,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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (11 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-09 10:07 ` [PATCH v4 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
14 siblings, 1 reply; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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>
An ENETC VF sends MAC filter changes to the PF over the VSI mailbox,
whose send path may sleep for up to 200ms waiting for completion. Since
the legacy ndo_set_rx_mode runs in atomic context, use
ndo_set_rx_mode_async instead, which runs from a workqueue under
rtnl_lock and receives pre-snapshotted unicast and multicast address
lists from the core.
Add two helpers built on the VSI mailbox:
- enetc_vf_set_mac_promisc() sends a promiscuous mode message for a
given filter type (unicast, multicast or both).
- enetc_vf_set_mac_hash_filter() sends the 64-bit MAC hash filter table,
built from the snapshotted address lists.
The callback picks the configuration from the current netdev flags:
- IFF_PROMISC: enable 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 promiscuous mode and apply both unicast and
multicast hash filters.
These requests are subject to the PF-side ENETC_VF_FLAG_TRUSTED check.
For an untrusted VF (the default) the PF denies promiscuous mode and
unicast hash filtering, so only the multicast hash filter is applied,
limited to ENETC_VF_MC_HASH_BITS_MAX buckets. Mark a VF trusted via
'ip link set <pf> vf N trust on' for the full behaviour.
Set IFF_UNICAST_FLT for ENETC v4 VFs so the stack does not needlessly
fall back to full promiscuous mode; whether a unicast hash filter is
actually programmed still depends on the PF trust policy.
Since a denied request always fails, map -EOPNOTSUPP, -EACCES and -EPERM
to 0 so the core does not retry an operation that can never succeed.
ENETC v1 (LS1028A) does not support VF-to-PF MAC filter messaging and
keeps using the legacy ndev ops.
On VF removal, disable promiscuous mode and clear the MAC filters, so
that stale configuration does not persist if the VF is later bound to
another driver.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
.../net/ethernet/freescale/enetc/enetc_vf.c | 214 +++++++++++++++++-
1 file changed, 213 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 322705202d49..8cf38c426dae 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -213,6 +213,193 @@ 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 promisc, bool flush_macs)
+{
+ 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, promisc);
+ msg->config |= FIELD_PREP(ENETC_MSG_MAC_FLUSH_MACS, flush_macs);
+ 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 enetc_msg_swbd msg_swbd;
+ struct netdev_hw_addr *ha;
+ int mac_filter_type = 0;
+ u32 tbl_cnt = 0;
+ u32 msg_size;
+ int i = 0;
+
+ if (!uc && !mc)
+ return 0;
+
+ if (uc) {
+ tbl_cnt += 2;
+ mac_filter_type |= ENETC_MAC_FILTER_TYPE_UC;
+ }
+
+ if (mc) {
+ tbl_cnt += 2;
+ mac_filter_type |= ENETC_MAC_FILTER_TYPE_MC;
+ }
+
+ 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 (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 (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_enable_iff_promisc(struct enetc_si *si)
+{
+ int err;
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ true, true);
+ if (err)
+ dev_err(&si->pdev->dev,
+ "Failed to enable promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int enetc_vf_disable_iff_promisc(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ int err;
+
+ err = enetc_vf_set_mac_hash_filter(si, uc, mc);
+ if (err) {
+ dev_err_once(&si->pdev->dev,
+ "Failed to set MAC hash filters, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ false, false);
+ if (err)
+ dev_err_once(&si->pdev->dev,
+ "Failed to disable promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int enetc_vf_enable_iff_allmulti(struct enetc_si *si,
+ struct netdev_hw_addr_list *uc)
+{
+ int err;
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
+ true, true);
+ if (err) {
+ dev_err(&si->pdev->dev,
+ "Failed to enable multicast promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_hash_filter(si, uc, NULL);
+ if (err) {
+ dev_err(&si->pdev->dev,
+ "Failed to set unicast filter, err: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
+ false, false);
+ if (err)
+ dev_err(&si->pdev->dev,
+ "Failed to disable unicast promiscuous mode, err: %pe\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+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;
+
+ if (ndev->flags & IFF_PROMISC)
+ err = enetc_vf_enable_iff_promisc(si);
+ else if (ndev->flags & IFF_ALLMULTI)
+ err = enetc_vf_enable_iff_allmulti(si, uc);
+ else
+ err = enetc_vf_disable_iff_promisc(si, uc, mc);
+
+ /* 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,
@@ -227,6 +414,20 @@ static const struct net_device_ops enetc_ndev_ops = {
.ndo_hwtstamp_set = enetc_hwtstamp_set,
};
+static const struct net_device_ops enetc4_ndev_ops = {
+ .ndo_open = enetc_open,
+ .ndo_stop = enetc_close,
+ .ndo_start_xmit = enetc_xmit,
+ .ndo_get_stats = enetc_get_stats,
+ .ndo_set_mac_address = enetc_vf_set_mac_addr,
+ .ndo_set_features = enetc_vf_set_features,
+ .ndo_eth_ioctl = enetc_ioctl,
+ .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)
{
int ip_mn;
@@ -280,6 +481,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;
@@ -352,7 +556,10 @@ static int enetc_vf_probe(struct pci_dev *pdev,
goto err_alloc_netdev;
}
- enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops);
+ if (is_enetc_rev1(si))
+ enetc_vf_netdev_setup(si, ndev, &enetc_ndev_ops);
+ else
+ enetc_vf_netdev_setup(si, ndev, &enetc4_ndev_ops);
priv = netdev_priv(ndev);
@@ -416,6 +623,11 @@ static void enetc_vf_remove(struct pci_dev *pdev)
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
+ /* Disable promiscuous mode and clear MAC filters */
+ if (!is_enetc_rev1(si))
+ enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
+ false, true);
+
enetc_free_msix(priv);
enetc_free_si_resources(priv);
--
2.34.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (12 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-09-09 10:07 ` wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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>
Let ENETC v4 VFs track PF link status through the PSI-to-VSI messaging
channel. Add two enetc_si_ops hooks, vf_reg_link_status_notifier and
vf_unreg_link_status_notifier, populated only in enetc4_vsi_ops; rev1
hardware is unaffected.
A dedicated MSI-X vector on the VF handles incoming PSI-to-VSI messages.
Its handler schedules a work item on an ordered workqueue that reads the
notification via VSIMSGRR, updates the carrier state and sets congestion
mode from the PF TX PAUSE state in the message. Reading VSIMSGRR also
acknowledges the PF so it can send the next message. The workqueue is
set up in enetc_vf_probe() and torn down in enetc_vf_remove().
On a VF, enetc_phylink_connect() registers the notifier with the PF and
enetc_close() unregisters it. On registration the PF immediately sends
the current link status and then broadcasts every later transition; if
registration fails, fall back to the LS1028A behaviour and assert
carrier unconditionally. Register the notifier only after the Tx/Rx
resources are allocated in enetc_open(), because once registered the PF
may queue si->msg_task via a link-up message. An error unwind cannot
drain that work item, since enetc_open() and the work item both take the
RTNL lock and waiting would deadlock; and as __LINK_STATE_START is set
before ndo_open() runs, netif_running() stays true during the unwind, so
the queued work could still call netif_carrier_on() and leave carrier
'on' after a failed open.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/ethernet/freescale/enetc/enetc.c | 48 +++-
drivers/net/ethernet/freescale/enetc/enetc.h | 4 +
.../net/ethernet/freescale/enetc/enetc_hw.h | 9 +
.../net/ethernet/freescale/enetc/enetc_vf.c | 209 +++++++++++++++++-
4 files changed, 261 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 803c5c541a5c..04c49b680709 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;
}
@@ -3024,10 +3044,6 @@ int enetc_open(struct net_device *ndev)
if (err)
goto err_setup_irqs;
- err = enetc_phylink_connect(ndev);
- if (err)
- goto err_phy_connect;
-
tx_res = enetc_alloc_tx_resources(priv);
if (IS_ERR(tx_res)) {
err = PTR_ERR(tx_res);
@@ -3040,6 +3056,10 @@ int enetc_open(struct net_device *ndev)
goto err_alloc_rx;
}
+ err = enetc_phylink_connect(ndev);
+ if (err)
+ goto err_phy_connect;
+
enetc_tx_onestep_tstamp_init(priv);
enetc_assign_tx_resources(priv, tx_res);
enetc_assign_rx_resources(priv, rx_res);
@@ -3048,12 +3068,11 @@ int enetc_open(struct net_device *ndev)
return 0;
+err_phy_connect:
+ enetc_free_rx_resources(rx_res, priv->num_rx_rings);
err_alloc_rx:
enetc_free_tx_resources(tx_res, priv->num_tx_rings);
err_alloc_tx:
- if (priv->phylink)
- phylink_disconnect_phy(priv->phylink);
-err_phy_connect:
enetc_free_irqs(priv);
err_setup_irqs:
clk_disable_unprepare(priv->ref_clk);
@@ -3093,6 +3112,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 +3120,20 @@ 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)
+ goto carrier_off;
+
+ /* No need to check whether the previous registration was
+ * successful. Sending the deregistration message has no
+ * impact; the PF side simply clears the corresponding bit
+ * in link_status_ms_mask for the VF.
+ */
+ 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 0501cc2fc304..d9e91832a9c1 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 */
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 8cf38c426dae..ee9567998c7c 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -135,6 +135,37 @@ 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)
+{
+ return enetc_msg_link_status_notifier(si, true);
+}
+
+static int enetc_vf_unreg_link_status_notifier(struct enetc_si *si)
+{
+ return enetc_msg_link_status_notifier(si, false);
+}
+
static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
struct sockaddr *saddr)
{
@@ -499,6 +530,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,
@@ -511,8 +664,41 @@ 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);
+
+ /* Disable the MR interrupt */
+ enetc_vf_disable_mr_int(si);
+ enetc_wr(&si->hw, ENETC_VSIIDR, VSIIDR_MR);
+ /* Reading VSIMSGRR to clear the MS bit on the PF's side */
+ enetc_rd(&si->hw, ENETC_VSIMSGRR);
+
+ destroy_workqueue(si->workqueue);
+}
+
static int enetc_vf_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -587,15 +773,32 @@ 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;
+ }
+
+ netif_carrier_off(ndev);
+ /* Enable message received interrupt */
+ enetc_vf_enable_mr_int(si);
err = register_netdev(ndev);
if (err)
goto err_reg_netdev;
- 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:
@@ -628,6 +831,8 @@ static void enetc_vf_remove(struct pci_dev *pdev)
enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
false, true);
+ 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 [flat|nested] 32+ messages in thread
* [PATCH v4 net-next 15/15] net: enetc: add ndo_get_vf_config() support
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
` (13 preceding siblings ...)
2026-09-09 10:07 ` [PATCH v4 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
@ 2026-09-09 10:07 ` wei.fang
14 siblings, 0 replies; 32+ messages in thread
From: wei.fang @ 2026-09-09 10:07 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 | 34 +++++++++++++++++++
.../freescale/enetc/enetc_pf_common.h | 2 ++
5 files changed, 65 insertions(+)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 24c1fd869c2c..71c971618388 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 1a9fa3714c3c..1f92d16d8ca8 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -13,6 +13,7 @@ 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 {
@@ -20,6 +21,9 @@ struct enetc_vf_state {
enum enetc_vf_flags flags;
/* Number of consecutive failures to send PF-to-VF messages */
int msg_fail_cnt;
+ 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 86958f1e9cd3..8206884294a4 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -682,5 +682,39 @@ 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;
+ /* ENETC v4 has not support spoofchk yet */
+ if (is_enetc_rev1(priv->si))
+ 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 7d7e67a12278..c9eed879d5a3 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 [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 01/15] net: enetc: add trusted VF support
2026-09-09 10:07 ` [PATCH v4 net-next 01/15] net: enetc: add trusted " wei.fang
@ 2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 2:29 ` Wei Fang
0 siblings, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:20 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: 1 · Low: 3
- [High] Trust revocation ("ip link set <dev> vf <N> trust off") clears
only the software bit in pf->vf_state[vf].flags; it never reprograms…
- [Medium] Default-deny of the VF primary-MAC mailbox message is a user-
visible behaviour change that the commit message understates, and the…
- [Low] Reported for completeness and marked as resolved within the
series: at this commit both enetc_ndev_ops (enetc_pf.c:482-503) and…
- [Low] Reported for completeness and marked as resolved within the
series: at this commit enetc4_ndev_ops advertises .ndo_set_vf_trust…
- [Low] Reported for completeness and assessed as intended, documented
behaviour: ENETC_VF_FLAG_TRUSTED lives in pf->vf_state[] which is…
--
net: enetc: add trusted VF support
This adds an ENETC_VF_FLAG_TRUSTED per-VF flag and wires up
ndo_set_vf_trust through the new shared enetc_pf_set_vf_trust() helper for
both the enetc and enetc4 PF drivers. The first mailbox message gated on
the flag is the VF primary MAC address change, which now returns
ENETC_MSG_CLASS_ID_PERMISSION_DENY for untrusted VFs.
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 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,
> };
[Severity: Low]
At this commit enetc4_pf_driver has no .sriov_configure member and enetc4
never calls enetc_msg_psi_init(), so VFs cannot be instantiated on ENETC v4
and the mailbox worker is never armed.
The only reader of ENETC_VF_FLAG_TRUSTED is
enetc_msg_set_vf_primary_mac_addr(), reached via enetc_msg_task() which is
only set up by enetc_sriov_configure(). Does that mean
ip link set <enetc4 pf> vf N trust on
returns 0 here with no observable effect, and that the new
enetc_init_sriov_resources() call allocates rxmsg/vf_state plus total_vfs
mutexes for slots that cannot exist yet?
This is resolved later in the same series: the patch adding VF support for
i.MX94 and i.MX95 adds .sriov_configure = enetc_sriov_configure to
enetc4_pf_driver, and enetc_msg.c then gates several ENETC v4 mailbox
operations on the trust flag. Noting it only because the knob is advertised
one patch before it can do anything.
> 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
> @@ -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: Medium]
Is this a user-visible behaviour change for existing SR-IOV deployments?
ENETC_VF_FLAG_TRUSTED is zero for every slot, since vf_state is
devm_kcalloc()'d in enetc_init_sriov_resources() and only
"ip link set <pf> vf N trust on" sets the bit. On the VF side
enetc_msg_vsi_send() maps the new response to an errno:
case ENETC_MSG_CLASS_ID_PERMISSION_DENY:
return -EACCES;
So after this patch enetc_vf_set_mac_addr() -> enetc_msg_vsi_send() fails
with -EACCES, and an in-guest "ip link set <vf> address ..." or enslaving
the VF into a bond/macvlan stops working after a kernel upgrade with no
configuration change.
The commit message says:
This does not regress VF bring-up. The PF programs a valid
primary MAC into every VF slot at probe, and the VF loads it from its
own SIPMAR0/1 registers.
Bring-up is indeed unaffected, but could the message also state that guests
which previously changed their own VF MAC now need the host to mark the slot
trusted first?
Related question on tags: the message describes this as preventing a
malicious VM from eavesdropping on other SIs and from spoofing MAC
addresses, and the VF-set-MAC path has existed since commit beb74ac878c8
("enetc: Add vf to pf messaging support"). Should this carry a Fixes: tag
or stable guidance for backporters?
> 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;
> }
> 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,
[Severity: Low]
At this commit neither enetc_ndev_ops nor enetc4_ndev_ops implements
.ndo_get_vf_config, and rtnl_fill_vf() returns early when
ndo_get_vf_config is NULL, so IFLA_VF_TRUST is never emitted and
"ip link show <pf>" prints no VF block.
Since the remediation the commit message recommends is
"ip link set <dev> vf <N> trust off", would it be worth making the trust
state readable in the same patch that makes it settable?
This is closed later in the series by the patch adding ndo_get_vf_config()
support, which reports ivi->trusted from the same flag.
> 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;
[Severity: Low]
Trust lives in pf->vf_state[], which is allocated once at PF probe, and the
bound here is pf->total_vfs rather than pf->num_vfs. It is not cleared by
enetc_sriov_configure(pdev, 0), by pci_disable_sriov(), or by VF FLR, so a
slot trusted for one guest stays trusted when the VF is destroyed and
re-created for another guest.
The commit message states this is deliberate:
it is intentionally preserved across an SR-IOV disable/enable
cycle. The bounds check uses pf->total_vfs, the hardware maximum and
the size of pf->vf_state[], so pre-configuring an uninstantiated slot
is in-bounds. If a slot may be reassigned to another guest, the admin
clears trust with "ip link set <dev> vf <N> trust off".
Later in the series enetc_msg_clear_vf_config() encodes the same rationale
in a comment, so this looks intended and is mentioned only for the record.
> +
> + 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;
[Severity: High]
Does clearing trust here leave the hardware MAC filter that the VF
programmed while it was trusted?
While trusted, the guest can put any valid unicast address into the SI
primary MAC registers, including the PF's or another VF's:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_primary_mac_addr() {
...
enetc_set_si_hw_addr(pf, vf_id + 1, addr);
...
}
The trust-off branch above only clears the software bit, so ENETC_PSIPMAR0/1
(ENETC4_PSIPMAR0/1 on v4) for that SI keep the guest-chosen address.
The other paths that could restore it do not. enetc_setup_mac_addresses()
is the only place a PF-chosen MAC is written into every VF slot, and it runs
at probe:
drivers/net/ethernet/freescale/enetc/enetc_pf_common.c:enetc_setup_mac_addresses() {
...
for (i = 0; i < pf->total_vfs; i++) {
err = enetc_setup_mac_address(NULL, pf, i + 1);
...
}
enetc4_pf_vf_flr_handler() re-applies only promiscuous state, so the address
also survives a VF FLR (guest reboot or driver reload), and the SR-IOV
disable path does not touch it either.
So after the documented "ip link set <dev> vf <N> trust off", frames for the
spoofed address still hit that SI's exact-match filter, and a slot later
reassigned to a different guest inherits the address, because the VF driver
reads its MAC out of SIPMAR0/1.
At the end of the series the trust-off branch grows scrubbing of
promiscuous mode and of the UC/MC hash filters, but still does not restore
the SI primary MAC. Should this branch reprogram the PF-assigned primary
MAC for the slot as well?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-09 10:07 ` [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
@ 2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 5:55 ` Wei Fang
2026-09-11 20:15 ` Claudiu Manoil
1 sibling, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:20 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 6 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 4 · Low: 2
- [Medium] A VF's hardware PSIMSGSR[MS(n)] doorbell can only be cleared
by the VSI (by reading VSIMSGRR); the PF cannot clear it (the code's…
- [Medium] enetc_pf_send_link_status_msg() treats the registered-VF set
as one atomic broadcast group even though it is also used to deliver a…
- [Medium] Link-status delivery is one-shot and based on a racy snapshot
of ENETC_PSIMSGSR.
- [Medium] Any VF, including an untrusted one assigned to a hostile
VM/container via VFIO, can register for link-status notification with…
- [Low] The comment in enetc_pf_send_link_status_msg() states that a VF
is removed from link_status_ms_mask "[i]f the PF fails to send…
- [Low] vf_state->msg_fail_cnt is not reset across an explicit
unregister/re-register cycle:…
--
net: enetc: add link status message support to PF driver
This adds message class 0x80 so a VF can query the PF link state or
register for link change notification. Notifications are written to
ENETC_PSIMSGSR from an ordered workqueue, and link_status_ms_mask tracks
which VFs are registered.
A few questions about the notification bookkeeping below.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index a89a5a418a23..79dbaf72fcff 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> @@ -136,6 +136,104 @@ 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 void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
> + int vf_id)
> +{
> + struct enetc_si *si = pf->si;
> +
> + spin_lock(&si->gen_lock);
> + enetc_pf_reply_msg(&si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
> +
> + /* 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) {
> + spin_unlock(&si->gen_lock);
> + return;
> + }
> +
> + pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
> + spin_unlock(&si->gen_lock);
> +
> + /* Notify VF the current link status */
> + queue_work(si->workqueue, &pf->link_status_task);
> +}
[Severity: Medium]
This queues the shared, VF-agnostic link_status_task in order to deliver one
VF's initial status. Does that mean a single VF registering re-sends the
notification to every VF currently set in link_status_ms_mask?
The retry decision in enetc_pf_send_link_status_msg() is made on the
aggregate mask rather than per VF:
ms_status = enetc_rd(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
if ((ms_mask & ms_status) && retry_num++ < 200) {
spin_unlock(&si->gen_lock);
usleep_range(1000, 1020);
goto retry;
}
Can one VF that never reads its mailbox therefore add up to roughly 200 ms
of sleeping to every registration and every link transition seen by all the
other registered VFs?
The ms_status_check loop also advances vf_state->msg_fail_cnt for every VF
in ms_mask. Can a VF that happened to be busy during a notification
triggered by some other VF's registration accumulate failures it did not
cause, and eventually be evicted from link_status_ms_mask?
> +
> +static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
> + int vf_id)
> +{
> + spin_lock(&pf->si->gen_lock);
> + pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
> + enetc_pf_reply_msg(&pf->si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
> + spin_unlock(&pf->si->gen_lock);
> +}
[Severity: Low]
Neither this function nor enetc_msg_register_link_status_notifier() resets
vf_state->msg_fail_cnt.
If a VF accumulated up to 10 consecutive delivery failures under a previous
registration, does the first busy notification after it re-registers
immediately clear its bit from link_status_ms_mask?
Later in the series the Linux VF driver unregisters on ndo_close and
registers again on ndo_open, so the counter survives an ifdown/ifup cycle.
Would clearing vf_state->msg_fail_cnt in the register path be preferable?
> +
> +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;
[Severity: Medium]
This dispatch has no ENETC_VF_FLAG_TRUSTED check and no rate limiting,
unlike enetc_msg_set_vf_primary_mac_addr() and the other MAC filter
handlers in this file. The link speed handler added later in the series
documents exactly this abuse class in its own comment ("A malicious or
malfunctioning VM could potentially spam these messages in a tight
loop...").
The registration handler replies ENETC_PF_MSG_SUCCESS first, which clears
the VF's MR bit and lets the VF post the next message right away, then
calls queue_work() unconditionally.
Can a VF assigned to an untrusted guest register, never consume its
mailbox, and keep re-sending ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER so
that the PF's single ordered workqueue spends 200 x ~1 ms sleeping on each
invocation, delaying carrier notifications for the co-tenant VFs?
The 10-failure eviction does not appear to bound this, since the VF can
simply register again and restore its bit in link_status_ms_mask.
> + 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;
> +}
[ ... ]
> @@ -340,6 +442,22 @@ 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;
> +
> + /* 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;
> +
> + spin_lock(&si->gen_lock);
> + vf_state->msg_fail_cnt = 0;
> + spin_unlock(&si->gen_lock);
> +}
[Severity: Medium]
This resets software state only; the hardware PSIMSGSR[MS] bit for that VSI
is left set. The comment in enetc_pf_send_link_status_msg() states that the
PF cannot write the MS bit as 0, so only the VSI can clear it by reading
VSIMSGRR.
Is there any path that disarms an MS bit left set by a VF that stopped
consuming messages, for example a crashed or paused guest, a VF FLR, a VFIO
release, or a killed DPDK application?
The commit message says:
"The link_status_ms_mask tracks the VFs registered for notification and
is cleared when SR-IOV is disabled."
but a VF FLR clears neither link_status_ms_mask nor msg_fail_cnt: the
enetc4_pf_vf_flr_handler() added later in the series restores only the
promiscuous state. Does a re-created VF at the same index then look
registered to the PF while the notification channel to it is blocked?
Two follow-on effects in enetc_pf_send_link_status_msg(): every later PF
link transition burns up to 200 x ~1 ms of polling because the retry gate
is keyed on the aggregate mask, and after 11 such events the VF's bit is
dropped from link_status_ms_mask with only a dev_warn_ratelimited(), while
the VF still considers itself registered because it was acked with
ENETC_PF_MSG_SUCCESS. Should there be an error reply, a re-arm, or a
periodic retry so the VF's carrier state and its RX BD congestion mode
decision (driven by the TX PAUSE bit) do not stay stale until an
administrative ifdown/ifup?
The later VF patch does drain VSIMSGRR in enetc_vf_wq_task_destroy(), but
that covers only the clean Linux VF unbind case, not FLR or guest death.
> static void enetc_msg_psi_free(struct enetc_pf *pf)
> {
> struct enetc_si *si = pf->si;
[ ... ]
> @@ -367,6 +487,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
> int err;
>
> if (!num_vfs) {
> + spin_lock(&si->gen_lock);
> + pf->sriov_enabled = false;
> + pf->link_status_ms_mask = 0;
> + spin_unlock(&si->gen_lock);
> +
> pci_disable_sriov(pdev);
> enetc_msg_psi_free(pf);
> pf->num_vfs = 0;
[ ... ]
> @@ -396,3 +535,114 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
> return err;
> }
> EXPORT_SYMBOL_GPL(enetc_sriov_configure);
> +
> +void enetc_pf_send_link_status_msg(struct enetc_pf *pf)
> +{
> + struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
> + u16 pf_msg, ms_mask, new_ms_msk, ms_status;
> + struct enetc_si *si = pf->si;
> + int retry_num = 0;
> +
> +retry:
> + spin_lock(&si->gen_lock);
> + ms_mask = pf->link_status_ms_mask;
> + /* VFs have unregistered link status notification, return directly */
> + if (!ms_mask)
> + goto unlock;
> +
> + /* 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(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
> + if ((ms_mask & ms_status) && retry_num++ < 200) {
> + spin_unlock(&si->gen_lock);
> + /* Wait VFs to handle the last message */
> + usleep_range(1000, 1020);
> + goto retry;
> + }
> +
> + /* None of the relevant VFs have processed the previous message, and
> + * the PF has tried 200 times. This situation indicates that VF has
> + * malfunctioned.
> + */
> + new_ms_msk = ms_mask & (~ms_status);
> + if (!new_ms_msk) {
> + dev_err_ratelimited(&si->pdev->dev,
> + "All registered VFs (MS: 0x%x) are busy\n",
> + ms_mask);
> + goto ms_status_check;
> + }
> +
> + if (new_ms_msk != ms_mask)
> + dev_warn_ratelimited(&si->pdev->dev,
> + "Failed to notify link status to VFs (MS: 0x%x)\n",
> + ms_mask ^ new_ms_msk);
> +
> + pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
> + enetc_wr(&si->hw, ENETC_PSIMSGSR,
> + FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
[Severity: Medium]
Delivery here looks one-shot. Both the "All registered VFs ... are busy"
path and the new_ms_msk != ms_mask path abandon the pending notification:
link_status_task is not re-queued and nothing records that the excluded VFs
still owe a message, only a rate-limited log line is emitted.
Is the ms_status snapshot also racy? A VSI clears its own MS bit
asynchronously and outside si->gen_lock, so if a VF clears its bit between:
ms_status = enetc_rd(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
and:
enetc_wr(&si->hw, ENETC_PSIMSGSR,
FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
does that VF consume the previous message content and never receive the
current link state? gen_lock cannot close this window because the MS bit is
mutated by a peer PCI function.
With the VF side added later in the series, a phy-less v4 VF relies
entirely on this notification for its carrier: enetc_phylink_connect()
returns without calling netif_carrier_on() when
vf_reg_link_status_notifier() succeeds. Does a dropped initial notification
then leave the VF netdev with no carrier until some later PF link change or
an administrative ifdown/ifup?
> +
> +ms_status_check:
> + /* If the PF fails to send messages to the corresponding VF for 10
> + * consecutive times, clear that VF's bit in link_status_ms_mask.
> + */
> + for (int i = 0; i < pf->num_vfs; i++) {
> + struct enetc_vf_state *vf_state = &pf->vf_state[i];
> +
> + if (!(PSIMSGSR_MS(i) & ms_mask))
> + continue;
> +
> + if (!(PSIMSGSR_MS(i) & ms_status)) {
> + vf_state->msg_fail_cnt = 0;
> + continue;
> + }
> +
> + if (vf_state->msg_fail_cnt++ < 10)
> + continue;
[Severity: Low]
The comment above says the bit is cleared after 10 consecutive failures,
but the post-increment test lets failures with observed values 0..9
continue, so the bit is only cleared on the 11th consecutive failure. Is
the comment or the comparison off by one?
> +
> + vf_state->msg_fail_cnt = 0;
> + pf->link_status_ms_mask &= ~PSIMSGSR_MS(i);
> + dev_warn_ratelimited(&si->pdev->dev,
> + "Clear VF%d's link status MS bit\n", i);
> + }
> +
> +unlock:
> + spin_unlock(&si->gen_lock);
> +}
> +EXPORT_SYMBOL_GPL(enetc_pf_send_link_status_msg);
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-09-09 10:07 ` [PATCH v4 net-next 04/15] net: enetc: add link speed " wei.fang
@ 2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 2:56 ` Wei Fang
2026-09-11 20:16 ` Claudiu Manoil
1 sibling, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:20 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] enetc_msg_get_link_speed()
(drivers/net/ethernet/freescale/enetc/enetc_msg.c:274) introduces an…
--
Verified the relevant code paths. Report below.
net: enetc: add link speed message support to PF driver
Adds a new mailbox message class 0x81 so a VF can query the current link
speed from the PF. The PF reads the speed with
phylink_ethtool_ksettings_get() under rtnl_lock() and encodes it into the
8-bit class code of the reply, restricting the query to trusted VFs.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> index 79dbaf72fcff..08a9ffdd3eb0 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> @@ -230,6 +230,93 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id,
> return 0;
> }
>
[ ... ]
> +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();
[Severity: Medium]
Is it safe to block on rtnl_lock() inside the PSI mailbox service work
item? This looks like the first sleeping, host-global lock acquisition on
that path: enetc_msg_handle_link_status() only uses si->gen_lock and never
sleeps.
The completion code is only written back to the VF after the class handler
returns, in enetc_msg_task():
for (i = 0; i < pf->num_vfs; i++) {
...
enetc_msg_handle_rxmsg(pf, i, &msg_code);
...
enetc_pf_reply_msg(hw, i, msg_code);
}
out:
enetc_msg_enable_mr_int(pf);
while the VF side waits with a fixed 200 ms bound in 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");
return err;
}
If any unrelated host rtnl holder (for example 'ip link del' going through
synchronize_net(), an ndo_open of another device, bonding/team
reconfiguration or a module load) keeps rtnl for longer than ~200 ms, does
the reply not arrive after the VF has already given up? In that case the
VF logs "VSI mailbox timeout" and returns -ETIMEDOUT, and because
VSIMSGSR[MB] stays set until the PF finally replies, subsequent VF sends
hit the earlier check and fail with -EIO ("VSI mailbox is busy").
There is a second effect from the same wait. enetc_msg_task() services all
VFs serially in one loop and only re-arms the PSIIER MR interrupt sources at
enetc_msg_enable_mr_int(pf) on exit, so while one VF's speed query sleeps on
rtnl, do the pending messages of the other VFs (set primary MAC, MAC hash
filter, promisc mode) not get delayed and potentially time out too?
Would a non-blocking source for the speed work here, or alternatively the
deferred-reply mechanism (ENETC_MSG_CLASS_ID_CMD_DEFERRED) so the mailbox
handshake is not held open across rtnl?
[Severity: Medium]
On the trust gate itself: the comment above the mutex_lock(&vf_state->lock)
check describes the tight-loop spam scenario, but the check only narrows
which VFs can reach rtnl_lock(), it does not bound the rate. Nothing
throttles inbound class 0x81 messages - the MR interrupt sources are simply
re-armed at the end of enetc_msg_task(), and the only per-VF counter
(msg_fail_cnt) tracks PF-to-VF send failures rather than inbound queries.
Can a trusted VF still drive repeated rtnl acquire/release from the PF work
item at mailbox round-trip rate, i.e. is the primitive the comment warns
about still reachable from guest-controlled code once an admin sets 'trust
on'?
For reference, a couple of related concerns were checked and do not appear
to be problems: vf_state->lock is dropped before rtnl_lock() so there is no
ordering issue against ndo_set_vf_trust, and enetc_msg_psi_free() (and its
cancel_work_sync()) is only reached from enetc_sriov_configure(), which does
not hold rtnl.
> +
> + return enetc_build_link_speed_msg(link_info.base.speed,
> + link_info.base.duplex);
> +}
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
2026-09-09 10:07 ` [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
@ 2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 6:13 ` Wei Fang
0 siblings, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:21 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: 0 · Low: 2
- [Low] DISCARDED (resolved later in the same series).
- [Low] struct enetc_msg_mac_hash_filter
(drivers/net/ethernet/freescale/enetc/enetc_mailbox.h:226-232)…
--
net: enetc: add MAC address filtering support for VFs of ENETC v4
This adds two VSI-to-PSI mailbox commands under the MAC filter class so an
ENETC v4 VF can ask the PF to program its unicast/multicast MAC hash filter
tables and to toggle promiscuous mode, with trusted-VF checks and a limit of
8 multicast hash buckets for untrusted VFs. It also takes si->gen_lock around
the read-modify-write of the shared PSIPMMR register, and clears a VF's
unicast hash filter and promiscuous mode when it loses trusted status.
A couple of questions below.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
> index 832d2f6ddcd8..6fa66c863748 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
[ ... ]
> @@ -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[];
> +};
[Severity: Low]
Should hash_tbl[] carry an explicit endianness annotation?
This struct describes a wire format: the message lives in a DMA buffer that
the mailbox hardware copies from the VSI buffer into the PSI buffer, and the
header in this same file already declares the byte order of its multi-byte
field:
struct enetc_msg_header {
__be16 crc16;
...
};
hash_tbl[] is the only other multi-byte payload field and it is plain u32,
so both endpoints are implicitly required to have the same byte order. The
producer fills it in host order and enetc_msg_set_vf_mac_hash_filter()
recombines it in host order:
uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
If a big-endian VSI kernel ever talks to a little-endian PSI (or a
non-Linux PSI implementing the documented protocol), would the hash buckets
end up byte-swapped, so the SI filters on the wrong buckets while both
sides report success? The CRC16 is computed byte-wise over the buffer, so
it would not catch that.
Would declaring the array as __le32 and converting with cpu_to_le32() /
le32_to_cpu() on both ends be preferable now, while the format is being
introduced?
> +
> +/* 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 08a9ffdd3eb0..22f5485e968a 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
[ ... ]
> @@ -116,6 +242,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;
> }
[Severity: Low]
At this commit, is there anything in the tree that sends
ENETC_MSG_SET_MAC_HASH_TABLE or ENETC_MSG_SET_MAC_PROMISC_MODE?
Both new handlers here, and the UCF_DENY mapping added to
enetc_msg_vsi_send() below, only become reachable with the later patch in
the series ("net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF"),
which adds enetc_vf_set_mac_hash_filter() and enetc_vf_set_mac_promisc()
and wires .ndo_set_rx_mode_async = enetc_vf_set_rx_mode into
enetc4_ndev_ops. So this is only a patch-ordering observation, the receiver
landing one patch before the sender, not a defect at the end of the series.
> 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:
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-09-09 10:07 ` [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
@ 2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 6:23 ` Wei Fang
2026-09-11 20:17 ` Claudiu Manoil
1 sibling, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:21 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 restoration is deferred to a workqueue:
enetc_msg_psi_msix() only calls enetc_disable_psiier_interrupts() +…
--
net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
A VF FLR on ENETC v4 resets PSIPMMR[SIn_MAC_UP] and PSIPMMR[SIn_MAC_MP]
to their promiscuous default, so the patch tracks the PF-managed UC/MC
promiscuous state per VF and re-applies it from a new vf_flr_handler
callback dispatched out of enetc_msg_task().
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
> index abe1e8dafe24..6a9b1e079ae2 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);
> +
> + spin_lock(&pf->si->gen_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);
> + spin_unlock(&pf->si->gen_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 aa37a0123274..ff99937815c0 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);
> }
[ ... ]
> @@ -542,6 +559,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);
> @@ -550,6 +590,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 default before this
restore runs?
The interrupt handler only masks PSIIER and queues the work:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_psi_msix() {
enetc_disable_psiier_interrupts(pf);
schedule_work(&si->msg_task);
}
so between the FLR taking effect (PSIPMMR[SIn_MAC_UP]/[SIn_MAC_MP] back at
their promiscuous reset values) and enetc_msg_task() ->
enetc_vf_flr_handler() -> enetc4_pf_vf_flr_handler() running, the SI keeps
receiving all port unicast and multicast traffic. Since the hardware default
ignores trust state, does this window also apply to an untrusted VF that has
never been granted promiscuous mode?
Is the window bounded here? PSIIER is only re-armed at the very end of
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);
so an FLR latched while the message loop is in progress is not serviced until
that loop finishes. For a trusted VF the loop can reach
enetc_msg_get_link_speed(), which blocks on the global lock:
rtnl_lock();
phylink_ethtool_ksettings_get(priv->phylink, &link_info);
rtnl_unlock();
Can a guest keep msg_task busy this way while another VF (or the same one)
sits in the FLR-induced promiscuous state?
Would it make sense to force UC/MC promiscuous off for the affected SI as
soon as the FLR event is observed, before the tracked policy is re-applied,
and to note the residual window in the commit message?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-09-09 10:07 ` [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
@ 2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 7:17 ` Wei Fang
0 siblings, 1 reply; 32+ messages in thread
From: netdev-bot+sashiko @ 2026-09-10 11:21 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] For the default (untrusted) ENETC v4 VF, the IFF_PROMISC and
IFF_ALLMULTI branches of the new enetc_vf_set_rx_mode() program no MAC…
- [Medium] enetc_vf_set_rx_mode()
(drivers/net/ethernet/freescale/enetc/enetc_vf.c:394-398) translates…
- [Medium] enetc_vf_disable_iff_promisc() (enetc_vf.c:323-345) treats a
documented *partial success* from the PF as fatal and therefore never…
--
net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
This converts ENETC v4 VFs to ndo_set_rx_mode_async so MAC filter updates
can sleep on the VSI mailbox, adds enetc_vf_set_mac_promisc() and
enetc_vf_set_mac_hash_filter() helpers, and picks promisc/allmulti/hash
filter configuration from the current netdev flags.
A few questions below about the untrusted-VF paths and the error handling.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> index 322705202d49..8cf38c426dae 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> @@ -213,6 +213,193 @@ static int enetc_vf_setup_tc(struct net_device *ndev, enum tc_setup_type type,
> }
> }
>
[ ... ]
> +static int enetc_vf_enable_iff_promisc(struct enetc_si *si)
> +{
> + int err;
> +
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> + true, true);
> + if (err)
> + dev_err(&si->pdev->dev,
> + "Failed to enable promiscuous mode, err: %pe\n",
> + ERR_PTR(err));
> +
> + return err;
> +}
> +
> +static int enetc_vf_disable_iff_promisc(struct enetc_si *si,
> + struct netdev_hw_addr_list *uc,
> + struct netdev_hw_addr_list *mc)
> +{
> + int err;
> +
> + err = enetc_vf_set_mac_hash_filter(si, uc, mc);
> + if (err) {
> + dev_err_once(&si->pdev->dev,
> + "Failed to set MAC hash filters, err: %pe\n",
> + ERR_PTR(err));
> + return err;
> + }
> +
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> + false, false);
[Severity: Medium]
Is the early return above correct when the PF only denies the unicast part
of the request?
enetc_vf_disable_iff_promisc() sends the combined hash filter first with
type = UC | MC. On the PF side, enetc_msg_set_vf_mac_hash_filter() handles
ENETC_MAC_FILTER_TYPE_ALL like this:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:
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;
}
so the multicast hash was applied and only unicast was refused, yet
enetc_msg_vsi_send() maps UCF_DENY to -EACCES. For the default untrusted
VF that means the 'return err' path is always taken and the following
enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL, false, false);
message, the one that actually leaves promiscuous mode, is never sent.
Would it be better to treat UCF_DENY as a partial success and continue, or
to send the promisc-off message before the hash filter?
The same ordering concern applies to enetc_vf_enable_iff_allmulti() below:
the hardware-changing step (enable MC promisc with flush_macs = true) runs
first and the restricting step (disable UC promisc) runs last, with no
rollback if a later mailbox transaction returns -EIO ("VSI mailbox is
busy"), -ETIME ("VSI mailbox timeout") or -EBUSY from the PSI. Can that
leave a trusted VF with UC/MC promiscuity armed in the SI while the netdev
has already left promiscuous mode?
Since the SI promiscuity bits live in PF register space indexed by si_id and
are re-applied after FLR by enetc4_pf_vf_flr_handler(), a skipped
promisc-off message appears to persist until the VF issues another one or
the PF revokes trust.
> + if (err)
> + dev_err_once(&si->pdev->dev,
> + "Failed to disable promiscuous mode, err: %pe\n",
> + ERR_PTR(err));
> +
> + return err;
> +}
> +
> +static int enetc_vf_enable_iff_allmulti(struct enetc_si *si,
> + struct netdev_hw_addr_list *uc)
> +{
> + int err;
> +
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
> + true, true);
> + if (err) {
> + dev_err(&si->pdev->dev,
> + "Failed to enable multicast promiscuous mode, err: %pe\n",
> + ERR_PTR(err));
> + return err;
> + }
[Severity: High]
For an untrusted VF, does this leave the IFF_ALLMULTI and IFF_PROMISC cases
with no MAC filter programmed at all?
Both enetc_vf_enable_iff_allmulti() and enetc_vf_enable_iff_promisc() start
by requesting promiscuous mode, and the PF rejects that unconditionally
before touching any register:
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;
}
That becomes -EACCES and both helpers return immediately, so the unicast
hash filter, the unicast-promisc-off message and any multicast hash filter
are never sent.
Note also that the multicast snapshot is not passed into either of these two
branches, and enetc_vf_set_mac_hash_filter(si, NULL, mc) has no caller in
the patch. The PF does permit a bounded multicast-only hash filter for an
untrusted VF:
drivers/net/ethernet/freescale/enetc/enetc_msg.c:
enetc_msg_set_vf_mac_hash_filter() {
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);
so multicast groups joined while the interface is in allmulti or promisc
mode appear to be silently dropped, with the SI keeping whatever hash filter
the last non-allmulti update left behind. IFF_ALLMULTI and IFF_PROMISC are
entered by ordinary operations (bridge/bond/macvlan enslavement, 'ip link
set ... allmulticast on', tcpdump), including from a guest owning the VF.
This also seems to contradict the commit message:
For an untrusted VF (the default) the PF denies promiscuous mode and
unicast hash filtering, so only the multicast hash filter is
applied, limited to ENETC_VF_MC_HASH_BITS_MAX buckets.
That only holds for the third (non-promisc, non-allmulti) branch, not for
these two.
One more thing: unlike enetc_vf_disable_iff_promisc(), which uses
dev_err_once(), these two helpers use plain dev_err(). Since the denial is
guaranteed for an untrusted VF, wouldn't that print an error line on every
rx_mode update, i.e. on every multicast join/leave and every bridge or bond
membership change?
> +
> + err = enetc_vf_set_mac_hash_filter(si, uc, NULL);
> + if (err) {
> + dev_err(&si->pdev->dev,
> + "Failed to set unicast filter, err: %pe\n",
> + ERR_PTR(err));
> + return err;
> + }
> +
> + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
> + false, false);
> + if (err)
> + dev_err(&si->pdev->dev,
> + "Failed to disable unicast promiscuous mode, err: %pe\n",
> + ERR_PTR(err));
> +
> + return err;
> +}
> +
> +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;
> +
> + if (ndev->flags & IFF_PROMISC)
> + err = enetc_vf_enable_iff_promisc(si);
> + else if (ndev->flags & IFF_ALLMULTI)
> + err = enetc_vf_enable_iff_allmulti(si, uc);
> + else
> + err = enetc_vf_disable_iff_promisc(si, uc, mc);
> +
> + /* 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: Medium]
Is the premise behind this mapping accurate? The comment, and the commit
message wording
Since a denied request always fails, map -EOPNOTSUPP, -EACCES and
-EPERM to 0 so the core does not retry an operation that can never
succeed.
assume the denial is permanent, but PF trust is runtime-mutable:
drivers/net/ethernet/freescale/enetc/enetc_pf_common.c:
enetc_pf_set_vf_trust() {
if (setting) {
vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
} else {
That path only flips the flag; it neither notifies the VF nor replays the
previously rejected configuration. So does
ip link set <pf> vf N trust on
as suggested in the commit message actually take effect before some
unrelated flag or address-list change happens to re-trigger the callback?
Also, ndo_set_rx_mode_async is documented in include/linux/netdevice.h as
returning 0 on success and a negative errno to request a core retry, and
netif_rx_mode_run() in net/core/dev_addr_lists.c treats 0 as installed:
if (err)
netif_rx_mode_schedule_retry(dev);
else
dev->rx_mode_retry_count = 0;
Reporting 0 for a rejected or partially applied configuration means the core
and userspace believe the filter is in hardware with no diagnostic emitted.
The -EOPNOTSUPP case looks similar: a PF or firmware that does not implement
the MAC filter message class returns it, and the VF would then report
success with nothing programmed.
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260909100733.1139689-1-wei.fang%40oss.nxp.com
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 01/15] net: enetc: add trusted VF support
2026-09-10 11:20 ` netdev-bot+sashiko
@ 2026-09-11 2:29 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 2:29 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > @@ -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,
> > };
>
> [Severity: Low]
> At this commit enetc4_pf_driver has no .sriov_configure member and enetc4
> never calls enetc_msg_psi_init(), so VFs cannot be instantiated on ENETC v4
> and the mailbox worker is never armed.
>
> The only reader of ENETC_VF_FLAG_TRUSTED is
> enetc_msg_set_vf_primary_mac_addr(), reached via enetc_msg_task() which is
> only set up by enetc_sriov_configure(). Does that mean
>
> ip link set <enetc4 pf> vf N trust on
>
> returns 0 here with no observable effect, and that the new
> enetc_init_sriov_resources() call allocates rxmsg/vf_state plus total_vfs
> mutexes for slots that cannot exist yet?
>
> This is resolved later in the same series: the patch adding VF support for
> i.MX94 and i.MX95 adds .sriov_configure = enetc_sriov_configure to
> enetc4_pf_driver, and enetc_msg.c then gates several ENETC v4 mailbox
> operations on the trust flag. Noting it only because the knob is advertised
> one patch before it can do anything.
No need fix, this patch is a preparatory step for ENETC v4 VF to support
MAC filters.
>
> > 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
> > @@ -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: Medium]
> Is this a user-visible behaviour change for existing SR-IOV deployments?
The VF will return error code when it has no privilege.
>
> ENETC_VF_FLAG_TRUSTED is zero for every slot, since vf_state is
> devm_kcalloc()'d in enetc_init_sriov_resources() and only
> "ip link set <pf> vf N trust on" sets the bit. On the VF side
> enetc_msg_vsi_send() maps the new response to an errno:
>
> case ENETC_MSG_CLASS_ID_PERMISSION_DENY:
> return -EACCES;
>
> So after this patch enetc_vf_set_mac_addr() -> enetc_msg_vsi_send() fails
> with -EACCES, and an in-guest "ip link set <vf> address ..." or enslaving
> the VF into a bond/macvlan stops working after a kernel upgrade with no
> configuration change.
>
> The commit message says:
>
> This does not regress VF bring-up. The PF programs a valid
> primary MAC into every VF slot at probe, and the VF loads it from its
> own SIPMAR0/1 registers.
>
> Bring-up is indeed unaffected, but could the message also state that guests
> which previously changed their own VF MAC now need the host to mark the slot
> trusted first?
>
> Related question on tags: the message describes this as preventing a
> malicious VM from eavesdropping on other SIs and from spoofing MAC
> addresses, and the VF-set-MAC path has existed since commit beb74ac878c8
> ("enetc: Add vf to pf messaging support"). Should this carry a Fixes: tag
> or stable guidance for backporters?
This is a new feature for both ENETC v1 and v4, I do not think we need a
Fixes tag for new feature.
> > 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,
>
> [Severity: Low]
> At this commit neither enetc_ndev_ops nor enetc4_ndev_ops implements
> .ndo_get_vf_config, and rtnl_fill_vf() returns early when
> ndo_get_vf_config is NULL, so IFLA_VF_TRUST is never emitted and
> "ip link show <pf>" prints no VF block.
>
> Since the remediation the commit message recommends is
> "ip link set <dev> vf <N> trust off", would it be worth making the trust
> state readable in the same patch that makes it settable?
>
> This is closed later in the series by the patch adding ndo_get_vf_config()
> support, which reports ivi->trusted from the same flag.
No need fix, the subsequent patch in this series adds ndo_get_vf_config()
support.
> > + 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;
>
> [Severity: High]
> Does clearing trust here leave the hardware MAC filter that the VF
> programmed while it was trusted?
>
> While trusted, the guest can put any valid unicast address into the SI
> primary MAC registers, including the PF's or another VF's:
A trusted host means it should not change its MAC address to a PF or other
VF address. If it needs to sniff traffic from other VFs or PFs, it can enable
promiscuous mode. So this is not an expected behavior of a trusted VF.
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_set_vf_primary
> _mac_addr() {
> ...
> enetc_set_si_hw_addr(pf, vf_id + 1, addr);
> ...
> }
>
> The trust-off branch above only clears the software bit, so ENETC_PSIPMAR0/1
> (ENETC4_PSIPMAR0/1 on v4) for that SI keep the guest-chosen address.
>
> The other paths that could restore it do not. enetc_setup_mac_addresses()
> is the only place a PF-chosen MAC is written into every VF slot, and it runs
> at probe:
>
> drivers/net/ethernet/freescale/enetc/enetc_pf_common.c:enetc_setup_mac_
> addresses() {
> ...
> for (i = 0; i < pf->total_vfs; i++) {
> err = enetc_setup_mac_address(NULL, pf, i + 1);
> ...
> }
>
> enetc4_pf_vf_flr_handler() re-applies only promiscuous state, so the address
> also survives a VF FLR (guest reboot or driver reload), and the SR-IOV
> disable path does not touch it either.
>
> So after the documented "ip link set <dev> vf <N> trust off", frames for the
> spoofed address still hit that SI's exact-match filter, and a slot later
> reassigned to a different guest inherits the address, because the VF driver
> reads its MAC out of SIPMAR0/1.
>
> At the end of the series the trust-off branch grows scrubbing of
> promiscuous mode and of the UC/MC hash filters, but still does not restore
> the SI primary MAC. Should this branch reprogram the PF-assigned primary
> MAC for the slot as well?
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.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-09-10 11:20 ` netdev-bot+sashiko
@ 2026-09-11 2:56 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 2:56 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > +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();
>
> [Severity: Medium]
> Is it safe to block on rtnl_lock() inside the PSI mailbox service work
> item? This looks like the first sleeping, host-global lock acquisition on
> that path: enetc_msg_handle_link_status() only uses si->gen_lock and never
> sleeps.
>
> The completion code is only written back to the VF after the class handler
> returns, in enetc_msg_task():
>
> for (i = 0; i < pf->num_vfs; i++) {
> ...
> enetc_msg_handle_rxmsg(pf, i, &msg_code);
> ...
> enetc_pf_reply_msg(hw, i, msg_code);
> }
>
> out:
> enetc_msg_enable_mr_int(pf);
>
> while the VF side waits with a fixed 200 ms bound in 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");
>
> return err;
> }
>
> If any unrelated host rtnl holder (for example 'ip link del' going through
> synchronize_net(), an ndo_open of another device, bonding/team
> reconfiguration or a module load) keeps rtnl for longer than ~200 ms, does
> the reply not arrive after the VF has already given up? In that case the
> VF logs "VSI mailbox timeout" and returns -ETIMEDOUT, and because
> VSIMSGSR[MB] stays set until the PF finally replies, subsequent VF sends
> hit the earlier check and fail with -EIO ("VSI mailbox is busy").
In this particular case, it is expected that VF will obtain an error code, and I
don't think there is anything wrong with that. Furthermore, aside from the
IP revision being obtained during VF probes, which is not under RTNL lock
protection, all other mailbox messages are within the RTNL lock context, so
the situation you mentioned doesn't exist.
>
> There is a second effect from the same wait. enetc_msg_task() services all
> VFs serially in one loop and only re-arms the PSIIER MR interrupt sources at
> enetc_msg_enable_mr_int(pf) on exit, so while one VF's speed query sleeps on
> rtnl, do the pending messages of the other VFs (set primary MAC, MAC hash
> filter, promisc mode) not get delayed and potentially time out too?
The VF side should handle these errors.
>
> Would a non-blocking source for the speed work here, or alternatively the
> deferred-reply mechanism (ENETC_MSG_CLASS_ID_CMD_DEFERRED) so the
> mailbox
> handshake is not held open across rtnl?
The commit message has explained why rntl_lock is used:
The speed is read via phylink_ethtool_ksettings_get() rather than the
speed passed to the mac_link_up() callback. When the MAC has a PCS
layer, mac_link_up() reports the PCS link speed, which may differ from
the external PHY link speed; phylink_ethtool_ksettings_get() returns the
actual external link speed.
>
> [Severity: Medium]
> On the trust gate itself: the comment above the mutex_lock(&vf_state->lock)
> check describes the tight-loop spam scenario, but the check only narrows
> which VFs can reach rtnl_lock(), it does not bound the rate. Nothing
> throttles inbound class 0x81 messages - the MR interrupt sources are simply
> re-armed at the end of enetc_msg_task(), and the only per-VF counter
> (msg_fail_cnt) tracks PF-to-VF send failures rather than inbound queries.
>
> Can a trusted VF still drive repeated rtnl acquire/release from the PF work
> item at mailbox round-trip rate, i.e. is the primitive the comment warns
> about still reachable from guest-controlled code once an admin sets 'trust
> on'?
The link speed query is a infrequent message. Normally, query speed is only
activated when the link status changes. A trusted VF means it will not perform
the abnormal behaviors; otherwise, the "trust" would be meaningless.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-10 11:20 ` netdev-bot+sashiko
@ 2026-09-11 5:55 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 5:55 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > +static void enetc_msg_register_link_status_notifier(struct enetc_pf *pf,
> > + int vf_id)
> > +{
> > + struct enetc_si *si = pf->si;
> > +
> > + spin_lock(&si->gen_lock);
> > + enetc_pf_reply_msg(&si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
> > +
> > + /* 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) {
> > + spin_unlock(&si->gen_lock);
> > + return;
> > + }
> > +
> > + pf->link_status_ms_mask |= PSIMSGSR_MS(vf_id);
> > + spin_unlock(&si->gen_lock);
> > +
> > + /* Notify VF the current link status */
> > + queue_work(si->workqueue, &pf->link_status_task);
> > +}
>
> [Severity: Medium]
> This queues the shared, VF-agnostic link_status_task in order to deliver one
> VF's initial status. Does that mean a single VF registering re-sends the
> notification to every VF currently set in link_status_ms_mask?
For a VF that has already obtained the link status, obtaining the same message
again will not have any impact, and the VF should ignore consecutive identical
link status messages.
>
> The retry decision in enetc_pf_send_link_status_msg() is made on the
> aggregate mask rather than per VF:
>
> ms_status = enetc_rd(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
> if ((ms_mask & ms_status) && retry_num++ < 200) {
> spin_unlock(&si->gen_lock);
> usleep_range(1000, 1020);
> goto retry;
> }
>
> Can one VF that never reads its mailbox therefore add up to roughly 200 ms
> of sleeping to every registration and every link transition seen by all the
> other registered VFs?
This situation will not have any impact; it will still send message notifications
to the normally functioning VF after the timeout.
>
> The ms_status_check loop also advances vf_state->msg_fail_cnt for every VF
> in ms_mask. Can a VF that happened to be busy during a notification
> triggered by some other VF's registration accumulate failures it did not
> cause, and eventually be evicted from link_status_ms_mask?
We set the failure count to 10, and the device will only be removed after ten
consecutive failures. If this happens, it means that the VF is abnormal, and
removal is the correct procedure.
>
> > +
> > +static void enetc_msg_unregister_link_status_notifier(struct enetc_pf *pf,
> > + int vf_id)
> > +{
> > + spin_lock(&pf->si->gen_lock);
> > + pf->link_status_ms_mask &= ~PSIMSGSR_MS(vf_id);
> > + enetc_pf_reply_msg(&pf->si->hw, vf_id, ENETC_PF_MSG_SUCCESS);
> > + spin_unlock(&pf->si->gen_lock);
> > +}
>
> [Severity: Low]
> Neither this function nor enetc_msg_register_link_status_notifier() resets
> vf_state->msg_fail_cnt.
>
> If a VF accumulated up to 10 consecutive delivery failures under a previous
> registration, does the first busy notification after it re-registers
> immediately clear its bit from link_status_ms_mask?
Yes, this is intentional. A normal VF can definitely process mailbox messages
within 200ms; otherwise, it's an abnormal VF deliberately creating a timeout.
>
> Later in the series the Linux VF driver unregisters on ndo_close and
> registers again on ndo_open, so the counter survives an ifdown/ifup cycle.
> Would clearing vf_state->msg_fail_cnt in the register path be preferable?
No, see above.
>
> > +
> > +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;
>
> [Severity: Medium]
> This dispatch has no ENETC_VF_FLAG_TRUSTED check and no rate limiting,
> unlike enetc_msg_set_vf_primary_mac_addr() and the other MAC filter
> handlers in this file. The link speed handler added later in the series
> documents exactly this abuse class in its own comment ("A malicious or
> malfunctioning VM could potentially spam these messages in a tight
> loop...").
>
> The registration handler replies ENETC_PF_MSG_SUCCESS first, which clears
> the VF's MR bit and lets the VF post the next message right away, then
> calls queue_work() unconditionally.
>
> Can a VF assigned to an untrusted guest register, never consume its
> mailbox, and keep re-sending
> ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER so
> that the PF's single ordered workqueue spends 200 x ~1 ms sleeping on each
> invocation, delaying carrier notifications for the co-tenant VFs?
>
> The 10-failure eviction does not appear to bound this, since the VF can
> simply register again and restore its bit in link_status_ms_mask.
Link status is a basic, read-only, non-privileged capability that every VF
needs to report carrier state to its guest stack/containers/VMs. Gating
it behind trusted would break the common untrusted-VF use case without
closing any real attack surface, since mailbox spam is already bounded by
the idempotent work, busy-VF exclusion, the 10-failure eviction, and
rate-limited logging.
Furthermore, on the PF side, the PHY driver itself checks the link status
once per second, so the interval between two link status updates is at
least greater than 1 second, thus it will not affect the link status notifications
of other VFs.
>
> > + 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;
> > +}
>
> [ ... ]
>
> > @@ -340,6 +442,22 @@ 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;
> > +
> > + /* 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;
> > +
> > + spin_lock(&si->gen_lock);
> > + vf_state->msg_fail_cnt = 0;
> > + spin_unlock(&si->gen_lock);
> > +}
>
> [Severity: Medium]
> This resets software state only; the hardware PSIMSGSR[MS] bit for that VSI
> is left set. The comment in enetc_pf_send_link_status_msg() states that the
> PF cannot write the MS bit as 0, so only the VSI can clear it by reading
> VSIMSGRR.
>
> Is there any path that disarms an MS bit left set by a VF that stopped
> consuming messages, for example a crashed or paused guest, a VF FLR, a VFIO
> release, or a killed DPDK application?
Usually, the VF driver will perform FLR at probe, the FLR will clear the state.
>
> The commit message says:
>
> "The link_status_ms_mask tracks the VFs registered for notification and
> is cleared when SR-IOV is disabled."
>
> but a VF FLR clears neither link_status_ms_mask nor msg_fail_cnt: the
> enetc4_pf_vf_flr_handler() added later in the series restores only the
> promiscuous state. Does a re-created VF at the same index then look
> registered to the PF while the notification channel to it is blocked?
As clarified above, the VF FLR will clear hardware state, so the notification
will not be blocked.
>
> Two follow-on effects in enetc_pf_send_link_status_msg(): every later PF
> link transition burns up to 200 x ~1 ms of polling because the retry gate
> is keyed on the aggregate mask, and after 11 such events the VF's bit is
> dropped from link_status_ms_mask with only a dev_warn_ratelimited(), while
> the VF still considers itself registered because it was acked with
> ENETC_PF_MSG_SUCCESS. Should there be an error reply, a re-arm, or a
> periodic retry so the VF's carrier state and its RX BD congestion mode
> decision (driven by the TX PAUSE bit) do not stay stale until an
> administrative ifdown/ifup?
The link status message is sent proactively by the PF to the VF, not by the
VF actively querying the link status. Therefore, it's impossible for the PF
to send an error response to the VF. Furthermore, since the VF is no longer
able to respond to link status messages at this point, it also won't receive
error notifications proactively sent by the PF.
Recovery is possible if the VF actively performs an ifup/down operation.
>
> The later VF patch does drain VSIMSGRR in enetc_vf_wq_task_destroy(), but
> that covers only the clean Linux VF unbind case, not FLR or guest death.
>
> > static void enetc_msg_psi_free(struct enetc_pf *pf)
> > {
> > struct enetc_si *si = pf->si;
>
> [ ... ]
>
> > @@ -367,6 +487,11 @@ int enetc_sriov_configure(struct pci_dev *pdev, int
> num_vfs)
> > int err;
> >
> > if (!num_vfs) {
> > + spin_lock(&si->gen_lock);
> > + pf->sriov_enabled = false;
> > + pf->link_status_ms_mask = 0;
> > + spin_unlock(&si->gen_lock);
> > +
> > pci_disable_sriov(pdev);
> > enetc_msg_psi_free(pf);
> > pf->num_vfs = 0;
>
> [ ... ]
>
> > @@ -396,3 +535,114 @@ int enetc_sriov_configure(struct pci_dev *pdev, int
> num_vfs)
> > return err;
> > }
> > EXPORT_SYMBOL_GPL(enetc_sriov_configure);
> > +
> > +void enetc_pf_send_link_status_msg(struct enetc_pf *pf)
> > +{
> > + struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev);
> > + u16 pf_msg, ms_mask, new_ms_msk, ms_status;
> > + struct enetc_si *si = pf->si;
> > + int retry_num = 0;
> > +
> > +retry:
> > + spin_lock(&si->gen_lock);
> > + ms_mask = pf->link_status_ms_mask;
> > + /* VFs have unregistered link status notification, return directly */
> > + if (!ms_mask)
> > + goto unlock;
> > +
> > + /* 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(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
> > + if ((ms_mask & ms_status) && retry_num++ < 200) {
> > + spin_unlock(&si->gen_lock);
> > + /* Wait VFs to handle the last message */
> > + usleep_range(1000, 1020);
> > + goto retry;
> > + }
> > +
> > + /* None of the relevant VFs have processed the previous message, and
> > + * the PF has tried 200 times. This situation indicates that VF has
> > + * malfunctioned.
> > + */
> > + new_ms_msk = ms_mask & (~ms_status);
> > + if (!new_ms_msk) {
> > + dev_err_ratelimited(&si->pdev->dev,
> > + "All registered VFs (MS: 0x%x) are busy\n",
> > + ms_mask);
> > + goto ms_status_check;
> > + }
> > +
> > + if (new_ms_msk != ms_mask)
> > + dev_warn_ratelimited(&si->pdev->dev,
> > + "Failed to notify link status to VFs (MS: 0x%x)\n",
> > + ms_mask ^ new_ms_msk);
> > +
> > + pf_msg = enetc_build_link_status_msg(priv, pf->link_up);
> > + enetc_wr(&si->hw, ENETC_PSIMSGSR,
> > + FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
>
> [Severity: Medium]
> Delivery here looks one-shot. Both the "All registered VFs ... are busy"
> path and the new_ms_msk != ms_mask path abandon the pending notification:
> link_status_task is not re-queued and nothing records that the excluded VFs
> still owe a message, only a rate-limited log line is emitted.
>
> Is the ms_status snapshot also racy? A VSI clears its own MS bit
> asynchronously and outside si->gen_lock, so if a VF clears its bit between:
>
> ms_status = enetc_rd(&si->hw, ENETC_PSIMSGSR) & 0xfffe;
>
> and:
>
> enetc_wr(&si->hw, ENETC_PSIMSGSR,
> FIELD_PREP(PSIMSGSR_MC, pf_msg) | new_ms_msk);
>
> does that VF consume the previous message content and never receive the
> current link state? gen_lock cannot close this window because the MS bit is
> mutated by a peer PCI function.
This is a limitation; the software cannot guarantee that the VF will not clear
the MS bit during this window. However, this is only a theoretical issue. Before
the operation, the PF has already waited for 200ms, which is more than enough
time for a normally functioning VF to process the previous message. Therefore,
I don't think it's necessary to eliminate this theoretical issue.
>
> With the VF side added later in the series, a phy-less v4 VF relies
> entirely on this notification for its carrier: enetc_phylink_connect()
> returns without calling netif_carrier_on() when
> vf_reg_link_status_notifier() succeeds. Does a dropped initial notification
> then leave the VF netdev with no carrier until some later PF link change or
> an administrative ifdown/ifup?
When PF sends a message, VF will generate an interrupt for processing. First,
I don't think the interrupt will be lost. Second, normal interrupt processing is
usually in the microsecond range. We have set the timeout to 200ms, so a
normal VF won't lose the notification. For a VF that malfunctions, we do not
expect it to process notifications correctly. If it subsequently recovers, the link
message notification can be resumed using ifdown/ifup.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
2026-09-10 11:21 ` netdev-bot+sashiko
@ 2026-09-11 6:13 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 6:13 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > +/* 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[];
> > +};
>
> [Severity: Low]
>
> Should hash_tbl[] carry an explicit endianness annotation?
>
> This struct describes a wire format: the message lives in a DMA buffer that
> the mailbox hardware copies from the VSI buffer into the PSI buffer, and the
> header in this same file already declares the byte order of its multi-byte
> field:
>
> struct enetc_msg_header {
> __be16 crc16;
> ...
> };
>
> hash_tbl[] is the only other multi-byte payload field and it is plain u32,
> so both endpoints are implicitly required to have the same byte order. The
> producer fills it in host order and enetc_msg_set_vf_mac_hash_filter()
> recombines it in host order:
>
> uc_hash = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
>
> If a big-endian VSI kernel ever talks to a little-endian PSI (or a
> non-Linux PSI implementing the documented protocol), would the hash buckets
> end up byte-swapped, so the SI filters on the wrong buckets while both
> sides report success? The CRC16 is computed byte-wise over the buffer, so
> it would not catch that.
>
> Would declaring the array as __le32 and converting with cpu_to_le32() /
> le32_to_cpu() on both ends be preferable now, while the format is being
> introduced?
This combination does not exist in practice for ENETC. Both PF(host) and VF(VM)
use the same endianness, so no need an explicit endianness annotation.
>
> > +
> > +/* 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 08a9ffdd3eb0..22f5485e968a 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
>
> [ ... ]
>
> > @@ -116,6 +242,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;
> > }
>
> [Severity: Low]
>
> At this commit, is there anything in the tree that sends
> ENETC_MSG_SET_MAC_HASH_TABLE or
> ENETC_MSG_SET_MAC_PROMISC_MODE?
>
> Both new handlers here, and the UCF_DENY mapping added to
> enetc_msg_vsi_send() below, only become reachable with the later patch in
> the series ("net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF"),
> which adds enetc_vf_set_mac_hash_filter() and enetc_vf_set_mac_promisc()
> and wires .ndo_set_rx_mode_async = enetc_vf_set_rx_mode into
> enetc4_ndev_ops. So this is only a patch-ordering observation, the receiver
> landing one patch before the sender, not a defect at the end of the series.
This is not an issue, no need fix.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-09-10 11:21 ` netdev-bot+sashiko
@ 2026-09-11 6:23 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 6:23 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > @@ -542,6 +559,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);
> > @@ -550,6 +590,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 default before this
> restore runs?
>
> The interrupt handler only masks PSIIER and queues the work:
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:enetc_msg_psi_msix() {
> enetc_disable_psiier_interrupts(pf);
> schedule_work(&si->msg_task);
> }
>
> so between the FLR taking effect (PSIPMMR[SIn_MAC_UP]/[SIn_MAC_MP] back
> at their promiscuous reset values) and enetc_msg_task() ->
> enetc_vf_flr_handler() -> enetc4_pf_vf_flr_handler() running, the SI keeps
> receiving all port unicast and multicast traffic. Since the hardware default
> ignores trust state, does this window also apply to an untrusted VF that has
> never been granted promiscuous mode?
>
> Is the window bounded here? PSIIER is only re-armed at the very end of
> 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);
>
> so an FLR latched while the message loop is in progress is not serviced until that
> loop finishes. For a trusted VF the loop can reach enetc_msg_get_link_speed(),
> which blocks on the global lock:
>
> rtnl_lock();
> phylink_ethtool_ksettings_get(priv->phylink, &link_info);
> rtnl_unlock();
>
> Can a guest keep msg_task busy this way while another VF (or the same one) sits
> in the FLR-induced promiscuous state?
>
> Would it make sense to force UC/MC promiscuous off for the affected SI as soon
> as the FLR event is observed, before the tracked policy is re-applied, and to note
> the residual window in the commit message?
>
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, between tens and hundreds of
microseconds).
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.
So I'd keep the current design: clear/re-apply the policy from the work item.
No change needed.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF
2026-09-10 11:21 ` netdev-bot+sashiko
@ 2026-09-11 7:17 ` Wei Fang
0 siblings, 0 replies; 32+ messages in thread
From: Wei Fang @ 2026-09-11 7:17 UTC (permalink / raw)
To: netdev-bot+sashiko, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang, andrew, olteanv,
andrew+netdev, davem, edumazet, kuba, pabeni, linux, imx, netdev,
linux-kernel
> > +static int enetc_vf_enable_iff_promisc(struct enetc_si *si) {
> > + int err;
> > +
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> > + true, true);
> > + if (err)
> > + dev_err(&si->pdev->dev,
> > + "Failed to enable promiscuous mode, err: %pe\n",
> > + ERR_PTR(err));
> > +
> > + return err;
> > +}
> > +
> > +static int enetc_vf_disable_iff_promisc(struct enetc_si *si,
> > + struct netdev_hw_addr_list *uc,
> > + struct netdev_hw_addr_list *mc)
> > +{
> > + int err;
> > +
> > + err = enetc_vf_set_mac_hash_filter(si, uc, mc);
> > + if (err) {
> > + dev_err_once(&si->pdev->dev,
> > + "Failed to set MAC hash filters, err: %pe\n",
> > + ERR_PTR(err));
> > + return err;
> > + }
> > +
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL,
> > + false, false);
>
> [Severity: Medium]
> Is the early return above correct when the PF only denies the unicast part of the
> request?
>
> enetc_vf_disable_iff_promisc() sends the combined hash filter first with type =
> UC | MC. On the PF side, enetc_msg_set_vf_mac_hash_filter() handles
> ENETC_MAC_FILTER_TYPE_ALL like this:
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:
> 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;
> }
>
> so the multicast hash was applied and only unicast was refused, yet
> enetc_msg_vsi_send() maps UCF_DENY to -EACCES. For the default untrusted
> VF that means the 'return err' path is always taken and the following
>
> enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_ALL, false, false);
>
> message, the one that actually leaves promiscuous mode, is never sent.
> Would it be better to treat UCF_DENY as a partial success and continue, or to
> send the promisc-off message before the hash filter?
This is a false positive. When a VF changes from trusted to untrusted, its
promiscuous mode is already disabled on the PF side. No need to send
the promisc-off message, its pointless.
>
> The same ordering concern applies to enetc_vf_enable_iff_allmulti() below:
> the hardware-changing step (enable MC promisc with flush_macs = true) runs
> first and the restricting step (disable UC promisc) runs last, with no rollback if a
> later mailbox transaction returns -EIO ("VSI mailbox is busy"), -ETIME ("VSI
> mailbox timeout") or -EBUSY from the PSI. Can that leave a trusted VF with
> UC/MC promiscuity armed in the SI while the netdev has already left
> promiscuous mode?
This is also a false positive, the ndo_set_rx_mode_async function is called
within the workqueue. When these errors are returned, the work is
rescheduled, so the process will continue to attempt. If PF works without
faults, the setup will eventually complete successfully; if there are faults,
moving the order will not solve the problem.
>
> Since the SI promiscuity bits live in PF register space indexed by si_id and are
> re-applied after FLR by enetc4_pf_vf_flr_handler(), a skipped promisc-off
> message appears to persist until the VF issues another one or the PF revokes
> trust.
>
> > + if (err)
> > + dev_err_once(&si->pdev->dev,
> > + "Failed to disable promiscuous mode, err: %pe\n",
> > + ERR_PTR(err));
> > +
> > + return err;
> > +}
> > +
> > +static int enetc_vf_enable_iff_allmulti(struct enetc_si *si,
> > + struct netdev_hw_addr_list *uc)
> > +{
> > + int err;
> > +
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_MC,
> > + true, true);
> > + if (err) {
> > + dev_err(&si->pdev->dev,
> > + "Failed to enable multicast promiscuous mode, err: %pe\n",
> > + ERR_PTR(err));
> > + return err;
> > + }
>
> [Severity: High]
> For an untrusted VF, does this leave the IFF_ALLMULTI and IFF_PROMISC cases
> with no MAC filter programmed at all?
Yes, for untrusted VFs, attempting to enable IFF_ALLMULTI or IFF_PROMISC will
return directly without configuring any MAC filter. Configuring a restricted
multicast filter is only permitted in the third case (IFF_ALLMULTI and IFF_PROMISC
flags are not set).
>
> Both enetc_vf_enable_iff_allmulti() and enetc_vf_enable_iff_promisc() start by
> requesting promiscuous mode, and the PF rejects that unconditionally before
> touching any register:
>
> 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;
> }
>
> That becomes -EACCES and both helpers return immediately, so the unicast hash
> filter, the unicast-promisc-off message and any multicast hash filter are never
> sent.
>
> Note also that the multicast snapshot is not passed into either of these two
> branches, and enetc_vf_set_mac_hash_filter(si, NULL, mc) has no caller in the
> patch. The PF does permit a bounded multicast-only hash filter for an
> untrusted VF:
>
> drivers/net/ethernet/freescale/enetc/enetc_msg.c:
> enetc_msg_set_vf_mac_hash_filter() {
> 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);
>
> so multicast groups joined while the interface is in allmulti or promisc mode
> appear to be silently dropped, with the SI keeping whatever hash filter the last
> non-allmulti update left behind. IFF_ALLMULTI and IFF_PROMISC are entered
> by ordinary operations (bridge/bond/macvlan enslavement, 'ip link set ...
> allmulticast on', tcpdump), including from a guest owning the VF.
>
> This also seems to contradict the commit message:
>
> For an untrusted VF (the default) the PF denies promiscuous mode and
> unicast hash filtering, so only the multicast hash filter is
> applied, limited to ENETC_VF_MC_HASH_BITS_MAX buckets.
>
> That only holds for the third (non-promisc, non-allmulti) branch, not for these
> two.
The original intention of the commit message is to indicate that multicast
filtering with configuration restrictions is only allowed in the third case.
>
> One more thing: unlike enetc_vf_disable_iff_promisc(), which uses
> dev_err_once(), these two helpers use plain dev_err(). Since the denial is
> guaranteed for an untrusted VF, wouldn't that print an error line on every
> rx_mode update, i.e. on every multicast join/leave and every bridge or bond
> membership change?
This is intentional. Compared to other interfaces, ndo_set_rx_mode_async()
is called relatively frequently. If the VF is not trusted, printing an error log
every time it is called would annoy users, so printing it only once is sufficient.
>
> > +
> > + err = enetc_vf_set_mac_hash_filter(si, uc, NULL);
> > + if (err) {
> > + dev_err(&si->pdev->dev,
> > + "Failed to set unicast filter, err: %pe\n",
> > + ERR_PTR(err));
> > + return err;
> > + }
> > +
> > + err = enetc_vf_set_mac_promisc(si, ENETC_MAC_FILTER_TYPE_UC,
> > + false, false);
> > + if (err)
> > + dev_err(&si->pdev->dev,
> > + "Failed to disable unicast promiscuous mode, err: %pe\n",
> > + ERR_PTR(err));
> > +
> > + return err;
> > +}
> > +
> > +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;
> > +
> > + if (ndev->flags & IFF_PROMISC)
> > + err = enetc_vf_enable_iff_promisc(si);
> > + else if (ndev->flags & IFF_ALLMULTI)
> > + err = enetc_vf_enable_iff_allmulti(si, uc);
> > + else
> > + err = enetc_vf_disable_iff_promisc(si, uc, mc);
> > +
> > + /* 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: Medium]
> Is the premise behind this mapping accurate? The comment, and the commit
> message wording
>
> Since a denied request always fails, map -EOPNOTSUPP, -EACCES and
> -EPERM to 0 so the core does not retry an operation that can never
> succeed.
>
> assume the denial is permanent, but PF trust is runtime-mutable:
"The denial is permanent" is not true, enetc_vf_set_rx_mode() will
not return -EACCES or -EPERM when VF is changed to trusted.
>
> drivers/net/ethernet/freescale/enetc/enetc_pf_common.c:
> enetc_pf_set_vf_trust() {
> if (setting) {
> vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
> } else {
>
> That path only flips the flag; it neither notifies the VF nor replays the previously
> rejected configuration. So does
>
> ip link set <pf> vf N trust on
>
> as suggested in the commit message actually take effect before some unrelated
> flag or address-list change happens to re-trigger the callback?
>
> Also, ndo_set_rx_mode_async is documented in include/linux/netdevice.h as
> returning 0 on success and a negative errno to request a core retry, and
> netif_rx_mode_run() in net/core/dev_addr_lists.c treats 0 as installed:
>
> if (err)
> netif_rx_mode_schedule_retry(dev);
> else
> dev->rx_mode_retry_count = 0;
>
> Reporting 0 for a rejected or partially applied configuration means the core and
> userspace believe the filter is in hardware with no diagnostic emitted.
> The -EOPNOTSUPP case looks similar: a PF or firmware that does not implement
> the MAC filter message class returns it, and the VF would then report success
> with nothing programmed.
Mapping -EOPNOTSUPP, -EACCES and -EPERM to 0 is an intentional design
trade-off, and I'd keep it as is.
The core's retry is exponential backoff meant for transient failures that may heal
on retry. A trust-based or capability denial is not transient in that sense - until an
admin changes trust, every retry is guaranteed to fail, so it just makes the core
spin for ~15s and log "retry limit reached" on every address-list change. For a
default-untrusted VF this is recurring dmesg noise with no benefit, since the core
can only retry and retrying cannot succeed.
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si
2026-09-09 10:07 ` [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
@ 2026-09-11 20:14 ` Claudiu Manoil
0 siblings, 0 replies; 32+ messages in thread
From: Claudiu Manoil @ 2026-09-11 20:14 UTC (permalink / raw)
To: Wei Fang (OSS),
Vladimir Oltean, Clark Wang, andrew, olteanv, andrew+netdev,
davem, edumazet, kuba, pabeni, linux
Cc: Wei Fang, imx, netdev, linux-kernel
NXP Confidential
> -----Original Message-----
> From: Wei Fang (OSS) <wei.fang@oss.nxp.com>
> Sent: Wednesday, September 9, 2026 1:07 PM
[...]
> netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: [PATCH v4 net-next 02/15] net: enetc: move msg_task and
> msg_int_name to struct enetc_si
>
> 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>
Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com>
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver
2026-09-09 10:07 ` [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
@ 2026-09-11 20:15 ` Claudiu Manoil
1 sibling, 0 replies; 32+ messages in thread
From: Claudiu Manoil @ 2026-09-11 20:15 UTC (permalink / raw)
To: Wei Fang (OSS),
Vladimir Oltean, Clark Wang, andrew, olteanv, andrew+netdev,
davem, edumazet, kuba, pabeni, linux
Cc: Wei Fang, imx, netdev, linux-kernel
NXP Confidential
> -----Original Message-----
> From: Wei Fang (OSS) <wei.fang@oss.nxp.com>
> Sent: Wednesday, September 9, 2026 1:07 PM
[...]
> Subject: [PATCH v4 net-next 03/15] net: enetc: add link status message support
> to PF driver
>
> From: Wei Fang <wei.fang@nxp.com>
>
> Add a mechanism for VFs to learn the PF link state, using message class
> 0x80 (ENETC_MSG_CLASS_ID_LINK_STATUS) with three VSI-to-PSI commands:
>
> 1. ENETC_MSG_GET_CURRENT_LINK_STATUS: the VF queries the current PF link
> status synchronously. This is intended for DPDK-owned VFs and is not
> used by the Linux VF driver.
> 2. ENETC_MSG_REGISTER_LINK_CHANGE_NOTIFIER: the VF registers for link
> change notification. The PF then reports the current link status and
> notifies the VF on every later link change.
> 3. ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER: the VF unregisters.
>
> The PSI-to-VSI notification is 16 bits wide: the upper 8 bits carry the
> class ID and the lower 8 bits the class code. Bit 0 of the class code
> indicates the link state (1 = down, 0 = up) and bit 1 indicates whether
> TX PAUSE is enabled on the PF. The TX PAUSE state is included so a VF
> can decide whether to enable congestion mode on its RX BD rings, which
> only works when the PF can actually send PAUSE frames.
>
> Notifications are sent through the ENETC_PSIMSGSR register. Since sending
> a notification may take a long time, as it polls the per-VF message
> status bits, the actual transmission is deferred to an ordered workqueue
> rather than running in the phylink link_up/link_down callbacks. The
> link_status_ms_mask tracks the VFs registered for notification and is
> cleared when SR-IOV is disabled.
>
> Export enetc_pf_notify_vf_link_up() and enetc_pf_notify_vf_link_down()
> for the PF phylink callbacks. Through this, VFs can perceive the link
> status and report it to upper layers such as the kernel network stack,
> containers and virtual machines.
>
> Currently only the ENETC v4 driver supports this feature; v1 does not.
> And the SR-IOV feature of ENETC v4 will be added by subsequent patches.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com>
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 04/15] net: enetc: add link speed message support to PF driver
2026-09-09 10:07 ` [PATCH v4 net-next 04/15] net: enetc: add link speed " wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
@ 2026-09-11 20:16 ` Claudiu Manoil
1 sibling, 0 replies; 32+ messages in thread
From: Claudiu Manoil @ 2026-09-11 20:16 UTC (permalink / raw)
To: Wei Fang (OSS),
Vladimir Oltean, Clark Wang, andrew, olteanv, andrew+netdev,
davem, edumazet, kuba, pabeni, linux
Cc: Wei Fang, imx, netdev, linux-kernel
NXP Confidential
> -----Original Message-----
> From: Wei Fang (OSS) <wei.fang@oss.nxp.com>
> Sent: Wednesday, September 9, 2026 1:07 PM
[...]
> Subject: [PATCH v4 net-next 04/15] net: enetc: add link speed message
> support to PF driver
>
> From: Wei Fang <wei.fang@nxp.com>
>
> When a VF is driven by DPDK, its user space application needs accurate
> link speed information to make correct forwarding and configuration
> decisions. Add link speed message support so the PF replies with the
> current link speed when it receives a get-link-speed message from a VF.
>
> Use a new message class 0x81 (ENETC_MSG_CLASS_ID_LINK_SPEED). The
> PSI-to-VSI message is 16 bits: the high 8 bits are the class ID and the
> low 8 bits are the speed code, so up to 255 speed values are supported
> (ENETC_MSG_SPEED_MAX = 0xff). Instead of enumerating every speed above
> 5Gbps, use a formula so future high speeds need no enum or switch
> changes:
>
> speed_code = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G
>
> The speed is read via phylink_ethtool_ksettings_get() rather than the
> speed passed to the mac_link_up() callback. When the MAC has a PCS
> layer, mac_link_up() reports the PCS link speed, which may differ from
> the external PHY link speed; phylink_ethtool_ksettings_get() returns the
> actual external link speed.
>
> Unlike the link status message (class 0x80), the get-link-speed message
> is only permitted for trusted VFs. Reading the speed requires the PF to
> take rtnl_lock(), so an untrusted VF spamming this query could cause
> rtnl_lock contention and starve routine network configuration on the
> host. An untrusted VF therefore receives a permission-deny response; a
> VF can be marked trusted via 'ip link set <pf> vf N trust on'.
>
> Note that link speed change notification is not supported yet.
>
> Signed-off-by: Wei Fang <wei.fang@nxp.com>
Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com>
^ permalink raw reply [flat|nested] 32+ messages in thread
* RE: [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4
2026-09-09 10:07 ` [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
@ 2026-09-11 20:17 ` Claudiu Manoil
1 sibling, 0 replies; 32+ messages in thread
From: Claudiu Manoil @ 2026-09-11 20:17 UTC (permalink / raw)
To: Wei Fang (OSS),
Vladimir Oltean, Clark Wang, andrew, olteanv, andrew+netdev,
davem, edumazet, kuba, pabeni, linux
Cc: Wei Fang, imx, netdev, linux-kernel
NXP Confidential
> -----Original Message-----
> From: Wei Fang (OSS) <wei.fang@oss.nxp.com>
> Sent: Wednesday, September 9, 2026 1:07 PM
[...]
> Subject: [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous
> mode after FLR for ENETC v4
>
> 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>
Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com>
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2026-09-11 20:17 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-09 10:07 [PATCH v4 net-next 00/15] net: enetc: SR-IOV improvements and ENETC v4 VF support wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 01/15] net: enetc: add trusted " wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 2:29 ` Wei Fang
2026-09-09 10:07 ` [PATCH v4 net-next 02/15] net: enetc: move msg_task and msg_int_name to struct enetc_si wei.fang
2026-09-11 20:14 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 03/15] net: enetc: add link status message support to PF driver wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 5:55 ` Wei Fang
2026-09-11 20:15 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 04/15] net: enetc: add link speed " wei.fang
2026-09-10 11:20 ` netdev-bot+sashiko
2026-09-11 2:56 ` Wei Fang
2026-09-11 20:16 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 05/15] net: enetc: use enetc_set_si_hw_addr() to set VF MAC address wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 06/15] net: enetc: relocate enetc_pf_set_vf_mac() for common PF support wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 07/15] net: enetc: add .ndo_set_vf_mac() to the enetc v4 driver wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 08/15] net: enetc: move mac_filter from struct enetc_pf to struct enetc_si wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4 wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 6:13 ` Wei Fang
2026-09-09 10:07 ` [PATCH v4 net-next 10/15] net: enetc: simplify and rename PSIIER enable/disable helpers wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 11/15] net: enetc: restore VF MAC promiscuous mode after FLR for ENETC v4 wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 6:23 ` Wei Fang
2026-09-11 20:17 ` Claudiu Manoil
2026-09-09 10:07 ` [PATCH v4 net-next 12/15] net: enetc: add VF support for i.MX94 and i.MX95 wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 13/15] net: enetc: implement ndo_set_rx_mode_async for ENETC v4 VF wei.fang
2026-09-10 11:21 ` netdev-bot+sashiko
2026-09-11 7:17 ` Wei Fang
2026-09-09 10:07 ` [PATCH v4 net-next 14/15] net: enetc: add PSI-to-VSI link status notification support for VF wei.fang
2026-09-09 10:07 ` [PATCH v4 net-next 15/15] net: enetc: add ndo_get_vf_config() support wei.fang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®