예제 #1
0
파일: pktio.c 프로젝트: kalray/odp-mppa
static void test_txrx(odp_pktio_input_mode_t in_mode, int num_pkts)
{
	int ret, i, if_b;
	pktio_info_t pktios[MAX_NUM_IFACES];
	pktio_info_t *io;
	uint32_t mtu, min_mtu = UINT32_MAX;

	/* create pktios and associate input/output queues */
	for (i = 0; i < num_ifaces; ++i) {
		io = &pktios[i];

		io->name = iface_name[i];
		io->id   = create_pktio(i, in_mode, ODP_PKTOUT_MODE_SEND);
		if (io->id == ODP_PKTIO_INVALID) {
			CU_FAIL("failed to open iface");
			return;
		}
		io->outq = odp_pktio_outq_getdef(io->id);
		io->in_mode = in_mode;

		if (in_mode == ODP_PKTIN_MODE_POLL) {
			create_inq(io->id, ODP_QUEUE_TYPE_POLL);
			io->inq = odp_pktio_inq_getdef(io->id);
		} else if (in_mode == ODP_PKTIN_MODE_SCHED) {
			create_inq(io->id, ODP_QUEUE_TYPE_SCHED);
			io->inq = ODP_QUEUE_INVALID;
		}

	}
	for (i = 0; i < num_ifaces; ++i) {
		io = &pktios[i];
		ret = odp_pktio_start(io->id);
		CU_ASSERT(ret == 0);

		mtu = odp_pktio_mtu(io->id);
		if (mtu < min_mtu)
			min_mtu = mtu;
	}

	/* Skip test if packet len is larger than the MTU */
	if (min_mtu >= packet_len) {
		/* if we have two interfaces then send through one and receive
		 * on another but if there's only one assume it's a loopback */
		if_b = (num_ifaces == 1) ? 0 : 1;
		pktio_txrx_multi(&pktios[0], &pktios[if_b], num_pkts);
	}

	for (i = 0; i < num_ifaces; ++i) {
		ret = odp_pktio_stop(pktios[i].id);
		CU_ASSERT(ret == 0);
		if (in_mode != ODP_PKTIN_MODE_RECV)
			destroy_inq(pktios[i].id);
		ret = odp_pktio_close(pktios[i].id);
		CU_ASSERT(ret == 0);
	}
}
static inline
void enqueue_loop_interface(odp_packet_t pkt)
{
	odp_event_t ev;
	odp_queue_t defqueue = odp_pktio_outq_getdef(pktio_loop);

	ev = odp_packet_to_event(pkt);
	if (!(CU_ASSERT(odp_queue_enq(defqueue, ev) == 0)))
		odp_packet_free(pkt);
}
예제 #3
0
void enqueue_pktio_interface(odp_packet_t pkt, odp_pktio_t pktio)
{
	odp_event_t ev;
	odp_queue_t defqueue;

	defqueue  = odp_pktio_outq_getdef(pktio);
	CU_ASSERT(defqueue != ODP_QUEUE_INVALID);

	ev = odp_packet_to_event(pkt);
	CU_ASSERT(odp_queue_enq(defqueue, ev) == 0);
}
예제 #4
0
/**
 * Lookup the destination pktio for a given packet
 */
static inline odp_queue_t lookup_dest_q(odp_packet_t pkt)
{
	int i, src_idx, dst_idx;
	odp_pktio_t pktio_src, pktio_dst;

	pktio_src = odp_packet_input(pkt);

	for (src_idx = -1, i = 0; gbl_args->pktios[i] != ODP_PKTIO_INVALID; ++i)
		if (gbl_args->pktios[i] == pktio_src)
			src_idx = i;

	if (src_idx == -1)
		LOG_ABORT("Failed to determine pktio input\n");

	dst_idx = (src_idx % 2 == 0) ? src_idx+1 : src_idx-1;
	pktio_dst = gbl_args->pktios[dst_idx];

	return odp_pktio_outq_getdef(pktio_dst);
}
예제 #5
0
static int send_recv(odp_packet_t packet, int send_nb, int expt_nb)
{
	static int test_id = 0;
	printf("send_recv %d\n", test_id++);
	odp_pktio_t pktio;
	odp_queue_t outq_def;
	odp_packet_t pkt_tbl[MAX_PKT_BURST];

	pktio = odp_pktio_lookup(pktio_valid_name);

	if (pktio == ODP_PKTIO_INVALID) {
		printf("Error: lookup of pktio %s failed\n",
			    pktio_valid_name);
		return 1;
	}

	outq_def = odp_pktio_outq_getdef(pktio);
	if (outq_def == ODP_QUEUE_INVALID) {
		printf("Error: def output-Q query\n");
		return 1;
	}
	int i;
	for ( i = 0; i < send_nb; ++i ) {
		odp_queue_enq(outq_def, (odp_event_t)packet);
	}
	int ret = 0;

	int start = __k1_read_dsu_timestamp();
	while ( ret >= 0 && ret < expt_nb && ( __k1_read_dsu_timestamp() - start ) < 10 * __bsp_frequency ) {
		int n_pkt = odp_pktio_recv(pktio, pkt_tbl, MAX_PKT_BURST);
		ret += n_pkt;
		if (n_pkt > 0)
			odp_packet_free_multi(pkt_tbl, n_pkt);
	}
	test_assert_ret(ret == expt_nb);
	odp_packet_free(packet);
	printf("send_recv %d OK\n", test_id);

	return 0;
}
예제 #6
0
/**
 * Initialize interface
 *
 * Initialize ODP pktio and queues, query MAC address and update
 * forwarding database.
 *
 * @param intf     Interface name string
 */
