Change libtle_udp to use dring.
[tldk.git] / lib / libtle_udp / 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 #ifdef __cplusplus
20 extern "C" {
21 #endif
22
23 static inline int
24 ymm_mask_cmp(const _ymm_t *da, const _ymm_t *sa, const _ymm_t *sm)
25 {
26         uint64_t ret;
27
28         ret = ((sa->u64[0] & sm->u64[0]) ^ da->u64[0]) |
29                 ((sa->u64[1] & sm->u64[1]) ^ da->u64[1]) |
30                 ((sa->u64[2] & sm->u64[2]) ^ da->u64[2]) |
31                 ((sa->u64[3] & sm->u64[3]) ^ da->u64[3]);
32
33         return (ret == 0);
34 }
35
36 /*
37  * Setup tx_offload field inside mbuf using raw 64-bit field.
38  * Consider to move it into DPDK librte_mbuf.
39  */
40 static inline uint64_t
41 _mbuf_tx_offload(uint64_t il2, uint64_t il3, uint64_t il4, uint64_t tso,
42         uint64_t ol3, uint64_t ol2)
43 {
44         return il2 | il3 << 7 | il4 << 16 | tso << 24 | ol3 << 40 | ol2 << 49;
45 }
46
47 /*
48  * Given the value of mbuf's tx_offload, calculate L4 payload offset.
49  */
50 static inline uint32_t
51 _tx_offload_l4_offset(uint64_t ofl)
52 {
53         uint32_t l2, l3, l4;
54
55         l2 = ofl & 0x7f;
56         l3 = ofl >> 7 & 0x1ff;
57         l4 = ofl >> 16 & UINT8_MAX;
58
59         return l2 + l3 + l4;
60 }
61
62 /*
63  * Routines to calculate L3/L4 checksums in SW.
64  * Pretty similar to ones from DPDK librte_net/rte_ip.h,
65  * but provide better performance (at least for tested configurations),
66  * and extended functionality.
67  * Consider to move them into DPDK librte_net/rte_ip.h.
68  */
69
70 /* make compiler to generate: add %r1, %r2; adc $0, %r1. */
71 #define CKSUM_ADD_CARRY(s, v)   do {       \
72         (s) += (v);                        \
73         (s) = ((s) < (v)) ? (s) + 1 : (s); \
74 } while (0)
75
76 /**
77  * Process the non-complemented checksum of a buffer.
78  * Similar  to rte_raw_cksum(), but provide better perfomance
79  * (at least on IA platforms).
80  * @param buf
81  *   Pointer to the buffer.
82  * @param len
83  *   Length of the buffer.
84  * @return
85  *   The non-complemented checksum.
86  */
87 static inline uint16_t
88 __raw_cksum(const uint8_t *buf, uint32_t size)
89 {
90         uint64_t s, sum;
91         uint32_t i, n;
92         uint32_t  dw1, dw2;
93         uint16_t w1, w2;
94         const uint64_t *b;
95
96         b = (const uint64_t *)buf;
97         n = size / sizeof(*b);
98         sum = 0;
99
100         /* main loop, consume 8 bytes per iteration. */
101         for (i = 0; i != n; i++) {
102                 s = b[i];
103                 CKSUM_ADD_CARRY(sum, s);
104         }
105
106         /* consume the remainder. */
107         n = size % sizeof(*b);
108         if (n != 0) {
109                 /* position of the of last 8 bytes of data. */
110                 b = (const uint64_t *)((uintptr_t)(b + i) + n - sizeof(*b));
111                 /* calculate shift amount. */
112                 n = (sizeof(*b) - n) * CHAR_BIT;
113                 s = b[0] >> n;
114                 CKSUM_ADD_CARRY(sum, s);
115         }
116
117         /* reduce to 16 bits */
118         dw1 = sum;
119         dw2 = sum >> 32;
120         CKSUM_ADD_CARRY(dw1, dw2);
121         w1 = dw1;
122         w2 = dw1 >> 16;
123         CKSUM_ADD_CARRY(w1, w2);
124         return w1;
125 }
126
127
128 /**
129  * Process UDP or TCP checksum over possibly multi-segmented packet.
130  * @param mb
131  *   The pointer to the mbuf with the packet.
132  * @param l4_ofs
133  *   Offset to the beginning of the L4 header (should be in first segment).
134  * @param cksum
135  *   Already pre-calculated pseudo-header checksum value.
136  * @return
137  *   The complemented checksum.
138  */
139 static inline uint32_t
140 __udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
141         uint32_t cksum)
142 {
143         uint32_t dlen, i, plen;
144         const struct rte_mbuf *ms;
145         const void *data;
146
147         plen = rte_pktmbuf_pkt_len(mb);
148         ms = mb;
149
150         for (i = l4_ofs; i < plen && ms != NULL; i += dlen) {
151                 data = rte_pktmbuf_mtod_offset(ms, const void *, l4_ofs);
152                 dlen = rte_pktmbuf_data_len(ms) - l4_ofs;
153                 cksum += __raw_cksum(data, dlen);
154                 ms = ms->next;
155                 l4_ofs = 0;
156         }
157
158         cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
159         cksum = (~cksum) & 0xffff;
160         if (cksum == 0)
161                 cksum = 0xffff;
162
163         return cksum;
164 }
165
166 /**
167  * Process the pseudo-header checksum of an IPv4 header.
168  *
169  * Depending on the ol_flags, the pseudo-header checksum expected by the
170  * drivers is not the same. For instance, when TSO is enabled, the IP
171  * payload length must not be included in the packet.
172  *
173  * When ol_flags is 0, it computes the standard pseudo-header checksum.
174  *
175  * @param ipv4_hdr
176  *   The pointer to the contiguous IPv4 header.
177  * @param ipv4_len
178  *   Length of the IPv4 header.
179  * @param ol_flags
180  *   The ol_flags of the associated mbuf.
181  * @return
182  *   The non-complemented checksum to set in the L4 header.
183  */
184 static inline uint16_t
185 _ipv4x_phdr_cksum(const struct ipv4_hdr *ipv4_hdr, size_t ipv4h_len,
186         uint64_t ol_flags)
187 {
188         uint32_t s0, s1;
189
190         s0 = ipv4_hdr->src_addr;
191         s1 = ipv4_hdr->dst_addr;
192         CKSUM_ADD_CARRY(s0, s1);
193
194         if (ol_flags & PKT_TX_TCP_SEG)
195                 s1 = 0;
196         else
197                 s1 = rte_cpu_to_be_16(
198                         (uint16_t)(rte_be_to_cpu_16(ipv4_hdr->total_length) -
199                         ipv4h_len));
200
201         s1 += rte_cpu_to_be_16(ipv4_hdr->next_proto_id);
202         CKSUM_ADD_CARRY(s0, s1);
203
204         return __rte_raw_cksum_reduce(s0);
205 }
206
207 /**
208  * Process the IPv4 UDP or TCP checksum.
209  *
210  * @param mb
211  *   The pointer to the IPv4 packet.
212  * @param l4_ofs
213  *   Offset to the beginning of the L4 header (should be in first segment).
214  * @param ipv4_hdr
215  *   The pointer to the contiguous IPv4 header.
216  * @return
217  *   The complemented checksum to set in the IP packet.
218  */
219 static inline int
220 _ipv4_udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
221         const struct ipv4_hdr *ipv4_hdr)
222 {
223         uint32_t cksum;
224
225         cksum = _ipv4x_phdr_cksum(ipv4_hdr, mb->l3_len, 0);
226         cksum  = __udptcp_mbuf_cksum(mb, l4_ofs, cksum);
227
228         return cksum;
229 }
230
231 /**
232  * Process the IPv6 UDP or TCP checksum.
233  *
234  * @param mb
235  *   The pointer to the IPv6 packet.
236  * @param l4_ofs
237  *   Offset to the beginning of the L4 header (should be in first segment).
238  * @param ipv6_hdr
239  *   The pointer to the contiguous IPv6 header.
240  * @return
241  *   The complemented checksum to set in the IP packet.
242  */
243 static inline int
244 _ipv6_udptcp_mbuf_cksum(const struct rte_mbuf *mb, uint16_t l4_ofs,
245         const struct ipv6_hdr *ipv6_hdr)
246 {
247         uint32_t cksum;
248
249         cksum = rte_ipv6_phdr_cksum(ipv6_hdr, 0);
250         cksum  = __udptcp_mbuf_cksum(mb, l4_ofs, cksum);
251
252         return cksum;
253 }
254
255 static inline uint16_t
256 _ipv4x_cksum(const void *iph, size_t len)
257 {
258         uint16_t cksum;
259
260         cksum = __raw_cksum(iph, len);
261         return (cksum == 0xffff) ? cksum : ~cksum;
262 }
263
264
265 /*
266  * Analog of read-write locks, very much in favour of read side.
267  * Assumes, that there are no more then INT32_MAX concurrent readers.
268  * Consider to move into DPDK librte_eal.
269  */
270
271 static inline int
272 rwl_try_acquire(rte_atomic32_t *p)
273 {
274         return rte_atomic32_add_return(p, 1);
275 }
276
277 static inline void
278 rwl_release(rte_atomic32_t *p)
279 {
280         rte_atomic32_sub(p, 1);
281 }
282
283 static inline int
284 rwl_acquire(rte_atomic32_t *p)
285 {
286         int32_t rc;
287
288         rc = rwl_try_acquire(p);
289         if (rc < 0)
290                 rwl_release(p);
291         return rc;
292 }
293
294 static inline void
295 rwl_down(rte_atomic32_t *p)
296 {
297          while (rte_atomic32_cmpset((volatile uint32_t *)p, 0, INT32_MIN) == 0)
298                 rte_pause();
299 }
300
301 static inline void
302 rwl_up(rte_atomic32_t *p)
303 {
304         rte_atomic32_sub(p, INT32_MIN);
305 }
306
307 #ifdef __cplusplus
308 }
309 #endif
310
311 #endif /* _MISC_H_ */