misc.h revision fbba0a3b
1/*
2 * Copyright (c) 2016  Intel Corporation.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef _MISC_H_
17#define _MISC_H_
18
19#include <tle_dpdk_wrapper.h>
20
21#ifdef __cplusplus
22extern "C" {
23#endif
24
25static inline int
26xmm_cmp(const rte_xmm_t *da, const rte_xmm_t *sa)
27{
28	uint64_t ret;
29
30	ret = (sa->u64[0] ^ da->u64[0]) |
31		(sa->u64[1] ^ da->u64[1]);
32
33	return (ret != 0);
34}
35
36static inline int
37ymm_cmp(const _ymm_t *da, const _ymm_t *sa)
38{
39	uint64_t ret;
40
41	ret = (sa->u64[0] ^ da->u64[0]) |
42		(sa->u64[1] ^ da->u64[1]) |
43		(sa->u64[2] ^ da->u64[2]) |
44		(sa->u64[3] ^ da->u64[3]);
45
46	return (ret != 0);
47}
48
49static inline int
50ymm_mask_cmp(const _ymm_t *da, const _ymm_t *sa, const _ymm_t *sm)
51{
52	uint64_t ret;
53
54	ret = ((da->u64[0] & sm->u64[0]) ^ sa->u64[0]) |
55		((da->u64[1] & sm->u64[1]) ^ sa->u64[1]) |
56		((da->u64[2] & sm->u64[2]) ^ sa->u64[2]) |
57		((da->u64[3] & sm->u64[3]) ^ sa->u64[3]);
58
59	return (ret != 0);
60}
61
62/*
63 * Setup tx_offload field inside mbuf using raw 64-bit field.
64 * Consider to move it into DPDK librte_mbuf.
65 */
66static inline uint64_t
67_mbuf_tx_offload(uint64_t il2, uint64_t il3, uint64_t il4, uint64_t tso,
68	uint64_t ol3, uint64_t ol2)
69{
70	return il2 | il3 << 7 | il4 << 16 | tso << 24 | ol3 << 40 | ol2 << 49;
71}
72
73/*
74 * Given the value of mbuf's tx_offload, calculate L4 payload offset.
75 */
76static inline uint32_t
77_tx_offload_l4_offset(uint64_t ofl)
78{
79	uint32_t l2, l3, l4;
80
81	l2 = ofl & 0x7f;
82	l3 = ofl >> 7 & 0x1ff;
83	l4 = ofl >> 16 & UINT8_MAX;
84
85	return l2 + l3 + l4;
86}
87
88/*
89 * Routines to calculate L3/L4 checksums in SW.
90 * Pretty similar to ones from DPDK librte_net/rte_ip.h,
91 * but provide better performance (at least for tested configurations),
92 * and extended functionality.
93 * Consider to move them into DPDK librte_net/rte_ip.h.
94 */
95
96/* make compiler to generate: add %r1, %r2; adc $0, %r1. */
97#define CKSUM_ADD_CARRY(s, v)	do {       \
98	(s) += (v);                        \
99	(s) = ((s) < (v)) ? (s) + 1 : (s); \
100} while (0)
101
102/**
103 * Process the non-complemented checksum of a buffer.
104 * Similar  to rte_raw_cksum(), but provide better performance
105 * (at least on IA platforms).
106 * @param buf
107 *   Pointer to the buffer.
108 * @param size
109 *   Length of the buffer.
110 * @return
111 *   The non-complemented checksum.
112 */
113static inline uint16_t
114__raw_cksum(const uint8_t *buf, uint32_t size)
115{
116	uint64_t s, sum;
117	uint32_t i, n;
118	uint32_t dw1, dw2;
119	uint16_t w1, w2;
120	const uint64_t *b;
121
122	b = (const uint64_t *)buf;
123	n = size / sizeof(*b);
124	sum = 0;
125
126	/* main loop, consume 8 bytes per iteration. */
127	for (i = 0; i != n; i++) {
128		s = b[i];
129		CKSUM_ADD_CARRY(sum, s);
130	}
131
132	/* consume the remainder. */
133	n = size % sizeof(*b);
134	if (n != 0) {
135		/* position of the of last 8 bytes of data. */
136		b = (const uint64_t *)((uintptr_t)(b + i) + n - sizeof(*b));
137		/* calculate shift amount. */
138		n = (sizeof(*b) - n) * CHAR_BIT;
139		s = b[0] >> n;
140		CKSUM_ADD_CARRY(sum, s);
141	}
142
143	/* reduce to 16 bits */
144	dw1 = sum;
145	dw2 = sum >> 32;
146	CKSUM_ADD_CARRY(dw1, dw2);
147	w1 = dw1;
148	w2 = dw1 >> 16;
149	CKSUM_ADD_CARRY(w1, w2);
150	return w1;
151}
152
153/**
154 * Process UDP or TCP checksum over possibly multi-segmented packet.
155 * @param mb
156 *   The pointer to the mbuf with the packet.
157 * @param l4_ofs
158 *   Offset to the beginning of the L4 header (should be in first segment).
159 * @param cksum
160 *   Already pre-calculated pseudo-header checksum value.
161 * @return
162 *   The complemented checksum.
163 */
164static inline uint32_t
165__udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
166	uint32_t cksum)
167{
168	uint32_t dlen, i, plen;
169	const struct rte_mbuf *ms;
170	const void *data;
171
172	plen = rte_pktmbuf_pkt_len(mb);
173	ms = mb;
174
175	for (i = l4_ofs; i < plen && ms != NULL; i += dlen) {
176		data = rte_pktmbuf_mtod_offset(ms, const void *, l4_ofs);
177		dlen = rte_pktmbuf_data_len(ms) - l4_ofs;
178		cksum += __raw_cksum(data, dlen);
179		ms = ms->next;
180		l4_ofs = 0;
181	}
182
183	cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
184	cksum = (~cksum) & 0xffff;
185	if (cksum == 0)
186		cksum = 0xffff;
187
188	return cksum;
189}
190
191/**
192 * Process the pseudo-header checksum of an IPv4 header.
193 *
194 * Depending on the ol_flags, the pseudo-header checksum expected by the
195 * drivers is not the same. For instance, when TSO is enabled, the IP
196 * payload length must not be included in the packet.
197 *
198 * When ol_flags is 0, it computes the standard pseudo-header checksum.
199 *
200 * @param ipv4_hdr
201 *   The pointer to the contiguous IPv4 header.
202 * @param ipv4_len
203 *   Length of the IPv4 header.
204 * @param ol_flags
205 *   The ol_flags of the associated mbuf.
206 * @return
207 *   The non-complemented checksum to set in the L4 header.
208 */
209static inline uint16_t
210_ipv4x_phdr_cksum(const struct ipv4_hdr *ipv4_hdr, size_t ipv4h_len,
211	uint64_t ol_flags)
212{
213	uint32_t s0, s1;
214
215	s0 = ipv4_hdr->src_addr;
216	s1 = ipv4_hdr->dst_addr;
217	CKSUM_ADD_CARRY(s0, s1);
218
219	if (ol_flags & PKT_TX_TCP_SEG)
220		s1 = 0;
221	else
222		s1 = rte_cpu_to_be_16(
223			(uint16_t)(rte_be_to_cpu_16(ipv4_hdr->total_length) -
224			ipv4h_len));
225
226	s1 += rte_cpu_to_be_16(ipv4_hdr->next_proto_id);
227	CKSUM_ADD_CARRY(s0, s1);
228
229	return __rte_raw_cksum_reduce(s0);
230}
231
232/**
233 * Process the IPv4 UDP or TCP checksum.
234 *
235 * @param mb
236 *   The pointer to the IPv4 packet.
237 * @param l4_ofs
238 *   Offset to the beginning of the L4 header (should be in first segment).
239 * @param ipv4_hdr
240 *   The pointer to the contiguous IPv4 header.
241 * @return
242 *   The complemented checksum to set in the IP packet.
243 */
244static inline int
245_ipv4_udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
246	const struct ipv4_hdr *ipv4_hdr)
247{
248	uint32_t cksum;
249
250	cksum = _ipv4x_phdr_cksum(ipv4_hdr, mb->l3_len, 0);
251	cksum = __udptcp_mbuf_cksum(mb, l4_ofs, cksum);
252
253	return cksum;
254}
255
256/**
257 * Process the IPv6 UDP or TCP checksum.
258 *
259 * @param mb
260 *   The pointer to the IPv6 packet.
261 * @param l4_ofs
262 *   Offset to the beginning of the L4 header (should be in first segment).
263 * @param ipv6_hdr
264 *   The pointer to the contiguous IPv6 header.
265 * @return
266 *   The complemented checksum to set in the IP packet.
267 */
268static inline int
269_ipv6_udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
270	const struct ipv6_hdr *ipv6_hdr)
271{
272	uint32_t cksum;
273
274	cksum = rte_ipv6_phdr_cksum(ipv6_hdr, 0);
275	cksum = __udptcp_mbuf_cksum(mb, l4_ofs, cksum);
276
277	return cksum;
278}
279
280static inline uint16_t
281_ipv4x_cksum(const void *iph, size_t len)
282{
283	uint16_t cksum;
284
285	cksum = __raw_cksum(iph, len);
286	return (cksum == 0xffff) ? cksum : ~cksum;
287}
288
289static inline int
290check_pkt_csum(const struct rte_mbuf *m, uint64_t ol_flags, uint32_t type,
291	uint32_t proto)
292{
293	const struct ipv4_hdr *l3h4;
294	const struct ipv6_hdr *l3h6;
295	const struct udp_hdr *l4h;
296	int32_t ret;
297	uint16_t csum;
298
299	ret = 0;
300	l3h4 = rte_pktmbuf_mtod_offset(m, const struct ipv4_hdr *, m->l2_len);
301	l3h6 = rte_pktmbuf_mtod_offset(m, const struct ipv6_hdr *, m->l2_len);
302
303	if ((ol_flags & PKT_RX_IP_CKSUM_BAD) != 0) {
304		csum = _ipv4x_cksum(l3h4, m->l3_len);
305		ret = (csum != UINT16_MAX);
306	}
307
308	if (ret == 0 && (ol_flags & PKT_RX_L4_CKSUM_BAD) != 0) {
309
310		/*
311		 * for IPv4 it is allowed to have zero UDP cksum,
312		 * for IPv6 valid UDP cksum is mandatory.
313		 */
314		if (type == TLE_V4) {
315			l4h = (const struct udp_hdr *)((uintptr_t)l3h4 +
316				m->l3_len);
317			csum = (proto == IPPROTO_UDP && l4h->dgram_cksum == 0) ?
318				UINT16_MAX : _ipv4_udptcp_mbuf_cksum(m,
319				m->l2_len + m->l3_len, l3h4);
320		} else
321			csum = _ipv6_udptcp_mbuf_cksum(m,
322				m->l2_len + m->l3_len, l3h6);
323
324		ret = (csum != UINT16_MAX);
325	}
326
327	return ret;
328}
329
330/*
331 * Analog of read-write locks, very much in favour of read side.
332 * Assumes, that there are no more then INT32_MAX concurrent readers.
333 * Consider to move into DPDK librte_eal.
334 */
335
336static inline int
337rwl_try_acquire(rte_atomic32_t *p)
338{
339	return rte_atomic32_add_return(p, 1);
340}
341
342static inline void
343rwl_release(rte_atomic32_t *p)
344{
345	rte_atomic32_sub(p, 1);
346}
347
348static inline int
349rwl_acquire(rte_atomic32_t *p)
350{
351	int32_t rc;
352
353	rc = rwl_try_acquire(p);
354	if (rc < 0)
355		rwl_release(p);
356	return rc;
357}
358
359static inline void
360rwl_down(rte_atomic32_t *p)
361{
362	 while (rte_atomic32_cmpset((volatile uint32_t *)p, 0, INT32_MIN) == 0)
363		rte_pause();
364}
365
366static inline void
367rwl_up(rte_atomic32_t *p)
368{
369	rte_atomic32_sub(p, INT32_MIN);
370}
371
372/* exclude NULLs from the final list of packets. */
373static inline uint32_t
374compress_pkt_list(struct rte_mbuf *pkt[], uint32_t nb_pkt, uint32_t nb_zero)
375{
376	uint32_t i, j, k, l;
377
378	for (j = nb_pkt; nb_zero != 0 && j-- != 0; ) {
379
380		/* found a hole. */
381		if (pkt[j] == NULL) {
382
383			/* find how big is it. */
384			for (i = j; i-- != 0 && pkt[i] == NULL; )
385				;
386			/* fill the hole. */
387			for (k = j + 1, l = i + 1; k != nb_pkt; k++, l++)
388				pkt[l] = pkt[k];
389
390			nb_pkt -= j - i;
391			nb_zero -= j - i;
392			j = i + 1;
393		}
394	}
395
396	return nb_pkt;
397}
398
399/* empty ring and free queued mbufs */
400static inline void
401empty_mbuf_ring(struct rte_ring *r)
402{
403	uint32_t i, n;
404	struct rte_mbuf *mb[MAX_PKT_BURST];
405
406	do {
407		n = _rte_ring_dequeue_burst(r, (void **)mb, RTE_DIM(mb));
408		for (i = 0; i != n; i++)
409			rte_pktmbuf_free(mb[i]);
410	} while (n != 0);
411}
412
413#ifdef __cplusplus
414}
415#endif
416
417#endif /* _MISC_H_ */
418