Add l4fwd RXTX mode
[tldk.git] / lib / libtle_l4p / misc.h
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
22 extern "C" {
23 #endif
24
25 static inline int
26 xmm_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
36 static inline int
37 ymm_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
49 static inline int
50 ymm_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  */
66 static 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  */
76 static 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  */
113 static 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  */
164 static 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  */
209 static 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  */
244 static 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  */
268 static 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
280 static 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
289 static inline int
290 check_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
336 static inline int
337 rwl_try_acquire(rte_atomic32_t *p)
338 {
339         return rte_atomic32_add_return(p, 1);
340 }
341
342 static inline void
343 rwl_release(rte_atomic32_t *p)
344 {
345         rte_atomic32_sub(p, 1);
346 }
347
348 static inline int
349 rwl_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
359 static inline void
360 rwl_down(rte_atomic32_t *p)
361 {
362          while (rte_atomic32_cmpset((volatile uint32_t *)p, 0, INT32_MIN) == 0)
363                 rte_pause();
364 }
365
366 static inline void
367 rwl_up(rte_atomic32_t *p)
368 {
369         rte_atomic32_sub(p, INT32_MIN);
370 }
371
372 /* exclude NULLs from the final list of packets. */
373 static inline uint32_t
374 compress_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 */
400 static inline void
401 empty_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_ */