fd3e019a22b152aaefc0cb64248375bc529d2116
[dpdk.git] / lib / librte_mbuf / rte_mbuf_dyn.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019 6WIND S.A.
3  */
4
5 #include <sys/queue.h>
6 #include <stdint.h>
7 #include <limits.h>
8
9 #include <rte_common.h>
10 #include <rte_eal.h>
11 #include <rte_eal_memconfig.h>
12 #include <rte_tailq.h>
13 #include <rte_errno.h>
14 #include <rte_malloc.h>
15 #include <rte_string_fns.h>
16 #include <rte_bitops.h>
17 #include <rte_mbuf.h>
18 #include <rte_mbuf_dyn.h>
19
20 #define RTE_MBUF_DYN_MZNAME "rte_mbuf_dyn"
21
22 struct mbuf_dynfield_elt {
23         struct rte_mbuf_dynfield params;
24         size_t offset;
25 };
26 TAILQ_HEAD(mbuf_dynfield_list, rte_tailq_entry);
27
28 static struct rte_tailq_elem mbuf_dynfield_tailq = {
29         .name = "RTE_MBUF_DYNFIELD",
30 };
31 EAL_REGISTER_TAILQ(mbuf_dynfield_tailq);
32
33 struct mbuf_dynflag_elt {
34         struct rte_mbuf_dynflag params;
35         unsigned int bitnum;
36 };
37 TAILQ_HEAD(mbuf_dynflag_list, rte_tailq_entry);
38
39 static struct rte_tailq_elem mbuf_dynflag_tailq = {
40         .name = "RTE_MBUF_DYNFLAG",
41 };
42 EAL_REGISTER_TAILQ(mbuf_dynflag_tailq);
43
44 struct mbuf_dyn_shm {
45         /**
46          * For each mbuf byte, free_space[i] != 0 if space is free.
47          * The value is the size of the biggest aligned element that
48          * can fit in the zone.
49          */
50         uint8_t free_space[sizeof(struct rte_mbuf)];
51         /** Bitfield of available flags. */
52         uint64_t free_flags;
53 };
54 static struct mbuf_dyn_shm *shm;
55
56 /* Set the value of free_space[] according to the size and alignment of
57  * the free areas. This helps to select the best place when reserving a
58  * dynamic field. Assume tailq is locked.
59  */
60 static void
61 process_score(void)
62 {
63         size_t off, align, size, i;
64
65         /* first, erase previous info */
66         for (i = 0; i < sizeof(struct rte_mbuf); i++) {
67                 if (shm->free_space[i])
68                         shm->free_space[i] = 1;
69         }
70
71         off = 0;
72         while (off < sizeof(struct rte_mbuf)) {
73                 /* get the size of the free zone */
74                 for (size = 0; (off + size) < sizeof(struct rte_mbuf) &&
75                              shm->free_space[off + size]; size++)
76                         ;
77                 if (size == 0) {
78                         off++;
79                         continue;
80                 }
81
82                 /* get the alignment of biggest object that can fit in
83                  * the zone at this offset.
84                  */
85                 for (align = 1;
86                      (off % (align << 1)) == 0 && (align << 1) <= size;
87                      align <<= 1)
88                         ;
89
90                 /* save it in free_space[] */
91                 for (i = off; i < off + align; i++)
92                         shm->free_space[i] = RTE_MAX(align, shm->free_space[i]);
93
94                 off += align;
95         }
96 }
97
98 /* Mark the area occupied by a mbuf field as available in the shm. */
99 #define mark_free(field)                                                \
100         memset(&shm->free_space[offsetof(struct rte_mbuf, field)],      \
101                 1, sizeof(((struct rte_mbuf *)0)->field))
102
103 /* Allocate and initialize the shared memory. Assume tailq is locked */
104 static int
105 init_shared_mem(void)
106 {
107         const struct rte_memzone *mz;
108         uint64_t mask;
109
110         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
111                 mz = rte_memzone_reserve_aligned(RTE_MBUF_DYN_MZNAME,
112                                                 sizeof(struct mbuf_dyn_shm),
113                                                 SOCKET_ID_ANY, 0,
114                                                 RTE_CACHE_LINE_SIZE);
115         } else {
116                 mz = rte_memzone_lookup(RTE_MBUF_DYN_MZNAME);
117         }
118         if (mz == NULL)
119                 return -1;
120
121         shm = mz->addr;
122
123         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
124                 /* init free_space, keep it sync'd with
125                  * rte_mbuf_dynfield_copy().
126                  */
127                 memset(shm, 0, sizeof(*shm));
128                 mark_free(dynfield0);
129                 mark_free(dynfield1);
130
131                 /* init free_flags */
132                 for (mask = PKT_FIRST_FREE; mask <= PKT_LAST_FREE; mask <<= 1)
133                         shm->free_flags |= mask;
134
135                 process_score();
136         }
137
138         return 0;
139 }
140
141 /* check if this offset can be used */
142 static int
143 check_offset(size_t offset, size_t size, size_t align)
144 {
145         size_t i;
146
147         if ((offset & (align - 1)) != 0)
148                 return -1;
149         if (offset + size > sizeof(struct rte_mbuf))
150                 return -1;
151
152         for (i = 0; i < size; i++) {
153                 if (!shm->free_space[i + offset])
154                         return -1;
155         }
156
157         return 0;
158 }
159
160 /* assume tailq is locked */
161 static struct mbuf_dynfield_elt *
162 __mbuf_dynfield_lookup(const char *name)
163 {
164         struct mbuf_dynfield_list *mbuf_dynfield_list;
165         struct mbuf_dynfield_elt *mbuf_dynfield;
166         struct rte_tailq_entry *te;
167
168         mbuf_dynfield_list = RTE_TAILQ_CAST(
169                 mbuf_dynfield_tailq.head, mbuf_dynfield_list);
170
171         TAILQ_FOREACH(te, mbuf_dynfield_list, next) {
172                 mbuf_dynfield = (struct mbuf_dynfield_elt *)te->data;
173                 if (strcmp(name, mbuf_dynfield->params.name) == 0)
174                         break;
175         }
176
177         if (te == NULL || mbuf_dynfield == NULL) {
178                 rte_errno = ENOENT;
179                 return NULL;
180         }
181
182         return mbuf_dynfield;
183 }
184
185 int
186 rte_mbuf_dynfield_lookup(const char *name, struct rte_mbuf_dynfield *params)
187 {
188         struct mbuf_dynfield_elt *mbuf_dynfield;
189
190         rte_mcfg_tailq_read_lock();
191         if (shm == NULL && init_shared_mem() < 0)
192                 mbuf_dynfield = NULL;
193         else
194                 mbuf_dynfield = __mbuf_dynfield_lookup(name);
195         rte_mcfg_tailq_read_unlock();
196
197         if (mbuf_dynfield == NULL)
198                 return -1;
199
200         if (params != NULL)
201                 memcpy(params, &mbuf_dynfield->params, sizeof(*params));
202
203         return mbuf_dynfield->offset;
204 }
205
206 static int mbuf_dynfield_cmp(const struct rte_mbuf_dynfield *params1,
207                 const struct rte_mbuf_dynfield *params2)
208 {
209         if (strcmp(params1->name, params2->name))
210                 return -1;
211         if (params1->size != params2->size)
212                 return -1;
213         if (params1->align != params2->align)
214                 return -1;
215         if (params1->flags != params2->flags)
216                 return -1;
217         return 0;
218 }
219
220 /* assume tailq is locked */
221 static int
222 __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
223                                 size_t req)
224 {
225         struct mbuf_dynfield_list *mbuf_dynfield_list;
226         struct mbuf_dynfield_elt *mbuf_dynfield = NULL;
227         struct rte_tailq_entry *te = NULL;
228         unsigned int best_zone = UINT_MAX;
229         size_t i, offset;
230         int ret;
231
232         if (shm == NULL && init_shared_mem() < 0)
233                 return -1;
234
235         mbuf_dynfield = __mbuf_dynfield_lookup(params->name);
236         if (mbuf_dynfield != NULL) {
237                 if (req != SIZE_MAX && req != mbuf_dynfield->offset) {
238                         rte_errno = EEXIST;
239                         return -1;
240                 }
241                 if (mbuf_dynfield_cmp(params, &mbuf_dynfield->params) < 0) {
242                         rte_errno = EEXIST;
243                         return -1;
244                 }
245                 return mbuf_dynfield->offset;
246         }
247
248         if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
249                 rte_errno = EPERM;
250                 return -1;
251         }
252
253         if (req == SIZE_MAX) {
254                 /* Find the best place to put this field: we search the
255                  * lowest value of shm->free_space[offset]: the zones
256                  * containing room for larger fields are kept for later.
257                  */
258                 for (offset = 0;
259                      offset < sizeof(struct rte_mbuf);
260                      offset++) {
261                         if (check_offset(offset, params->size,
262                                                 params->align) == 0 &&
263                                         shm->free_space[offset] < best_zone) {
264                                 best_zone = shm->free_space[offset];
265                                 req = offset;
266                         }
267                 }
268                 if (req == SIZE_MAX) {
269                         rte_errno = ENOENT;
270                         return -1;
271                 }
272         } else {
273                 if (check_offset(req, params->size, params->align) < 0) {
274                         rte_errno = EBUSY;
275                         return -1;
276                 }
277         }
278
279         offset = req;
280         mbuf_dynfield_list = RTE_TAILQ_CAST(
281                 mbuf_dynfield_tailq.head, mbuf_dynfield_list);
282
283         te = rte_zmalloc("MBUF_DYNFIELD_TAILQ_ENTRY", sizeof(*te), 0);
284         if (te == NULL) {
285                 rte_errno = ENOMEM;
286                 return -1;
287         }
288
289         mbuf_dynfield = rte_zmalloc("mbuf_dynfield", sizeof(*mbuf_dynfield), 0);
290         if (mbuf_dynfield == NULL) {
291                 rte_free(te);
292                 rte_errno = ENOMEM;
293                 return -1;
294         }
295
296         ret = strlcpy(mbuf_dynfield->params.name, params->name,
297                 sizeof(mbuf_dynfield->params.name));
298         if (ret < 0 || ret >= (int)sizeof(mbuf_dynfield->params.name)) {
299                 rte_errno = ENAMETOOLONG;
300                 rte_free(mbuf_dynfield);
301                 rte_free(te);
302                 return -1;
303         }
304         memcpy(&mbuf_dynfield->params, params, sizeof(mbuf_dynfield->params));
305         mbuf_dynfield->offset = offset;
306         te->data = mbuf_dynfield;
307
308         TAILQ_INSERT_TAIL(mbuf_dynfield_list, te, next);
309
310         for (i = offset; i < offset + params->size; i++)
311                 shm->free_space[i] = 0;
312         process_score();
313
314         RTE_LOG(DEBUG, MBUF, "Registered dynamic field %s (sz=%zu, al=%zu, fl=0x%x) -> %zd\n",
315                 params->name, params->size, params->align, params->flags,
316                 offset);
317
318         return offset;
319 }
320
321 int
322 rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
323                                 size_t req)
324 {
325         int ret;
326
327         if (params->size >= sizeof(struct rte_mbuf)) {
328                 rte_errno = EINVAL;
329                 return -1;
330         }
331         if (!rte_is_power_of_2(params->align)) {
332                 rte_errno = EINVAL;
333                 return -1;
334         }
335         if (params->flags != 0) {
336                 rte_errno = EINVAL;
337                 return -1;
338         }
339
340         rte_mcfg_tailq_write_lock();
341         ret = __rte_mbuf_dynfield_register_offset(params, req);
342         rte_mcfg_tailq_write_unlock();
343
344         return ret;
345 }
346
347 int
348 rte_mbuf_dynfield_register(const struct rte_mbuf_dynfield *params)
349 {
350         return rte_mbuf_dynfield_register_offset(params, SIZE_MAX);
351 }
352
353 /* assume tailq is locked */
354 static struct mbuf_dynflag_elt *
355 __mbuf_dynflag_lookup(const char *name)
356 {
357         struct mbuf_dynflag_list *mbuf_dynflag_list;
358         struct mbuf_dynflag_elt *mbuf_dynflag;
359         struct rte_tailq_entry *te;
360
361         mbuf_dynflag_list = RTE_TAILQ_CAST(
362                 mbuf_dynflag_tailq.head, mbuf_dynflag_list);
363
364         TAILQ_FOREACH(te, mbuf_dynflag_list, next) {
365                 mbuf_dynflag = (struct mbuf_dynflag_elt *)te->data;
366                 if (strncmp(name, mbuf_dynflag->params.name,
367                                 RTE_MBUF_DYN_NAMESIZE) == 0)
368                         break;
369         }
370
371         if (te == NULL) {
372                 rte_errno = ENOENT;
373                 return NULL;
374         }
375
376         return mbuf_dynflag;
377 }
378
379 int
380 rte_mbuf_dynflag_lookup(const char *name,
381                         struct rte_mbuf_dynflag *params)
382 {
383         struct mbuf_dynflag_elt *mbuf_dynflag;
384
385         rte_mcfg_tailq_read_lock();
386         if (shm == NULL && init_shared_mem() < 0)
387                 mbuf_dynflag = NULL;
388         else
389                 mbuf_dynflag = __mbuf_dynflag_lookup(name);
390         rte_mcfg_tailq_read_unlock();
391
392         if (mbuf_dynflag == NULL)
393                 return -1;
394
395         if (params != NULL)
396                 memcpy(params, &mbuf_dynflag->params, sizeof(*params));
397
398         return mbuf_dynflag->bitnum;
399 }
400
401 static int mbuf_dynflag_cmp(const struct rte_mbuf_dynflag *params1,
402                 const struct rte_mbuf_dynflag *params2)
403 {
404         if (strcmp(params1->name, params2->name))
405                 return -1;
406         if (params1->flags != params2->flags)
407                 return -1;
408         return 0;
409 }
410
411 /* assume tailq is locked */
412 static int
413 __rte_mbuf_dynflag_register_bitnum(const struct rte_mbuf_dynflag *params,
414                                 unsigned int req)
415 {
416         struct mbuf_dynflag_list *mbuf_dynflag_list;
417         struct mbuf_dynflag_elt *mbuf_dynflag = NULL;
418         struct rte_tailq_entry *te = NULL;
419         unsigned int bitnum;
420         int ret;
421
422         if (shm == NULL && init_shared_mem() < 0)
423                 return -1;
424
425         mbuf_dynflag = __mbuf_dynflag_lookup(params->name);
426         if (mbuf_dynflag != NULL) {
427                 if (req != UINT_MAX && req != mbuf_dynflag->bitnum) {
428                         rte_errno = EEXIST;
429                         return -1;
430                 }
431                 if (mbuf_dynflag_cmp(params, &mbuf_dynflag->params) < 0) {
432                         rte_errno = EEXIST;
433                         return -1;
434                 }
435                 return mbuf_dynflag->bitnum;
436         }
437
438         if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
439                 rte_errno = EPERM;
440                 return -1;
441         }
442
443         if (req == UINT_MAX) {
444                 if (shm->free_flags == 0) {
445                         rte_errno = ENOENT;
446                         return -1;
447                 }
448                 bitnum = rte_bsf64(shm->free_flags);
449         } else {
450                 if ((shm->free_flags & (1ULL << req)) == 0) {
451                         rte_errno = EBUSY;
452                         return -1;
453                 }
454                 bitnum = req;
455         }
456
457         mbuf_dynflag_list = RTE_TAILQ_CAST(
458                 mbuf_dynflag_tailq.head, mbuf_dynflag_list);
459
460         te = rte_zmalloc("MBUF_DYNFLAG_TAILQ_ENTRY", sizeof(*te), 0);
461         if (te == NULL) {
462                 rte_errno = ENOMEM;
463                 return -1;
464         }
465
466         mbuf_dynflag = rte_zmalloc("mbuf_dynflag", sizeof(*mbuf_dynflag), 0);
467         if (mbuf_dynflag == NULL) {
468                 rte_free(te);
469                 rte_errno = ENOMEM;
470                 return -1;
471         }
472
473         ret = strlcpy(mbuf_dynflag->params.name, params->name,
474                 sizeof(mbuf_dynflag->params.name));
475         if (ret < 0 || ret >= (int)sizeof(mbuf_dynflag->params.name)) {
476                 rte_free(mbuf_dynflag);
477                 rte_free(te);
478                 rte_errno = ENAMETOOLONG;
479                 return -1;
480         }
481         mbuf_dynflag->bitnum = bitnum;
482         te->data = mbuf_dynflag;
483
484         TAILQ_INSERT_TAIL(mbuf_dynflag_list, te, next);
485
486         shm->free_flags &= ~(1ULL << bitnum);
487
488         RTE_LOG(DEBUG, MBUF, "Registered dynamic flag %s (fl=0x%x) -> %u\n",
489                 params->name, params->flags, bitnum);
490
491         return bitnum;
492 }
493
494 int
495 rte_mbuf_dynflag_register_bitnum(const struct rte_mbuf_dynflag *params,
496                                 unsigned int req)
497 {
498         int ret;
499
500         if (req >= RTE_SIZEOF_FIELD(struct rte_mbuf, ol_flags) * CHAR_BIT &&
501                         req != UINT_MAX) {
502                 rte_errno = EINVAL;
503                 return -1;
504         }
505
506         rte_mcfg_tailq_write_lock();
507         ret = __rte_mbuf_dynflag_register_bitnum(params, req);
508         rte_mcfg_tailq_write_unlock();
509
510         return ret;
511 }
512
513 int
514 rte_mbuf_dynflag_register(const struct rte_mbuf_dynflag *params)
515 {
516         return rte_mbuf_dynflag_register_bitnum(params, UINT_MAX);
517 }
518
519 void rte_mbuf_dyn_dump(FILE *out)
520 {
521         struct mbuf_dynfield_list *mbuf_dynfield_list;
522         struct mbuf_dynfield_elt *dynfield;
523         struct mbuf_dynflag_list *mbuf_dynflag_list;
524         struct mbuf_dynflag_elt *dynflag;
525         struct rte_tailq_entry *te;
526         size_t i;
527
528         rte_mcfg_tailq_write_lock();
529         init_shared_mem();
530         fprintf(out, "Reserved fields:\n");
531         mbuf_dynfield_list = RTE_TAILQ_CAST(
532                 mbuf_dynfield_tailq.head, mbuf_dynfield_list);
533         TAILQ_FOREACH(te, mbuf_dynfield_list, next) {
534                 dynfield = (struct mbuf_dynfield_elt *)te->data;
535                 fprintf(out, "  name=%s offset=%zd size=%zd align=%zd flags=%x\n",
536                         dynfield->params.name, dynfield->offset,
537                         dynfield->params.size, dynfield->params.align,
538                         dynfield->params.flags);
539         }
540         fprintf(out, "Reserved flags:\n");
541         mbuf_dynflag_list = RTE_TAILQ_CAST(
542                 mbuf_dynflag_tailq.head, mbuf_dynflag_list);
543         TAILQ_FOREACH(te, mbuf_dynflag_list, next) {
544                 dynflag = (struct mbuf_dynflag_elt *)te->data;
545                 fprintf(out, "  name=%s bitnum=%u flags=%x\n",
546                         dynflag->params.name, dynflag->bitnum,
547                         dynflag->params.flags);
548         }
549         fprintf(out, "Free space in mbuf (0 = occupied, value = free zone alignment):\n");
550         for (i = 0; i < sizeof(struct rte_mbuf); i++) {
551                 if ((i % 8) == 0)
552                         fprintf(out, "  %4.4zx: ", i);
553                 fprintf(out, "%2.2x%s", shm->free_space[i],
554                         (i % 8 != 7) ? " " : "\n");
555         }
556         fprintf(out, "Free bit in mbuf->ol_flags (0 = occupied, 1 = free):\n");
557         for (i = 0; i < sizeof(uint64_t) * CHAR_BIT; i++) {
558                 if ((i % 8) == 0)
559                         fprintf(out, "  %4.4zx: ", i);
560                 fprintf(out, "%1.1x%s", (shm->free_flags & (1ULL << i)) ? 1 : 0,
561                         (i % 8 != 7) ? " " : "\n");
562         }
563
564         rte_mcfg_tailq_write_unlock();
565 }
566
567 static int
568 rte_mbuf_dyn_timestamp_register(int *field_offset, uint64_t *flag,
569                 const char *direction, const char *flag_name)
570 {
571         static const struct rte_mbuf_dynfield field_desc = {
572                 .name = RTE_MBUF_DYNFIELD_TIMESTAMP_NAME,
573                 .size = sizeof(rte_mbuf_timestamp_t),
574                 .align = __alignof__(rte_mbuf_timestamp_t),
575         };
576         struct rte_mbuf_dynflag flag_desc = {};
577         int offset;
578
579         offset = rte_mbuf_dynfield_register(&field_desc);
580         if (offset < 0) {
581                 RTE_LOG(ERR, MBUF,
582                         "Failed to register mbuf field for timestamp\n");
583                 return -1;
584         }
585         if (field_offset != NULL)
586                 *field_offset = offset;
587
588         strlcpy(flag_desc.name, flag_name, sizeof(flag_desc.name));
589         offset = rte_mbuf_dynflag_register(&flag_desc);
590         if (offset < 0) {
591                 RTE_LOG(ERR, MBUF,
592                         "Failed to register mbuf flag for %s timestamp\n",
593                         direction);
594                 return -1;
595         }
596         if (flag != NULL)
597                 *flag = RTE_BIT64(offset);
598
599         return 0;
600 }
601
602 int
603 rte_mbuf_dyn_rx_timestamp_register(int *field_offset, uint64_t *rx_flag)
604 {
605         return rte_mbuf_dyn_timestamp_register(field_offset, rx_flag,
606                         "Rx", RTE_MBUF_DYNFLAG_RX_TIMESTAMP_NAME);
607 }
608
609 int
610 rte_mbuf_dyn_tx_timestamp_register(int *field_offset, uint64_t *tx_flag)
611 {
612         return rte_mbuf_dyn_timestamp_register(field_offset, tx_flag,
613                         "Tx", RTE_MBUF_DYNFLAG_TX_TIMESTAMP_NAME);
614 }