106ac4c87SKonstantin Ananyev/*
206ac4c87SKonstantin Ananyev * Copyright (c) 2016  Intel Corporation.
306ac4c87SKonstantin Ananyev * Licensed under the Apache License, Version 2.0 (the "License");
406ac4c87SKonstantin Ananyev * you may not use this file except in compliance with the License.
506ac4c87SKonstantin Ananyev * You may obtain a copy of the License at:
606ac4c87SKonstantin Ananyev *
706ac4c87SKonstantin Ananyev *     http://www.apache.org/licenses/LICENSE-2.0
806ac4c87SKonstantin Ananyev *
906ac4c87SKonstantin Ananyev * Unless required by applicable law or agreed to in writing, software
1006ac4c87SKonstantin Ananyev * distributed under the License is distributed on an "AS IS" BASIS,
1106ac4c87SKonstantin Ananyev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1206ac4c87SKonstantin Ananyev * See the License for the specific language governing permissions and
1306ac4c87SKonstantin Ananyev * limitations under the License.
1406ac4c87SKonstantin Ananyev */
153395610eSKonstantin Ananyev
163395610eSKonstantin Ananyev#include <rte_malloc.h>
173395610eSKonstantin Ananyev#include <rte_errno.h>
183395610eSKonstantin Ananyev#include <rte_ethdev.h>
193395610eSKonstantin Ananyev#include <rte_ip.h>
203395610eSKonstantin Ananyev#include <rte_ip_frag.h>
213395610eSKonstantin Ananyev#include <rte_udp.h>
223395610eSKonstantin Ananyev
23aa97dd1cSKonstantin Ananyev#include "udp_stream.h"
243395610eSKonstantin Ananyev#include "misc.h"
253395610eSKonstantin Ananyev
263395610eSKonstantin Ananyevstatic inline struct tle_udp_stream *
27aa97dd1cSKonstantin Ananyevrx_stream_obtain(struct tle_dev *dev, uint32_t type, uint32_t port)
283395610eSKonstantin Ananyev{
293395610eSKonstantin Ananyev	struct tle_udp_stream *s;
303395610eSKonstantin Ananyev
31aa97dd1cSKonstantin Ananyev	if (type >= TLE_VNUM || dev->dp[type] == NULL)
323395610eSKonstantin Ananyev		return NULL;
333395610eSKonstantin Ananyev
34aa97dd1cSKonstantin Ananyev	s = (struct tle_udp_stream *)dev->dp[type]->streams[port];
353395610eSKonstantin Ananyev	if (s == NULL)
363395610eSKonstantin Ananyev		return NULL;
373395610eSKonstantin Ananyev
383395610eSKonstantin Ananyev	if (rwl_acquire(&s->rx.use) < 0)
393395610eSKonstantin Ananyev		return NULL;
403395610eSKonstantin Ananyev
413395610eSKonstantin Ananyev	return s;
423395610eSKonstantin Ananyev}
433395610eSKonstantin Ananyev
443395610eSKonstantin Ananyevstatic inline uint16_t
453395610eSKonstantin Ananyevget_pkt_type(const struct rte_mbuf *m)
463395610eSKonstantin Ananyev{
473395610eSKonstantin Ananyev	uint32_t v;
483395610eSKonstantin Ananyev
493395610eSKonstantin Ananyev	v = m->packet_type &
503395610eSKonstantin Ananyev		(RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_MASK);
513395610eSKonstantin Ananyev	if (v == (RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP))
52aa97dd1cSKonstantin Ananyev		return TLE_V4;
533395610eSKonstantin Ananyev	else if (v == (RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP))
54aa97dd1cSKonstantin Ananyev		return TLE_V6;
553395610eSKonstantin Ananyev	else
56aa97dd1cSKonstantin Ananyev		return TLE_VNUM;
573395610eSKonstantin Ananyev}
583395610eSKonstantin Ananyev
59aa97dd1cSKonstantin Ananyevstatic inline union l4_ports
60c5f8f7f0SJianfeng Tanpkt_info(struct rte_mbuf *m, union l4_ports *ports, union ipv4_addrs *addr4,
613395610eSKonstantin Ananyev	union ipv6_addrs **addr6)
623395610eSKonstantin Ananyev{
633395610eSKonstantin Ananyev	uint32_t len;
64aa97dd1cSKonstantin Ananyev	union l4_ports ret, *up;
653395610eSKonstantin Ananyev	union ipv4_addrs *pa4;
663395610eSKonstantin Ananyev
673395610eSKonstantin Ananyev	ret.src = get_pkt_type(m);
683395610eSKonstantin Ananyev
693395610eSKonstantin Ananyev	len = m->l2_len;
70aa97dd1cSKonstantin Ananyev	if (ret.src == TLE_V4) {
713395610eSKonstantin Ananyev		pa4 = rte_pktmbuf_mtod_offset(m, union ipv4_addrs *,
723395610eSKonstantin Ananyev			len + offsetof(struct ipv4_hdr, src_addr));
733395610eSKonstantin Ananyev		addr4->raw = pa4->raw;
74aa97dd1cSKonstantin Ananyev	} else if (ret.src == TLE_V6) {
753395610eSKonstantin Ananyev		*addr6 = rte_pktmbuf_mtod_offset(m, union ipv6_addrs *,
763395610eSKonstantin Ananyev			len + offsetof(struct ipv6_hdr, src_addr));
773395610eSKonstantin Ananyev	}
783395610eSKonstantin Ananyev
793395610eSKonstantin Ananyev	len += m->l3_len;
80aa97dd1cSKonstantin Ananyev	up = rte_pktmbuf_mtod_offset(m, union l4_ports *,
813395610eSKonstantin Ananyev		len + offsetof(struct udp_hdr, src_port));
823395610eSKonstantin Ananyev	ports->raw = up->raw;
833395610eSKonstantin Ananyev	ret.dst = ports->dst;
843395610eSKonstantin Ananyev	return ret;
853395610eSKonstantin Ananyev}
863395610eSKonstantin Ananyev
873395610eSKonstantin Ananyev/*
883395610eSKonstantin Ananyev * Helper routine, enqueues packets to the stream and calls RX
893395610eSKonstantin Ananyev * notification callback, if needed.
903395610eSKonstantin Ananyev */
913395610eSKonstantin Ananyevstatic inline uint16_t
923395610eSKonstantin Ananyevrx_stream(struct tle_udp_stream *s, void *mb[], struct rte_mbuf *rp[],
933395610eSKonstantin Ananyev	int32_t rc[], uint32_t num)
943395610eSKonstantin Ananyev{
953395610eSKonstantin Ananyev	uint32_t i, k, r;
963395610eSKonstantin Ananyev
97fbba0a3bSMohammad Abdul Awal	r = _rte_ring_enqueue_burst(s->rx.q, mb, num);
983395610eSKonstantin Ananyev
993395610eSKonstantin Ananyev	/* if RX queue was empty invoke user RX notification callback. */
1003395610eSKonstantin Ananyev	if (s->rx.cb.func != NULL && r != 0 && rte_ring_count(s->rx.q) == r)
101aa97dd1cSKonstantin Ananyev		s->rx.cb.func(s->rx.cb.data, &s->s);
1023395610eSKonstantin Ananyev
1033395610eSKonstantin Ananyev	for (i = r, k = 0; i != num; i++, k++) {
1043395610eSKonstantin Ananyev		rc[k] = ENOBUFS;
1053395610eSKonstantin Ananyev		rp[k] = mb[i];
1063395610eSKonstantin Ananyev	}
1073395610eSKonstantin Ananyev
1083395610eSKonstantin Ananyev	return r;
1093395610eSKonstantin Ananyev}
1103395610eSKonstantin Ananyev
1113395610eSKonstantin Ananyevstatic inline uint16_t
1123395610eSKonstantin Ananyevrx_stream6(struct tle_udp_stream *s, struct rte_mbuf *pkt[],
113aa97dd1cSKonstantin Ananyev	union ipv6_addrs *addr[], union l4_ports port[],
1143395610eSKonstantin Ananyev	struct rte_mbuf *rp[], int32_t rc[], uint16_t num)
1153395610eSKonstantin Ananyev{
1163395610eSKonstantin Ananyev	uint32_t i, k, n;
1173395610eSKonstantin Ananyev	void *mb[num];
1183395610eSKonstantin Ananyev
1193395610eSKonstantin Ananyev	k = 0;
1203395610eSKonstantin Ananyev	n = 0;
1213395610eSKonstantin Ananyev
1223395610eSKonstantin Ananyev	for (i = 0; i != num; i++) {
1233395610eSKonstantin Ananyev
124aa97dd1cSKonstantin Ananyev		if ((port[i].raw & s->s.pmsk.raw) != s->s.port.raw ||
125aa97dd1cSKonstantin Ananyev				ymm_mask_cmp(&addr[i]->raw, &s->s.ipv6.addr.raw,
126aa97dd1cSKonstantin Ananyev				&s->s.ipv6.mask.raw) != 0) {
1273395610eSKonstantin Ananyev			rc[k] = ENOENT;
1283395610eSKonstantin Ananyev			rp[k] = pkt[i];
1293395610eSKonstantin Ananyev			k++;
1303395610eSKonstantin Ananyev		} else {
1313395610eSKonstantin Ananyev			mb[n] = pkt[i];
1323395610eSKonstantin Ananyev			n++;
1333395610eSKonstantin Ananyev		}
1343395610eSKonstantin Ananyev	}
1353395610eSKonstantin Ananyev
1363395610eSKonstantin Ananyev	return rx_stream(s, mb, rp + k, rc + k, n);
1373395610eSKonstantin Ananyev}
1383395610eSKonstantin Ananyev
1393395610eSKonstantin Ananyevstatic inline uint16_t
1403395610eSKonstantin Ananyevrx_stream4(struct tle_udp_stream *s, struct rte_mbuf *pkt[],
141aa97dd1cSKonstantin Ananyev	union ipv4_addrs addr[], union l4_ports port[],
1423395610eSKonstantin Ananyev	struct rte_mbuf *rp[], int32_t rc[], uint16_t num)
1433395610eSKonstantin Ananyev{
1443395610eSKonstantin Ananyev	uint32_t i, k, n;
1453395610eSKonstantin Ananyev	void *mb[num];
1463395610eSKonstantin Ananyev
1473395610eSKonstantin Ananyev	k = 0;
1483395610eSKonstantin Ananyev	n = 0;
1493395610eSKonstantin Ananyev
1503395610eSKonstantin Ananyev	for (i = 0; i != num; i++) {
1513395610eSKonstantin Ananyev
152aa97dd1cSKonstantin Ananyev		if ((addr[i].raw & s->s.ipv4.mask.raw) != s->s.ipv4.addr.raw ||
153aa97dd1cSKonstantin Ananyev				(port[i].raw & s->s.pmsk.raw) !=
154aa97dd1cSKonstantin Ananyev				s->s.port.raw) {
1553395610eSKonstantin Ananyev			rc[k] = ENOENT;
1563395610eSKonstantin Ananyev			rp[k] = pkt[i];
1573395610eSKonstantin Ananyev			k++;
1583395610eSKonstantin Ananyev		} else {
1593395610eSKonstantin Ananyev			mb[n] = pkt[i];
1603395610eSKonstantin Ananyev			n++;
1613395610eSKonstantin Ananyev		}
1623395610eSKonstantin Ananyev	}
1633395610eSKonstantin Ananyev
1643395610eSKonstantin Ananyev	return rx_stream(s, mb, rp + k, rc + k, n);
1653395610eSKonstantin Ananyev}
1663395610eSKonstantin Ananyev
1673395610eSKonstantin Ananyevuint16_t
168aa97dd1cSKonstantin Ananyevtle_udp_rx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[],
1693395610eSKonstantin Ananyev	struct rte_mbuf *rp[], int32_t rc[], uint16_t num)
1703395610eSKonstantin Ananyev{
1713395610eSKonstantin Ananyev	struct tle_udp_stream *s;
1723395610eSKonstantin Ananyev	uint32_t i, j, k, n, p, t;
173aa97dd1cSKonstantin Ananyev	union l4_ports tp[num], port[num];
1743395610eSKonstantin Ananyev	union ipv4_addrs a4[num];
1753395610eSKonstantin Ananyev	union ipv6_addrs *pa6[num];
1763395610eSKonstantin Ananyev
1773395610eSKonstantin Ananyev	for (i = 0; i != num; i++)
178c5f8f7f0SJianfeng Tan		tp[i] = pkt_info(pkt[i], &port[i], &a4[i], &pa6[i]);
1793395610eSKonstantin Ananyev
1803395610eSKonstantin Ananyev	k = 0;
1813395610eSKonstantin Ananyev	for (i = 0; i != num; i = j) {
1823395610eSKonstantin Ananyev
1833395610eSKonstantin Ananyev		for (j = i + 1; j != num && tp[j].raw == tp[i].raw; j++)
1843395610eSKonstantin Ananyev			;
1853395610eSKonstantin Ananyev
1863395610eSKonstantin Ananyev		t = tp[i].src;
1873395610eSKonstantin Ananyev		p = tp[i].dst;
1883395610eSKonstantin Ananyev		s = rx_stream_obtain(dev, t, p);
1893395610eSKonstantin Ananyev		if (s != NULL) {
1903395610eSKonstantin Ananyev
191aa97dd1cSKonstantin Ananyev			if (t == TLE_V4)
1923395610eSKonstantin Ananyev				n = rx_stream4(s, pkt + i, a4 + i,
1933395610eSKonstantin Ananyev					port + i, rp + k, rc + k, j - i);
1943395610eSKonstantin Ananyev			else
1953395610eSKonstantin Ananyev				n = rx_stream6(s, pkt + i, pa6 + i, port + i,
1963395610eSKonstantin Ananyev					rp + k, rc + k, j - i);
1973395610eSKonstantin Ananyev
1983395610eSKonstantin Ananyev			k += j - i - n;
1993395610eSKonstantin Ananyev
2003395610eSKonstantin Ananyev			if (s->rx.ev != NULL)
2013395610eSKonstantin Ananyev				tle_event_raise(s->rx.ev);
2023395610eSKonstantin Ananyev			rwl_release(&s->rx.use);
2033395610eSKonstantin Ananyev
2043395610eSKonstantin Ananyev		} else {
2053395610eSKonstantin Ananyev			for (; i != j; i++) {
2063395610eSKonstantin Ananyev				rc[k] = ENOENT;
2073395610eSKonstantin Ananyev				rp[k] = pkt[i];
2083395610eSKonstantin Ananyev				k++;
2093395610eSKonstantin Ananyev			}
2103395610eSKonstantin Ananyev		}
2113395610eSKonstantin Ananyev	}
2123395610eSKonstantin Ananyev
2133395610eSKonstantin Ananyev	return num - k;
2143395610eSKonstantin Ananyev}
2153395610eSKonstantin Ananyev
2163395610eSKonstantin Ananyevstatic inline void
217aa97dd1cSKonstantin Ananyevstream_drb_release(struct tle_udp_stream *s, struct tle_drb *drb[],
2188efc4c11SKonstantin Ananyev	uint32_t nb_drb)
2193395610eSKonstantin Ananyev{
2203395610eSKonstantin Ananyev	uint32_t n;
2213395610eSKonstantin Ananyev
2228efc4c11SKonstantin Ananyev	n = rte_ring_count(s->tx.drb.r);
223fbba0a3bSMohammad Abdul Awal	_rte_ring_enqueue_burst(s->tx.drb.r, (void **)drb, nb_drb);
2243395610eSKonstantin Ananyev
2253395610eSKonstantin Ananyev	/* If stream is still open, then mark it as avaialble for writing. */
2263395610eSKonstantin Ananyev	if (rwl_try_acquire(&s->tx.use) > 0) {
2273395610eSKonstantin Ananyev
2283395610eSKonstantin Ananyev		if (s->tx.ev != NULL)
2293395610eSKonstantin Ananyev			tle_event_raise(s->tx.ev);
2303395610eSKonstantin Ananyev
2313395610eSKonstantin Ananyev		/* if stream send buffer was full invoke TX callback */
2328efc4c11SKonstantin Ananyev		else if (s->tx.cb.func != NULL && n == 0)
233aa97dd1cSKonstantin Ananyev			s->tx.cb.func(s->tx.cb.data, &s->s);
2343395610eSKonstantin Ananyev
2353395610eSKonstantin Ananyev	}
2363395610eSKonstantin Ananyev
2373395610eSKonstantin Ananyev	rwl_release(&s->tx.use);
2383395610eSKonstantin Ananyev}
2393395610eSKonstantin Ananyev
2403395610eSKonstantin Ananyevuint16_t
241aa97dd1cSKonstantin Ananyevtle_udp_tx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], uint16_t num)
2423395610eSKonstantin Ananyev{
2438efc4c11SKonstantin Ananyev	uint32_t i, j, k, n;
2448efc4c11SKonstantin Ananyev	struct tle_drb *drb[num];
2458efc4c11SKonstantin Ananyev	struct tle_udp_stream *s;
2463395610eSKonstantin Ananyev
2478efc4c11SKonstantin Ananyev	/* extract packets from device TX queue. */
2483395610eSKonstantin Ananyev
2498efc4c11SKonstantin Ananyev	k = num;
2508efc4c11SKonstantin Ananyev	n = tle_dring_sc_dequeue(&dev->tx.dr, (const void **)(uintptr_t)pkt,
2518efc4c11SKonstantin Ananyev		num, drb, &k);
2523395610eSKonstantin Ananyev
2538efc4c11SKonstantin Ananyev	if (n == 0)
2548efc4c11SKonstantin Ananyev		return 0;
2553395610eSKonstantin Ananyev
2568efc4c11SKonstantin Ananyev	/* free empty drbs and notify related streams. */
2578efc4c11SKonstantin Ananyev
2588efc4c11SKonstantin Ananyev	for (i = 0; i != k; i = j) {
2598efc4c11SKonstantin Ananyev		s = drb[i]->udata;
260aa97dd1cSKonstantin Ananyev		for (j = i + 1; j != k && s == drb[j]->udata; j++)
2618efc4c11SKonstantin Ananyev			;
2628efc4c11SKonstantin Ananyev		stream_drb_release(s, drb + i, j - i);
2633395610eSKonstantin Ananyev	}
2643395610eSKonstantin Ananyev
2658efc4c11SKonstantin Ananyev	return n;
2663395610eSKonstantin Ananyev}
2673395610eSKonstantin Ananyev
2683395610eSKonstantin Ananyev/*
2693395610eSKonstantin Ananyev * helper function, do the necessary pre-processing for the received packets
2703395610eSKonstantin Ananyev * before handiing them to the strem_recv caller.
2713395610eSKonstantin Ananyev */
2728efc4c11SKonstantin Ananyevstatic inline uint32_t
2738efc4c11SKonstantin Ananyevrecv_pkt_process(struct rte_mbuf *m[], uint32_t num, uint32_t type)
2743395610eSKonstantin Ananyev{
2758efc4c11SKonstantin Ananyev	uint32_t i, k;
276c5f8f7f0SJianfeng Tan	uint64_t flg[num], ofl[num];
2778efc4c11SKonstantin Ananyev
2788efc4c11SKonstantin Ananyev	for (i = 0; i != num; i++) {
2798efc4c11SKonstantin Ananyev		flg[i] = m[i]->ol_flags;
2808efc4c11SKonstantin Ananyev		ofl[i] = m[i]->tx_offload;
2818efc4c11SKonstantin Ananyev	}
2828efc4c11SKonstantin Ananyev
2838efc4c11SKonstantin Ananyev	k = 0;
2848efc4c11SKonstantin Ananyev	for (i = 0; i != num; i++) {
2858efc4c11SKonstantin Ananyev
2868efc4c11SKonstantin Ananyev		/* drop packets with invalid cksum(s). */
287c5f8f7f0SJianfeng Tan		if (check_pkt_csum(m[i], flg[i], type, IPPROTO_UDP) != 0) {
2888efc4c11SKonstantin Ananyev			rte_pktmbuf_free(m[i]);
2898efc4c11SKonstantin Ananyev			m[i] = NULL;
2908efc4c11SKonstantin Ananyev			k++;
291c5f8f7f0SJianfeng Tan		} else
2929c7aa95eSKonstantin Ananyev			rte_pktmbuf_adj(m[i], _tx_offload_l4_offset(ofl[i]));
2933395610eSKonstantin Ananyev	}
2943395610eSKonstantin Ananyev
2958efc4c11SKonstantin Ananyev	return k;
2963395610eSKonstantin Ananyev}
2973395610eSKonstantin Ananyev
2983395610eSKonstantin Ananyevuint16_t
299aa97dd1cSKonstantin Ananyevtle_udp_stream_recv(struct tle_stream *us, struct rte_mbuf *pkt[], uint16_t num)
3003395610eSKonstantin Ananyev{
3018efc4c11SKonstantin Ananyev	uint32_t k, n;
302aa97dd1cSKonstantin Ananyev	struct tle_udp_stream *s;
3033395610eSKonstantin Ananyev
304aa97dd1cSKonstantin Ananyev	s = UDP_STREAM(us);
305fbba0a3bSMohammad Abdul Awal	n = _rte_ring_mc_dequeue_burst(s->rx.q, (void **)pkt, num);
3063395610eSKonstantin Ananyev	if (n == 0)
3073395610eSKonstantin Ananyev		return 0;
3083395610eSKonstantin Ananyev
3093395610eSKonstantin Ananyev	/*
3103395610eSKonstantin Ananyev	 * if we still have packets to read,
3113395610eSKonstantin Ananyev	 * then rearm stream RX event.
3123395610eSKonstantin Ananyev	 */
3133395610eSKonstantin Ananyev	if (n == num && rte_ring_count(s->rx.q) != 0) {
3143395610eSKonstantin Ananyev		if (rwl_try_acquire(&s->rx.use) > 0 && s->rx.ev != NULL)
3153395610eSKonstantin Ananyev			tle_event_raise(s->rx.ev);
3163395610eSKonstantin Ananyev		rwl_release(&s->rx.use);
3173395610eSKonstantin Ananyev	}
3183395610eSKonstantin Ananyev
319aa97dd1cSKonstantin Ananyev	k = recv_pkt_process(pkt, n, s->s.type);
3203395610eSKonstantin Ananyev	return compress_pkt_list(pkt, n, k);
3213395610eSKonstantin Ananyev}
3223395610eSKonstantin Ananyev
3233395610eSKonstantin Ananyevstatic inline int
3243395610eSKonstantin Ananyevudp_fill_mbuf(struct rte_mbuf *m,
3253395610eSKonstantin Ananyev	uint32_t type, uint64_t ol_flags, uint32_t pid,
326aa97dd1cSKonstantin Ananyev	union udph udph, const struct tle_dest *dst)
3273395610eSKonstantin Ananyev{
3283395610eSKonstantin Ananyev	uint32_t len, plen;
3293395610eSKonstantin Ananyev	char *l2h;
3303395610eSKonstantin Ananyev	union udph *l4h;
3313395610eSKonstantin Ananyev
3323395610eSKonstantin Ananyev	len = dst->l2_len + dst->l3_len;
3333395610eSKonstantin Ananyev	plen = m->pkt_len;
3343395610eSKonstantin Ananyev
3353395610eSKonstantin Ananyev	/* copy to mbuf L2/L3 header template. */
3363395610eSKonstantin Ananyev
3373395610eSKonstantin Ananyev	l2h = rte_pktmbuf_prepend(m, len + sizeof(*l4h));
3383395610eSKonstantin Ananyev	if (l2h == NULL)
3393395610eSKonstantin Ananyev		return -ENOBUFS;
3403395610eSKonstantin Ananyev
3413395610eSKonstantin Ananyev	/* copy L2/L3 header */
3423395610eSKonstantin Ananyev	rte_memcpy(l2h, dst->hdr, len);
3433395610eSKonstantin Ananyev
3443395610eSKonstantin Ananyev	/* copy UDP header */
3453395610eSKonstantin Ananyev	l4h = (union udph *)(l2h + len);
3463395610eSKonstantin Ananyev	l4h->raw = udph.raw;
3473395610eSKonstantin Ananyev
3483395610eSKonstantin Ananyev	/* setup mbuf TX offload related fields. */
3493395610eSKonstantin Ananyev	m->tx_offload = _mbuf_tx_offload(dst->l2_len, dst->l3_len,
3503395610eSKonstantin Ananyev		sizeof(*l4h), 0, 0, 0);
3513395610eSKonstantin Ananyev	m->ol_flags |= ol_flags;
3523395610eSKonstantin Ananyev
3533395610eSKonstantin Ananyev	l4h->len = rte_cpu_to_be_16(plen + sizeof(*l4h));
3543395610eSKonstantin Ananyev
3553395610eSKonstantin Ananyev	/* update proto specific fields. */
3563395610eSKonstantin Ananyev
357aa97dd1cSKonstantin Ananyev	if (type == TLE_V4) {
3583395610eSKonstantin Ananyev		struct ipv4_hdr *l3h;
3593395610eSKonstantin Ananyev		l3h = (struct ipv4_hdr *)(l2h + dst->l2_len);
3603395610eSKonstantin Ananyev		l3h->packet_id = rte_cpu_to_be_16(pid);
3613395610eSKonstantin Ananyev		l3h->total_length = rte_cpu_to_be_16(plen + dst->l3_len +
3623395610eSKonstantin Ananyev			sizeof(*l4h));
3633395610eSKonstantin Ananyev
3643395610eSKonstantin Ananyev		if ((ol_flags & PKT_TX_UDP_CKSUM) != 0)
3653395610eSKonstantin Ananyev			l4h->cksum = _ipv4x_phdr_cksum(l3h, m->l3_len,
3663395610eSKonstantin Ananyev				ol_flags);
3673395610eSKonstantin Ananyev		else
3683395610eSKonstantin Ananyev			l4h->cksum = _ipv4_udptcp_mbuf_cksum(m, len, l3h);
3693395610eSKonstantin Ananyev
3703395610eSKonstantin Ananyev		if ((ol_flags & PKT_TX_IP_CKSUM) == 0)
3713395610eSKonstantin Ananyev			l3h->hdr_checksum = _ipv4x_cksum(l3h, m->l3_len);
3723395610eSKonstantin Ananyev	} else {
3733395610eSKonstantin Ananyev		struct ipv6_hdr *l3h;
3743395610eSKonstantin Ananyev		l3h = (struct ipv6_hdr *)(l2h + dst->l2_len);
3753395610eSKonstantin Ananyev		l3h->payload_len = rte_cpu_to_be_16(plen + sizeof(*l4h));
3763395610eSKonstantin Ananyev		if ((ol_flags & PKT_TX_UDP_CKSUM) != 0)
3773395610eSKonstantin Ananyev			l4h->cksum = rte_ipv6_phdr_cksum(l3h, ol_flags);
3783395610eSKonstantin Ananyev		else
3793395610eSKonstantin Ananyev			l4h->cksum = _ipv6_udptcp_mbuf_cksum(m, len, l3h);
3803395610eSKonstantin Ananyev	}
3813395610eSKonstantin Ananyev
3823395610eSKonstantin Ananyev	return 0;
3833395610eSKonstantin Ananyev}
3843395610eSKonstantin Ananyev
3853395610eSKonstantin Ananyev/* ???
3863395610eSKonstantin Ananyev * probably this function should be there -
3873395610eSKonstantin Ananyev * rte_ipv[4,6]_fragment_packet should do that.
3883395610eSKonstantin Ananyev */
3893395610eSKonstantin Ananyevstatic inline void
3903395610eSKonstantin Ananyevfrag_fixup(const struct rte_mbuf *ms, struct rte_mbuf *mf, uint32_t type)
3913395610eSKonstantin Ananyev{
3923395610eSKonstantin Ananyev	struct ipv4_hdr *l3h;
3933395610eSKonstantin Ananyev
3943395610eSKonstantin Ananyev	mf->ol_flags = ms->ol_flags;
3953395610eSKonstantin Ananyev	mf->tx_offload = ms->tx_offload;
3963395610eSKonstantin Ananyev
397aa97dd1cSKonstantin Ananyev	if (type == TLE_V4 && (ms->ol_flags & PKT_TX_IP_CKSUM) == 0) {
3983395610eSKonstantin Ananyev		l3h = rte_pktmbuf_mtod(mf, struct ipv4_hdr *);
3993395610eSKonstantin Ananyev		l3h->hdr_checksum = _ipv4x_cksum(l3h, mf->l3_len);
4003395610eSKonstantin Ananyev	}
4013395610eSKonstantin Ananyev}
4023395610eSKonstantin Ananyev
4033395610eSKonstantin Ananyev/*
4043395610eSKonstantin Ananyev * Returns negative for failure to fragment or actual number of fragments.
4053395610eSKonstantin Ananyev */
4063395610eSKonstantin Ananyevstatic inline int
4073395610eSKonstantin Ananyevfragment(struct rte_mbuf *pkt, struct rte_mbuf *frag[], uint32_t num,
408aa97dd1cSKonstantin Ananyev	uint32_t type, const struct tle_dest *dst)
4093395610eSKonstantin Ananyev{
4103395610eSKonstantin Ananyev	int32_t frag_num, i;
4113395610eSKonstantin Ananyev	uint16_t mtu;
4123395610eSKonstantin Ananyev	void *eth_hdr;
4133395610eSKonstantin Ananyev
4143395610eSKonstantin Ananyev	/* Remove the Ethernet header from the input packet */
4153395610eSKonstantin Ananyev	rte_pktmbuf_adj(pkt, dst->l2_len);
4163395610eSKonstantin Ananyev	mtu = dst->mtu - dst->l2_len;
4173395610eSKonstantin Ananyev
4183395610eSKonstantin Ananyev	/* fragment packet */
419aa97dd1cSKonstantin Ananyev	if (type == TLE_V4)
4203395610eSKonstantin Ananyev		frag_num = rte_ipv4_fragment_packet(pkt, frag, num, mtu,
4213395610eSKonstantin Ananyev			dst->head_mp, dst->head_mp);
4223395610eSKonstantin Ananyev	else
4233395610eSKonstantin Ananyev		frag_num = rte_ipv6_fragment_packet(pkt, frag, num, mtu,
4243395610eSKonstantin Ananyev			dst->head_mp, dst->head_mp);
4253395610eSKonstantin Ananyev
4263395610eSKonstantin Ananyev	if (frag_num > 0) {
4273395610eSKonstantin Ananyev		for (i = 0; i != frag_num; i++) {
4283395610eSKonstantin Ananyev
4293395610eSKonstantin Ananyev			frag_fixup(pkt, frag[i], type);
4303395610eSKonstantin Ananyev
4313395610eSKonstantin Ananyev			/* Move data_off to include l2 header first */
4323395610eSKonstantin Ananyev			eth_hdr = rte_pktmbuf_prepend(frag[i], dst->l2_len);
4333395610eSKonstantin Ananyev
4343395610eSKonstantin Ananyev			/* copy l2 header into fragment */
4353395610eSKonstantin Ananyev			rte_memcpy(eth_hdr, dst->hdr, dst->l2_len);
4363395610eSKonstantin Ananyev		}
4373395610eSKonstantin Ananyev	}
4383395610eSKonstantin Ananyev
4393395610eSKonstantin Ananyev	return frag_num;
4403395610eSKonstantin Ananyev}
4413395610eSKonstantin Ananyev
4428efc4c11SKonstantin Ananyevstatic inline void
4438efc4c11SKonstantin Ananyevstream_drb_free(struct tle_udp_stream *s, struct tle_drb *drbs[],
4448efc4c11SKonstantin Ananyev	uint32_t nb_drb)
4458efc4c11SKonstantin Ananyev{
446fbba0a3bSMohammad Abdul Awal	_rte_ring_enqueue_burst(s->tx.drb.r, (void **)drbs, nb_drb);
4478efc4c11SKonstantin Ananyev}
4488efc4c11SKonstantin Ananyev
4498efc4c11SKonstantin Ananyevstatic inline uint32_t
4508efc4c11SKonstantin Ananyevstream_drb_alloc(struct tle_udp_stream *s, struct tle_drb *drbs[],
4518efc4c11SKonstantin Ananyev	uint32_t nb_drb)
4528efc4c11SKonstantin Ananyev{
453fbba0a3bSMohammad Abdul Awal	return _rte_ring_dequeue_burst(s->tx.drb.r, (void **)drbs, nb_drb);
4548efc4c11SKonstantin Ananyev}
4558efc4c11SKonstantin Ananyev
4563395610eSKonstantin Ananyev/* enqueue up to num packets to the destination device queue. */
4573395610eSKonstantin Ananyevstatic inline uint16_t
458aa97dd1cSKonstantin Ananyevqueue_pkt_out(struct tle_udp_stream *s, struct tle_dev *dev,
4598efc4c11SKonstantin Ananyev		const void *pkt[], uint16_t nb_pkt,
4603fbc22a6SJielong Zhou		struct tle_drb *drbs[], uint32_t *nb_drb, uint8_t all_or_nothing)
4613395610eSKonstantin Ananyev{
4628efc4c11SKonstantin Ananyev	uint32_t bsz, i, n, nb, nbc, nbm;
4633395610eSKonstantin Ananyev
4648efc4c11SKonstantin Ananyev	bsz = s->tx.drb.nb_elem;
4653395610eSKonstantin Ananyev
4668efc4c11SKonstantin Ananyev	/* calulate how many drbs are needed.*/
4678efc4c11SKonstantin Ananyev	nbc = *nb_drb;
4688efc4c11SKonstantin Ananyev	nbm = (nb_pkt + bsz - 1) / bsz;
4698efc4c11SKonstantin Ananyev	nb = RTE_MAX(nbm, nbc) - nbc;
4703395610eSKonstantin Ananyev
4718efc4c11SKonstantin Ananyev	/* allocate required drbs */
4728efc4c11SKonstantin Ananyev	if (nb != 0)
4738efc4c11SKonstantin Ananyev		nb = stream_drb_alloc(s, drbs + nbc, nb);
4743395610eSKonstantin Ananyev
4758efc4c11SKonstantin Ananyev	nb += nbc;
4763395610eSKonstantin Ananyev
4778efc4c11SKonstantin Ananyev	/* no free drbs, can't send anything */
4788efc4c11SKonstantin Ananyev	if (nb == 0)
4798efc4c11SKonstantin Ananyev		return 0;
4803395610eSKonstantin Ananyev
4818efc4c11SKonstantin Ananyev	/* not enough free drbs, reduce number of packets to send. */
4823fbc22a6SJielong Zhou	else if (nb != nbm) {
4833fbc22a6SJielong Zhou		if (all_or_nothing)
4843fbc22a6SJielong Zhou			return 0;
4858efc4c11SKonstantin Ananyev		nb_pkt = nb * bsz;
4863fbc22a6SJielong Zhou	}
4873395610eSKonstantin Ananyev
4888efc4c11SKonstantin Ananyev	/* enqueue packets to the destination device. */
4898efc4c11SKonstantin Ananyev	nbc = nb;
4908efc4c11SKonstantin Ananyev	n = tle_dring_mp_enqueue(&dev->tx.dr, pkt, nb_pkt, drbs, &nb);
4913395610eSKonstantin Ananyev
4928efc4c11SKonstantin Ananyev	/* if not all available drbs were consumed, move them to the start. */
4938efc4c11SKonstantin Ananyev	nbc -= nb;
4948efc4c11SKonstantin Ananyev	for (i = 0; i != nb; i++)
4958efc4c11SKonstantin Ananyev		drbs[i] = drbs[nbc + i];
4968efc4c11SKonstantin Ananyev
4978efc4c11SKonstantin Ananyev	*nb_drb = nb;
4983395610eSKonstantin Ananyev	return n;
4993395610eSKonstantin Ananyev}
5003395610eSKonstantin Ananyev
5013395610eSKonstantin Ananyevuint16_t
502aa97dd1cSKonstantin Ananyevtle_udp_stream_send(struct tle_stream *us, struct rte_mbuf *pkt[],
5033395610eSKonstantin Ananyev	uint16_t num, const struct sockaddr *dst_addr)
5043395610eSKonstantin Ananyev{
5053395610eSKonstantin Ananyev	int32_t di, frg, rc;
5063395610eSKonstantin Ananyev	uint64_t ol_flags;
5078efc4c11SKonstantin Ananyev	uint32_t i, k, n, nb;
5083395610eSKonstantin Ananyev	uint32_t mtu, pid, type;
5093395610eSKonstantin Ananyev	const struct sockaddr_in *d4;
5103395610eSKonstantin Ananyev	const struct sockaddr_in6 *d6;
511aa97dd1cSKonstantin Ananyev	struct tle_udp_stream *s;
5123395610eSKonstantin Ananyev	const void *da;
5133395610eSKonstantin Ananyev	union udph udph;
514aa97dd1cSKonstantin Ananyev	struct tle_dest dst;
5158efc4c11SKonstantin Ananyev	struct tle_drb *drb[num];
5163395610eSKonstantin Ananyev
517aa97dd1cSKonstantin Ananyev	s = UDP_STREAM(us);
518aa97dd1cSKonstantin Ananyev	type = s->s.type;
5193395610eSKonstantin Ananyev
5203395610eSKonstantin Ananyev	/* start filling UDP header. */
5213395610eSKonstantin Ananyev	udph.raw = 0;
522aa97dd1cSKonstantin Ananyev	udph.ports.src = s->s.port.dst;
5233395610eSKonstantin Ananyev
5243395610eSKonstantin Ananyev	/* figure out what destination addr/port to use. */
5253395610eSKonstantin Ananyev	if (dst_addr != NULL) {
5263395610eSKonstantin Ananyev		if (dst_addr->sa_family != s->prm.remote_addr.ss_family) {
5273395610eSKonstantin Ananyev			rte_errno = EINVAL;
5283395610eSKonstantin Ananyev			return 0;
5293395610eSKonstantin Ananyev		}
530aa97dd1cSKonstantin Ananyev		if (type == TLE_V4) {
5313395610eSKonstantin Ananyev			d4 = (const struct sockaddr_in *)dst_addr;
5323395610eSKonstantin Ananyev			da = &d4->sin_addr;
5333395610eSKonstantin Ananyev			udph.ports.dst = d4->sin_port;
5343395610eSKonstantin Ananyev		} else {
5353395610eSKonstantin Ananyev			d6 = (const struct sockaddr_in6 *)dst_addr;
5363395610eSKonstantin Ananyev			da = &d6->sin6_addr;
5373395610eSKonstantin Ananyev			udph.ports.dst = d6->sin6_port;
5383395610eSKonstantin Ananyev		}
5393395610eSKonstantin Ananyev	} else {
540aa97dd1cSKonstantin Ananyev		udph.ports.dst = s->s.port.src;
541aa97dd1cSKonstantin Ananyev		if (type == TLE_V4)
542aa97dd1cSKonstantin Ananyev			da = &s->s.ipv4.addr.src;
5433395610eSKonstantin Ananyev		else
544aa97dd1cSKonstantin Ananyev			da = &s->s.ipv6.addr.src;
5453395610eSKonstantin Ananyev	}
5463395610eSKonstantin Ananyev
547aa97dd1cSKonstantin Ananyev	di = stream_get_dest(&s->s, da, &dst);
5483395610eSKonstantin Ananyev	if (di < 0) {
5493395610eSKonstantin Ananyev		rte_errno = -di;
5503395610eSKonstantin Ananyev		return 0;
5513395610eSKonstantin Ananyev	}
5523395610eSKonstantin Ananyev
5533395610eSKonstantin Ananyev	pid = rte_atomic32_add_return(&dst.dev->tx.packet_id[type], num) - num;
5543395610eSKonstantin Ananyev	mtu = dst.mtu - dst.l2_len - dst.l3_len;
5553395610eSKonstantin Ananyev
5563395610eSKonstantin Ananyev	/* mark stream as not closable. */
5570104c556SJianfeng Tan	if (rwl_acquire(&s->tx.use) < 0) {
5580104c556SJianfeng Tan		rte_errno = EAGAIN;
5593395610eSKonstantin Ananyev		return 0;
5600104c556SJianfeng Tan	}
5613395610eSKonstantin Ananyev
5628efc4c11SKonstantin Ananyev	nb = 0;
5633395610eSKonstantin Ananyev	for (i = 0, k = 0; k != num; k = i) {
5643395610eSKonstantin Ananyev
5653395610eSKonstantin Ananyev		/* copy L2/L3/L4 headers into mbufs, setup mbufs metadata. */
5663395610eSKonstantin Ananyev
5673395610eSKonstantin Ananyev		frg = 0;
5683395610eSKonstantin Ananyev		ol_flags = dst.dev->tx.ol_flags[type];
5693395610eSKonstantin Ananyev
5703395610eSKonstantin Ananyev		while (i != num && frg == 0) {
5713395610eSKonstantin Ananyev			frg = pkt[i]->pkt_len > mtu;
5723395610eSKonstantin Ananyev			if (frg != 0)
5733395610eSKonstantin Ananyev				ol_flags &= ~PKT_TX_UDP_CKSUM;
5743395610eSKonstantin Ananyev			rc = udp_fill_mbuf(pkt[i], type, ol_flags, pid + i,
5753395610eSKonstantin Ananyev				udph, &dst);
5763395610eSKonstantin Ananyev			if (rc != 0) {
5773395610eSKonstantin Ananyev				rte_errno = -rc;
5783395610eSKonstantin Ananyev				goto out;
5793395610eSKonstantin Ananyev			}
5803395610eSKonstantin Ananyev			i += (frg == 0);
5813395610eSKonstantin Ananyev		}
5823395610eSKonstantin Ananyev
5833395610eSKonstantin Ananyev		/* enqueue non-fragment packets to the destination device. */
5843395610eSKonstantin Ananyev		if (k != i) {
5858efc4c11SKonstantin Ananyev			k += queue_pkt_out(s, dst.dev,
5868efc4c11SKonstantin Ananyev				(const void **)(uintptr_t)&pkt[k], i - k,
5873fbc22a6SJielong Zhou				drb, &nb, 0);
5883395610eSKonstantin Ananyev
5893395610eSKonstantin Ananyev			/* stream TX queue is full. */
5900104c556SJianfeng Tan			if (k != i) {
5910104c556SJianfeng Tan				rte_errno = EAGAIN;
5923395610eSKonstantin Ananyev				break;
5930104c556SJianfeng Tan			}
5943395610eSKonstantin Ananyev		}
5953395610eSKonstantin Ananyev
5963395610eSKonstantin Ananyev		/* enqueue packet that need to be fragmented */
5973395610eSKonstantin Ananyev		if (i != num) {
5983395610eSKonstantin Ananyev
5993395610eSKonstantin Ananyev			struct rte_mbuf *frag[RTE_LIBRTE_IP_FRAG_MAX_FRAG];
6003395610eSKonstantin Ananyev
6013395610eSKonstantin Ananyev			/* fragment the packet. */
6023395610eSKonstantin Ananyev			rc = fragment(pkt[i], frag, RTE_DIM(frag), type, &dst);
6033395610eSKonstantin Ananyev			if (rc < 0) {
6043395610eSKonstantin Ananyev				rte_errno = -rc;
6053395610eSKonstantin Ananyev				break;
6063395610eSKonstantin Ananyev			}
6073395610eSKonstantin Ananyev
6088efc4c11SKonstantin Ananyev			n = queue_pkt_out(s, dst.dev,
6093fbc22a6SJielong Zhou				(const void **)(uintptr_t)frag, rc, drb, &nb, 1);
6103395610eSKonstantin Ananyev			if (n == 0) {
6113395610eSKonstantin Ananyev				while (rc-- != 0)
6123395610eSKonstantin Ananyev					rte_pktmbuf_free(frag[rc]);
6130104c556SJianfeng Tan				rte_errno = EAGAIN;
6143395610eSKonstantin Ananyev				break;
6153395610eSKonstantin Ananyev			}
6163395610eSKonstantin Ananyev
6173395610eSKonstantin Ananyev			/* all fragments enqueued, free the original packet. */
6183395610eSKonstantin Ananyev			rte_pktmbuf_free(pkt[i]);
6193395610eSKonstantin Ananyev			i++;
6203395610eSKonstantin Ananyev		}
6213395610eSKonstantin Ananyev	}
6223395610eSKonstantin Ananyev
6233395610eSKonstantin Ananyev	/* if possible, rearm socket write event. */
6243395610eSKonstantin Ananyev	if (k == num && s->tx.ev != NULL)
6253395610eSKonstantin Ananyev		tle_event_raise(s->tx.ev);
6263395610eSKonstantin Ananyev
6273395610eSKonstantin Ananyevout:
6288efc4c11SKonstantin Ananyev	/* free unused drbs. */
6298efc4c11SKonstantin Ananyev	if (nb != 0)
6308efc4c11SKonstantin Ananyev		stream_drb_free(s, drb, nb);
6318efc4c11SKonstantin Ananyev
6328efc4c11SKonstantin Ananyev	/* stream can be closed. */
6333395610eSKonstantin Ananyev	rwl_release(&s->tx.use);
6343395610eSKonstantin Ananyev
6353395610eSKonstantin Ananyev	/*
6363395610eSKonstantin Ananyev	 * remove pkt l2/l3 headers, restore ol_flags for unsent, but
6373395610eSKonstantin Ananyev	 * already modified packets.
6383395610eSKonstantin Ananyev	 */
6393395610eSKonstantin Ananyev	ol_flags = ~dst.dev->tx.ol_flags[type];
6403395610eSKonstantin Ananyev	for (n = k; n != i; n++) {
6413395610eSKonstantin Ananyev		rte_pktmbuf_adj(pkt[n], dst.l2_len + dst.l3_len + sizeof(udph));
6423395610eSKonstantin Ananyev		pkt[n]->ol_flags &= ol_flags;
6433395610eSKonstantin Ananyev	}
6443395610eSKonstantin Ananyev
6453395610eSKonstantin Ananyev	return k;
6463395610eSKonstantin Ananyev}