The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=dcede1ec9de3b24fc9fbeeebc99ca34d226995f9
commit dcede1ec9de3b24fc9fbeeebc99ca34d226995f9 Author: Nick Price <[email protected]> AuthorDate: 2026-07-19 16:40:36 +0000 Commit: Adrian Chadd <[email protected]> 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;
