Subject: 802.1q hardware support
To: None <tech-net@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-net
Date: 11/13/2000 23:02:41
--zhXaljGHf11kAtnf
Content-Type: text/plain; charset=us-ascii

Hi,
I added 802.1q hardware support for NICs that can intert the VLAN tag
themselve on the fly. With a Tigon II gigabit NIC in a celeron 600,
this has a real impact on performances (10% on troughput with ttcp -u,
and a very noticable cut in latency with ping).

The receive part is the easiest: the hardware gives us an ethernet packet
without the 802.1q encaptulation, and separately the tag value.
Instead of calling if_input(), if calls vlan_input_tag().
In if_vlan.c I moved code from vlan_input() to vlan_input_ifv(), both
vlan_input() and vlan_input_tag() call it once they know the ifv (and
eventually remove the 802.1q encap).

Now the transmit part: we need to pass to the NIC driver the tag number
and the mbuf. Adding a new callback for this doesn't look like the best
way, so we have to hold it in the mbuf header.
To store the tag in the header it needs to be expanded. I added a mh_info,
which is an union of a int and void * (so it's generic enouth).
I added a ethernet-specific flag M_VLAN1Q, defined as M_LINK1 (LINK0 is
already used). When set, then the hardware has to insert a tag with the
mh_info value.
I added a ETHERCAP_VLAN_HWTAGGING capabilitie (I guess ETHERCAP_VLAN_TAGGING
was here for this purpose but I find the ETHERCAP_VLAN_HWTAGGING name
is clearer).

Note that M_VLAN1Q/mh_info can be used for the receive part as well,
to avoid calling vlan_input_tag() from the driver itself (will be from
if_input).

Note that the lines in the patch below won't match for if_ti.c: to get
hardware tagging to work I had to update the firmware, so I started pulling
up changes from FreeBSD. This will give us support for the copper variant
of the board (gigabit over cat5).

Comments ? I'd like to commit this soon, so that I can start working
on hardware checksum (I'm sure someone would ask :)

--
Manuel Bouyer <bouyer@antioche.eu.org>
--

--zhXaljGHf11kAtnf
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="vlan.diff"

Index: sys/mbuf.h
===================================================================
RCS file: /cvsroot/syssrc/sys/sys/mbuf.h,v
retrieving revision 1.53
diff -u -r1.53 mbuf.h
--- sys/mbuf.h	2000/08/18 16:19:23	1.53
+++ sys/mbuf.h	2000/11/13 21:48:11
@@ -114,6 +114,10 @@
 	int	mh_len;			/* amount of data in this mbuf */
 	short	mh_type;		/* type of data in this mbuf */
 	short	mh_flags;		/* flags; see below */
+	union   {
+		int mh_info;
+		void *mh_infop;
+	} mh_info;			/* link-layer specific info */
 };
 
 /* record/packet header in first mbuf of chain; valid if M_PKTHDR set */
@@ -159,6 +163,8 @@
 #define	m_data		m_hdr.mh_data
 #define	m_type		m_hdr.mh_type
 #define	m_flags		m_hdr.mh_flags
+#define m_info		m_hdr.mh_info.mh_info
+#define m_infop		m_hdr.mh_info.mh_infop
 #define	m_nextpkt	m_hdr.mh_nextpkt
 #define	m_act		m_nextpkt
 #define	m_pkthdr	M_dat.MH.MH_pkthdr
Index: dev/pci/if_ti.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/pci/if_ti.c,v
retrieving revision 1.12
diff -u -r1.12 if_ti.c
--- dev/pci/if_ti.c	2000/11/12 18:32:43	1.12
+++ dev/pci/if_ti.c	2000/11/13 21:48:11
@@ -81,6 +81,7 @@
  */
 
 #include "bpfilter.h"
+#include "vlan.h"
 #include "opt_inet.h"
 #include "opt_ns.h"
 
@@ -105,6 +106,10 @@
 #include <net/bpf.h>
 #endif
 
+#if NVLAN > 0
+#include <net/if_vlanvar.h>
+#endif
+
 #ifdef INET
 #include <netinet/in.h>
 #include <netinet/if_inarp.h>
@@ -1806,16 +1829,34 @@
 	/*
 	 * We can support 802.1Q VLAN-sized frames.
 	 */
-	sc->ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
+	sc->ethercom.ec_capabilities |=
+	    ETHERCAP_VLAN_MTU | ETHERCAP_VLAN_HWTAGGING;
 
 	/* Set up ifmedia support. */
 	ifmedia_init(&sc->ifmedia, IFM_IMASK, ti_ifmedia_upd, ti_ifmedia_sts);
@@ -1953,13 +1994,13 @@
 			m->m_flags |= M_HWCKSUM;
 #endif
 
-#if NVLAN > 0 /* XXX NetBSD: broken because m points to ether pkt */
+#if NVLAN > 0 
 		/*
 		 * If we received a packet with a vlan tag, pass it
 		 * to vlan_input() instead of ether_input().
 		 */
 		if (have_tag) {
-			vlan_input_tag(eh, m, vlan_tag);
+			vlan_input_tag(ifp, m, vlan_tag);
 			have_tag = vlan_tag = 0;
 			continue;
 		}
@@ -2102,15 +2143,7 @@
 	struct txdmamap_pool_entry *dma;
 	bus_dmamap_t dmamap;
 	int error, i;
-#if NVLAN > 0
-	struct ifvlan		*ifv = NULL;
 
-	if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) &&
-	    m_head->m_pkthdr.rcvif != NULL &&
-	    m_head->m_pkthdr.rcvif->if_type == IFT_8021_VLAN)
-		ifv = m_head->m_pkthdr.rcvif->if_softc;
-#endif
-
 	dma = SIMPLEQ_FIRST(&sc->txdma_list);
 	if (dma == NULL) {
 		return ENOMEM;
@@ -2158,9 +2191,9 @@
 			f->ti_len = dmamap->dm_segs[i].ds_len;
 			f->ti_flags = 0;
 #if NVLAN > 0
-			if (ifv != NULL) {
+			if (m_head->m_flags & M_VLAN1Q) {
 				f->ti_flags |= TI_BDFLAG_VLAN_TAG;
-				f->ti_vlan_tag = ifv->ifv_tag;
+				f->ti_vlan_tag = m_head->m_info;
 			} else {
 				f->ti_vlan_tag = 0;
 			}
Index: net/if_ether.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_ether.h,v
retrieving revision 1.20
diff -u -r1.20 if_ether.h
--- net/if_ether.h	2000/10/11 16:53:41	1.20
+++ net/if_ether.h	2000/11/13 21:48:23
@@ -98,6 +98,7 @@
  * Ethernet-specific mbuf flags.
  */
 #define	M_HASFCS	M_LINK0		/* FCS included at end of frame */
+#define	M_VLAN1Q	M_LINK1		/* has 802.1q tag */
 
 #ifdef _KERNEL
 /*
@@ -154,7 +155,7 @@
 };
 
 #define	ETHERCAP_VLAN_MTU	0x00000001	/* VLAN-compatible MTU */
-#define	ETHERCAP_VLAN_TAGGING	0x00000002	/* VLAN tag support */
+#define	ETHERCAP_VLAN_HWTAGGING	0x00000002	/* hardware VLAN tag support */
 
 #ifdef	_KERNEL
 extern u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN];
Index: net/if_vlan.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlan.c,v
retrieving revision 1.21
diff -u -r1.21 if_vlan.c
--- net/if_vlan.c	2000/11/12 19:39:42	1.21
+++ net/if_vlan.c	2000/11/13 21:48:28
@@ -182,6 +182,7 @@
 static int	vlan_ioctl(struct ifnet *, u_long, caddr_t);
 static void	vlan_start(struct ifnet *);
 static void	vlan_unconfig(struct ifnet *);
+static void	vlan_input_ifv(struct ifnet *, struct ifvlan *, struct mbuf *);
 
 void		vlanattach(int);
 
@@ -672,6 +673,7 @@
 {
 	struct ifvlan *ifv = ifp->if_softc;
 	struct ifnet *p = ifv->ifv_p;
+	struct ethercom *ec = (void *) ifv->ifv_p;
 	struct mbuf *m;
 
 	ifp->if_flags |= IFF_OACTIVE;
@@ -685,51 +687,59 @@
 		if (ifp->if_bpf)
 			bpf_mtap(ifp->if_bpf, m);
 #endif
-
 		/*
-		 * XXX Should handle the case where the underlying hardware
-		 * interface can do VLAN tag insertion itself.
+		 * If the parent can insert the tag itself, just mark
+		 * the tag in the mbuf header.
 		 */
-		M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
-		if (m == NULL) {
-			printf("%s: unable to prepend encap header",
-			    ifv->ifv_p->if_xname);
-			ifp->if_oerrors++;
-			continue;
-		}
-
-		switch (p->if_type) {
-		case IFT_ETHER:
-		    {
-			struct ether_vlan_header *evl;
-
-			if (m->m_len < sizeof(struct ether_vlan_header) &&
-			    (m = m_pullup(m,
-			     sizeof(struct ether_vlan_header))) == NULL) {
-				printf("%s: unable to pullup encap header",
+		if (ec->ec_capabilities & ETHERCAP_VLAN_HWTAGGING) {
+			m->m_flags |= M_VLAN1Q;
+			m->m_info = ifv->ifv_tag;
+		} else {
+			/*
+			 * insert the tag ourselve
+			 */
+			M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
+			if (m == NULL) {
+				printf("%s: unable to prepend encap header",
 				    ifv->ifv_p->if_xname);
 				ifp->if_oerrors++;
 				continue;
 			}
 
-			/*
-			 * Transform the Ethernet header into an Ethernet
-			 * header with 802.1Q encapsulation.
-			 */
-			memmove(mtod(m, caddr_t),
-			    mtod(m, caddr_t) + ifv->ifv_encaplen, 
-			    sizeof(struct ether_header));
-			evl = mtod(m, struct ether_vlan_header *);
-			evl->evl_proto = evl->evl_encap_proto;
-			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-			evl->evl_tag = htons(ifv->ifv_tag);
-			break;
-		    }
+			switch (p->if_type) {
+			case IFT_ETHER:
+			    {
+				struct ether_vlan_header *evl;
+
+				if (m->m_len < sizeof(struct ether_vlan_header))
+					m = m_pullup(m,
+					    sizeof(struct ether_vlan_header));
+				if (m == NULL) {
+					printf("%s: unable to pullup encap "
+					    "header", ifv->ifv_p->if_xname);
+					ifp->if_oerrors++;
+					continue;
+				}
+
+				/*
+				 * Transform the Ethernet header into an
+				 * Ethernet header with 802.1Q encapsulation.
+				 */
+				memmove(mtod(m, caddr_t),
+				    mtod(m, caddr_t) + ifv->ifv_encaplen, 
+				    sizeof(struct ether_header));
+				evl = mtod(m, struct ether_vlan_header *);
+				evl->evl_proto = evl->evl_encap_proto;
+				evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
+				evl->evl_tag = htons(ifv->ifv_tag);
+				break;
+			    }
 
 #ifdef DIAGNOSTIC
-		default:
-			panic("vlan_start: impossible");
+			default:
+				panic("vlan_start: impossible");
 #endif
+			}
 		}
 
 		/*
@@ -745,9 +755,9 @@
 		}
 	
 		IF_ENQUEUE(&p->if_snd, m);
+		ifp->if_opackets++;
 		if ((p->if_flags & IFF_OACTIVE) == 0) {
 			(*p->if_start)(p);
-			ifp->if_opackets++;
 		}
 	}
 
@@ -803,6 +813,38 @@
 		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
 			break;
 
+
+	/*
+	 * Now, remove the encapsulation header.  The original
+	 * header has already been fixed up above.
+	 */
+	if (ifv) {
+		memmove(mtod(m, caddr_t) + ifv->ifv_encaplen, mtod(m, caddr_t),
+		    ifv->ifv_encaplen);
+		m_adj(m, ifv->ifv_encaplen);
+	}
+	vlan_input_ifv(ifp, ifv, m);
+}
+
+/*
+ * vlan_input() for interfaces with hardware tagged queuing support
+ */
+
+void
+vlan_input_tag(struct ifnet *ifp, struct mbuf *m, u_int tag)
+{
+	struct ifvlan *ifv;
+
+	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
+	     ifv = LIST_NEXT(ifv, ifv_list))
+		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
+			break;
+	vlan_input_ifv(ifp, ifv, m);
+}
+
+void
+vlan_input_ifv(struct ifnet *ifp, struct ifvlan *ifv, struct mbuf *m)
+{
 	if (ifv == NULL ||
 	    (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
 	     (IFF_UP|IFF_RUNNING)) {
@@ -810,15 +852,6 @@
 		ifp->if_noproto++;
 		return;
 	}
-
-	/*
-	 * Now, remove the encapsulation header.  The original
-	 * header has already been fixed up above.
-	 */
-	memmove(mtod(m, caddr_t) + ifv->ifv_encaplen, mtod(m, caddr_t),
-	    ifv->ifv_encaplen);
-	m_adj(m, ifv->ifv_encaplen);
-
 	m->m_pkthdr.rcvif = &ifv->ifv_if;
 	ifv->ifv_if.if_ipackets++;
 
Index: net/if_vlanvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if_vlanvar.h,v
retrieving revision 1.4
diff -u -r1.4 if_vlanvar.h
--- net/if_vlanvar.h	2000/10/03 23:50:52	1.4
+++ net/if_vlanvar.h	2000/11/13 21:48:28
@@ -92,6 +92,7 @@
 
 #ifdef _KERNEL
 void	vlan_input(struct ifnet *, struct mbuf *);
+void	vlan_input_tag(struct ifnet *, struct mbuf *, u_int);
 void	vlan_ifdetach(struct ifnet *);
 #endif	/* _KERNEL */
 

--zhXaljGHf11kAtnf--