static
void initialize_intf(char *intf)
{
	odp_pktio_t pktio;
	odp_queue_t outq_def;
	odp_queue_t inq_def;
	char inq_name[ODP_QUEUE_NAME_LEN];
	odp_queue_param_t qparam;
	int ret;
	uint8_t src_mac[ODPH_ETHADDR_LEN];
	char src_mac_str[MAX_STRING];
	odp_pktio_param_t pktio_param;

	odp_pktio_param_init(&pktio_param);

	if (getenv("ODP_IPSEC_USE_POLL_QUEUES"))
		pktio_param.in_mode = ODP_PKTIN_MODE_QUEUE;
	else
		pktio_param.in_mode = ODP_PKTIN_MODE_SCHED;

	/*
	 * Open a packet IO instance for thread and get default output queue
	 */
	pktio = odp_pktio_open(intf, pkt_pool, &pktio_param);
	if (ODP_PKTIO_INVALID == pktio) {
		EXAMPLE_ERR("Error: pktio create failed for %s\n", intf);
		exit(EXIT_FAILURE);
	}
	outq_def = odp_pktio_outq_getdef(pktio);

	/*
	 * Create and set the default INPUT queue associated with the 'pktio'
	 * resource
	 */
	odp_queue_param_init(&qparam);
	qparam.type        = ODP_QUEUE_TYPE_PKTIN;
	qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
	qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
	qparam.sched.group = ODP_SCHED_GROUP_ALL;
	snprintf(inq_name, sizeof(inq_name), "%" PRIu64 "-pktio_inq_def",
		 odp_pktio_to_u64(pktio));
	inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';

	inq_def = queue_create(inq_name, &qparam);
	if (ODP_QUEUE_INVALID == inq_def) {
		EXAMPLE_ERR("Error: pktio queue creation failed for %s\n",
			    intf);
		exit(EXIT_FAILURE);
	}

	ret = odp_pktio_inq_setdef(pktio, inq_def);
	if (ret) {
		EXAMPLE_ERR("Error: default input-Q setup for %s\n", intf);
		exit(EXIT_FAILURE);
	}

	ret = odp_pktio_start(pktio);
	if (ret) {
		EXAMPLE_ERR("Error: unable to start %s\n", intf);
		exit(EXIT_FAILURE);
	}

	/* Read the source MAC address for this interface */
	ret = odp_pktio_mac_addr(pktio, src_mac, sizeof(src_mac));
	if (ret <= 0) {
		EXAMPLE_ERR("Error: failed during MAC address get for %s\n",
			    intf);
		exit(EXIT_FAILURE);
	}

	printf("Created pktio:%02" PRIu64 ", queue mode (ATOMIC queues)\n"
	       "          default pktio%02" PRIu64 "-INPUT queue:%" PRIu64 "\n"
	       "          source mac address %s\n",
	       odp_pktio_to_u64(pktio), odp_pktio_to_u64(pktio),
	       odp_queue_to_u64(inq_def),
	       mac_addr_str(src_mac_str, src_mac));

	/* Resolve any routes using this interface for output */
	resolve_fwd_db(intf, outq_def, src_mac);
}
예제 #7
0
/**
 * Packet IO loopback worker thread using ODP queues
 *
 * @param arg  thread arguments of type 'thread_args_t *'
 */
