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;

Reply via email to