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