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
This commit is contained in:
Zbigniew Bodek 2016-02-25 14:29:57 +00:00
parent 3bdcfead19
commit af8fe8f169
5 changed files with 94 additions and 54 deletions

View File

@ -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;

View File

@ -66,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <net/if_vlan_var.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netinet/tcp_lro.h>
@ -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

View File

@ -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 */

View File

@ -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)

View File

@ -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
};