This implements vlan offload in mcx(4). vlan stripping is fairly straightforward, as the nic just removes the tag and populates a field in the completion queue entry. vlan insertion is a bit funny, as the nic doesn't do any of the work here at all. The driver has to copy at least the L2 headers of the packet into the send queue entry, so it can insert a tag into a previously untagged packet while it's doing that, and somewhat lower cost than shuffling the packet data around in an mbuf.
I've tested that this doesn't break tcp or udp checksums on vlan-tagged packets (including udp fragments). ok? Index: if_mcx.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_mcx.c,v retrieving revision 1.48 diff -u -p -r1.48 if_mcx.c --- if_mcx.c 27 May 2020 04:03:20 -0000 1.48 +++ if_mcx.c 28 May 2020 09:30:31 -0000 @@ -18,6 +18,7 @@ */ #include "bpfilter.h" +#include "vlan.h" #include <sys/param.h> #include <sys/systm.h> @@ -92,6 +93,7 @@ ((1 << MCX_LOG_FLOW_TABLE_SIZE) - MCX_NUM_STATIC_FLOWS) #define MCX_SQ_INLINE_SIZE 18 +CTASSERT(ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN == MCX_SQ_INLINE_SIZE); /* doorbell offsets */ #define MCX_CQ_DOORBELL_OFFSET 0 @@ -1258,6 +1260,8 @@ struct mcx_cq_entry { #define MCX_CQ_ENTRY_FLAGS_L4_OK (1 << 26) #define MCX_CQ_ENTRY_FLAGS_L3_OK (1 << 25) #define MCX_CQ_ENTRY_FLAGS_L2_OK (1 << 24) +#define MCX_CQ_ENTRY_FLAGS_CV (1 << 16) +#define MCX_CQ_ENTRY_FLAGS_VLAN_MASK (0xffff) uint32_t cq_lro_srqn; uint32_t __reserved__[2]; @@ -2363,6 +2367,9 @@ mcx_attach(struct device *parent, struct ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_IPv4 | IFCAP_CSUM_UDPv4 | IFCAP_CSUM_UDPv6 | IFCAP_CSUM_TCPv4 | IFCAP_CSUM_TCPv6; +#if NVLAN > 0 + ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING; +#endif IFQ_SET_MAXLEN(&ifp->if_snd, 1024); ifmedia_init(&sc->sc_media, IFM_IMASK, mcx_media_change, @@ -4013,6 +4020,7 @@ mcx_create_rq(struct mcx_softc *sc, int struct mcx_rq_ctx *mbin; int error; uint64_t *pas; + uint32_t rq_flags; uint8_t *doorbell; int insize, npages, paslen, token; @@ -4044,7 +4052,11 @@ mcx_create_rq(struct mcx_softc *sc, int goto free; } mbin = (struct mcx_rq_ctx *)(((char *)mcx_cq_mbox_data(mcx_cq_mbox(&mxm, 0))) + 0x10); - mbin->rq_flags = htobe32(MCX_RQ_CTX_RLKEY | MCX_RQ_CTX_VLAN_STRIP_DIS); + rq_flags = MCX_RQ_CTX_RLKEY; +#if NVLAN == 0 + rq_flags |= MCX_RQ_CTX_VLAN_STRIP_DIS; +#endif + mbin->rq_flags = htobe32(rq_flags); mbin->rq_cqn = htobe32(cqn); mbin->rq_wq.wq_type = MCX_WQ_CTX_TYPE_CYCLIC; mbin->rq_wq.wq_pd = htobe32(sc->sc_pd); @@ -5697,6 +5709,13 @@ mcx_process_rx(struct mcx_softc *sc, str if (flags & MCX_CQ_ENTRY_FLAGS_L4_OK) m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK; +#if NVLAN > 0 + if (flags & MCX_CQ_ENTRY_FLAGS_CV) { + m->m_pkthdr.ether_vtag = (flags & + MCX_CQ_ENTRY_FLAGS_VLAN_MASK); + m->m_flags |= M_VLANTAG; + } +#endif if (c->c_tdiff) { uint64_t t = bemtoh64(&cqe->cq_timestamp) - c->c_timestamp; @@ -6369,9 +6388,26 @@ mcx_start(struct ifqueue *ifq) csum |= MCX_SQE_L4_CSUM; sqe->sqe_mss_csum = htobe32(csum); sqe->sqe_inline_header_size = htobe16(MCX_SQ_INLINE_SIZE); - m_copydata(m, 0, MCX_SQ_INLINE_SIZE, - (caddr_t)sqe->sqe_inline_headers); - m_adj(m, MCX_SQ_INLINE_SIZE); +#if NVLAN > 0 + if (m->m_flags & M_VLANTAG) { + struct ether_vlan_header *evh; + evh = (struct ether_vlan_header *) + &sqe->sqe_inline_headers; + + /* slightly cheaper vlan_inject() */ + m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)evh); + evh->evl_proto = evh->evl_encap_proto; + evh->evl_encap_proto = htons(ETHERTYPE_VLAN); + evh->evl_tag = htons(m->m_pkthdr.ether_vtag); + + m_adj(m, ETHER_HDR_LEN); + } else +#endif + { + m_copydata(m, 0, MCX_SQ_INLINE_SIZE, + (caddr_t)sqe->sqe_inline_headers); + m_adj(m, MCX_SQ_INLINE_SIZE); + } if (mcx_load_mbuf(sc, ms, m) != 0) { m_freem(m);