git: 2231e505d1d4 - main - ice: Make VF VLAN requests idempotent
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 17 Sep 2026 08:36:28 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=2231e505d1d40441b80cfacb5a92111d1408a935
commit 2231e505d1d40441b80cfacb5a92111d1408a935
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-18 10:44:24 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-17 08:34:55 +0000
ice: Make VF VLAN requests idempotent
VF drivers replay their VLAN filters after a reset and may retry a
request whose reply was lost. The PF tracked only a count and sent
every requested ID back to the switch. After PF reset replay had
already restored the filters, duplicate VID 0 failed with
ICE_ERR_ALREADY_EXISTS and NACKed the entire VF batch.
Track exact VLAN membership for each VF. Compact requests to unique
IDs whose membership changes, enforce the configured limit against
those IDs, and update membership after each hardware operation so
partial failures cannot undercount filters. Treat already-present
adds and already-absent deletes as successful reconciliation and
suppress their misleading low-level error dump.
Validated on an E810-XXV with a host-attached iavf VF. A three-filter
limit was filled with VIDs 0, 1, and 4094. PF and CORE resets replayed
all three without a duplicate warning or ADD_VLAN NACK, and DTrace
confirmed a three-VID replay reached the PF. A fourth unique VID was
rejected without changing the count; deleting an absent VID was a
no-op; deleting and replacing a present VID updated the count exactly.
A 128 four-queue VF create/destroy cycle also completed cleanly, and a
newly recreated VF started with an empty membership map.
MFC after: 2 weeks
Sponsored by: BBOX.io
Differential Revision: https://reviews.freebsd.org/D58909
---
sys/dev/ice/ice_iov.c | 107 +++++++++++++++++++++++++++++++++++++++-----------
sys/dev/ice/ice_iov.h | 3 ++
sys/dev/ice/ice_lib.c | 4 +-
3 files changed, 89 insertions(+), 25 deletions(-)
diff --git a/sys/dev/ice/ice_iov.c b/sys/dev/ice/ice_iov.c
index 869fe43a9411..61f53aad89b9 100644
--- a/sys/dev/ice/ice_iov.c
+++ b/sys/dev/ice/ice_iov.c
@@ -79,6 +79,8 @@ static void ice_vc_add_vlan_msg(struct ice_softc *sc, struct ice_vf *vf,
u8 *msg_buf);
static void ice_vc_del_vlan_msg(struct ice_softc *sc, struct ice_vf *vf,
u8 *msg_buf);
+static int ice_vc_select_vlans(struct ice_vf *vf, u16 *vids, u16 count,
+ bool add, u16 *selected_count);
static enum virtchnl_status_code ice_iov_err_to_virt_err(int ice_err);
static int ice_vf_validate_mac(struct ice_vf *vf, const uint8_t *addr);
@@ -959,6 +961,43 @@ ice_vc_del_eth_addr_msg(struct ice_softc *sc, struct ice_vf *vf, u8 *msg_buf)
v_status, NULL, 0, NULL);
}
+/**
+ * ice_vc_select_vlans - Compact a VF VLAN request in place
+ * @vf: VF tracking structure
+ * @vids: VLAN IDs supplied by the VF
+ * @count: number of VLAN IDs in the request
+ * @add: select absent VLANs for add, or present VLANs for delete
+ * @selected_count: returned number of VLAN IDs requiring a hardware change
+ *
+ * A VF may replay its entire VLAN configuration after a reset or retry a
+ * request whose reply was lost. Select only unique IDs whose membership
+ * actually changes so those requests remain idempotent and filter accounting
+ * continues to enforce the configured limit.
+ */
+static int
+ice_vc_select_vlans(struct ice_vf *vf, u16 *vids, u16 count, bool add,
+ u16 *selected_count)
+{
+ bitstr_t bit_decl(seen, ICE_VF_VLAN_MAP_LEN);
+ u16 selected, vid;
+
+ bzero(seen, sizeof(seen));
+ selected = 0;
+ for (u16 i = 0; i < count; i++) {
+ vid = vids[i];
+ if (vid > EVL_VLID_MASK)
+ return (EINVAL);
+ if (bit_test(seen, vid))
+ continue;
+ bit_set(seen, vid);
+ if (bit_test(vf->vlans_map, vid) == add)
+ continue;
+ vids[selected++] = vid;
+ }
+ *selected_count = selected;
+ return (0);
+}
+
/**
* ice_vc_add_vlan_msg - Handle VIRTCHNL_OP_ADD_VLAN msg from VF
* @sc: PF's softc structure
@@ -972,6 +1011,7 @@ ice_vc_add_vlan_msg(struct ice_softc *sc, struct ice_vf *vf, u8 *msg_buf)
{
struct ice_hw *hw = &sc->hw;
struct virtchnl_vlan_filter_list *vlan_list;
+ u16 selected;
int status = 0;
enum virtchnl_status_code v_status = VIRTCHNL_STATUS_SUCCESS;
struct ice_vsi *vsi = vf->vsi;
@@ -986,23 +1026,34 @@ ice_vc_add_vlan_msg(struct ice_softc *sc, struct ice_vf *vf, u8 *msg_buf)
goto done;
}
- if (vlan_list->num_elements > (vf->vlan_limit - vf->vlan_cnt)) {
- v_status = VIRTCHNL_STATUS_ERR_NO_MEMORY;
+ status = ice_vc_select_vlans(vf, vlan_list->vlan_id,
+ vlan_list->num_elements, true, &selected);
+ if (status != 0) {
+ v_status = VIRTCHNL_STATUS_ERR_PARAM;
goto done;
}
- status = ice_add_vlan_hw_filters(vsi, vlan_list->vlan_id,
- vlan_list->num_elements);
- if (status) {
- device_printf(sc->dev,
- "VF-%d: Failure adding VLANs to VSI %d, err %s aq_err %s\n",
- vf->vf_num, vsi->idx, ice_status_str(status),
- ice_aq_str(sc->hw.adminq.sq_last_status));
- v_status = ice_iov_err_to_virt_err(status);
+ if ((u32)vf->vlan_cnt + selected > vf->vlan_limit) {
+ v_status = VIRTCHNL_STATUS_ERR_NO_MEMORY;
goto done;
}
+ if (selected == 0)
+ goto done;
- vf->vlan_cnt += vlan_list->num_elements;
+ for (u16 i = 0; i < selected; i++) {
+ status = ice_add_vlan_hw_filter(vsi, vlan_list->vlan_id[i]);
+ if (status != 0 && status != ICE_ERR_ALREADY_EXISTS) {
+ device_printf(sc->dev,
+ "VF-%d: Failure adding VLAN %d to VSI %d, err %s aq_err %s\n",
+ vf->vf_num, vlan_list->vlan_id[i], vsi->idx,
+ ice_status_str(status),
+ ice_aq_str(sc->hw.adminq.sq_last_status));
+ v_status = ice_iov_err_to_virt_err(status);
+ goto done;
+ }
+ bit_set(vf->vlans_map, vlan_list->vlan_id[i]);
+ vf->vlan_cnt++;
+ }
done:
ice_aq_send_msg_to_vf(hw, vf->vf_num, VIRTCHNL_OP_ADD_VLAN,
@@ -1022,6 +1073,7 @@ ice_vc_del_vlan_msg(struct ice_softc *sc, struct ice_vf *vf, u8 *msg_buf)
{
struct ice_hw *hw = &sc->hw;
struct virtchnl_vlan_filter_list *vlan_list;
+ u16 selected;
int status = 0;
enum virtchnl_status_code v_status = VIRTCHNL_STATUS_SUCCESS;
struct ice_vsi *vsi = vf->vsi;
@@ -1036,21 +1088,30 @@ ice_vc_del_vlan_msg(struct ice_softc *sc, struct ice_vf *vf, u8 *msg_buf)
goto done;
}
- status = ice_remove_vlan_hw_filters(vsi, vlan_list->vlan_id,
- vlan_list->num_elements);
- if (status) {
- device_printf(sc->dev,
- "VF-%d: Failure deleting VLANs from VSI %d, err %s aq_err %s\n",
- vf->vf_num, vsi->idx, ice_status_str(status),
- ice_aq_str(sc->hw.adminq.sq_last_status));
- v_status = ice_iov_err_to_virt_err(status);
+ status = ice_vc_select_vlans(vf, vlan_list->vlan_id,
+ vlan_list->num_elements, false, &selected);
+ if (status != 0) {
+ v_status = VIRTCHNL_STATUS_ERR_PARAM;
goto done;
}
+ if (selected == 0)
+ goto done;
- if (vlan_list->num_elements >= vf->vlan_cnt)
- vf->vlan_cnt = 0;
- else
- vf->vlan_cnt -= vlan_list->num_elements;
+ for (u16 i = 0; i < selected; i++) {
+ status = ice_remove_vlan_hw_filter(vsi, vlan_list->vlan_id[i]);
+ if (status != 0 && status != ICE_ERR_DOES_NOT_EXIST) {
+ device_printf(sc->dev,
+ "VF-%d: Failure deleting VLAN %d from VSI %d, err %s aq_err %s\n",
+ vf->vf_num, vlan_list->vlan_id[i], vsi->idx,
+ ice_status_str(status),
+ ice_aq_str(sc->hw.adminq.sq_last_status));
+ v_status = ice_iov_err_to_virt_err(status);
+ goto done;
+ }
+ bit_clear(vf->vlans_map, vlan_list->vlan_id[i]);
+ MPASS(vf->vlan_cnt > 0);
+ vf->vlan_cnt--;
+ }
done:
ice_aq_send_msg_to_vf(hw, vf->vf_num, VIRTCHNL_OP_DEL_VLAN,
diff --git a/sys/dev/ice/ice_iov.h b/sys/dev/ice/ice_iov.h
index 7d6d7fdb1211..0ebd1b1c0555 100644
--- a/sys/dev/ice/ice_iov.h
+++ b/sys/dev/ice/ice_iov.h
@@ -41,6 +41,7 @@
#define _ICE_IOV_H_
#include <sys/types.h>
+#include <sys/bitstring.h>
#include <sys/bus.h>
#include <sys/nv.h>
#include <sys/iov_schema.h>
@@ -91,6 +92,8 @@ struct ice_vf {
u16 mac_filter_cnt;
u16 vlan_limit;
u16 vlan_cnt;
+#define ICE_VF_VLAN_MAP_LEN (EVL_VLID_MASK + 1)
+ bitstr_t bit_decl(vlans_map, ICE_VF_VLAN_MAP_LEN);
u16 num_irq_vectors;
u16 *vf_imap;
diff --git a/sys/dev/ice/ice_lib.c b/sys/dev/ice/ice_lib.c
index b92b55208b78..e7dce30222d5 100644
--- a/sys/dev/ice/ice_lib.c
+++ b/sys/dev/ice/ice_lib.c
@@ -5592,7 +5592,7 @@ ice_add_vlan_hw_filters(struct ice_vsi *vsi, u16 *vid, u16 length)
}
status = ice_add_vlan(hw, &vlan_list);
- if (!status)
+ if (!status || status == ICE_ERR_ALREADY_EXISTS)
goto done;
device_printf(vsi->sc->dev, "Failed to add VLAN filters:\n");
@@ -5657,7 +5657,7 @@ ice_remove_vlan_hw_filters(struct ice_vsi *vsi, u16 *vid, u16 length)
}
status = ice_remove_vlan(hw, &vlan_list);
- if (!status)
+ if (!status || status == ICE_ERR_DOES_NOT_EXIST)
goto done;
device_printf(vsi->sc->dev, "Failed to remove VLAN filters:\n");