[PATCH net-next 09/15] net: enetc: add MAC address filtering support for VFs of ENETC v4
From: wei . fang
Date: Fri Jun 05 2026 - 03:37:51 EST
From: Wei Fang <wei.fang@xxxxxxx>
The ENETC v4 VF hardware supports MAC address filtering, but the
underlying hardware resources (PSIPMMR register and per-SI hash filter
tables) are owned and managed exclusively by the PF driver. Add
VSI-to-PSI mailbox message support so that VFs can request MAC filter
configuration from the PF.
Two new command IDs are introduced under the existing MAC filter message
class (0x20):
1. ENETC_MSG_SET_MAC_HASH_TABLE (cmd_id 3): allows a trusted VF to
program its unicast and/or multicast MAC hash filter table. The PF
validates that the hardware-supported 64-bit table size is requested
before applying the configuration via the per-SI hash filter registers.
2. ENETC_MSG_SET_MAC_PROMISC_MODE (cmd_id 5): allows a VF to enable or
disable unicast/multicast promiscuous mode, and optionally flush the
associated hash filter table. Enabling promiscuous mode requires the VF
to be marked as trusted, since it widens the traffic received by the VF.
Flushing the hash table without enabling promiscuous mode does not
require elevated privilege.
To accommodate independent per-type control, refactor
enetc4_pf_set_si_mac_promisc() to accept an enetc_mac_addr_type enum
and a single enable flag instead of two separate boolean parameters.
This allows callers to set unicast and multicast promiscuous modes in
separate steps.
The PSIPMMR register holds promiscuous mode bits for all SIs and is
modified by both the PF rx_mode workqueue (enetc4_psi_do_set_rx_mode)
and the VF message handler workqueue (enetc_msg_task). Since both
workqueues can run concurrently on SMP systems and
enetc4_pf_set_si_mac_promisc() performs a non-atomic read-modify-write,
protect all accesses to this register with pf->msg_lock to prevent lost
updates.
When a VF loses trusted status via ndo_set_vf_trust(), its unicast and
multicast MAC hash filters are cleared and promiscuous mode is disabled
to prevent it from receiving traffic beyond its allowed scope.
Signed-off-by: Wei Fang <wei.fang@xxxxxxx>
---
.../net/ethernet/freescale/enetc/enetc4_pf.c | 40 +++++--
.../ethernet/freescale/enetc/enetc_mailbox.h | 39 ++++++
.../net/ethernet/freescale/enetc/enetc_msg.c | 113 ++++++++++++++++++
.../net/ethernet/freescale/enetc/enetc_pf.h | 4 +
.../freescale/enetc/enetc_pf_common.c | 21 +++-
5 files changed, 204 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index f6920ded9f7e..2e081a59154e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -76,19 +76,22 @@ static void enetc4_pf_get_si_primary_mac(struct enetc_hw *hw, int si,
}
static void enetc4_pf_set_si_mac_promisc(struct enetc_hw *hw, int si,
- bool uc_promisc, bool mc_promisc)
+ enum enetc_mac_addr_type type,
+ bool en)
{
u32 val = enetc_port_rd(hw, ENETC4_PSIPMMR);
- if (uc_promisc)
- val |= PSIPMMR_SI_MAC_UP(si);
- else
- val &= ~PSIPMMR_SI_MAC_UP(si);
-
- if (mc_promisc)
- val |= PSIPMMR_SI_MAC_MP(si);
- else
- val &= ~PSIPMMR_SI_MAC_MP(si);
+ if (type == UC) {
+ if (en)
+ val |= PSIPMMR_SI_MAC_UP(si);
+ else
+ val &= ~PSIPMMR_SI_MAC_UP(si);
+ } else if (type == MC) {
+ if (en)
+ val |= PSIPMMR_SI_MAC_MP(si);
+ else
+ val &= ~PSIPMMR_SI_MAC_MP(si);
+ }
enetc_port_wr(hw, ENETC4_PSIPMMR, val);
}
@@ -107,6 +110,16 @@ static void enetc4_pf_set_si_mc_hash_filter(struct enetc_hw *hw, int si,
enetc_port_wr(hw, ENETC4_PSIMMHFR1(si), upper_32_bits(hash));
}
+static void enetc4_pf_set_si_mac_hash_filter(struct enetc_hw *hw, int si,
+ enum enetc_mac_addr_type type,
+ u64 hash)
+{
+ if (type == UC)
+ enetc4_pf_set_si_uc_hash_filter(hw, si, hash);
+ else if (type == MC)
+ enetc4_pf_set_si_mc_hash_filter(hw, si, hash);
+}
+
static void enetc4_pf_set_loopback(struct net_device *ndev, bool en)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
@@ -273,6 +286,8 @@ static void enetc4_pf_set_mac_filter(struct enetc_pf *pf, int type)
static const struct enetc_pf_ops enetc4_pf_ops = {
.set_si_primary_mac = enetc4_pf_set_si_primary_mac,
.get_si_primary_mac = enetc4_pf_get_si_primary_mac,
+ .set_si_mac_promisc = enetc4_pf_set_si_mac_promisc,
+ .set_si_mac_hash_filter = enetc4_pf_set_si_mac_hash_filter,
};
static int enetc4_pf_struct_init(struct enetc_si *si)
@@ -513,7 +528,10 @@ static void enetc4_psi_do_set_rx_mode(struct work_struct *work)
type = ENETC_MAC_FILTER_TYPE_ALL;
}
- enetc4_pf_set_si_mac_promisc(hw, 0, uc_promisc, mc_promisc);
+ mutex_lock(&pf->msg_lock);
+ enetc4_pf_set_si_mac_promisc(hw, 0, UC, uc_promisc);
+ enetc4_pf_set_si_mac_promisc(hw, 0, MC, mc_promisc);
+ mutex_unlock(&pf->msg_lock);
if (uc_promisc) {
enetc4_pf_set_si_uc_hash_filter(hw, 0, 0);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
index 44c8196accaa..9e23499e290f 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h
@@ -91,6 +91,17 @@
#define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0)
#define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8)
+#define ENETC_MAC_HASH_TABLE_SIZE_64 0
+#define ENETC_MSG_MAC_HASH_SIZE GENMASK(5, 0)
+#define ENETC_MSG_MAC_TYPE GENMASK(7, 6)
+#define ENETC_MAC_FILTER_TYPE_UC BIT(0)
+#define ENETC_MAC_FILTER_TYPE_MC BIT(1)
+#define ENETC_MAC_FILTER_TYPE_ALL (ENETC_MAC_FILTER_TYPE_UC | \
+ ENETC_MAC_FILTER_TYPE_MC)
+
+#define ENETC_MSG_MAC_FLUSH_MACS BIT(0)
+#define ENETC_MSG_MAC_PROMISC_MODE BIT(1)
+
enum enetc_msg_class_id {
/* Class ID for PSI-to-VSI messages */
ENETC_MSG_CLASS_ID_CMD_SUCCESS = 1,
@@ -114,6 +125,8 @@ enum enetc_msg_class_id {
enum enetc_msg_mac_filter_cmd_id {
ENETC_MSG_SET_PRIMARY_MAC,
+ ENETC_MSG_SET_MAC_HASH_TABLE = 3,
+ ENETC_MSG_SET_MAC_PROMISC_MODE = 5,
};
enum enetc_msg_ip_revision_cmd_id {
@@ -195,6 +208,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 49c1ca14735c..7dcb1dcdec84 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -101,6 +101,115 @@ static u16 enetc_msg_set_vf_primary_mac_addr(struct enetc_pf *pf, int vf_id,
return pf_msg;
}
+static u16 enetc_msg_set_vf_mac_hash_filter(struct enetc_pf *pf, int vf_id,
+ void *vf_msg)
+{
+ struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+ struct enetc_msg_mac_hash_filter *msg = vf_msg;
+ struct enetc_hw *hw = &pf->si->hw;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+ int si_id = vf_id + 1;
+ u64 hash_tbl;
+ int type;
+
+ mutex_lock(&vf_state->lock);
+
+ if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+
+ if (!pf->ops->set_si_mac_hash_filter) {
+ pf_msg = ENETC_PF_MSG_NOTSUPP;
+ goto vf_state_unlock;
+ }
+
+ /* 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) {
+ pf_msg = ENETC_PF_MSG_NOTSUPP;
+ goto vf_state_unlock;
+ }
+
+ type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->sz_type);
+ hash_tbl = (u64)msg->hash_tbl[1] << 32 | msg->hash_tbl[0];
+ if (type == ENETC_MAC_FILTER_TYPE_UC) {
+ pf->ops->set_si_mac_hash_filter(hw, si_id, UC, hash_tbl);
+ } else if (type == ENETC_MAC_FILTER_TYPE_MC) {
+ pf->ops->set_si_mac_hash_filter(hw, si_id, MC, hash_tbl);
+ } else if (type == ENETC_MAC_FILTER_TYPE_ALL) {
+ if (!msg->hdr.len) {
+ pf_msg = FIELD_PREP(ENETC_PF_MSG_CLASS_ID,
+ ENETC_MSG_CLASS_ID_INVALID_MSG_LEN);
+ goto vf_state_unlock;
+ }
+
+ pf->ops->set_si_mac_hash_filter(hw, si_id, UC, hash_tbl);
+ hash_tbl = (u64)msg->hash_tbl[3] << 32 | msg->hash_tbl[2];
+ pf->ops->set_si_mac_hash_filter(hw, si_id, MC, hash_tbl);
+ }
+
+vf_state_unlock:
+ mutex_unlock(&vf_state->lock);
+
+ return pf_msg;
+}
+
+static u16 enetc_msg_set_vf_mac_promisc_mode(struct enetc_pf *pf, int vf_id,
+ void *vf_msg)
+{
+ struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
+ struct enetc_msg_mac_promisc_mode *msg = vf_msg;
+ u16 pf_msg = ENETC_PF_MSG_SUCCESS;
+ struct enetc_hw *hw = &pf->si->hw;
+ bool promisc, flush_macs;
+ int si_id = vf_id + 1;
+ int type;
+
+ mutex_lock(&vf_state->lock);
+ if (msg->config & ENETC_MSG_MAC_PROMISC_MODE) {
+ if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) {
+ pf_msg = ENETC_PF_MSG_PERM_DENY;
+ goto vf_state_unlock;
+ }
+ }
+
+ if (!pf->ops->set_si_mac_promisc) {
+ pf_msg = ENETC_PF_MSG_NOTSUPP;
+ goto vf_state_unlock;
+ }
+
+ flush_macs = !!(msg->config & ENETC_MSG_MAC_FLUSH_MACS);
+ if (flush_macs && !pf->ops->set_si_mac_hash_filter) {
+ pf_msg = ENETC_PF_MSG_NOTSUPP;
+ goto vf_state_unlock;
+ }
+
+ type = FIELD_GET(ENETC_MSG_MAC_TYPE, msg->config);
+ promisc = !!(msg->config & ENETC_MSG_MAC_PROMISC_MODE);
+
+ mutex_lock(&pf->msg_lock);
+
+ if (type & ENETC_MAC_FILTER_TYPE_UC)
+ pf->ops->set_si_mac_promisc(hw, si_id, UC, promisc);
+
+ if (type & ENETC_MAC_FILTER_TYPE_MC)
+ pf->ops->set_si_mac_promisc(hw, si_id, MC, promisc);
+
+ mutex_unlock(&pf->msg_lock);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_UC) && flush_macs)
+ pf->ops->set_si_mac_hash_filter(hw, si_id, UC, 0);
+
+ if ((type & ENETC_MAC_FILTER_TYPE_MC) && flush_macs)
+ pf->ops->set_si_mac_hash_filter(hw, si_id, MC, 0);
+
+vf_state_unlock:
+ mutex_unlock(&vf_state->lock);
+
+ return pf_msg;
+}
+
static u16 enetc_msg_handle_mac_filter(struct enetc_pf *pf, int vf_id,
void *vf_msg)
{
@@ -109,6 +218,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;
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index b90a3d24d854..7ca85731d6cc 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -34,6 +34,10 @@ struct enetc_pf_ops {
struct phylink_pcs *(*create_pcs)(struct enetc_pf *pf, struct mii_bus *bus);
void (*destroy_pcs)(struct phylink_pcs *pcs);
int (*enable_psfp)(struct enetc_ndev_priv *priv);
+ void (*set_si_mac_promisc)(struct enetc_hw *hw, int si,
+ enum enetc_mac_addr_type type, bool en);
+ void (*set_si_mac_hash_filter)(struct enetc_hw *hw, int si,
+ enum enetc_mac_addr_type type, u64 hash);
};
struct enetc_pf {
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index 85e1efa6a8ce..66a9a734d18e 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -468,6 +468,7 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_pf *pf = enetc_si_priv(priv->si);
+ struct enetc_hw *hw = &pf->si->hw;
struct enetc_vf_state *vf_state;
if (vf >= pf->total_vfs)
@@ -476,11 +477,27 @@ int enetc_pf_set_vf_trust(struct net_device *ndev, int vf, bool setting)
vf_state = &pf->vf_state[vf];
mutex_lock(&vf_state->lock);
- if (setting)
+ if (setting) {
vf_state->flags |= ENETC_VF_FLAG_TRUSTED;
- else
+ } else {
vf_state->flags &= ~ENETC_VF_FLAG_TRUSTED;
+ /* Clear MAC hash filters and disable MAC promiscuous modes
+ * if the VF is untrusted.
+ */
+ if (pf->ops->set_si_mac_hash_filter) {
+ pf->ops->set_si_mac_hash_filter(hw, vf + 1, UC, 0);
+ pf->ops->set_si_mac_hash_filter(hw, vf + 1, MC, 0);
+ }
+
+ mutex_lock(&pf->msg_lock);
+ if (pf->ops->set_si_mac_promisc) {
+ pf->ops->set_si_mac_promisc(hw, vf + 1, UC, false);
+ pf->ops->set_si_mac_promisc(hw, vf + 1, MC, false);
+ }
+ mutex_unlock(&pf->msg_lock);
+ }
+
mutex_unlock(&vf_state->lock);
return 0;
--
2.34.1