static void *pktio_queue_thread(void *arg)
{
	int thr;
	odp_buffer_pool_t pkt_pool;
	odp_pktio_t pktio;
	thread_args_t *thr_args;
	odp_queue_t outq_def;
	odp_queue_t inq_def;
	char inq_name[ODP_QUEUE_NAME_LEN];
	odp_queue_param_t qparam;
	odp_packet_t pkt;
	odp_buffer_t buf;
	int ret;
	unsigned long pkt_cnt = 0;
	unsigned long err_cnt = 0;
	odp_pktio_params_t params;
	socket_params_t *sock_params = &params.sock_params;

	thr = odp_thread_id();
	thr_args = arg;

	printf("Pktio thread [%02i] starts, pktio_dev:%s\n", thr,
	       thr_args->pktio_dev);

	/* Lookup the packet pool */
	pkt_pool = odp_buffer_pool_lookup("packet_pool");
	if (pkt_pool == ODP_BUFFER_POOL_INVALID || pkt_pool != thr_args->pool) {
		ODP_ERR("  [%02i] Error: pkt_pool not found\n", thr);
		return NULL;
	}

	/* Open a packet IO instance for this thread */
	sock_params->type = thr_args->type;
	sock_params->fanout = thr_args->fanout;
	pktio = odp_pktio_open(thr_args->pktio_dev, pkt_pool, &params);
	if (pktio == ODP_PKTIO_INVALID) {
		ODP_ERR("  [%02i] Error: pktio create failed\n", thr);
		return NULL;
	}

	/*
	 * Create and set the default INPUT queue associated with the 'pktio'
	 * resource
	 */
	qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
	qparam.sched.sync  = ODP_SCHED_SYNC_ATOMIC;
	qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
	snprintf(inq_name, sizeof(inq_name), "%i-pktio_inq_def", (int)pktio);
	inq_name[ODP_QUEUE_NAME_LEN - 1] = '\0';

	inq_def = odp_queue_create(inq_name, ODP_QUEUE_TYPE_PKTIN, &qparam);
	if (inq_def == ODP_QUEUE_INVALID) {
		ODP_ERR("  [%02i] Error: pktio queue creation failed\n", thr);
		return NULL;
	}

	ret = odp_pktio_inq_setdef(pktio, inq_def);
	if (ret != 0) {
		ODP_ERR("  [%02i] Error: default input-Q setup\n", thr);
		return NULL;
	}

	printf("  [%02i] created pktio:%02i, queue mode (ATOMIC queues)\n"
	       "          default pktio%02i-INPUT queue:%u\n",
		thr, pktio, pktio, inq_def);

	/* Loop packets */
	for (;;) {
		odp_pktio_t pktio_tmp;

#if 1
		/* Use schedule to get buf from any input queue */
		buf = odp_schedule(NULL, ODP_SCHED_WAIT);
#else
		/* Always dequeue from the same input queue */
		buf = odp_queue_deq(inq_def);
		if (!odp_buffer_is_valid(buf))
			continue;
#endif

		pkt = odp_packet_from_buffer(buf);

		/* Drop packets with errors */
		if (odp_unlikely(drop_err_pkts(&pkt, 1) == 0)) {
			ODP_ERR("Drop frame - err_cnt:%lu\n", ++err_cnt);
			continue;
		}

		pktio_tmp = odp_pktio_get_input(pkt);
		outq_def = odp_pktio_outq_getdef(pktio_tmp);

		if (outq_def == ODP_QUEUE_INVALID) {
			ODP_ERR("  [%02i] Error: def output-Q query\n", thr);
			return NULL;
		}

		/* Swap Eth MACs and possibly IP-addrs before sending back */
		swap_pkt_addrs(&pkt, 1);

		/* Enqueue the packet for output */
		odp_queue_enq(outq_def, buf);

		/* Print packet counts every once in a while */
		if (odp_unlikely(pkt_cnt++ % 100000 == 0)) {
			printf("  [%02i] pkt_cnt:%lu\n", thr, pkt_cnt);
			fflush(NULL);
		}
	}

/* unreachable */
}
예제 #8
0
/*
 * Main packet transmit routine. Transmit packets at a fixed rate for
 * specified length of time.
 */
