Revert "l4p/tcp: introduce tle_tcp_stream_establish() API"
[tldk.git] / lib / libtle_l4p / misc.h
1 /*
2  * Copyright (c) 2016-2017  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 rte_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 rte_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 rte_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 /*
290  * helper function to check csum.
291  */
292 static inline int
293 check_pkt_csum(const struct rte_mbuf *m, uint64_t ol_flags, uint32_t type,
294         uint32_t proto)
295 {
296         const struct rte_ipv4_hdr *l3h4;
297         const struct rte_ipv6_hdr *l3h6;
298         const struct rte_udp_hdr *l4h;
299         uint64_t fl3, fl4;
300         uint16_t csum;
301         int32_t ret;
302
303         fl4 = ol_flags & PKT_RX_L4_CKSUM_MASK;
304         fl3 = (type == TLE_V4) ?
305                 (ol_flags & PKT_RX_IP_CKSUM_MASK) : PKT_RX_IP_CKSUM_GOOD;
306
307         /* case 0: both ip and l4 cksum is verified or data is valid */
308         if ((fl3 | fl4) == (PKT_RX_IP_CKSUM_GOOD | PKT_RX_L4_CKSUM_GOOD))
309                 return 0;
310
311         /* case 1: either ip or l4 cksum bad */
312         if (fl3 == PKT_RX_IP_CKSUM_BAD || fl4 == PKT_RX_L4_CKSUM_BAD)
313                 return 1;
314
315         /* case 2: either ip or l4 or both cksum is unknown */
316         l3h4 = rte_pktmbuf_mtod_offset(m, const struct rte_ipv4_hdr *,
317                 m->l2_len);
318         l3h6 = rte_pktmbuf_mtod_offset(m, const struct rte_ipv6_hdr *,
319                 m->l2_len);
320
321         ret = 0;
322         if (fl3 == PKT_RX_IP_CKSUM_UNKNOWN && l3h4->hdr_checksum != 0) {
323                 csum = _ipv4x_cksum(l3h4, m->l3_len);
324                 ret = (csum != UINT16_MAX);
325         }
326
327         if (ret == 0 && fl4 == PKT_RX_L4_CKSUM_UNKNOWN) {
328
329                 /*
330                  * for IPv4 it is allowed to have zero UDP cksum,
331                  * for IPv6 valid UDP cksum is mandatory.
332                  */
333                 if (type == TLE_V4) {
334                         l4h = (const struct rte_udp_hdr *)((uintptr_t)l3h4 +
335                                 m->l3_len);
336                         csum = (proto == IPPROTO_UDP && l4h->dgram_cksum == 0) ?
337                                 UINT16_MAX : _ipv4_udptcp_mbuf_cksum(m,
338                                 m->l2_len + m->l3_len, l3h4);
339                 } else
340                         csum = _ipv6_udptcp_mbuf_cksum(m,
341                                 m->l2_len + m->l3_len, l3h6);
342
343                 ret = (csum != UINT16_MAX);
344         }
345
346         return ret;
347 }
348
349 /*
350  * Analog of read-write locks, very much in favour of read side.
351  * Assumes, that there are no more then INT32_MAX concurrent readers.
352  * Consider to move into DPDK librte_eal.
353  */
354
355 static inline int
356 rwl_try_acquire(rte_atomic32_t *p)
357 {
358         return rte_atomic32_add_return(p, 1);
359 }
360
361 static inline void
362 rwl_release(rte_atomic32_t *p)
363 {
364         rte_atomic32_sub(p, 1);
365 }
366
367 static inline int
368 rwl_acquire(rte_atomic32_t *p)
369 {
370         int32_t rc;
371
372         rc = rwl_try_acquire(p);
373         if (rc < 0)
374                 rwl_release(p);
375         return rc;
376 }
377
378 static inline void
379 rwl_down(rte_atomic32_t *p)
380 {
381          while (rte_atomic32_cmpset((volatile uint32_t *)p, 0, INT32_MIN) == 0)
382                 rte_pause();
383 }
384
385 static inline void
386 rwl_up(rte_atomic32_t *p)
387 {
388         rte_atomic32_sub(p, INT32_MIN);
389 }
390
391 /* exclude NULLs from the final list of packets. */
392 static inline uint32_t
393 compress_pkt_list(struct rte_mbuf *pkt[], uint32_t nb_pkt, uint32_t nb_zero)
394 {
395         uint32_t i, j, k, l;
396
397         for (j = nb_pkt; nb_zero != 0 && j-- != 0; ) {
398
399                 /* found a hole. */
400                 if (pkt[j] == NULL) {
401
402                         /* find how big is it. */
403                         for (i = j; i-- != 0 && pkt[i] == NULL; )
404                                 ;
405                         /* fill the hole. */
406                         for (k = j + 1, l = i + 1; k != nb_pkt; k++, l++)
407                                 pkt[l] = pkt[k];
408
409                         nb_pkt -= j - i;
410                         nb_zero -= j - i;
411                         j = i + 1;
412                 }
413         }
414
415         return nb_pkt;
416 }
417
418 static inline void
419 free_mbufs(struct rte_mbuf *mb[], uint32_t num)
420 {
421         uint32_t i;
422
423         for (i = 0; i != num; i++)
424                 rte_pktmbuf_free(mb[i]);
425 }
426
427 /* empty ring and free queued mbufs */
428 static inline void
429 empty_mbuf_ring(struct rte_ring *r)
430 {
431         uint32_t n;
432         struct rte_mbuf *mb[MAX_PKT_BURST];
433
434         do {
435                 n = _rte_ring_dequeue_burst(r, (void **)mb, RTE_DIM(mb));
436                 free_mbufs(mb, n);
437         } while (n != 0);
438 }
439
440 static inline uint32_t
441 _mbus_to_iovec(struct iovec *iv, struct rte_mbuf *mb[], uint32_t num)
442 {
443         uint32_t i, ns;
444         uint32_t len, slen, tlen;
445         struct rte_mbuf *m, *next;
446         const void *src;
447
448         for (i = 0; i != num; i++) {
449
450                 m = mb[i];
451                 tlen = 0;
452                 ns = 0;
453
454                 do {
455                         slen = m->data_len;
456                         src = rte_pktmbuf_mtod(m, const void *);
457                         len = RTE_MIN(iv->iov_len - tlen, slen);
458                         rte_memcpy((uint8_t *)iv->iov_base + tlen, src, len);
459                         slen -= len;
460                         tlen += len;
461                         if (slen != 0)
462                                 break;
463                         ns++;
464                         next = m->next;
465                         rte_pktmbuf_free_seg(m);
466                         m = next;
467                  } while (m != NULL);
468
469                 iv->iov_base = (uint8_t *)iv->iov_base + tlen;
470                 iv->iov_len -= tlen;
471
472                 /* partly consumed mbuf */
473                 if (m != NULL) {
474                         m->pkt_len = mb[i]->pkt_len - tlen;
475                         m->data_len = slen;
476                         m->data_off += len;
477                         m->nb_segs = mb[i]->nb_segs - ns;
478                         mb[i] = m;
479                         break;
480                 }
481         }
482
483         return i;
484 }
485
486 static inline uint32_t
487 _iovec_to_mbsegs(struct iovec *iv, uint32_t seglen, struct rte_mbuf *mb[],
488         uint32_t num)
489 {
490         uint32_t i;
491         uint32_t len, slen, tlen;
492         struct rte_mbuf *m;
493         void *dst;
494
495         tlen = 0;
496         for (i = 0; i != num; i++) {
497
498                 m = mb[i];
499                 slen = rte_pktmbuf_tailroom(m);
500                 slen = RTE_MIN(slen, seglen - m->data_len);
501                 len = RTE_MIN(iv->iov_len - tlen, slen);
502                 dst = rte_pktmbuf_append(m, len);
503                 rte_memcpy(dst, (uint8_t *)iv->iov_base + tlen, len);
504                 tlen += len;
505                 if (len != slen)
506                         break;
507         }
508
509         iv->iov_base = (uint8_t *)iv->iov_base + tlen;
510         iv->iov_len -= tlen;
511
512         return i;
513 }
514
515 /**
516  * Remove len bytes at the beginning of an mbuf.
517  *
518  * It's an enhancement version of rte_pktmbuf_abj which not support
519  * adjusting length greater than the length of the first segment.
520  *
521  * Returns a pointer to the new mbuf. If the
522  * length is greater than the total length of the mbuf, then the
523  * function will fail and return NULL, without modifying the mbuf.
524  *
525  * @param m
526  *   The packet mbuf.
527  * @param len
528  *   The amount of data to remove (in bytes).
529  * @return
530  *   A pointer to the new start of the data.
531  */
532 static inline struct rte_mbuf *
533 _rte_pktmbuf_adj(struct rte_mbuf *m, uint32_t len)
534 {
535         struct rte_mbuf *next;
536         uint32_t remain, plen;
537         uint16_t segs;
538
539         if (unlikely(len > m->pkt_len))
540                 return NULL;
541
542         plen = m->pkt_len;
543         remain = len;
544         segs = m->nb_segs;
545         /* don't free last segment */
546         while (remain >= m->data_len && m->next) {
547                 next = m->next;
548                 remain -= m->data_len;
549                 segs--;
550                 rte_pktmbuf_free_seg(m);
551                 m = next;
552         }
553
554         if (remain) {
555                 m->data_len = (uint16_t)(m->data_len - remain);
556                 m->data_off = (uint16_t)(m->data_off + remain);
557         }
558
559         m->pkt_len = plen - len;
560         m->nb_segs = segs;
561         return m;
562 }
563
564 /**
565  * Remove len bytes of data at the end of the mbuf.
566  *
567  * It's an enhancement version of rte_pktmbuf_trim, which not support
568  * removing length greater than the length of the last segment.
569  *
570  * @param m
571  *   The packet mbuf.
572  * @param len
573  *   The amount of data to remove (in bytes).
574  * @return
575  *   - 0: On success.
576  *   - -1: On error.
577  */
578 static inline int
579 _rte_pktmbuf_trim(struct rte_mbuf *m, uint32_t len)
580 {
581         struct rte_mbuf *last, *next, *tmp;
582         uint32_t remain;
583         uint16_t segs;
584
585         if (unlikely(len > m->pkt_len))
586                 return -1;
587
588         tmp = m;
589         /* find the last segment will remain after trim */
590         remain = m->pkt_len - len;
591         while (remain > tmp->data_len) {
592                 remain -= tmp->data_len;
593                 tmp = tmp->next;
594         }
595
596         /* trim the remained last segment */
597         tmp->data_len = remain;
598
599         /* remove trimmed segments */
600         segs = m->nb_segs;
601         last = tmp;
602         for (tmp = tmp->next; tmp != NULL; tmp = next) {
603                 next = tmp->next;
604                 rte_pktmbuf_free_seg(tmp);
605                 segs--;
606         }
607
608         last->next = NULL;
609         m->pkt_len -= len;
610         m->nb_segs = segs;
611
612         return 0;
613 }
614
615 #ifdef __cplusplus
616 }
617 #endif
618
619 #endif /* _MISC_H_ */