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);

Reply via email to