New upstream version 17.11-rc3
[deb_dpdk.git] / lib / librte_reorder / rte_reorder.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <inttypes.h>
35 #include <string.h>
36
37 #include <rte_log.h>
38 #include <rte_mbuf.h>
39 #include <rte_eal_memconfig.h>
40 #include <rte_errno.h>
41 #include <rte_malloc.h>
42
43 #include "rte_reorder.h"
44
45 TAILQ_HEAD(rte_reorder_list, rte_tailq_entry);
46
47 static struct rte_tailq_elem rte_reorder_tailq = {
48         .name = "RTE_REORDER",
49 };
50 EAL_REGISTER_TAILQ(rte_reorder_tailq)
51
52 #define NO_FLAGS 0
53 #define RTE_REORDER_PREFIX "RO_"
54 #define RTE_REORDER_NAMESIZE 32
55
56 /* Macros for printing using RTE_LOG */
57 #define RTE_LOGTYPE_REORDER     RTE_LOGTYPE_USER1
58
59 /* A generic circular buffer */
60 struct cir_buffer {
61         unsigned int size;   /**< Number of entries that can be stored */
62         unsigned int mask;   /**< [buffer_size - 1]: used for wrap-around */
63         unsigned int head;   /**< insertion point in buffer */
64         unsigned int tail;   /**< extraction point in buffer */
65         struct rte_mbuf **entries;
66 } __rte_cache_aligned;
67
68 /* The reorder buffer data structure itself */
69 struct rte_reorder_buffer {
70         char name[RTE_REORDER_NAMESIZE];
71         uint32_t min_seqn;  /**< Lowest seq. number that can be in the buffer */
72         unsigned int memsize; /**< memory area size of reorder buffer */
73         struct cir_buffer ready_buf; /**< temp buffer for dequeued entries */
74         struct cir_buffer order_buf; /**< buffer used to reorder entries */
75         int is_initialized;
76 } __rte_cache_aligned;
77
78 static void
79 rte_reorder_free_mbufs(struct rte_reorder_buffer *b);
80
81 struct rte_reorder_buffer *
82 rte_reorder_init(struct rte_reorder_buffer *b, unsigned int bufsize,
83                 const char *name, unsigned int size)
84 {
85         const unsigned int min_bufsize = sizeof(*b) +
86                                         (2 * size * sizeof(struct rte_mbuf *));
87
88         if (b == NULL) {
89                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer parameter:"
90                                         " NULL\n");
91                 rte_errno = EINVAL;
92                 return NULL;
93         }
94         if (!rte_is_power_of_2(size)) {
95                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer size"
96                                 " - Not a power of 2\n");
97                 rte_errno = EINVAL;
98                 return NULL;
99         }
100         if (name == NULL) {
101                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:"
102                                         " NULL\n");
103                 rte_errno = EINVAL;
104                 return NULL;
105         }
106         if (bufsize < min_bufsize) {
107                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer memory size: %u, "
108                         "minimum required: %u\n", bufsize, min_bufsize);
109                 rte_errno = EINVAL;
110                 return NULL;
111         }
112
113         memset(b, 0, bufsize);
114         snprintf(b->name, sizeof(b->name), "%s", name);
115         b->memsize = bufsize;
116         b->order_buf.size = b->ready_buf.size = size;
117         b->order_buf.mask = b->ready_buf.mask = size - 1;
118         b->ready_buf.entries = (void *)&b[1];
119         b->order_buf.entries = RTE_PTR_ADD(&b[1],
120                         size * sizeof(b->ready_buf.entries[0]));
121
122         return b;
123 }
124
125 struct rte_reorder_buffer*
126 rte_reorder_create(const char *name, unsigned socket_id, unsigned int size)
127 {
128         struct rte_reorder_buffer *b = NULL;
129         struct rte_tailq_entry *te;
130         struct rte_reorder_list *reorder_list;
131         const unsigned int bufsize = sizeof(struct rte_reorder_buffer) +
132                                         (2 * size * sizeof(struct rte_mbuf *));
133
134         reorder_list = RTE_TAILQ_CAST(rte_reorder_tailq.head, rte_reorder_list);
135
136         /* Check user arguments. */
137         if (!rte_is_power_of_2(size)) {
138                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer size"
139                                 " - Not a power of 2\n");
140                 rte_errno = EINVAL;
141                 return NULL;
142         }
143         if (name == NULL) {
144                 RTE_LOG(ERR, REORDER, "Invalid reorder buffer name ptr:"
145                                         " NULL\n");
146                 rte_errno = EINVAL;
147                 return NULL;
148         }
149
150         rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
151
152         /* guarantee there's no existing */
153         TAILQ_FOREACH(te, reorder_list, next) {
154                 b = (struct rte_reorder_buffer *) te->data;
155                 if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0)
156                         break;
157         }
158         if (te != NULL)
159                 goto exit;
160
161         /* allocate tailq entry */
162         te = rte_zmalloc("REORDER_TAILQ_ENTRY", sizeof(*te), 0);
163         if (te == NULL) {
164                 RTE_LOG(ERR, REORDER, "Failed to allocate tailq entry\n");
165                 rte_errno = ENOMEM;
166                 b = NULL;
167                 goto exit;
168         }
169
170         /* Allocate memory to store the reorder buffer structure. */
171         b = rte_zmalloc_socket("REORDER_BUFFER", bufsize, 0, socket_id);
172         if (b == NULL) {
173                 RTE_LOG(ERR, REORDER, "Memzone allocation failed\n");
174                 rte_errno = ENOMEM;
175                 rte_free(te);
176         } else {
177                 rte_reorder_init(b, bufsize, name, size);
178                 te->data = (void *)b;
179                 TAILQ_INSERT_TAIL(reorder_list, te, next);
180         }
181
182 exit:
183         rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
184         return b;
185 }
186
187 void
188 rte_reorder_reset(struct rte_reorder_buffer *b)
189 {
190         char name[RTE_REORDER_NAMESIZE];
191
192         rte_reorder_free_mbufs(b);
193         snprintf(name, sizeof(name), "%s", b->name);
194         /* No error checking as current values should be valid */
195         rte_reorder_init(b, b->memsize, name, b->order_buf.size);
196 }
197
198 static void
199 rte_reorder_free_mbufs(struct rte_reorder_buffer *b)
200 {
201         unsigned i;
202
203         /* Free up the mbufs of order buffer & ready buffer */
204         for (i = 0; i < b->order_buf.size; i++) {
205                 if (b->order_buf.entries[i])
206                         rte_pktmbuf_free(b->order_buf.entries[i]);
207                 if (b->ready_buf.entries[i])
208                         rte_pktmbuf_free(b->ready_buf.entries[i]);
209         }
210 }
211
212 void
213 rte_reorder_free(struct rte_reorder_buffer *b)
214 {
215         struct rte_reorder_list *reorder_list;
216         struct rte_tailq_entry *te;
217
218         /* Check user arguments. */
219         if (b == NULL)
220                 return;
221
222         reorder_list = RTE_TAILQ_CAST(rte_reorder_tailq.head, rte_reorder_list);
223
224         rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
225
226         /* find our tailq entry */
227         TAILQ_FOREACH(te, reorder_list, next) {
228                 if (te->data == (void *) b)
229                         break;
230         }
231         if (te == NULL) {
232                 rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
233                 return;
234         }
235
236         TAILQ_REMOVE(reorder_list, te, next);
237
238         rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
239
240         rte_reorder_free_mbufs(b);
241
242         rte_free(b);
243         rte_free(te);
244 }
245
246 struct rte_reorder_buffer *
247 rte_reorder_find_existing(const char *name)
248 {
249         struct rte_reorder_buffer *b = NULL;
250         struct rte_tailq_entry *te;
251         struct rte_reorder_list *reorder_list;
252
253         reorder_list = RTE_TAILQ_CAST(rte_reorder_tailq.head, rte_reorder_list);
254
255         rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK);
256         TAILQ_FOREACH(te, reorder_list, next) {
257                 b = (struct rte_reorder_buffer *) te->data;
258                 if (strncmp(name, b->name, RTE_REORDER_NAMESIZE) == 0)
259                         break;
260         }
261         rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK);
262
263         if (te == NULL) {
264                 rte_errno = ENOENT;
265                 return NULL;
266         }
267
268         return b;
269 }
270
271 static unsigned
272 rte_reorder_fill_overflow(struct rte_reorder_buffer *b, unsigned n)
273 {
274         /*
275          * 1. Move all ready entries that fit to the ready_buf
276          * 2. check if we meet the minimum needed (n).
277          * 3. If not, then skip any gaps and keep moving.
278          * 4. If at any point the ready buffer is full, stop
279          * 5. Return the number of positions the order_buf head has moved
280          */
281
282         struct cir_buffer *order_buf = &b->order_buf,
283                         *ready_buf = &b->ready_buf;
284
285         unsigned int order_head_adv = 0;
286
287         /*
288          * move at least n packets to ready buffer, assuming ready buffer
289          * has room for those packets.
290          */
291         while (order_head_adv < n &&
292                         ((ready_buf->head + 1) & ready_buf->mask) != ready_buf->tail) {
293
294                 /* if we are blocked waiting on a packet, skip it */
295                 if (order_buf->entries[order_buf->head] == NULL) {
296                         order_buf->head = (order_buf->head + 1) & order_buf->mask;
297                         order_head_adv++;
298                 }
299
300                 /* Move all ready entries that fit to the ready_buf */
301                 while (order_buf->entries[order_buf->head] != NULL) {
302                         ready_buf->entries[ready_buf->head] =
303                                         order_buf->entries[order_buf->head];
304
305                         order_buf->entries[order_buf->head] = NULL;
306                         order_head_adv++;
307
308                         order_buf->head = (order_buf->head + 1) & order_buf->mask;
309
310                         if (((ready_buf->head + 1) & ready_buf->mask) == ready_buf->tail)
311                                 break;
312
313                         ready_buf->head = (ready_buf->head + 1) & ready_buf->mask;
314                 }
315         }
316
317         b->min_seqn += order_head_adv;
318         /* Return the number of positions the order_buf head has moved */
319         return order_head_adv;
320 }
321
322 int
323 rte_reorder_insert(struct rte_reorder_buffer *b, struct rte_mbuf *mbuf)
324 {
325         uint32_t offset, position;
326         struct cir_buffer *order_buf = &b->order_buf;
327
328         if (!b->is_initialized) {
329                 b->min_seqn = mbuf->seqn;
330                 b->is_initialized = 1;
331         }
332
333         /*
334          * calculate the offset from the head pointer we need to go.
335          * The subtraction takes care of the sequence number wrapping.
336          * For example (using 16-bit for brevity):
337          *      min_seqn  = 0xFFFD
338          *      mbuf_seqn = 0x0010
339          *      offset    = 0x0010 - 0xFFFD = 0x13
340          */
341         offset = mbuf->seqn - b->min_seqn;
342
343         /*
344          * action to take depends on offset.
345          * offset < buffer->size: the mbuf fits within the current window of
346          *    sequence numbers we can reorder. EXPECTED CASE.
347          * offset > buffer->size: the mbuf is outside the current window. There
348          *    are a number of cases to consider:
349          *    1. The packet sequence is just outside the window, then we need
350          *       to see about shifting the head pointer and taking any ready
351          *       to return packets out of the ring. If there was a delayed
352          *       or dropped packet preventing drains from shifting the window
353          *       this case will skip over the dropped packet instead, and any
354          *       packets dequeued here will be returned on the next drain call.
355          *    2. The packet sequence number is vastly outside our window, taken
356          *       here as having offset greater than twice the buffer size. In
357          *       this case, the packet is probably an old or late packet that
358          *       was previously skipped, so just enqueue the packet for
359          *       immediate return on the next drain call, or else return error.
360          */
361         if (offset < b->order_buf.size) {
362                 position = (order_buf->head + offset) & order_buf->mask;
363                 order_buf->entries[position] = mbuf;
364         } else if (offset < 2 * b->order_buf.size) {
365                 if (rte_reorder_fill_overflow(b, offset + 1 - order_buf->size)
366                                 < (offset + 1 - order_buf->size)) {
367                         /* Put in handling for enqueue straight to output */
368                         rte_errno = ENOSPC;
369                         return -1;
370                 }
371                 offset = mbuf->seqn - b->min_seqn;
372                 position = (order_buf->head + offset) & order_buf->mask;
373                 order_buf->entries[position] = mbuf;
374         } else {
375                 /* Put in handling for enqueue straight to output */
376                 rte_errno = ERANGE;
377                 return -1;
378         }
379         return 0;
380 }
381
382 unsigned int
383 rte_reorder_drain(struct rte_reorder_buffer *b, struct rte_mbuf **mbufs,
384                 unsigned max_mbufs)
385 {
386         unsigned int drain_cnt = 0;
387
388         struct cir_buffer *order_buf = &b->order_buf,
389                         *ready_buf = &b->ready_buf;
390
391         /* Try to fetch requested number of mbufs from ready buffer */
392         while ((drain_cnt < max_mbufs) && (ready_buf->tail != ready_buf->head)) {
393                 mbufs[drain_cnt++] = ready_buf->entries[ready_buf->tail];
394                 ready_buf->tail = (ready_buf->tail + 1) & ready_buf->mask;
395         }
396
397         /*
398          * If requested number of buffers not fetched from ready buffer, fetch
399          * remaining buffers from order buffer
400          */
401         while ((drain_cnt < max_mbufs) &&
402                         (order_buf->entries[order_buf->head] != NULL)) {
403                 mbufs[drain_cnt++] = order_buf->entries[order_buf->head];
404                 order_buf->entries[order_buf->head] = NULL;
405                 b->min_seqn++;
406                 order_buf->head = (order_buf->head + 1) & order_buf->mask;
407         }
408
409         return drain_cnt;
410 }