Date: Fri, 8 Jul 2016 15:44:54 GMT From: vincenzo@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r305832 - soc2016/vincenzo/head/sys/dev/netmap Message-ID: <201607081544.u68FisbQ008863@socsvn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: vincenzo Date: Fri Jul 8 15:44:54 2016 New Revision: 305832 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=305832 Log: freebsd: ptnet_rx_eof: process checksumming offloadings 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 15:44:45 2016 (r305831) +++ soc2016/vincenzo/head/sys/dev/netmap/if_ptnet.c Fri Jul 8 15:44:54 2016 (r305832) @@ -63,6 +63,7 @@ #include <netinet6/ip6_var.h> #include <netinet/udp.h> #include <netinet/tcp.h> +#include <netinet/sctp.h> #include <machine/bus.h> #include <machine/resource.h> @@ -1495,22 +1496,6 @@ return 0; } -/* This function should be shared with the virtio-net driver. */ -static void -ptnet_vlan_tag_remove(struct mbuf *m) -{ - struct ether_vlan_header *evh; - - evh = mtod(m, struct ether_vlan_header *); - m->m_pkthdr.ether_vtag = ntohs(evh->evl_tag); - m->m_flags |= M_VLANTAG; - - /* Strip the 802.1Q header. */ - bcopy((char *) evh, (char *) evh + ETHER_VLAN_ENCAP_LEN, - ETHER_HDR_LEN - ETHER_TYPE_LEN); - m_adj(m, ETHER_VLAN_ENCAP_LEN); -} - static int ptnet_transmit(struct ifnet *ifp, struct mbuf *m) { @@ -1551,6 +1536,180 @@ return ptnet_drain_transmit_queue(pq); } +/* This function should be shared with the virtio-net driver. */ +static void +ptnet_vlan_tag_remove(struct mbuf *m) +{ + struct ether_vlan_header *evh; + + evh = mtod(m, struct ether_vlan_header *); + m->m_pkthdr.ether_vtag = ntohs(evh->evl_tag); + m->m_flags |= M_VLANTAG; + + /* Strip the 802.1Q header. */ + bcopy((char *) evh, (char *) evh + ETHER_VLAN_ENCAP_LEN, + ETHER_HDR_LEN - ETHER_TYPE_LEN); + m_adj(m, ETHER_VLAN_ENCAP_LEN); +} + +/* + * Use the checksum offset in the VirtIO header to set the + * correct CSUM_* flags. + */ +static int +ptnet_rx_csum_by_offset(struct mbuf *m, uint16_t eth_type, int ip_start, + struct virtio_net_hdr *hdr) +{ +#if defined(INET) || defined(INET6) + int offset = hdr->csum_start + hdr->csum_offset; +#endif + + /* Only do a basic sanity check on the offset. */ + switch (eth_type) { +#if defined(INET) + case ETHERTYPE_IP: + if (__predict_false(offset < ip_start + sizeof(struct ip))) + return (1); + break; +#endif +#if defined(INET6) + case ETHERTYPE_IPV6: + if (__predict_false(offset < ip_start + sizeof(struct ip6_hdr))) + return (1); + break; +#endif + default: + /* Here we should increment the rx_csum_bad_ethtype counter. */ + return (1); + } + + /* + * Use the offset to determine the appropriate CSUM_* flags. This is + * a bit dirty, but we can get by with it since the checksum offsets + * happen to be different. We assume the host host does not do IPv4 + * header checksum offloading. + */ + switch (hdr->csum_offset) { + case offsetof(struct udphdr, uh_sum): + case offsetof(struct tcphdr, th_sum): + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + break; + case offsetof(struct sctphdr, checksum): + m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; + break; + default: + /* Here we should increment the rx_csum_bad_offset counter. */ + return (1); + } + + return (0); +} + +static int +ptnet_rx_csum_by_parse(struct mbuf *m, uint16_t eth_type, int ip_start, + struct virtio_net_hdr *hdr) +{ + int offset, proto; + + switch (eth_type) { +#if defined(INET) + case ETHERTYPE_IP: { + struct ip *ip; + if (__predict_false(m->m_len < ip_start + sizeof(struct ip))) + return (1); + ip = (struct ip *)(m->m_data + ip_start); + proto = ip->ip_p; + offset = ip_start + (ip->ip_hl << 2); + break; + } +#endif +#if defined(INET6) + case ETHERTYPE_IPV6: + if (__predict_false(m->m_len < ip_start + + sizeof(struct ip6_hdr))) + return (1); + offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto); + if (__predict_false(offset < 0)) + return (1); + break; +#endif + default: + /* Here we should increment the rx_csum_bad_ethtype counter. */ + return (1); + } + + switch (proto) { + case IPPROTO_TCP: + if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + break; + case IPPROTO_UDP: + if (__predict_false(m->m_len < offset + sizeof(struct udphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xFFFF; + break; + case IPPROTO_SCTP: + if (__predict_false(m->m_len < offset + sizeof(struct sctphdr))) + return (1); + m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; + break; + default: + /* + * For the remaining protocols, FreeBSD does not support + * checksum offloading, so the checksum will be recomputed. + */ +#if 0 + if_printf(ifp, "cksum offload of unsupported " + "protocol eth_type=%#x proto=%d csum_start=%d " + "csum_offset=%d\n", __func__, eth_type, proto, + hdr->csum_start, hdr->csum_offset); +#endif + break; + } + + return (0); +} + +/* + * Set the appropriate CSUM_* flags. Unfortunately, the information + * provided is not directly useful to us. The VirtIO header gives the + * offset of the checksum, which is all Linux needs, but this is not + * how FreeBSD does things. We are forced to peek inside the packet + * a bit. + * + * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD + * could accept the offsets and let the stack figure it out. + */ +static int +ptnet_rx_csum(struct mbuf *m, struct virtio_net_hdr *hdr) +{ + struct ether_header *eh; + struct ether_vlan_header *evh; + uint16_t eth_type; + int offset, error; + + eh = mtod(m, struct ether_header *); + eth_type = ntohs(eh->ether_type); + if (eth_type == ETHERTYPE_VLAN) { + /* BMV: We should handle nested VLAN tags too. */ + evh = mtod(m, struct ether_vlan_header *); + eth_type = ntohs(evh->evl_proto); + offset = sizeof(struct ether_vlan_header); + } else + offset = sizeof(struct ether_header); + + if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) + error = ptnet_rx_csum_by_offset(m, eth_type, offset, hdr); + else + error = ptnet_rx_csum_by_parse(m, eth_type, offset, hdr); + + return (error); +} + static unsigned int ptnet_rx_discard(struct netmap_kring *kring, unsigned int head) { @@ -1764,6 +1923,11 @@ } } + if (have_vnet_hdr && (vh->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM + | VIRTIO_NET_HDR_F_DATA_VALID))) { + ptnet_rx_csum(mhead, vh); + } + PTNET_Q_UNLOCK(pq); (*ifp->if_input)(ifp, mhead); PTNET_Q_LOCK(pq);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607081544.u68FisbQ008863>