git: dcede1ec9de3 - main - aq(4): expand and correct offloads, fix VLAN/multicast filtering
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 19 Jul 2026 16:49:10 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=dcede1ec9de3b24fc9fbeeebc99ca34d226995f9
commit dcede1ec9de3b24fc9fbeeebc99ca34d226995f9
Author: Nick Price <nick@spun.io>
AuthorDate: 2026-07-19 16:40:36 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-07-19 16:40:36 +0000
aq(4): expand and correct offloads, fix VLAN/multicast filtering
Advertise the offloads the hardware already performs, correct the TX
descriptor's L3 family selection, and correct the VLAN and multicast
receive-filter paths.
Offloads: advertise IFCAP_HWCSUM_IPV6 (adding CSUM_IP6_TCP/UDP/TSO to
isc_tx_csum_flags) and IFCAP_VLAN_HWTSO, and enable the RX outer
(S-VLAN) tag parse mode in aq_hw_offload_set().
TX descriptor L3 family: aq_setup_offloads() derived tx_desc_cmd_ipv4
from CSUM_IP|CSUM_TSO, but CSUM_TSO is (CSUM_IP_TSO|CSUM_IP6_TSO) and
tcp_output() sets both bits without regard to address family, so an
IPv6 TSO frame matched on CSUM_IP_TSO and went out with the IPv4
header-checksum command set on a frame that carries no IPv4 header.
The checksum flags cannot distinguish the family; key the bit off
IPI_TX_IPV4 instead, which iflib derives from the parsed ethertype,
as the IPI_TX_INTR test below it already does. Plain IPv6 checksum
offload was unaffected, as CSUM_IP6_TCP alone never matched the mask.
RX VLAN tag stripping: ring init hardwired hardware tag stripping off
while the RX path still set M_VLANTAG and the writeback tag for every
tagged frame, so a tagged frame arrived with the tag in line while the
mbuf claimed it stripped and ether_demux() parsed four bytes short of
the payload. Program per-ring stripping from IFCAP_VLAN_HWTAGGING and
set M_VLANTAG only under the same capability, so the two states stay
coherent.
VLAN filter and promiscuous edge cases: filter only when 1..16 VLANs are
registered -- with none (or more than the 16 the table holds) fall back
to VLAN-promiscuous and pass all tags, rather than dropping every tagged
frame against an empty filter table; and keep VLAN-promiscuous set
whenever the interface is IFF_PROMISC, so adding or removing a VLAN under
promisc does not clear it and start dropping tagged frames.
Multicast reconcile: ifdi_multi_set is declarative, but aq_if_multi_set()
only added -- shrinking the list left accept-all-multicast latched or
stale exact slots enabled, defeating hardware multicast filtering until
a reinit. Clear the exact slots before reprogramming the current list,
and always drive accept-all-multicast from the current state so a shrink
clears it.
Reviewed by: adrian
Differential Revision: https://reviews.freebsd.org/D58145
---
sys/dev/aq/aq_hw.c | 3 +++
sys/dev/aq/aq_hw_llh.c | 8 ++++++++
sys/dev/aq/aq_hw_llh.h | 4 ++++
sys/dev/aq/aq_hw_llh_internal.h | 13 +++++++++++++
sys/dev/aq/aq_main.c | 43 ++++++++++++++++++++++++++---------------
sys/dev/aq/aq_ring.c | 10 +++++++---
6 files changed, 62 insertions(+), 19 deletions(-)
diff --git a/sys/dev/aq/aq_hw.c b/sys/dev/aq/aq_hw.c
index da07c876b959..1ad9d2e15ed7 100644
--- a/sys/dev/aq/aq_hw.c
+++ b/sys/dev/aq/aq_hw.c
@@ -459,6 +459,9 @@ aq_hw_offload_set(struct aq_hw *hw)
/* LSO offloads*/
tdm_large_send_offload_en_set(hw, 0xFFFFFFFFU);
+ /* Outer (S-VLAN) tag parse mode */
+ rpo_outer_vlan_tag_mode_set(hw, 1U);
+
{
uint32_t i = 0;
uint32_t val = (8U < HW_ATL_B0_LRO_RXD_MAX) ? 0x3U :
diff --git a/sys/dev/aq/aq_hw_llh.c b/sys/dev/aq/aq_hw_llh.c
index eb053a4bdce6..dba02c467e55 100644
--- a/sys/dev/aq/aq_hw_llh.c
+++ b/sys/dev/aq/aq_hw_llh.c
@@ -1441,6 +1441,14 @@ rpo_tcp_udp_crc_offload_en_set(struct aq_hw *aq_hw,
rpol4chk_en_shift, tcp_udp_crc_offload_en);
}
+void
+rpo_outer_vlan_tag_mode_set(struct aq_hw *aq_hw, uint32_t outer_vlan_tag_mode)
+{
+ AQ_WRITE_REG_BIT(aq_hw, rpo_outer_vl_ins_mode_adr,
+ rpo_outer_vl_ins_mode_msk, rpo_outer_vl_ins_mode_shift,
+ outer_vlan_tag_mode);
+}
+
void
rpo_lro_en_set(struct aq_hw *aq_hw, uint32_t lro_en)
{
diff --git a/sys/dev/aq/aq_hw_llh.h b/sys/dev/aq/aq_hw_llh.h
index 68cc666be1a3..47011295aa4c 100644
--- a/sys/dev/aq/aq_hw_llh.h
+++ b/sys/dev/aq/aq_hw_llh.h
@@ -808,6 +808,10 @@ void rpo_rx_desc_vlan_stripping_set(struct aq_hw *aq_hw,
void rpo_tcp_udp_crc_offload_en_set(struct aq_hw *aq_hw,
uint32_t tcp_udp_crc_offload_en);
+/* set RX outer (S-VLAN) tag parse mode */
+void rpo_outer_vlan_tag_mode_set(struct aq_hw *aq_hw,
+ uint32_t outer_vlan_tag_mode);
+
/* Set LRO Patch Optimization Enable. */
void rpo_lro_patch_optimization_en_set(struct aq_hw *aq_hw,
uint32_t lro_patch_optimization_en);
diff --git a/sys/dev/aq/aq_hw_llh_internal.h b/sys/dev/aq/aq_hw_llh_internal.h
index bad3c1640cf8..fa1c9a83985b 100644
--- a/sys/dev/aq/aq_hw_llh_internal.h
+++ b/sys/dev/aq/aq_hw_llh_internal.h
@@ -2145,6 +2145,19 @@
/* default value of bitfield ipv4_chk_en */
#define rpo_ipv4chk_en_default 0x0
+/* register address for bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_adr 0x00005580
+/* bitmask for bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_msk 0x00000004
+/* inverted bitmask for bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_mskn 0xfffffffb
+/* lower bit position of bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_shift 2
+/* width of bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_width 1
+/* default value of bitfield outer_vl_ins_mode */
+#define rpo_outer_vl_ins_mode_default 0x0
+
/* rx desc{d}_vl_strip bitfield definitions
* preprocessor definitions for the bitfield "desc{d}_vl_strip".
* parameter: descriptor {d} | stride size 0x20 | range [0, 31]
diff --git a/sys/dev/aq/aq_main.c b/sys/dev/aq/aq_main.c
index d4f687fee8f1..5d2d9061c548 100644
--- a/sys/dev/aq/aq_main.c
+++ b/sys/dev/aq/aq_main.c
@@ -371,10 +371,12 @@ aq_if_attach_pre(if_ctx_t ctx)
softc->admin_ticks = 0;
iflib_set_mac(ctx, hw->mac_addr);
- scctx->isc_tx_csum_flags = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TSO;
+ scctx->isc_tx_csum_flags = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TSO |
+ CSUM_IP6_TCP | CSUM_IP6_UDP | CSUM_IP6_TSO;
scctx->isc_capabilities = IFCAP_RXCSUM | IFCAP_TXCSUM | IFCAP_HWCSUM |
- IFCAP_TSO | IFCAP_LRO | IFCAP_JUMBO_MTU | IFCAP_VLAN_HWFILTER |
- IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM;
+ IFCAP_HWCSUM_IPV6 | IFCAP_TSO | IFCAP_LRO | IFCAP_JUMBO_MTU |
+ IFCAP_VLAN_HWFILTER | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING |
+ IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO;
scctx->isc_capenable = scctx->isc_capabilities;
scctx->isc_tx_nsegments = 31;
scctx->isc_tx_tso_segments_max = 31;
@@ -739,6 +741,11 @@ aq_if_init(if_ctx_t ctx)
aq_hw_udp_rss_enable(hw, aq_enable_rss_udp);
aq_hw_set_link_speed(hw, hw->link_rate);
+ /* iflib does not replay filter state after init; aq_hw_init() clears it. */
+ aq_if_multi_set(ctx);
+ if (aq_if_promisc_set(ctx, if_getflags(iflib_get_ifp(ctx))) != 0)
+ device_printf(softc->dev, "could not restore promiscuous mode\n");
+
AQ_DBG_EXIT(0);
}
@@ -822,14 +829,18 @@ aq_if_multi_set(if_ctx_t ctx)
if_t ifp = iflib_get_ifp(ctx);
struct aq_hw *hw = &softc->hw;
AQ_DBG_ENTER();
- softc->mcnt = if_llmaddr_count(iflib_get_ifp(ctx));
- if (softc->mcnt >= AQ_HW_MAC_MAX) {
- aq_hw_set_promisc(hw, !!(if_getflags(ifp) & IFF_PROMISC),
- aq_is_vlan_promisc_required(softc),
- !!(if_getflags(ifp) & IFF_ALLMULTI) || aq_is_mc_promisc_required(softc));
- } else {
- if_foreach_llmaddr(iflib_get_ifp(ctx), &aq_mc_filter_apply, softc);
+ softc->mcnt = if_llmaddr_count(ifp);
+
+ /* Reconcile HW to the current list: clear stale slots, reprogram. */
+ if (softc->mcnt < AQ_HW_MAC_MAX) {
+ for (int i = 1; i < AQ_HW_MAC_MAX; i++)
+ rpfl2_uc_flr_en_set(hw, 0U, i);
+ if_foreach_llmaddr(ifp, &aq_mc_filter_apply, softc);
}
+
+ aq_hw_set_promisc(hw, !!(if_getflags(ifp) & IFF_PROMISC),
+ aq_is_vlan_promisc_required(softc),
+ !!(if_getflags(ifp) & IFF_ALLMULTI) || aq_is_mc_promisc_required(softc));
AQ_DBG_EXIT(0);
}
@@ -1056,11 +1067,9 @@ aq_is_vlan_promisc_required(struct aq_dev *softc)
bit_count(softc->vlan_tags, 0, 4096, &vlan_tag_count);
- if (vlan_tag_count <= AQ_HW_VLAN_MAX_FILTERS)
- return (false);
- else
- return (true);
-
+ /* Filter only with 1..16 VLANs; 0 or >16 pass all tags. */
+ return (vlan_tag_count == 0 ||
+ vlan_tag_count > AQ_HW_VLAN_MAX_FILTERS);
}
static void
@@ -1087,7 +1096,9 @@ aq_update_vlan_filters(struct aq_dev *softc)
}
hw_atl_b0_hw_vlan_set(hw, aq_vlans);
- hw_atl_b0_hw_vlan_promisc_set(hw, aq_is_vlan_promisc_required(softc));
+ hw_atl_b0_hw_vlan_promisc_set(hw,
+ aq_is_vlan_promisc_required(softc) ||
+ (if_getflags(iflib_get_ifp(softc->ctx)) & IFF_PROMISC) != 0);
}
/* VLAN support */
diff --git a/sys/dev/aq/aq_ring.c b/sys/dev/aq/aq_ring.c
index 311a5c9abf28..8307fb0b3474 100644
--- a/sys/dev/aq/aq_ring.c
+++ b/sys/dev/aq/aq_ring.c
@@ -110,7 +110,9 @@ aq_ring_rx_init(struct aq_hw *hw, struct aq_ring *ring)
rdm_rx_desc_head_buff_size_set(hw, 0U, ring->index);
rdm_rx_desc_head_splitting_set(hw, 0U, ring->index);
- rpo_rx_desc_vlan_stripping_set(hw, 0U, ring->index);
+ rpo_rx_desc_vlan_stripping_set(hw,
+ (if_getcapenable(iflib_get_ifp(ring->dev->ctx)) &
+ IFCAP_VLAN_HWTAGGING) != 0 ? 1U : 0U, ring->index);
/* Rx ring set mode */
@@ -363,7 +365,8 @@ aq_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri)
ri->iri_frags[i].irf_idx = cidx;
ri->iri_frags[i].irf_len = len;
- if ((rx_desc->wb.pkt_type & 0x60) != 0) {
+ if ((if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) != 0 &&
+ (rx_desc->wb.pkt_type & 0x60) != 0) {
ri->iri_flags |= M_VLANTAG;
ri->iri_vtag = le32toh(rx_desc->wb.vlan);
}
@@ -405,7 +408,8 @@ aq_setup_offloads(struct aq_dev *aq_dev, if_pkt_info_t pi, volatile struct aq_tx
{
AQ_DBG_ENTER();
txd->cmd |= tx_desc_cmd_fcs;
- txd->cmd |= (pi->ipi_csum_flags & (CSUM_IP|CSUM_TSO)) ?
+ txd->cmd |= ((pi->ipi_flags & IPI_TX_IPV4) &&
+ (pi->ipi_csum_flags & (CSUM_IP | CSUM_IP_TSO))) ?
tx_desc_cmd_ipv4 : 0;
txd->cmd |= (pi->ipi_csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP |
CSUM_IP_UDP | CSUM_IP6_UDP)) ? tx_desc_cmd_l4cs : 0;