static void *run_thread_tx(void *arg)
{
	test_globals_t *globals;
	int thr_id;
	odp_queue_t outq;
	pkt_tx_stats_t *stats;
	uint64_t next_tx_cycles, end_cycles, cur_cycles;
	uint64_t burst_gap_cycles;
	uint32_t batch_len;
	int unsent_pkts = 0;
	odp_event_t  tx_event[BATCH_LEN_MAX];
	uint64_t idle_start = 0;

	thread_args_t *targs = arg;

	batch_len = targs->batch_len;

	if (batch_len > BATCH_LEN_MAX)
		batch_len = BATCH_LEN_MAX;

	thr_id = odp_thread_id();

	globals = odp_shm_addr(odp_shm_lookup("test_globals"));
	stats = &globals->tx_stats[thr_id];

	outq = odp_pktio_outq_getdef(globals->pktio_tx);
	if (outq == ODP_QUEUE_INVALID)
		LOG_ABORT("Failed to get output queue for thread %d\n", thr_id);

	burst_gap_cycles = odp_time_ns_to_cycles(
						 (ODP_TIME_SEC * 999) / (1000 * targs->pps / (targs->batch_len)));

	odp_barrier_wait(&globals->tx_barrier);

	cur_cycles     = odp_time_cycles();
	next_tx_cycles = cur_cycles;
	end_cycles     = cur_cycles +
			 odp_time_ns_to_cycles(targs->duration * ODP_TIME_SEC);

	while (cur_cycles < end_cycles) {
		unsigned alloc_cnt = 0, tx_cnt;

		if (cur_cycles < next_tx_cycles) {
			cur_cycles = odp_time_cycles();
			if (idle_start == 0)
				idle_start = cur_cycles;
			continue;
		}

		if (idle_start) {
			stats->s.idle_cycles += odp_time_diff_cycles(
							idle_start, cur_cycles);
			idle_start = 0;
		}

		next_tx_cycles += burst_gap_cycles;

		alloc_cnt = alloc_packets(tx_event, batch_len - unsent_pkts);
		if (alloc_cnt != batch_len)
			stats->s.alloc_failures++;

		tx_cnt = send_packets(outq, tx_event, alloc_cnt);
		unsent_pkts = alloc_cnt - tx_cnt;
		stats->s.enq_failures += unsent_pkts;
		stats->s.tx_cnt += tx_cnt;

		cur_cycles = odp_time_cycles();
	}

	VPRINT(" %02d: TxPkts %-8"PRIu64" EnqFail %-6"PRIu64
	       " AllocFail %-6"PRIu64" Idle %"PRIu64"ms\n",
	       thr_id, stats->s.tx_cnt,
	       stats->s.enq_failures, stats->s.alloc_failures,
	       odp_time_cycles_to_ns(stats->s.idle_cycles)/1000/1000);

	return NULL;
}
예제 #9
0
static void *gen_send_thread(void *arg)
{
	int thr;
	odp_pktio_t pktio;
	thread_args_t *thr_args;
	odp_queue_t outq_def;
	odp_packet_t pkt[PKT_BURST_SZ];

	thr = odp_thread_id();
	thr_args = arg;

	pktio = odp_pktio_lookup(thr_args->pktio_dev);
	if (pktio == ODP_PKTIO_INVALID) {
		EXAMPLE_ERR("  [%02i] Error: lookup of pktio %s failed\n",
			    thr, thr_args->pktio_dev);
		return NULL;
	}

	outq_def = odp_pktio_outq_getdef(pktio);
	if (outq_def == ODP_QUEUE_INVALID) {
		EXAMPLE_ERR("  [%02i] Error: def output-Q query\n", thr);
		return NULL;
	}

	printf("  [%02i] created mode: SEND\n", thr);
	for (int i = 0; i < PKT_BURST_SZ; ++i) {
		if (args->appl.mode == APPL_MODE_UDP)
			pkt[i] = pack_udp_pkt(thr_args->pool);
		else if (args->appl.mode == APPL_MODE_PING)
			pkt[i] = pack_icmp_pkt(thr_args->pool);
		else
			pkt[i] = ODP_PACKET_INVALID;

		if (!odp_packet_is_valid(pkt[i])) {
			EXAMPLE_ERR("  [%2i] alloc_single failed\n", thr);
			return NULL;
		}
	}

	for (;;) {
		int err;

		err = odp_queue_enq_multi(outq_def, (odp_event_t*)pkt, PKT_BURST_SZ);
		if (err != PKT_BURST_SZ) {
			/* EXAMPLE_ERR("  [%02i] send pkt err!\n", thr); */
			/* return NULL; */
		}

		static uint64_t toto = 0;
		if (args->appl.interval != 0) {
			printf("  [%02i] send pkt no:%"PRIu64" seq %"PRIu64"\n",
			       thr,
			       odp_atomic_load_u64(&counters.seq),
				   toto++);
			usleep(args->appl.interval);
			/* millisleep(args->appl.interval, */
			/* 	   thr_args->tp, */
			/* 	   thr_args->tim, */
			/* 	   thr_args->tq, */
			/* 	   thr_args->tmo_ev); */

		}
	}
	printf("Done\n");
	_exit(0);
	/* receive number of reply pks until timeout */
	if (args->appl.mode == APPL_MODE_PING && args->appl.number > 0) {
		while (args->appl.timeout >= 0) {
			if (odp_atomic_load_u64(&counters.icmp) >=
			    (unsigned int)args->appl.number)
				break;
			millisleep(DEFAULT_PKT_INTERVAL,
				   thr_args->tp,
				   thr_args->tim,
				   thr_args->tq,
				   thr_args->tmo_ev);
			args->appl.timeout--;
		}
	}

	return arg;
}
예제 #10
0
파일: pktio.c 프로젝트: kalray/odp-mppa
void pktio_test_start_stop(void)
{
	odp_pktio_t pktio[MAX_NUM_IFACES];
	odp_packet_t pkt;
	odp_event_t tx_ev[100];
	odp_event_t ev;
	int i, pkts, ret, alloc = 0;
	odp_queue_t outq;
	uint64_t wait = odp_schedule_wait_time(ODP_TIME_MSEC_IN_NS);

	for (i = 0; i < num_ifaces; i++) {
		pktio[i] = create_pktio(i, ODP_PKTIN_MODE_SCHED,
					ODP_PKTOUT_MODE_SEND);
		CU_ASSERT_FATAL(pktio[i] != ODP_PKTIO_INVALID);
		create_inq(pktio[i],  ODP_QUEUE_TYPE_SCHED);
	}

	outq = odp_pktio_outq_getdef(pktio[0]);

	/* Interfaces are stopped by default,
	 * Check that stop when stopped generates an error */
	ret = odp_pktio_stop(pktio[0]);
	CU_ASSERT(ret <= 0);

	/* start first */
	ret = odp_pktio_start(pktio[0]);
	CU_ASSERT(ret == 0);
	/* Check that start when started generates an error */
	ret = odp_pktio_start(pktio[0]);
	CU_ASSERT(ret < 0);

	/* Test Rx on a stopped interface. Only works if there are 2 */
	if (num_ifaces > 1) {
		for (alloc = 0; alloc < 100; alloc++) {
			pkt = odp_packet_alloc(default_pkt_pool, packet_len);
			if (pkt == ODP_PACKET_INVALID)
				break;
			pktio_init_packet(pkt);

			pktio_pkt_set_macs(pkt, pktio[0], pktio[1]);
			if (pktio_fixup_checksums(pkt) != 0) {
				odp_packet_free(pkt);
				break;
			}

			tx_ev[alloc] = odp_packet_to_event(pkt);
		}

		for (pkts = 0; pkts != alloc; ) {
			ret = odp_queue_enq_multi(outq, &tx_ev[pkts],
						  alloc - pkts);
			if (ret < 0) {
				CU_FAIL("unable to enqueue packet\n");
				break;
			}
			pkts += ret;
		}
		/* check that packets did not arrive */
		for (i = 0, pkts = 0; i < 1000; i++) {
			ev = odp_schedule(NULL, wait);
			if (ev == ODP_EVENT_INVALID)
				continue;

			if (odp_event_type(ev) == ODP_EVENT_PACKET) {
				pkt = odp_packet_from_event(ev);
				if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
					pkts++;
			}
			odp_event_free(ev);
		}
		if (pkts)
			CU_FAIL("pktio stopped, received unexpected events");

		/* start both, send and get packets */
		/* 0 already started */
		ret = odp_pktio_start(pktio[1]);
		CU_ASSERT(ret == 0);

		/* flush packets with magic number in pipes */
		for (i = 0; i < 1000; i++) {
			ev = odp_schedule(NULL, wait);
			if (ev != ODP_EVENT_INVALID)
				odp_event_free(ev);
		}
	}

	/* alloc */
	for (alloc = 0; alloc < 100; alloc++) {
		pkt = odp_packet_alloc(default_pkt_pool, packet_len);
		if (pkt == ODP_PACKET_INVALID)
			break;
		pktio_init_packet(pkt);
		if (num_ifaces > 1) {
			pktio_pkt_set_macs(pkt, pktio[0], pktio[1]);
			if (pktio_fixup_checksums(pkt) != 0) {
				odp_packet_free(pkt);
				break;
			}
		}
		tx_ev[alloc] = odp_packet_to_event(pkt);
	}

	/* send */
	for (pkts = 0; pkts != alloc; ) {
		ret = odp_queue_enq_multi(outq, &tx_ev[pkts], alloc - pkts);
		if (ret < 0) {
			CU_FAIL("unable to enqueue packet\n");
			break;
		}
		pkts += ret;
	}

	/* get */
	for (i = 0, pkts = 0; i < 100; i++) {
		ev = odp_schedule(NULL, wait);
		if (ev != ODP_EVENT_INVALID) {
			if (odp_event_type(ev) == ODP_EVENT_PACKET) {
				pkt = odp_packet_from_event(ev);
				if (pktio_pkt_seq(pkt) != TEST_SEQ_INVALID)
					pkts++;
			}
			odp_event_free(ev);
		}
	}
	CU_ASSERT(pkts == alloc);

	for (i = 0; i < num_ifaces; i++) {
		CU_ASSERT(odp_pktio_stop(pktio[i]) == 0);
		destroy_inq(pktio[i]);
		CU_ASSERT(odp_pktio_close(pktio[i]) == 0);
	}
}
예제 #11
0
/*
 * Main packet transmit routine. Transmit packets at a fixed rate for
 * specified length of time.
 */
