svn commit: r308632 - stable/11/sys/dev/hyperv/netvsc
Sepherosa Ziehau
sephe at FreeBSD.org
Mon Nov 14 06:25:23 UTC 2016
Author: sephe
Date: Mon Nov 14 06:25:22 2016
New Revision: 308632
URL: https://svnweb.freebsd.org/changeset/base/308632
Log:
MFC 308166,308167
308166
hyperv/hn: Move TSO packet fixup to an earlier place for if_transmit.
While TSO packet header may be still cache-hot.
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8393
308167
hyperv/hn: Directly fill chimney sending buffer for small packets.
Sponsored by: Microsoft
Differential Revision: https://reviews.freebsd.org/D8394
Modified:
stable/11/sys/dev/hyperv/netvsc/if_hn.c
Directory Properties:
stable/11/ (props changed)
Modified: stable/11/sys/dev/hyperv/netvsc/if_hn.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hn.c Mon Nov 14 06:03:29 2016 (r308631)
+++ stable/11/sys/dev/hyperv/netvsc/if_hn.c Mon Nov 14 06:25:22 2016 (r308632)
@@ -547,6 +547,80 @@ hn_chim_free(struct hn_softc *sc, uint32
atomic_clear_long(&sc->hn_chim_bmap[idx], mask);
}
+#if defined(INET6) || defined(INET)
+/*
+ * NOTE: If this function failed, the m_head would be freed.
+ */
+static __inline struct mbuf *
+hn_tso_fixup(struct mbuf *m_head)
+{
+ struct ether_vlan_header *evl;
+ struct tcphdr *th;
+ int ehlen;
+
+ KASSERT(M_WRITABLE(m_head), ("TSO mbuf not writable"));
+
+#define PULLUP_HDR(m, len) \
+do { \
+ if (__predict_false((m)->m_len < (len))) { \
+ (m) = m_pullup((m), (len)); \
+ if ((m) == NULL) \
+ return (NULL); \
+ } \
+} while (0)
+
+ PULLUP_HDR(m_head, sizeof(*evl));
+ evl = mtod(m_head, struct ether_vlan_header *);
+ if (evl->evl_encap_proto == ntohs(ETHERTYPE_VLAN))
+ ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ else
+ ehlen = ETHER_HDR_LEN;
+
+#ifdef INET
+ if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
+ struct ip *ip;
+ int iphlen;
+
+ PULLUP_HDR(m_head, ehlen + sizeof(*ip));
+ ip = mtodo(m_head, ehlen);
+ iphlen = ip->ip_hl << 2;
+
+ PULLUP_HDR(m_head, ehlen + iphlen + sizeof(*th));
+ th = mtodo(m_head, ehlen + iphlen);
+
+ ip->ip_len = 0;
+ ip->ip_sum = 0;
+ th->th_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(IPPROTO_TCP));
+ }
+#endif
+#if defined(INET6) && defined(INET)
+ else
+#endif
+#ifdef INET6
+ {
+ struct ip6_hdr *ip6;
+
+ PULLUP_HDR(m_head, ehlen + sizeof(*ip6));
+ ip6 = mtodo(m_head, ehlen);
+ if (ip6->ip6_nxt != IPPROTO_TCP) {
+ m_freem(m_head);
+ return (NULL);
+ }
+
+ PULLUP_HDR(m_head, ehlen + sizeof(*ip6) + sizeof(*th));
+ th = mtodo(m_head, ehlen + sizeof(*ip6));
+
+ ip6->ip6_plen = 0;
+ th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
+ }
+#endif
+ return (m_head);
+
+#undef PULLUP_HDR
+}
+#endif /* INET6 || INET */
+
static int
hn_set_rxfilter(struct hn_softc *sc)
{
@@ -1320,15 +1394,28 @@ hn_encap(struct hn_tx_ring *txr, struct
struct mbuf *m_head = *m_head0;
struct rndis_packet_msg *pkt;
uint32_t *pi_data;
+ void *chim = NULL;
int pktlen;
- /*
- * extension points to the area reserved for the
- * rndis_filter_packet, which is placed just after
- * the netvsc_packet (and rppi struct, if present;
- * length is updated later).
- */
pkt = txd->rndis_pkt;
+ if (m_head->m_pkthdr.len + HN_RNDIS_PKT_LEN < txr->hn_chim_size) {
+ /*
+ * This packet is small enough to fit into a chimney sending
+ * buffer. Try allocating one chimney sending buffer now.
+ */
+ txr->hn_tx_chimney_tried++;
+ txd->chim_index = hn_chim_alloc(txr->hn_sc);
+ if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
+ chim = txr->hn_sc->hn_chim +
+ (txd->chim_index * txr->hn_sc->hn_chim_szmax);
+ /*
+ * Directly fill the chimney sending buffer w/ the
+ * RNDIS packet message.
+ */
+ pkt = chim;
+ }
+ }
+
pkt->rm_type = REMOTE_NDIS_PACKET_MSG;
pkt->rm_len = sizeof(*pkt) + m_head->m_pkthdr.len;
pkt->rm_dataoffset = sizeof(*pkt);
@@ -1358,32 +1445,10 @@ hn_encap(struct hn_tx_ring *txr, struct
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
#if defined(INET6) || defined(INET)
- struct ether_vlan_header *eh;
- int ether_len;
-
- /*
- * XXX need m_pullup and use mtodo
- */
- eh = mtod(m_head, struct ether_vlan_header*);
- if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN))
- ether_len = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
- else
- ether_len = ETHER_HDR_LEN;
-
pi_data = hn_rndis_pktinfo_append(pkt, HN_RNDIS_PKT_LEN,
NDIS_LSO2_INFO_SIZE, NDIS_PKTINFO_TYPE_LSO);
#ifdef INET
if (m_head->m_pkthdr.csum_flags & CSUM_IP_TSO) {
- struct ip *ip =
- (struct ip *)(m_head->m_data + ether_len);
- unsigned long iph_len = ip->ip_hl << 2;
- struct tcphdr *th =
- (struct tcphdr *)((caddr_t)ip + iph_len);
-
- ip->ip_len = 0;
- ip->ip_sum = 0;
- th->th_sum = in_pseudo(ip->ip_src.s_addr,
- ip->ip_dst.s_addr, htons(IPPROTO_TCP));
*pi_data = NDIS_LSO2_INFO_MAKEIPV4(0,
m_head->m_pkthdr.tso_segsz);
}
@@ -1393,12 +1458,6 @@ hn_encap(struct hn_tx_ring *txr, struct
#endif
#ifdef INET6
{
- struct ip6_hdr *ip6 = (struct ip6_hdr *)
- (m_head->m_data + ether_len);
- struct tcphdr *th = (struct tcphdr *)(ip6 + 1);
-
- ip6->ip6_plen = 0;
- th->th_sum = in6_cksum_pseudo(ip6, 0, IPPROTO_TCP, 0);
*pi_data = NDIS_LSO2_INFO_MAKEIPV6(0,
m_head->m_pkthdr.tso_segsz);
}
@@ -1429,26 +1488,25 @@ hn_encap(struct hn_tx_ring *txr, struct
pkt->rm_pktinfooffset = hn_rndis_pktmsg_offset(pkt->rm_pktinfooffset);
/*
- * Chimney send, if the packet could fit into one chimney buffer.
+ * Fast path: Chimney sending.
*/
- if (pkt->rm_len < txr->hn_chim_size) {
- txr->hn_tx_chimney_tried++;
- txd->chim_index = hn_chim_alloc(txr->hn_sc);
- if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) {
- uint8_t *dest = txr->hn_sc->hn_chim +
- (txd->chim_index * txr->hn_sc->hn_chim_szmax);
-
- memcpy(dest, pkt, pktlen);
- dest += pktlen;
- m_copydata(m_head, 0, m_head->m_pkthdr.len, dest);
-
- txd->chim_size = pkt->rm_len;
- txr->hn_gpa_cnt = 0;
- txr->hn_tx_chimney++;
- txr->hn_sendpkt = hn_txpkt_chim;
- goto done;
- }
- }
+ if (chim != NULL) {
+ KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID,
+ ("chimney buffer is not used"));
+ KASSERT(pkt == chim, ("RNDIS pkt not in chimney buffer"));
+
+ m_copydata(m_head, 0, m_head->m_pkthdr.len,
+ ((uint8_t *)chim) + pktlen);
+
+ txd->chim_size = pkt->rm_len;
+ txr->hn_gpa_cnt = 0;
+ txr->hn_tx_chimney++;
+ txr->hn_sendpkt = hn_txpkt_chim;
+ goto done;
+ }
+ KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID,
+ ("chimney buffer is used"));
+ KASSERT(pkt == txd->rndis_pkt, ("RNDIS pkt not in txdesc"));
error = hn_txdesc_dmamap_load(txr, txd, &m_head, segs, &nsegs);
if (error) {
@@ -3280,6 +3338,16 @@ hn_start_locked(struct hn_tx_ring *txr,
return 1;
}
+#if defined(INET6) || defined(INET)
+ if (m_head->m_pkthdr.csum_flags & CSUM_TSO) {
+ m_head = hn_tso_fixup(m_head);
+ if (__predict_false(m_head == NULL)) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ continue;
+ }
+ }
+#endif
+
txd = hn_txdesc_get(txr);
if (txd == NULL) {
txr->hn_no_txdescs++;
@@ -3442,6 +3510,20 @@ hn_transmit(struct ifnet *ifp, struct mb
struct hn_tx_ring *txr;
int error, idx = 0;
+#if defined(INET6) || defined(INET)
+ /*
+ * Perform TSO packet header fixup now, since the TSO
+ * packet header should be cache-hot.
+ */
+ if (m->m_pkthdr.csum_flags & CSUM_TSO) {
+ m = hn_tso_fixup(m);
+ if (__predict_false(m == NULL)) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return EIO;
+ }
+ }
+#endif
+
/*
* Select the TX ring based on flowid
*/
More information about the svn-src-stable
mailing list