Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 8 Jul 2016 15:42:40 GMT
From:      vincenzo@FreeBSD.org
To:        svn-soc-all@FreeBSD.org
Subject:   socsvn commit: r305818 - soc2016/vincenzo/head/sys/dev/netmap
Message-ID:  <201607081542.u68Fge5j005917@socsvn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: vincenzo
Date: Fri Jul  8 15:42:40 2016
New Revision: 305818
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=305818

Log:
   freebsd: ptnet_drain_transmit_queue: add virtio-net-header support

Modified:
  soc2016/vincenzo/head/sys/dev/netmap/if_ptnet.c

Modified: soc2016/vincenzo/head/sys/dev/netmap/if_ptnet.c
==============================================================================
--- soc2016/vincenzo/head/sys/dev/netmap/if_ptnet.c	Fri Jul  8 13:29:47 2016	(r305817)
+++ soc2016/vincenzo/head/sys/dev/netmap/if_ptnet.c	Fri Jul  8 15:42:40 2016	(r305818)
@@ -80,6 +80,7 @@
 #include <dev/netmap/netmap_kern.h>
 #include <dev/netmap/netmap_virt.h>
 #include <dev/netmap/netmap_mem2.h>
+#include <dev/virtio/network/virtio_net.h>
 
 #ifndef PTNET_CSB_ALLOC
 #error "No support for on-device CSB"
@@ -224,7 +225,12 @@
 #define PTNET_BUF_RING_SIZE	4096
 #define PTNET_RX_BUDGET		512
 #define PTNET_TX_BATCH		64
+#define PTNET_HDR_SIZE		sizeof(struct virtio_net_hdr_mrg_rxbuf)
 
+#define PTNET_CSUM_OFFLOAD	(CSUM_TCP | CSUM_UDP | CSUM_SCTP)
+#define PTNET_CSUM_OFFLOAD_IPV6	(CSUM_TCP_IPV6 | CSUM_UDP_IPV6 |\
+				 CSUM_SCTP_IPV6)
+#define PTNET_ALL_OFFLOAD	(CSUM_TSO | PTNET_CSUM_OFFLOAD | CSUM_TSO)
 
 static int
 ptnet_attach(device_t dev)
@@ -1140,6 +1146,150 @@
 	ptnet_rx_eof(pq);
 }
 
