Download raw body.
dwqe(4) VLAN offload
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 <sys/param.h>
#include <sys/systm.h>
@@ -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
dwqe(4) VLAN offload