From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from proxy.6wind.com (host.76.145.23.62.rev.coltfrance.com [62.23.145.76]) by dpdk.org (Postfix) with ESMTP id 288B76CD4 for ; Thu, 13 Oct 2016 16:16:43 +0200 (CEST) Received: from glumotte.dev.6wind.com (unknown [10.16.0.195]) by proxy.6wind.com (Postfix) with ESMTP id 266C626CD5; Thu, 13 Oct 2016 16:16:39 +0200 (CEST) From: Olivier Matz To: dev@dpdk.org, yuanhan.liu@linux.intel.com Cc: konstantin.ananyev@intel.com, sugesh.chandran@intel.com, bruce.richardson@intel.com, jianfeng.tan@intel.com, helin.zhang@intel.com, adrien.mazarguil@6wind.com, stephen@networkplumber.org, dprovan@bivio.net, xiao.w.wang@intel.com, maxime.coquelin@redhat.com Date: Thu, 13 Oct 2016 16:16:11 +0200 Message-Id: <1476368171-18176-13-git-send-email-olivier.matz@6wind.com> X-Mailer: git-send-email 2.8.1 In-Reply-To: <1476368171-18176-1-git-send-email-olivier.matz@6wind.com> References: <1469088510-7552-1-git-send-email-olivier.matz@6wind.com> <1476368171-18176-1-git-send-email-olivier.matz@6wind.com> Subject: [dpdk-dev] [PATCH v3 12/12] net/virtio: add Tso support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 13 Oct 2016 14:16:44 -0000 Signed-off-by: Olivier Matz --- drivers/net/virtio/virtio_ethdev.c | 6 ++ drivers/net/virtio/virtio_ethdev.h | 2 + drivers/net/virtio/virtio_rxtx.c | 133 +++++++++++++++++++++++++++++++++++-- 3 files changed, 136 insertions(+), 5 deletions(-) diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c index 109f855..969edb6 100644 --- a/drivers/net/virtio/virtio_ethdev.c +++ b/drivers/net/virtio/virtio_ethdev.c @@ -1572,6 +1572,7 @@ virtio_dev_link_update(struct rte_eth_dev *dev, __rte_unused int wait_to_complet static void virtio_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) { + uint64_t tso_mask; struct virtio_hw *hw = dev->data->dev_private; if (dev->pci_dev) @@ -1599,6 +1600,11 @@ virtio_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info) DEV_TX_OFFLOAD_UDP_CKSUM | DEV_TX_OFFLOAD_TCP_CKSUM; } + + tso_mask = (1ULL << VIRTIO_NET_F_HOST_TSO4) | + (1ULL << VIRTIO_NET_F_HOST_TSO6); + if ((hw->guest_features & tso_mask) == tso_mask) + dev_info->tx_offload_capa |= DEV_TX_OFFLOAD_TCP_TSO; } /* diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h index d55e7ed..f77f618 100644 --- a/drivers/net/virtio/virtio_ethdev.h +++ b/drivers/net/virtio/virtio_ethdev.h @@ -63,6 +63,8 @@ 1u << VIRTIO_NET_F_CTRL_RX | \ 1u << VIRTIO_NET_F_CTRL_VLAN | \ 1u << VIRTIO_NET_F_CSUM | \ + 1u << VIRTIO_NET_F_HOST_TSO4 | \ + 1u << VIRTIO_NET_F_HOST_TSO6 | \ 1u << VIRTIO_NET_F_MRG_RXBUF | \ 1u << VIRTIO_RING_F_INDIRECT_DESC | \ 1ULL << VIRTIO_F_VERSION_1) diff --git a/drivers/net/virtio/virtio_rxtx.c b/drivers/net/virtio/virtio_rxtx.c index 0fa635a..4b01ea3 100644 --- a/drivers/net/virtio/virtio_rxtx.c +++ b/drivers/net/virtio/virtio_rxtx.c @@ -209,10 +209,117 @@ virtqueue_enqueue_recv_refill(struct virtqueue *vq, struct rte_mbuf *cookie) return 0; } +/* When doing TSO, the IP length is not included in the pseudo header + * checksum of the packet given to the PMD, but for virtio it is + * expected. + */ +static void +virtio_tso_fix_cksum(struct rte_mbuf *m) +{ + /* common case: header is not fragmented */ + if (likely(rte_pktmbuf_data_len(m) >= m->l2_len + m->l3_len + + m->l4_len)) { + struct ipv4_hdr *iph; + struct ipv6_hdr *ip6h; + struct tcp_hdr *th; + uint16_t prev_cksum, new_cksum, ip_len, ip_paylen; + uint32_t tmp; + + iph = rte_pktmbuf_mtod_offset(m, struct ipv4_hdr *, m->l2_len); + th = RTE_PTR_ADD(iph, m->l3_len); + if ((iph->version_ihl >> 4) == 4) { + iph->hdr_checksum = 0; + iph->hdr_checksum = rte_ipv4_cksum(iph); + ip_len = iph->total_length; + ip_paylen = rte_cpu_to_be_16(rte_be_to_cpu_16(ip_len) - + m->l3_len); + } else { + ip6h = (struct ipv6_hdr *)iph; + ip_paylen = ip6h->payload_len; + } + + /* calculate the new phdr checksum not including ip_paylen */ + prev_cksum = th->cksum; + tmp = prev_cksum; + tmp += ip_paylen; + tmp = (tmp & 0xffff) + (tmp >> 16); + new_cksum = tmp; + + /* replace it in the packet */ + th->cksum = new_cksum; + } else { + const struct ipv4_hdr *iph; + struct ipv4_hdr iph_copy; + union { + uint16_t u16; + uint8_t u8[2]; + } prev_cksum, new_cksum, ip_len, ip_paylen, ip_csum; + uint32_t tmp; + + /* Same code than above, but we use rte_pktmbuf_read() + * or we read/write in mbuf data one byte at a time to + * avoid issues if the packet is multi segmented. + */ + + uint8_t ip_version; + + ip_version = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len) >> 4; + + /* calculate ip checksum (API imposes to set it to 0) + * and get ip payload len */ + if (ip_version == 4) { + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 10) = 0; + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 11) = 0; + iph = rte_pktmbuf_read(m, m->l2_len, + sizeof(*iph), &iph_copy); + ip_csum.u16 = rte_ipv4_cksum(iph); + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 10) = ip_csum.u8[0]; + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 11) = ip_csum.u8[1]; + + ip_len.u8[0] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 2); + ip_len.u8[1] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 3); + + ip_paylen.u16 = rte_cpu_to_be_16( + rte_be_to_cpu_16(ip_len.u16) - m->l3_len); + } else { + ip_paylen.u8[0] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 4); + ip_paylen.u8[1] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + 5); + } + + /* calculate the new phdr checksum not including ip_paylen */ + /* get phdr cksum at offset 16 of TCP header */ + prev_cksum.u8[0] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + m->l3_len + 16); + prev_cksum.u8[1] = *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + m->l3_len + 17); + tmp = prev_cksum.u16; + tmp += ip_paylen.u16; + tmp = (tmp & 0xffff) + (tmp >> 16); + new_cksum.u16 = tmp; + + /* replace it in the packet */ + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + m->l3_len + 16) = new_cksum.u8[0]; + *rte_pktmbuf_mtod_offset(m, uint8_t *, + m->l2_len + m->l3_len + 17) = new_cksum.u8[1]; + } +} + static inline int tx_offload_enabled(struct virtio_hw *hw) { - return vtpci_with_feature(hw, VIRTIO_NET_F_CSUM); + return vtpci_with_feature(hw, VIRTIO_NET_F_CSUM) || + vtpci_with_feature(hw, VIRTIO_NET_F_HOST_TSO4) || + vtpci_with_feature(hw, VIRTIO_NET_F_HOST_TSO6); } static inline void @@ -274,8 +381,11 @@ virtqueue_enqueue_xmit(struct virtnet_tx *txvq, struct rte_mbuf *cookie, idx = start_dp[idx].next; } + /* Checksum Offload / TSO */ if (offload) { - /* Checksum Offload */ + if (cookie->ol_flags & PKT_TX_TCP_SEG) + cookie->ol_flags |= PKT_TX_TCP_CKSUM; + switch (cookie->ol_flags & PKT_TX_L4_MASK) { case PKT_TX_UDP_CKSUM: hdr->csum_start = cookie->l2_len + cookie->l3_len; @@ -297,9 +407,22 @@ virtqueue_enqueue_xmit(struct virtnet_tx *txvq, struct rte_mbuf *cookie, break; } - hdr->gso_type = 0; - hdr->gso_size = 0; - hdr->hdr_len = 0; + /* TCP Segmentation Offload */ + if (cookie->ol_flags & PKT_TX_TCP_SEG) { + virtio_tso_fix_cksum(cookie); + hdr->gso_type = (cookie->ol_flags & PKT_TX_IPV6) ? + VIRTIO_NET_HDR_GSO_TCPV6 : + VIRTIO_NET_HDR_GSO_TCPV4; + hdr->gso_size = cookie->tso_segsz; + hdr->hdr_len = + cookie->l2_len + + cookie->l3_len + + cookie->l4_len; + } else { + hdr->gso_type = 0; + hdr->gso_size = 0; + hdr->hdr_len = 0; + } } do { -- 2.8.1