From: Stefan Sperling Subject: Re: dwqe(4) VLAN offload To: Jonathan Matthew Cc: tech@openbsd.org Date: Tue, 4 Jun 2024 21:48:25 +0200 On Sun, Jun 02, 2024 at 09:55:43PM +1000, Jonathan Matthew wrote: > I've only tested the receive side of this so far, on an RK3568 system > that doesn't implement the transmit side. A couple of comments below. > > +#if NVLAN > 0 > > + /* VLAN tags require an extra Tx context descriptor. */ > > + if (dwqe_have_tx_vlan_offload(sc) && > > + (m->m_flags & M_VLANTAG) && > > + used + DWQE_NTXSEGS + 2 > left) { > > + ifq_set_oactive(ifq); > > + break; > > + } > > +#endif > > Usually we just check that there's space to fit any possible packet at > the top of the loop, rather than dequeueing and checking if that > specific packet fits. I think it'd be better to change the check above > this one to be 'used + DWQE_NTXSEGS + 2 > left', moving the comment about > when an extra descriptor is needed there too. I didn't want to change the non-VLAN path, but agreed, reserving one additional slot won't hurt anyone and keeps things simpler. > > +#if NVLAN > 0 > > + /* Enable outer VLAN tag stripping on Rx. */ > > + reg = dwqe_read(sc, GMAC_VLAN_TAG_CTRL); > > + reg |= GMAC_VLAN_TAG_CTRL_EVLRXS | GMAC_VLAN_TAG_CTRL_STRIP_ALWAYS; > > + dwqe_write(sc, GMAC_VLAN_TAG_CTRL, reg); > > +#endif > > Would it make sense to do this in dwqe_attach() where the transmit side > is configured? I had put this into a similar place where this setting is applied in the Linux driver. However, it still works with your suggestion and looks nicer, thanks! New diff below: diff refs/heads/master refs/heads/dwqe-vlan commit - 92119d768f45cf44645fc7f9c2fe90112b6d3f97 commit + f9140c07b7ab51b0bcca0931d1e4cb573e57e181 blob - 04cdc69851a5b230b81c8c46244384770207a025 blob + e7934be3cd39673e470d0affb49a24f1664a4449 --- sys/dev/ic/dwqe.c +++ sys/dev/ic/dwqe.c @@ -21,6 +21,7 @@ */ #include "bpfilter.h" +#include "vlan.h" #include #include @@ -100,6 +101,53 @@ dwqe_have_tx_csum_offload(struct dwqe_softc *sc) } int +dwqe_have_tx_vlan_offload(struct dwqe_softc *sc) +{ +#if NVLAN > 0 + return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_SAVLANINS); +#else + return 0; +#endif +} + +void +dwqe_set_vlan_rx_mode(struct dwqe_softc *sc) +{ +#if NVLAN > 0 + uint32_t reg; + + /* Enable outer VLAN tag stripping on Rx. */ + reg = dwqe_read(sc, GMAC_VLAN_TAG_CTRL); + reg |= GMAC_VLAN_TAG_CTRL_EVLRXS | GMAC_VLAN_TAG_CTRL_STRIP_ALWAYS; + dwqe_write(sc, GMAC_VLAN_TAG_CTRL, reg); +#endif +} + +void +dwqe_set_vlan_tx_mode(struct dwqe_softc *sc) +{ +#if NVLAN > 0 + uint32_t reg; + + reg = dwqe_read(sc, GMAC_VLAN_TAG_INCL); + + /* Enable insertion of outer VLAN tag. */ + reg |= GMAC_VLAN_TAG_INCL_INSERT; + + /* + * Generate C-VLAN tags (type 0x8100, 802.1Q). Setting this + * bit would result in S-VLAN tags (type 0x88A8, 802.1ad). + */ + reg &= ~GMAC_VLAN_TAG_INCL_CSVL; + + /* Use VLAN tags provided in Tx context descriptors. */ + reg |= GMAC_VLAN_TAG_INCL_VLTI; + + dwqe_write(sc, GMAC_VLAN_TAG_INCL, reg); +#endif +} + +int dwqe_attach(struct dwqe_softc *sc) { struct ifnet *ifp; @@ -127,6 +175,8 @@ dwqe_attach(struct dwqe_softc *sc) bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_capabilities = IFCAP_VLAN_MTU; + if (dwqe_have_tx_vlan_offload(sc)) + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; if (dwqe_have_tx_csum_offload(sc)) { ifp->if_capabilities |= (IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 | @@ -218,6 +268,14 @@ dwqe_attach(struct dwqe_softc *sc) if (!sc->sc_fixed_link) dwqe_mii_attach(sc); + /* + * All devices support VLAN tag stripping on Rx but inserting + * VLAN tags during Tx is an optional feature. + */ + dwqe_set_vlan_rx_mode(sc); + if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) + dwqe_set_vlan_tx_mode(sc); + if_attach(ifp); ether_ifattach(ifp); @@ -329,7 +387,8 @@ dwqe_start(struct ifqueue *ifq) used = 0; for (;;) { - if (used + DWQE_NTXSEGS + 1 > left) { + /* VLAN tags require an extra Tx context descriptor. */ + if (used + DWQE_NTXSEGS + 2 > left) { ifq_set_oactive(ifq); break; } @@ -715,6 +774,21 @@ dwqe_rx_csum(struct dwqe_softc *sc, struct mbuf *m, st } void +dwqe_vlan_strip(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *rxd) +{ +#if NVLAN > 0 + uint16_t tag; + + if ((rxd->sd_tdes3 & RDES3_RDES0_VALID) && + (rxd->sd_tdes3 & RDES3_LD)) { + tag = rxd->sd_tdes0 & RDES0_OVT; + m->m_pkthdr.ether_vtag = le16toh(tag); + m->m_flags |= M_VLANTAG; + } +#endif +} + +void dwqe_rx_proc(struct dwqe_softc *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; @@ -763,6 +837,7 @@ dwqe_rx_proc(struct dwqe_softc *sc) m->m_pkthdr.len = m->m_len = len; dwqe_rx_csum(sc, m, rxd); + dwqe_vlan_strip(sc, m, rxd); ml_enqueue(&ml, m); } @@ -1107,12 +1182,34 @@ dwqe_tx_csum(struct dwqe_softc *sc, struct mbuf *m, st txd->sd_tdes3 |= TDES3_CSUM_IPHDR_PAYLOAD_PSEUDOHDR; } +uint16_t +dwqe_set_tx_context_desc(struct dwqe_softc *sc, struct mbuf *m, int idx) +{ + uint16_t tag = 0; +#if NVLAN > 0 + struct dwqe_desc *ctxt_txd; + + if ((m->m_flags & M_VLANTAG) == 0) + return 0; + + tag = m->m_pkthdr.ether_vtag; + if (tag) { + ctxt_txd = &sc->sc_txdesc[idx]; + ctxt_txd->sd_tdes3 |= (htole16(tag) & TDES3_VLAN_TAG); + ctxt_txd->sd_tdes3 |= TDES3_VLAN_TAG_VALID; + ctxt_txd->sd_tdes3 |= (TDES3_CTXT | TDES3_OWN); + } +#endif + return tag; +} + int dwqe_encap(struct dwqe_softc *sc, struct mbuf *m, int *idx, int *used) { struct dwqe_desc *txd, *txd_start; bus_dmamap_t map; int cur, frag, i; + uint16_t vlan_tag = 0; cur = frag = *idx; map = sc->sc_txbuf[cur].tb_map; @@ -1128,6 +1225,17 @@ dwqe_encap(struct dwqe_softc *sc, struct mbuf *m, int bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, BUS_DMASYNC_PREWRITE); + if (dwqe_have_tx_vlan_offload(sc)) { + vlan_tag = dwqe_set_tx_context_desc(sc, m, frag); + if (vlan_tag) { + (*used)++; + if (frag == (DWQE_NTXDESC - 1)) + frag = 0; + else + frag++; + } + } + txd = txd_start = &sc->sc_txdesc[frag]; for (i = 0; i < map->dm_nsegs; i++) { /* TODO: check for 32-bit vs 64-bit support */ @@ -1140,6 +1248,8 @@ dwqe_encap(struct dwqe_softc *sc, struct mbuf *m, int if (i == 0) { txd->sd_tdes3 |= TDES3_FS; dwqe_tx_csum(sc, m, txd); + if (vlan_tag) + txd->sd_tdes2 |= TDES2_VLAN_TAG_INSERT; } if (i == (map->dm_nsegs - 1)) { txd->sd_tdes2 |= TDES2_IC; blob - ab479d54c139b877b2a111e0f99d267db3a17dc0 blob + 03f66fca83a72220fb718003aaec576c590448e2 --- sys/dev/ic/dwqereg.h +++ sys/dev/ic/dwqereg.h @@ -44,6 +44,19 @@ #define GMAC_INT_MASK_LPIIM (1 << 10) #define GMAC_INT_MASK_PIM (1 << 3) #define GMAC_INT_MASK_RIM (1 << 0) +#define GMAC_VLAN_TAG_CTRL 0x0050 +#define GMAC_VLAN_TAG_CTRL_EVLRXS (1 << 24) +#define GMAC_VLAN_TAG_CTRL_STRIP_ALWAYS ((1 << 21) | (1 << 22)) +#define GMAC_VLAN_TAG_DATA 0x0054 +#define GMAC_VLAN_TAG_INCL 0x0060 +#define GMAC_VLAN_TAG_INCL_VLTI (1 << 20) +#define GMAC_VLAN_TAG_INCL_CSVL (1 << 19) +#define GMAC_VLAN_TAG_INCL_DELETE 0x10000 +#define GMAC_VLAN_TAG_INCL_INSERT 0x20000 +#define GMAC_VLAN_TAG_INCL_REPLACE 0x30000 +#define GMAC_VLAN_TAG_INCL_VLT 0x0ffff +#define GMAC_VLAN_TAG_INCL_RDWR (1U << 30) +#define GMAC_VLAN_TAG_INCL_BUSY (1U << 31) #define GMAC_QX_TX_FLOW_CTRL(x) (0x0070 + (x) * 4) #define GMAC_QX_TX_FLOW_CTRL_PT_SHIFT 16 #define GMAC_QX_TX_FLOW_CTRL_TFE (1 << 0) @@ -64,6 +77,7 @@ #define GMAC_MAC_HW_FEATURE(x) (0x011c + (x) * 0x4) #define GMAC_MAC_HW_FEATURE0_TXCOESEL (1 << 14) #define GMAC_MAC_HW_FEATURE0_RXCOESEL (1 << 16) +#define GMAC_MAC_HW_FEATURE0_SAVLANINS (1 << 27) #define GMAC_MAC_HW_FEATURE1_TXFIFOSIZE(x) (((x) >> 6) & 0x1f) #define GMAC_MAC_HW_FEATURE1_RXFIFOSIZE(x) (((x) >> 0) & 0x1f) #define GMAC_MAC_MDIO_ADDR 0x0200 @@ -230,6 +244,12 @@ struct dwqe_desc { uint32_t sd_tdes3; }; +/* Tx context descriptor bits (host to device); precedes regular descriptor */ +#define TDES3_CTXT (1 << 30) +#define TDES3_VLAN_TAG_VALID (1 << 16) +#define TDES3_VLAN_TAG 0xffff +/* Bit 31 is the OWN bit, as in regular Tx descriptor. */ + /* Tx bits (read format; host to device) */ #define TDES2_HDR_LEN 0x000003ff /* if TSO is enabled */ #define TDES2_BUF1_LEN 0x00003fff /* if TSO is disabled */ @@ -250,6 +270,11 @@ struct dwqe_desc { #define TDES3_CSUM_IPHDR_PAYLOAD (0x2 << 16) #define TDES3_CSUM_IPHDR_PAYLOAD_PSEUDOHDR (0x3 << 16) #define TDES3_TSO_EN (1 << 18) +#define TDES3_CPC ((1 << 26) | (1 << 27)) /* if TSO is disabled */ +#define TDES3_CPC_CRC_AND_PAD (0x0 << 26) +#define TDES3_CPC_CRC_NO_PAD (0x1 << 26) +#define TDES3_CPC_DISABLE (0x2 << 26) +#define TDES3_CPC_CRC_REPLACE (0x3 << 26) #define TDES3_LS (1 << 28) #define TDES3_FS (1 << 29) #define TDES3_OWN (1U << 31) @@ -268,6 +293,8 @@ struct dwqe_desc { #define RDES3_OWN (1U << 31) /* Rx bits (writeback format; device to host) */ +#define RDES0_IVT 0xffff0000 +#define RDES0_OVT 0x0000ffff #define RDES1_IP_PAYLOAD_TYPE 0x7 #define RDES1_IP_PAYLOAD_UNKNOWN 0x0 #define RDES1_IP_PAYLOAD_UDP 0x1