[RFC] dpdk pktio: enable hardware checksum support

Message ID 1494424214-27170-2-git-send-email-bogdan.pricope@linaro.org
State New
Headers show

Commit Message

Bogdan Pricope May 10, 2017, 1:50 p.m.
Signed-off-by: Bogdan Pricope <bogdan.pricope@linaro.org>

---
 example/generator/odp_generator.c      | 102 ++++++++++++++++++++++++++++-----
 platform/linux-generic/odp_packet_io.c |   2 +
 platform/linux-generic/pktio/dpdk.c    |  96 +++++++++++++++++++++++++++++--
 3 files changed, 181 insertions(+), 19 deletions(-)

-- 
1.9.1

Patch

diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index ede1cdf..5a8fee4 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -46,6 +46,7 @@ 
 
 typedef struct {
 	odp_pktio_t pktio;
+	odp_pktio_config_t config;
 	odp_pktout_queue_t pktout[MAX_WORKERS];
 	unsigned pktout_count;
 } interface_t;
@@ -90,6 +91,7 @@  static struct {
  */
 typedef struct {
 	odp_pktout_queue_t pktout; /**< Packet output queue to use*/
+	odp_pktout_config_opt_t *pktout_cfg; /**< Packet output offload config*/
 	odp_pool_t pool;	/**< Pool for packet IO */
 	odp_timer_pool_t tp;	/**< Timer pool handle */
 	odp_queue_t tq;		/**< Queue for timeouts */
@@ -114,6 +116,9 @@  static args_t *args;
 /** Barrier to sync threads execution */
 static odp_barrier_t barrier;
 
+/** List of interfaces */
+static interface_t *ifs;
+
 /* helper funcs */
 static void parse_args(int argc, char *argv[], appl_args_t *appl_args);
 static void print_info(char *progname, appl_args_t *appl_args);
@@ -192,7 +197,8 @@  static int scan_ip(char *buf, unsigned int *paddr)
  * @return Handle of created packet
  * @retval ODP_PACKET_INVALID  Packet could not be created
  */
-static odp_packet_t setup_udp_pkt_ref(odp_pool_t pool)
+static odp_packet_t setup_udp_pkt_ref(odp_pool_t pool,
+									  odp_pktout_config_opt_t *pktout_cfg)
 {
 	odp_packet_t pkt;
 	char *buf;
@@ -237,7 +243,8 @@  static odp_packet_t setup_udp_pkt_ref(odp_pool_t pool)
 	udp->dst_port = 0;
 	udp->length = odp_cpu_to_be_16(args->appl.payload + ODPH_UDPHDR_LEN);
 	udp->chksum = 0;
-	udp->chksum = odph_ipv4_udp_chksum(pkt);
+	if (!pktout_cfg->bit.udp_chksum)
+		udp->chksum = odph_ipv4_udp_chksum(pkt);
 
 	return pkt;
 }
@@ -251,7 +258,8 @@  static odp_packet_t setup_udp_pkt_ref(odp_pool_t pool)
  * @return Handle of created packet
  * @retval ODP_PACKET_INVALID  Packet could not be created
  */
-static odp_packet_t pack_udp_pkt(odp_pool_t pool, odp_packet_t pkt_ref)
+static odp_packet_t pack_udp_pkt(odp_pool_t pool, odp_packet_t pkt_ref,
+								 odp_pktout_config_opt_t *pktout_cfg)
 {
 	odp_packet_t pkt;
 	char *buf;
@@ -273,7 +281,17 @@  static odp_packet_t pack_udp_pkt(odp_pool_t pool, odp_packet_t pkt_ref)
 	ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
 	seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xFFFF;
 	ip->id = odp_cpu_to_be_16(seq);
-	ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
+	ip->chksum = 0;
+	if (!pktout_cfg->bit.ipv4_chksum)
+		ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
+
+	if (pktout_cfg->bit.ipv4_chksum || pktout_cfg->bit.udp_chksum) {
+		odp_packet_l2_offset_set(pkt, 0);
+		odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
+		odp_packet_has_ipv4_set(pkt, 1);
+		odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+		odp_packet_has_udp_set(pkt, 1);
+	}
 
 	return pkt;
 }
@@ -342,7 +360,8 @@  static odp_packet_t setup_icmp_pkt_ref(odp_pool_t pool)
  * @return Handle of created packet
  * @retval ODP_PACKET_INVALID  Packet could not be created
  */
-static odp_packet_t pack_icmp_pkt(odp_pool_t pool, odp_packet_t pkt_ref)
+static odp_packet_t pack_icmp_pkt(odp_pool_t pool, odp_packet_t pkt_ref,
+								  odp_pktout_config_opt_t *pktout_cfg)
 {
 	odp_packet_t pkt;
 	char *buf;
@@ -367,7 +386,9 @@  static odp_packet_t pack_icmp_pkt(odp_pool_t pool, odp_packet_t pkt_ref)
 	ip = (odph_ipv4hdr_t *)(buf + ODPH_ETHHDR_LEN);
 	seq = odp_atomic_fetch_add_u64(&counters.seq, 1) % 0xffff;
 	ip->id = odp_cpu_to_be_16(seq);
-	ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
+	ip->chksum = 0;
+	if (!pktout_cfg->bit.ipv4_chksum)
+		ip->chksum = odph_chksum(ip, ODPH_IPV4HDR_LEN);
 
 	/* icmp */
 	icmp = (odph_icmphdr_t *)(buf + ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
@@ -381,6 +402,13 @@  static odp_packet_t pack_icmp_pkt(odp_pool_t pool, odp_packet_t pkt_ref)
 	icmp->chksum = 0;
 	icmp->chksum = odph_chksum(icmp, args->appl.payload + ODPH_ICMPHDR_LEN);
 
+	if (pktout_cfg->bit.ipv4_chksum) {
+		odp_packet_l2_offset_set(pkt, 0);
+		odp_packet_l3_offset_set(pkt, ODPH_ETHHDR_LEN);
+		odp_packet_has_ipv4_set(pkt, 1);
+		odp_packet_l4_offset_set(pkt, ODPH_ETHHDR_LEN + ODPH_IPV4HDR_LEN);
+	}
+
 	return pkt;
 }
 
@@ -421,6 +449,22 @@  static int create_pktio(const char *dev, odp_pool_t pool,
 			    dev);
 		return -1;
 	}
+
+	odp_pktio_config_init(&itf->config);
+	itf->config.pktin.bit.ipv4_chksum = capa.config.pktin.bit.ipv4_chksum;
+	itf->config.pktin.bit.udp_chksum = capa.config.pktin.bit.udp_chksum;
+	itf->config.pktin.bit.tcp_chksum = capa.config.pktin.bit.tcp_chksum;
+
+	itf->config.pktout.bit.ipv4_chksum = capa.config.pktout.bit.ipv4_chksum;
+	itf->config.pktout.bit.udp_chksum = capa.config.pktout.bit.udp_chksum;
+	itf->config.pktout.bit.tcp_chksum = capa.config.pktout.bit.tcp_chksum;
+
+	if (odp_pktio_config(itf->pktio, &itf->config)) {
+		EXAMPLE_ERR("Error: Failed to set interface configuration %s\n",
+			    dev);
+		return -1;
+	}
+
 	if (num_rx_queues > capa.max_input_queues)
 		num_rx_queues = capa.max_input_queues;
 
@@ -481,6 +525,7 @@  static int gen_send_thread(void *arg)
 	int ret, i;
 	thread_args_t *thr_args;
 	odp_pktout_queue_t pktout;
+	odp_pktout_config_opt_t *pktout_cfg;
 	odp_packet_t pkt_array[MAX_UDP_TX_BURST];
 	int pkt_array_size;
 	int burst_start, burst_size;
@@ -490,9 +535,10 @@  static int gen_send_thread(void *arg)
 	thr_args = arg;
 
 	pktout = thr_args->pktout;
+	pktout_cfg = thr_args->pktout_cfg;
 
 	if (args->appl.mode == APPL_MODE_UDP) {
-		pkt_ref = setup_udp_pkt_ref(thr_args->pool);
+		pkt_ref = setup_udp_pkt_ref(thr_args->pool, pktout_cfg);
 		pkt_array_size = args->appl.udp_tx_burst;
 	} else if (args->appl.mode == APPL_MODE_PING) {
 		pkt_ref = setup_icmp_pkt_ref(thr_args->pool);
@@ -521,7 +567,7 @@  static int gen_send_thread(void *arg)
 		if (args->appl.mode == APPL_MODE_UDP) {
 			for (i = 0; i < pkt_array_size; i++) {
 				pkt_array[i] = pack_udp_pkt(thr_args->pool,
-						pkt_ref);
+						pkt_ref, pktout_cfg);
 				if (!odp_packet_is_valid(pkt_array[i]))
 					break;
 			}
@@ -532,7 +578,7 @@  static int gen_send_thread(void *arg)
 				break;
 			}
 		} else if (args->appl.mode == APPL_MODE_PING) {
-			pkt_array[0] = pack_icmp_pkt(thr_args->pool, pkt_ref);
+			pkt_array[0] = pack_icmp_pkt(thr_args->pool, pkt_ref, pktout_cfg);
 			if (!odp_packet_is_valid(pkt_array[0])) {
 				EXAMPLE_ERR("  [%2i] alloc_single failed\n",
 					    thr);
@@ -671,6 +717,7 @@  static int gen_recv_thread(void *arg)
 	odp_packet_t pkts[MAX_RX_BURST], pkt;
 	odp_event_t events[MAX_RX_BURST];
 	int pkt_cnt, ev_cnt, i;
+	interface_t *itf;
 
 	thr = odp_thread_id();
 	(void)arg;
@@ -692,6 +739,27 @@  static int gen_recv_thread(void *arg)
 			continue;
 		for (i = 0, pkt_cnt = 0; i < ev_cnt; i++) {
 			pkt = odp_packet_from_event(events[i]);
+			itf = &ifs[odp_pktio_index(odp_packet_input(pkt))];
+
+			if (odp_packet_has_ipv4(pkt)) {
+				if (itf->config.pktin.bit.ipv4_chksum) { /* HW validation */
+					if (odp_packet_has_l3_error(pkt))
+						printf("HW detected L3 error\n");
+				} /* else SW validation */
+			}
+
+			if (odp_packet_has_udp(pkt)) {
+				if (itf->config.pktin.bit.udp_chksum) { /* HW validation*/
+					if (odp_packet_has_l4_error(pkt))
+						printf("HW detected L4 error\n");
+				} /* else SW validation */
+			}
+			if (odp_packet_has_tcp(pkt)) {
+				if (itf->config.pktin.bit.tcp_chksum) { /* HW validation */
+					if (odp_packet_has_l4_error(pkt))
+						printf("HW detected L4 error\n");
+				} /*else SW validation */
+			}
 
 			/* Drop packets with errors */
 			if (odp_unlikely(odp_packet_has_error(pkt))) {
@@ -701,9 +769,11 @@  static int gen_recv_thread(void *arg)
 			pkts[pkt_cnt++] = pkt;
 		}
 
-		print_pkts(thr, pkts, pkt_cnt);
+		if (pkt_cnt) {
+			print_pkts(thr, pkts, pkt_cnt);
 
-		odp_packet_free_multi(pkts, pkt_cnt);
+			odp_packet_free_multi(pkts, pkt_cnt);
+		}
 	}
 
 	return 0;
@@ -797,7 +867,6 @@  int main(int argc, char *argv[])
 	odp_pool_t tmop;
 	odp_queue_t tq;
 	odp_event_t ev;
-	interface_t *ifs;
 	odp_instance_t instance;
 	odph_odpthread_params_t thr_params;
 
@@ -959,6 +1028,7 @@  int main(int argc, char *argv[])
 			abort();
 		}
 		(void)args->thread[1].pktout; /* Not used*/
+		(void)args->thread[1].pktout_cfg; /* Not used*/
 		args->thread[1].pool = pool;
 		args->thread[1].tp = tp;
 		args->thread[1].tq = tq;
@@ -988,6 +1058,7 @@  int main(int argc, char *argv[])
 			abort();
 		}
 		args->thread[0].pktout = ifs[0].pktout[0];
+		args->thread[0].pktout_cfg = &ifs[0].config.pktout;
 		args->thread[0].pool = pool;
 		args->thread[0].tp = tp;
 		args->thread[0].tq = tq;
@@ -1019,15 +1090,18 @@  int main(int argc, char *argv[])
 			int (*thr_run_func)(void *);
 			int if_idx, pktout_idx;
 
-			if (args->appl.mode == APPL_MODE_RCV)
+			if (args->appl.mode == APPL_MODE_RCV) {
 				(void)args->thread[i].pktout; /*not used*/
-			else {
+				(void)args->thread[i].pktout_cfg; /*not used*/
+			} else {
 				if_idx = i % args->appl.if_count;
 				pktout_idx = (i / args->appl.if_count) %
 					ifs[if_idx].pktout_count;
 
 				args->thread[i].pktout =
 					ifs[if_idx].pktout[pktout_idx];
+				args->thread[i].pktout_cfg =
+					&ifs[if_idx].config.pktout;
 			}
 			tq = odp_queue_create("", NULL);
 			if (tq == ODP_QUEUE_INVALID) {
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index 98460a5..6c00b10 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -150,6 +150,8 @@  static void init_pktio_entry(pktio_entry_t *entry)
 	init_out_queues(entry);
 
 	pktio_classifier_init(entry);
+
+	odp_pktio_config_init(&entry->s.config);
 }
 
 static odp_pktio_t alloc_lock_pktio_entry(void)
diff --git a/platform/linux-generic/pktio/dpdk.c b/platform/linux-generic/pktio/dpdk.c
index 6ac89bd..8b54433 100644
--- a/platform/linux-generic/pktio/dpdk.c
+++ b/platform/linux-generic/pktio/dpdk.c
@@ -27,6 +27,9 @@ 
 #include <rte_config.h>
 #include <rte_mbuf.h>
 #include <rte_ethdev.h>
+#include <rte_ip.h>
+#include <rte_udp.h>
+#include <rte_tcp.h>
 #include <rte_string_fns.h>
 
 static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
@@ -189,6 +192,7 @@  static int dpdk_setup_port(pktio_entry_t *pktio_entry)
 	int ret;
 	pkt_dpdk_t *pkt_dpdk = &pktio_entry->s.pkt_dpdk;
 	struct rte_eth_rss_conf rss_conf;
+	uint16_t hw_ip_checksum = 0;
 
 	/* Always set some hash functions to enable DPDK RSS hash calculation */
 	if (pkt_dpdk->hash.all_bits == 0) {
@@ -198,13 +202,18 @@  static int dpdk_setup_port(pktio_entry_t *pktio_entry)
 		rss_conf_to_hash_proto(&rss_conf, &pkt_dpdk->hash);
 	}
 
+	if (pktio_entry->s.config.pktin.bit.ipv4_chksum ||
+		pktio_entry->s.config.pktin.bit.udp_chksum ||
+		pktio_entry->s.config.pktin.bit.tcp_chksum)
+		hw_ip_checksum = 1;
+
 	struct rte_eth_conf port_conf = {
 		.rxmode = {
 			.mq_mode = ETH_MQ_RX_RSS,
 			.max_rx_pkt_len = pkt_dpdk->data_room,
 			.split_hdr_size = 0,
 			.header_split   = 0,
-			.hw_ip_checksum = 0,
+			.hw_ip_checksum = hw_ip_checksum,
 			.hw_vlan_filter = 0,
 			.jumbo_frame    = 1,
 			.hw_strip_crc   = 0,
@@ -434,6 +443,19 @@  static void dpdk_init_capability(pktio_entry_t *pktio_entry,
 	odp_pktio_config_init(&capa->config);
 	capa->config.pktin.bit.ts_all = 1;
 	capa->config.pktin.bit.ts_ptp = 1;
+	capa->config.pktin.bit.ipv4_chksum =
+		(dev_info->rx_offload_capa & DEV_RX_OFFLOAD_IPV4_CKSUM)? 1:0;
+	capa->config.pktin.bit.udp_chksum =
+		(dev_info->rx_offload_capa & DEV_RX_OFFLOAD_UDP_CKSUM)? 1:0;
+	capa->config.pktin.bit.tcp_chksum =
+		(dev_info->rx_offload_capa & DEV_RX_OFFLOAD_TCP_CKSUM)? 1:0;
+
+	capa->config.pktout.bit.ipv4_chksum =
+		(dev_info->tx_offload_capa & DEV_TX_OFFLOAD_IPV4_CKSUM)? 1:0;
+	capa->config.pktout.bit.udp_chksum =
+		(dev_info->tx_offload_capa & DEV_TX_OFFLOAD_UDP_CKSUM)? 1:0;
+	capa->config.pktout.bit.tcp_chksum=
+		(dev_info->tx_offload_capa & DEV_TX_OFFLOAD_TCP_CKSUM)? 1:0;
 }
 
 static int dpdk_open(odp_pktio_t id ODP_UNUSED,
@@ -605,9 +627,11 @@  static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 	int nb_pkts = 0;
 	int alloc_len, num;
 	odp_pool_t pool = pktio_entry->s.pkt_dpdk.pool;
+	odp_pktin_config_opt_t *pktin_cfg;
 
 	/* Allocate maximum sized packets */
 	alloc_len = pktio_entry->s.pkt_dpdk.data_room;
+	pktin_cfg = &pktio_entry->s.config.pktin;
 
 	num = packet_alloc_multi(pool, alloc_len, pkt_table, mbuf_num);
 	if (num != mbuf_num) {
@@ -658,6 +682,16 @@  static inline int mbuf_to_pkt(pktio_entry_t *pktio_entry,
 		if (mbuf->ol_flags & PKT_RX_RSS_HASH)
 			odp_packet_flow_hash_set(pkt, mbuf->hash.rss);
 
+		if (pktin_cfg->bit.ipv4_chksum &&
+			mbuf->ol_flags & PKT_RX_IP_CKSUM_BAD)
+				pkt_hdr->p.error_flags.ip_err = 1;
+
+		if ((pktin_cfg->bit.udp_chksum || pktin_cfg->bit.tcp_chksum) &&
+			(mbuf->ol_flags & PKT_RX_L4_CKSUM_BAD)) {
+				pkt_hdr->p.error_flags.udp_err = 1;
+				pkt_hdr->p.error_flags.tcp_err = 1;
+		}
+
 		packet_set_ts(pkt_hdr, ts);
 
 		pkt_table[nb_pkts++] = pkt;
@@ -675,7 +709,15 @@  fail:
 
 	return (i > 0 ? i : -1);
 }
-
+static inline uint16_t phdr_csum(int ipv4,
+										  void *l3_hdr,
+										  uint64_t ol_flags)
+{
+	if (ipv4)
+		return rte_ipv4_phdr_cksum(l3_hdr, ol_flags);
+	else /*ipv6*/
+		return rte_ipv6_phdr_cksum(l3_hdr, ol_flags);
+}
 static inline int pkt_to_mbuf(pktio_entry_t *pktio_entry,
 			      struct rte_mbuf *mbuf_table[],
 			      const odp_packet_t pkt_table[], uint16_t num)
@@ -684,14 +726,23 @@  static inline int pkt_to_mbuf(pktio_entry_t *pktio_entry,
 	int i, j;
 	char *data;
 	uint16_t pkt_len;
+	odp_bool_t ipv4_chksum, udp_chksum, tcp_chksum;
+	odp_packet_t pkt;
+	struct rte_mbuf *mbuf;
 
 	if (odp_unlikely((rte_pktmbuf_alloc_bulk(pkt_dpdk->pkt_pool,
 						 mbuf_table, num)))) {
 		ODP_ERR("Failed to alloc mbuf\n");
 		return 0;
 	}
+	ipv4_chksum = pktio_entry->s.config.pktout.bit.ipv4_chksum ? 1 : 0;
+	udp_chksum = pktio_entry->s.config.pktout.bit.udp_chksum ? 1 : 0;
+	tcp_chksum = pktio_entry->s.config.pktout.bit.tcp_chksum ? 1 : 0;
+
 	for (i = 0; i < num; i++) {
-		pkt_len = _odp_packet_len(pkt_table[i]);
+		pkt = pkt_table[i];
+		mbuf = mbuf_table[i];
+		pkt_len = _odp_packet_len(pkt);
 
 		if (pkt_len > pkt_dpdk->mtu) {
 			if (i == 0)
@@ -700,9 +751,44 @@  static inline int pkt_to_mbuf(pktio_entry_t *pktio_entry,
 		}
 
 		/* Packet always fits in mbuf */
-		data = rte_pktmbuf_append(mbuf_table[i], pkt_len);
+		data = rte_pktmbuf_append(mbuf, pkt_len);
 
-		odp_packet_copy_to_mem(pkt_table[i], 0, pkt_len, data);
+		odp_packet_copy_to_mem(pkt, 0, pkt_len, data);
+
+		if (ipv4_chksum || udp_chksum || tcp_chksum) {
+			void *l3_hdr;
+
+			mbuf->l2_len = odp_packet_l3_offset(pkt) -
+				odp_packet_l2_offset(pkt);
+			mbuf->l3_len = odp_packet_l4_offset(pkt) -
+				odp_packet_l3_offset(pkt);
+			mbuf->ol_flags = 0;
+
+			l3_hdr = (void *)(data + mbuf->l2_len);
+
+			if (ipv4_chksum && odp_packet_has_ipv4(pkt)) {
+				struct ipv4_hdr *ip = (struct ipv4_hdr *)l3_hdr;
+
+				mbuf->ol_flags |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM;
+				ip->hdr_checksum = 0;
+			}
+
+			if (udp_chksum && odp_packet_has_udp(pkt)) {
+				struct udp_hdr *udp = (struct udp_hdr *)(data + mbuf->l2_len +
+														 mbuf->l3_len);
+
+				mbuf->ol_flags |= PKT_TX_UDP_CKSUM;
+				udp->dgram_cksum = phdr_csum(odp_packet_has_ipv4(pkt),
+											 l3_hdr, mbuf->ol_flags);
+			} else if (tcp_chksum && odp_packet_has_tcp(pkt)) {
+				struct tcp_hdr *tcp = (struct tcp_hdr *)(data + mbuf->l2_len +
+														 mbuf->l3_len);
+
+				mbuf->ol_flags |= PKT_TX_TCP_CKSUM;
+				tcp->cksum = phdr_csum(odp_packet_has_ipv4(pkt),
+											 l3_hdr, mbuf->ol_flags);
+			}
+		}
 	}
 	return i;