+/* The following three functions are taken from the virtio-net driver, but
+ * the same functionality applies to the ptnet driver.
+ * As a temporary solution, I copied this code from virtio-net and I started
+ * to generalize it (taking away driver-specific statistic accounting),
+ * making as little modifications as possible.
+ * In the future we need to share these functions between virtio-net and ptnet.
+ */
+static int
+ptnet_tx_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start)
+{
+	struct ether_vlan_header *evh;
+	int offset;
+
+	evh = mtod(m, struct ether_vlan_header *);
+	if (evh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
+		/* BMV: We should handle nested VLAN tags too. */
+		*etype = ntohs(evh->evl_proto);
+		offset = sizeof(struct ether_vlan_header);
+	} else {
+		*etype = ntohs(evh->evl_encap_proto);
+		offset = sizeof(struct ether_header);
+	}
+
+	switch (*etype) {
+#if defined(INET)
+	case ETHERTYPE_IP: {
+		struct ip *ip, iphdr;
+		if (__predict_false(m->m_len < offset + sizeof(struct ip))) {
+			m_copydata(m, offset, sizeof(struct ip),
+			    (caddr_t) &iphdr);
+			ip = &iphdr;
+		} else
+			ip = (struct ip *)(m->m_data + offset);
+		*proto = ip->ip_p;
+		*start = offset + (ip->ip_hl << 2);
+		break;
+	}
+#endif
+#if defined(INET6)
+	case ETHERTYPE_IPV6:
+		*proto = -1;
+		*start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto);
+		/* Assert the network stack sent us a valid packet. */
+		KASSERT(*start > offset,
+		    ("%s: mbuf %p start %d offset %d proto %d", __func__, m,
+		    *start, offset, *proto));
+		break;
+#endif
+	default:
+		/* Here we should increment the tx_csum_bad_ethtype counter. */
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static int
+ptnet_tx_offload_tso(struct ifnet *ifp, struct mbuf *m, int eth_type,
+		     int offset, bool allow_ecn, struct virtio_net_hdr *hdr)
+{
+	static struct timeval lastecn;
+	static int curecn;
+	struct tcphdr *tcp, tcphdr;
+
+	if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) {
+		m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr);
+		tcp = &tcphdr;
+	} else
+		tcp = (struct tcphdr *)(m->m_data + offset);
+
+	hdr->hdr_len = offset + (tcp->th_off << 2);
+	hdr->gso_size = m->m_pkthdr.tso_segsz;
+	hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 :
+	    VIRTIO_NET_HDR_GSO_TCPV6;
+
+	if (tcp->th_flags & TH_CWR) {
+		/*
+		 * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In FreeBSD,
+		 * ECN support is not on a per-interface basis, but globally via
+		 * the net.inet.tcp.ecn.enable sysctl knob. The default is off.
+		 */
+		if (!allow_ecn) {
+			if (ppsratecheck(&lastecn, &curecn, 1))
+				if_printf(ifp,
+				    "TSO with ECN not negotiated with host\n");
+			return (ENOTSUP);
+		}
+		hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN;
+	}
+
+	/* Here we should increment tx_tso counter. */
+
+	return (0);
+}
+
+static struct mbuf *
+ptnet_tx_offload(struct ifnet *ifp, struct mbuf *m, bool allow_ecn,
+		 struct virtio_net_hdr *hdr)
+{
+	int flags, etype, csum_start, proto, error;
+
+	flags = m->m_pkthdr.csum_flags;
+
+	error = ptnet_tx_offload_ctx(m, &etype, &proto, &csum_start);
+	if (error)
+		goto drop;
+
+	if ((etype == ETHERTYPE_IP && flags & PTNET_CSUM_OFFLOAD) ||
+	    (etype == ETHERTYPE_IPV6 && flags & PTNET_CSUM_OFFLOAD_IPV6)) {
+		/*
+		 * We could compare the IP protocol vs the CSUM_ flag too,
+		 * but that really should not be necessary.
+		 */
+		hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
+		hdr->csum_start = csum_start;
+		hdr->csum_offset = m->m_pkthdr.csum_data;
+		/* Here we should increment the tx_csum counter. */
+	}
+
+	if (flags & CSUM_TSO) {
+		if (__predict_false(proto != IPPROTO_TCP)) {
+			/* Likely failed to correctly parse the mbuf.
+			 * Here we should increment the tx_tso_not_tcp
+			 * counter. */
+			goto drop;
+		}
+
+		KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM,
+		    ("%s: mbuf %p TSO without checksum offload %#x",
+		    __func__, m, flags));
+
+		error = ptnet_tx_offload_tso(ifp, m, etype, csum_start,
+					     allow_ecn, hdr);
+		if (error)
+			goto drop;
+	}
+
+	return (m);
+
+drop:
+	m_freem(m);
+	return (NULL);
+}
+
 static inline void
 ptnet_sync_tail(struct ptnet_ring *ptring, struct netmap_kring *kring)
 {
@@ -1260,6 +1410,29 @@
 		nmbuf = NMB(na, slot);
 		nmbuf_bytes = 0;
 
+		/* If needed, prepare the virtio-net header at the beginning
+		 * of the first slot. */
+		if (sc->ptfeatures & NET_PTN_FEATURES_VNET_HDR) {
+			/* For performance, we could replace this memset() with
+			 * two 8-bytes-wide writes. */
+			memset(nmbuf, 0, PTNET_HDR_SIZE);
+			if (mhead->m_pkthdr.csum_flags & PTNET_ALL_OFFLOAD) {
+				mhead = ptnet_tx_offload(ifp, mhead, false,
+					    (struct virtio_net_hdr *)nmbuf);
+				if (unlikely(!mhead)) {
+					/* Packet dropped because errors
+					 * occurred while preparing the vnet
+					 * header. Let's go ahead with the next
+					 * packet. */
+					drbr_advance(ifp, pq->bufring);
+					continue;
+				}
+			}
+
+			nmbuf += PTNET_HDR_SIZE;
+			nmbuf_bytes += PTNET_HDR_SIZE;
+		}
+
 		for (mf = mhead; mf; mf = mf->m_next) {
 			uint8_t *mdata = mf->m_data;
 			int mlen = mf->m_len;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607081542.u68Fge5j005917>