static void *run_thread_tx(void *arg)
{
	test_globals_t *globals;
	int thr_id;
	odp_queue_t outq;
	pkt_tx_stats_t *stats;
	odp_time_t cur_time, send_time_end, send_duration;
	odp_time_t burst_gap_end, burst_gap;
	uint32_t batch_len;
	int unsent_pkts = 0;
	odp_event_t  tx_event[BATCH_LEN_MAX];
	odp_time_t idle_start = ODP_TIME_NULL;

	thread_args_t *targs = arg;

	batch_len = targs->batch_len;

	if (batch_len > BATCH_LEN_MAX)
		batch_len = BATCH_LEN_MAX;

	thr_id = odp_thread_id();

	globals = odp_shm_addr(odp_shm_lookup("test_globals"));
	stats = &globals->tx_stats[thr_id];

	outq = odp_pktio_outq_getdef(globals->pktio_tx);
	if (outq == ODP_QUEUE_INVALID)
		LOG_ABORT("Failed to get output queue for thread %d\n", thr_id);

	burst_gap = odp_time_local_from_ns(
			ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len));
	send_duration =
		odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS);

	odp_barrier_wait(&globals->tx_barrier);

	cur_time     = odp_time_local();
	send_time_end = odp_time_sum(cur_time, send_duration);
	burst_gap_end = cur_time;
	while (odp_time_cmp(send_time_end, cur_time) > 0) {
		unsigned alloc_cnt = 0, tx_cnt;

		if (odp_time_cmp(burst_gap_end, cur_time) > 0) {
			cur_time = odp_time_local();
			if (!odp_time_cmp(idle_start, ODP_TIME_NULL))
				idle_start = cur_time;
			continue;
		}

		if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) {
			odp_time_t diff = odp_time_diff(cur_time, idle_start);

			stats->s.idle_ticks =
				odp_time_sum(diff, stats->s.idle_ticks);

			idle_start = ODP_TIME_NULL;
		}

		burst_gap_end = odp_time_sum(burst_gap_end, burst_gap);

		alloc_cnt = alloc_packets(tx_event, batch_len - unsent_pkts);
		if (alloc_cnt != batch_len)
			stats->s.alloc_failures++;

		tx_cnt = send_packets(outq, tx_event, alloc_cnt);
		unsent_pkts = alloc_cnt - tx_cnt;
		stats->s.enq_failures += unsent_pkts;
		stats->s.tx_cnt += tx_cnt;

		cur_time = odp_time_local();
	}

	VPRINT(" %02d: TxPkts %-8"PRIu64" EnqFail %-6"PRIu64
	       " AllocFail %-6"PRIu64" Idle %"PRIu64"ms\n",
	       thr_id, stats->s.tx_cnt,
	       stats->s.enq_failures, stats->s.alloc_failures,
	       odp_time_to_ns(stats->s.idle_ticks) /
	       (uint64_t)ODP_TIME_MSEC_IN_NS);

	return NULL;
}