From af8fe8f1696c3dbd9e6fc02d360329685fe20b20 Mon Sep 17 00:00:00 2001 From: Zbigniew Bodek Date: Thu, 25 Feb 2016 14:29:57 +0000 Subject: [PATCH] Introduce HW TSO support for VNIC This feature was added in Pass2.0. Significantly improves VNIC's TCP performance on Tx. Reviewed by: wma Obtained from: Semihalf Sponsored by: Cavium Differential Revision: https://reviews.freebsd.org/D5424 --- sys/dev/vnic/nic.h | 1 + sys/dev/vnic/nicvf_main.c | 17 ++++++- sys/dev/vnic/nicvf_queues.c | 93 ++++++++++++++++++++++--------------- sys/dev/vnic/nicvf_queues.h | 7 ++- sys/dev/vnic/q_struct.h | 30 ++++++------ 5 files changed, 94 insertions(+), 54 deletions(-) diff --git a/sys/dev/vnic/nic.h b/sys/dev/vnic/nic.h index d969e635fbe8..a57ef1f4f7e3 100644 --- a/sys/dev/vnic/nic.h +++ b/sys/dev/vnic/nic.h @@ -292,6 +292,7 @@ struct nicvf { uint8_t max_queues; struct resource *reg_base; boolean_t link_up; + boolean_t hw_tso; uint8_t duplex; uint32_t speed; uint8_t cpi_alg; diff --git a/sys/dev/vnic/nicvf_main.c b/sys/dev/vnic/nicvf_main.c index eeca94ff7aeb..4625846fad75 100644 --- a/sys/dev/vnic/nicvf_main.c +++ b/sys/dev/vnic/nicvf_main.c @@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include @@ -194,6 +195,9 @@ nicvf_attach(device_t dev) nic->pnicvf = nic; NICVF_CORE_LOCK_INIT(nic); + /* Enable HW TSO on Pass2 */ + if (!pass1_silicon(dev)) + nic->hw_tso = TRUE; rid = VNIC_VF_REG_RID; nic->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, @@ -356,6 +360,14 @@ nicvf_setup_ifnet(struct nicvf *nic) /* Set the default values */ if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0); if_setcapabilitiesbit(ifp, IFCAP_LRO, 0); + if (nic->hw_tso) { + /* TSO */ + if_setcapabilitiesbit(ifp, IFCAP_TSO4, 0); + /* TSO parameters */ + ifp->if_hw_tsomax = NICVF_TSO_MAXSIZE; + ifp->if_hw_tsomaxsegcount = NICVF_TSO_NSEGS; + ifp->if_hw_tsomaxsegsize = MCLBYTES; + } /* IP/TCP/UDP HW checksums */ if_setcapabilitiesbit(ifp, IFCAP_HWCSUM, 0); if_setcapabilitiesbit(ifp, IFCAP_HWSTATS, 0); @@ -364,7 +376,8 @@ nicvf_setup_ifnet(struct nicvf *nic) */ if_clearhwassist(ifp); if_sethwassistbits(ifp, (CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_SCTP), 0); - + if (nic->hw_tso) + if_sethwassistbits(ifp, (CSUM_TSO), 0); if_setcapenable(ifp, if_getcapabilities(ifp)); return (0); @@ -513,6 +526,8 @@ nicvf_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) ifp->if_capenable ^= IFCAP_TXCSUM; if (mask & IFCAP_RXCSUM) ifp->if_capenable ^= IFCAP_RXCSUM; + if ((mask & IFCAP_TSO4) && nic->hw_tso) + ifp->if_capenable ^= IFCAP_TSO4; if (mask & IFCAP_LRO) { /* * Lock the driver for a moment to avoid diff --git a/sys/dev/vnic/nicvf_queues.c b/sys/dev/vnic/nicvf_queues.c index 6e85be8cad5f..1da61ea663af 100644 --- a/sys/dev/vnic/nicvf_queues.c +++ b/sys/dev/vnic/nicvf_queues.c @@ -1070,8 +1070,8 @@ nicvf_init_snd_queue(struct nicvf *nic, struct snd_queue *sq, int q_len, BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filtfunc, filtfuncarg */ - NICVF_TXBUF_MAXSIZE, /* maxsize */ - NICVF_TXBUF_NSEGS, /* nsegments */ + NICVF_TSO_MAXSIZE, /* maxsize */ + NICVF_TSO_NSEGS, /* nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockfuncarg */ @@ -1727,14 +1727,18 @@ static __inline int nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, int subdesc_cnt, struct mbuf *mbuf, int len) { + struct nicvf *nic; struct sq_hdr_subdesc *hdr; struct ether_vlan_header *eh; #ifdef INET struct ip *ip; + struct tcphdr *th; #endif uint16_t etype; int ehdrlen, iphlen, poff; + nic = sq->nic; + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); sq->snd_buff[qentry].mbuf = mbuf; @@ -1746,18 +1750,25 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, hdr->subdesc_cnt = subdesc_cnt; hdr->tot_len = len; - if (mbuf->m_pkthdr.csum_flags != 0) { - hdr->csum_l3 = 1; /* Enable IP csum calculation */ - - eh = mtod(mbuf, struct ether_vlan_header *); - if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { - ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; - etype = ntohs(eh->evl_proto); - } else { - ehdrlen = ETHER_HDR_LEN; - etype = ntohs(eh->evl_encap_proto); - } + eh = mtod(mbuf, struct ether_vlan_header *); + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + etype = ntohs(eh->evl_proto); + } else { + ehdrlen = ETHER_HDR_LEN; + etype = ntohs(eh->evl_encap_proto); + } + switch (etype) { +#ifdef INET6 + case ETHERTYPE_IPV6: + /* ARM64TODO: Add support for IPv6 */ + hdr->csum_l3 = 0; + sq->snd_buff[qentry].mbuf = NULL; + return (ENXIO); +#endif +#ifdef INET + case ETHERTYPE_IP: if (mbuf->m_len < ehdrlen + sizeof(struct ip)) { mbuf = m_pullup(mbuf, ehdrlen + sizeof(struct ip)); sq->snd_buff[qentry].mbuf = mbuf; @@ -1765,21 +1776,13 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, return (ENOBUFS); } - switch (etype) { -#ifdef INET6 - case ETHERTYPE_IPV6: - /* ARM64TODO: Add support for IPv6 */ - hdr->csum_l3 = 0; - sq->snd_buff[qentry].mbuf = NULL; - return (ENXIO); -#endif -#ifdef INET - case ETHERTYPE_IP: - ip = (struct ip *)(mbuf->m_data + ehdrlen); - ip->ip_sum = 0; - iphlen = ip->ip_hl << 2; - poff = ehdrlen + iphlen; + ip = (struct ip *)(mbuf->m_data + ehdrlen); + ip->ip_sum = 0; + iphlen = ip->ip_hl << 2; + poff = ehdrlen + iphlen; + if (mbuf->m_pkthdr.csum_flags != 0) { + hdr->csum_l3 = 1; /* Enable IP csum calculation */ switch (ip->ip_p) { case IPPROTO_TCP: if ((mbuf->m_pkthdr.csum_flags & CSUM_TCP) == 0) @@ -1820,17 +1823,28 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, default: break; } - break; -#endif - default: - hdr->csum_l3 = 0; - return (0); + hdr->l3_offset = ehdrlen; + hdr->l4_offset = ehdrlen + iphlen; } - hdr->l3_offset = ehdrlen; - hdr->l4_offset = ehdrlen + iphlen; - } else + if ((mbuf->m_pkthdr.tso_segsz != 0) && nic->hw_tso) { + /* + * Extract ip again as m_data could have been modified. + */ + ip = (struct ip *)(mbuf->m_data + ehdrlen); + th = (struct tcphdr *)((caddr_t)ip + iphlen); + + hdr->tso = 1; + hdr->tso_start = ehdrlen + iphlen + (th->th_off * 4); + hdr->tso_max_paysize = mbuf->m_pkthdr.tso_segsz; + hdr->inner_l3_offset = ehdrlen - 2; + nic->drv_stats.tx_tso++; + } + break; +#endif + default: hdr->csum_l3 = 0; + } return (0); } @@ -1859,10 +1873,11 @@ int nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) { bus_dma_segment_t segs[256]; + struct nicvf *nic; struct snd_buff *snd_buff; size_t seg; int nsegs, qentry; - int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT - 1; + int subdesc_cnt; int err; NICVF_TX_LOCK_ASSERT(sq); @@ -1880,7 +1895,11 @@ nicvf_tx_mbuf_locked(struct snd_queue *sq, struct mbuf *mbuf) } /* Set how many subdescriptors is required */ - subdesc_cnt += nsegs; + nic = sq->nic; + if (mbuf->m_pkthdr.tso_segsz != 0 && nic->hw_tso) + subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; + else + subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT + nsegs - 1; if (subdesc_cnt > sq->free_cnt) { /* ARM64TODO: Add mbuf defragmentation if we lack descriptors */ diff --git a/sys/dev/vnic/nicvf_queues.h b/sys/dev/vnic/nicvf_queues.h index 09dc447b552f..8a27880e0418 100644 --- a/sys/dev/vnic/nicvf_queues.h +++ b/sys/dev/vnic/nicvf_queues.h @@ -131,10 +131,13 @@ #define NICVF_RCV_BUF_ALIGN_LEN(addr) \ (NICVF_ALIGNED_ADDR((addr), NICVF_RCV_BUF_ALIGN_BYTES) - (addr)) -#define NICVF_TXBUF_MAXSIZE 9212 /* Total max payload without TSO */ +#define NICVF_TXBUF_MAXSIZE NIC_HW_MAX_FRS /* Total max payload without TSO */ #define NICVF_TXBUF_NSEGS 256 /* Single command is at most 256 buffers (hdr + 255 subcmds) */ - +/* TSO-related definitions */ +#define NICVF_TSO_MAXSIZE IP_MAXPACKET +#define NICVF_TSO_NSEGS NICVF_TXBUF_NSEGS +#define NICVF_TSO_HEADER_SIZE 128 /* Queue enable/disable */ #define NICVF_SQ_EN (1UL << 19) diff --git a/sys/dev/vnic/q_struct.h b/sys/dev/vnic/q_struct.h index 471cc4fada49..e9867ff7e93c 100644 --- a/sys/dev/vnic/q_struct.h +++ b/sys/dev/vnic/q_struct.h @@ -565,25 +565,28 @@ struct sq_hdr_subdesc { uint64_t subdesc_cnt:8; uint64_t csum_l4:2; uint64_t csum_l3:1; - uint64_t rsvd0:5; + uint64_t csum_inner_l4:2; + uint64_t csum_inner_l3:1; + uint64_t rsvd0:2; uint64_t l4_offset:8; uint64_t l3_offset:8; uint64_t rsvd1:4; uint64_t tot_len:20; /* W0 */ - uint64_t tso_sdc_cont:8; - uint64_t tso_sdc_first:8; - uint64_t tso_l4_offset:8; - uint64_t tso_flags_last:12; - uint64_t tso_flags_first:12; - uint64_t rsvd2:2; + uint64_t rsvd2:24; + uint64_t inner_l4_offset:8; + uint64_t inner_l3_offset:8; + uint64_t tso_start:8; + uint64_t rsvd3:2; uint64_t tso_max_paysize:14; /* W1 */ #elif defined(__LITTLE_ENDIAN_BITFIELD) uint64_t tot_len:20; uint64_t rsvd1:4; uint64_t l3_offset:8; uint64_t l4_offset:8; - uint64_t rsvd0:5; + uint64_t rsvd0:2; + uint64_t csum_inner_l3:1; + uint64_t csum_inner_l4:2; uint64_t csum_l3:1; uint64_t csum_l4:2; uint64_t subdesc_cnt:8; @@ -594,12 +597,11 @@ struct sq_hdr_subdesc { uint64_t subdesc_type:4; /* W0 */ uint64_t tso_max_paysize:14; - uint64_t rsvd2:2; - uint64_t tso_flags_first:12; - uint64_t tso_flags_last:12; - uint64_t tso_l4_offset:8; - uint64_t tso_sdc_first:8; - uint64_t tso_sdc_cont:8; /* W1 */ + uint64_t rsvd3:2; + uint64_t tso_start:8; + uint64_t inner_l3_offset:8; + uint64_t inner_l4_offset:8; + uint64_t rsvd2:24; #endif };