mbuf: support dynamic fields and flags
[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
7 #include <rte_common.h>
8 #include <rte_eal.h>
9 #include <rte_eal_memconfig.h>
10 #include <rte_tailq.h>
11 #include <rte_errno.h>
12 #include <rte_malloc.h>
13 #include <rte_string_fns.h>
14 #include <rte_mbuf.h>
15 #include <rte_mbuf_dyn.h>
16
17 #define RTE_MBUF_DYN_MZNAME "rte_mbuf_dyn"
18
19 struct mbuf_dynfield {
20         TAILQ_ENTRY(mbuf_dynfield) next;
21         char name[RTE_MBUF_DYN_NAMESIZE];
22         size_t size;
23         size_t align;
24         unsigned int flags;
25         int offset;
26 };
27 TAILQ_HEAD(mbuf_dynfield_list, rte_tailq_entry);
28
29 static struct rte_tailq_elem mbuf_dynfield_tailq = {
30         .name = "RTE_MBUF_DYNFIELD",
31 };
32 EAL_REGISTER_TAILQ(mbuf_dynfield_tailq);
33
34 struct mbuf_dynflag {
35         TAILQ_ENTRY(mbuf_dynflag) next;
36         char name[RTE_MBUF_DYN_NAMESIZE];
37         int bitnum;
38 };
39 TAILQ_HEAD(mbuf_dynflag_list, rte_tailq_entry);
40
41 static struct rte_tailq_elem mbuf_dynflag_tailq = {
42         .name = "RTE_MBUF_DYNFLAG",
43 };
44 EAL_REGISTER_TAILQ(mbuf_dynflag_tailq);
45
46 struct mbuf_dyn_shm {
47         /** For each mbuf byte, free_space[i] == 1 if space is free. */
48         uint8_t free_space[sizeof(struct rte_mbuf)];
49         /** Bitfield of available flags. */
50         uint64_t free_flags;
51 };
52 static struct mbuf_dyn_shm *shm;
53
54 /* allocate and initialize the shared memory */
55 static int
56 init_shared_mem(void)
57 {
58         const struct rte_memzone *mz;
59         uint64_t mask;
60
61         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
62                 mz = rte_memzone_reserve_aligned(RTE_MBUF_DYN_MZNAME,
63                                                 sizeof(struct mbuf_dyn_shm),
64                                                 SOCKET_ID_ANY, 0,
65                                                 RTE_CACHE_LINE_SIZE);
66         } else {
67                 mz = rte_memzone_lookup(RTE_MBUF_DYN_MZNAME);
68         }
69         if (mz == NULL)
70                 return -1;
71
72         shm = mz->addr;
73
74 #define mark_free(field)                                                \
75         memset(&shm->free_space[offsetof(struct rte_mbuf, field)],      \
76                 0xff, sizeof(((struct rte_mbuf *)0)->field))
77
78         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
79                 /* init free_space, keep it sync'd with
80                  * rte_mbuf_dynfield_copy().
81                  */
82                 memset(shm, 0, sizeof(*shm));
83                 mark_free(dynfield1);
84                 mark_free(dynfield2);
85
86                 /* init free_flags */
87                 for (mask = PKT_FIRST_FREE; mask <= PKT_LAST_FREE; mask <<= 1)
88                         shm->free_flags |= mask;
89         }
90 #undef mark_free
91
92         return 0;
93 }
94
95 /* check if this offset can be used */
96 static int
97 check_offset(size_t offset, size_t size, size_t align, unsigned int flags)
98 {
99         size_t i;
100
101         (void)flags;
102
103         if ((offset & (align - 1)) != 0)
104                 return -1;
105         if (offset + size > sizeof(struct rte_mbuf))
106                 return -1;
107
108         for (i = 0; i < size; i++) {
109                 if (!shm->free_space[i + offset])
110                         return -1;
111         }
112
113         return 0;
114 }
115
116 /* assume tailq is locked */
117 static struct mbuf_dynfield *
118 __mbuf_dynfield_lookup(const char *name)
119 {
120         struct mbuf_dynfield_list *mbuf_dynfield_list;
121         struct mbuf_dynfield *mbuf_dynfield;
122         struct rte_tailq_entry *te;
123
124         mbuf_dynfield_list = RTE_TAILQ_CAST(
125                 mbuf_dynfield_tailq.head, mbuf_dynfield_list);
126
127         TAILQ_FOREACH(te, mbuf_dynfield_list, next) {
128                 mbuf_dynfield = (struct mbuf_dynfield *)te->data;
129                 if (strncmp(name, mbuf_dynfield->name,
130                                 RTE_MBUF_DYN_NAMESIZE) == 0)
131                         break;
132         }
133
134         if (te == NULL) {
135                 rte_errno = ENOENT;
136                 return NULL;
137         }
138
139         return mbuf_dynfield;
140 }
141
142 int
143 rte_mbuf_dynfield_lookup(const char *name, size_t *size, size_t *align)
144 {
145         struct mbuf_dynfield *mbuf_dynfield;
146
147         if (shm == NULL) {
148                 rte_errno = ENOENT;
149                 return -1;
150         }
151
152         rte_mcfg_tailq_read_lock();
153         mbuf_dynfield = __mbuf_dynfield_lookup(name);
154         rte_mcfg_tailq_read_unlock();
155
156         if (mbuf_dynfield == NULL) {
157                 rte_errno = ENOENT;
158                 return -1;
159         }
160
161         if (size != NULL)
162                 *size = mbuf_dynfield->size;
163         if (align != NULL)
164                 *align = mbuf_dynfield->align;
165
166         return mbuf_dynfield->offset;
167 }
168
169 int
170 rte_mbuf_dynfield_register(const char *name, size_t size, size_t align,
171                         unsigned int flags)
172 {
173         struct mbuf_dynfield_list *mbuf_dynfield_list;
174         struct mbuf_dynfield *mbuf_dynfield = NULL;
175         struct rte_tailq_entry *te = NULL;
176         int offset, ret;
177         size_t i;
178
179         if (shm == NULL && init_shared_mem() < 0)
180                 goto fail;
181         if (size >= sizeof(struct rte_mbuf)) {
182                 rte_errno = EINVAL;
183                 goto fail;
184         }
185         if (!rte_is_power_of_2(align)) {
186                 rte_errno = EINVAL;
187                 goto fail;
188         }
189
190         rte_mcfg_tailq_write_lock();
191
192         mbuf_dynfield = __mbuf_dynfield_lookup(name);
193         if (mbuf_dynfield != NULL) {
194                 if (mbuf_dynfield->size != size ||
195                                 mbuf_dynfield->align != align ||
196                                 mbuf_dynfield->flags != flags) {
197                         rte_errno = EEXIST;
198                         goto fail_unlock;
199                 }
200                 offset = mbuf_dynfield->offset;
201                 goto out_unlock;
202         }
203
204         if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
205                 rte_errno = EPERM;
206                 goto fail_unlock;
207         }
208
209         for (offset = 0;
210              offset < (int)sizeof(struct rte_mbuf);
211              offset++) {
212                 if (check_offset(offset, size, align, flags) == 0)
213                         break;
214         }
215
216         if (offset == sizeof(struct rte_mbuf)) {
217                 rte_errno = ENOENT;
218                 goto fail_unlock;
219         }
220
221         mbuf_dynfield_list = RTE_TAILQ_CAST(
222                 mbuf_dynfield_tailq.head, mbuf_dynfield_list);
223
224         te = rte_zmalloc("MBUF_DYNFIELD_TAILQ_ENTRY", sizeof(*te), 0);
225         if (te == NULL)
226                 goto fail_unlock;
227
228         mbuf_dynfield = rte_zmalloc("mbuf_dynfield", sizeof(*mbuf_dynfield), 0);
229         if (mbuf_dynfield == NULL)
230                 goto fail_unlock;
231
232         ret = strlcpy(mbuf_dynfield->name, name, sizeof(mbuf_dynfield->name));
233         if (ret < 0 || ret >= (int)sizeof(mbuf_dynfield->name)) {
234                 rte_errno = ENAMETOOLONG;
235                 goto fail_unlock;
236         }
237         mbuf_dynfield->size = size;
238         mbuf_dynfield->align = align;
239         mbuf_dynfield->flags = flags;
240         mbuf_dynfield->offset = offset;
241         te->data = mbuf_dynfield;
242
243         TAILQ_INSERT_TAIL(mbuf_dynfield_list, te, next);
244
245         for (i = offset; i < offset + size; i++)
246                 shm->free_space[i] = 0;
247
248 out_unlock:
249         rte_mcfg_tailq_write_unlock();
250
251         return offset;
252
253 fail_unlock:
254         rte_mcfg_tailq_write_unlock();
255 fail:
256         rte_free(mbuf_dynfield);
257         rte_free(te);
258         return -1;
259 }
260
261 /* assume tailq is locked */
262 static struct mbuf_dynflag *
263 __mbuf_dynflag_lookup(const char *name)
264 {
265         struct mbuf_dynflag_list *mbuf_dynflag_list;
266         struct mbuf_dynflag *mbuf_dynflag;
267         struct rte_tailq_entry *te;
268
269         mbuf_dynflag_list = RTE_TAILQ_CAST(
270                 mbuf_dynflag_tailq.head, mbuf_dynflag_list);
271
272         TAILQ_FOREACH(te, mbuf_dynflag_list, next) {
273                 mbuf_dynflag = (struct mbuf_dynflag *)te->data;
274                 if (strncmp(name, mbuf_dynflag->name,
275                                 RTE_MBUF_DYN_NAMESIZE) == 0)
276                         break;
277         }
278
279         if (te == NULL) {
280                 rte_errno = ENOENT;
281                 return NULL;
282         }
283
284         return mbuf_dynflag;
285 }
286
287 int
288 rte_mbuf_dynflag_lookup(const char *name)
289 {
290         struct mbuf_dynflag *mbuf_dynflag;
291
292         if (shm == NULL) {
293                 rte_errno = ENOENT;
294                 return -1;
295         }
296
297         rte_mcfg_tailq_read_lock();
298         mbuf_dynflag = __mbuf_dynflag_lookup(name);
299         rte_mcfg_tailq_read_unlock();
300
301         if (mbuf_dynflag == NULL) {
302                 rte_errno = ENOENT;
303                 return -1;
304         }
305
306         return mbuf_dynflag->bitnum;
307 }
308
309 int
310 rte_mbuf_dynflag_register(const char *name)
311 {
312         struct mbuf_dynflag_list *mbuf_dynflag_list;
313         struct mbuf_dynflag *mbuf_dynflag = NULL;
314         struct rte_tailq_entry *te = NULL;
315         int bitnum, ret;
316
317         if (shm == NULL && init_shared_mem() < 0)
318                 goto fail;
319
320         rte_mcfg_tailq_write_lock();
321
322         mbuf_dynflag = __mbuf_dynflag_lookup(name);
323         if (mbuf_dynflag != NULL) {
324                 bitnum = mbuf_dynflag->bitnum;
325                 goto out_unlock;
326         }
327
328         if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
329                 rte_errno = EPERM;
330                 goto fail_unlock;
331         }
332
333         if (shm->free_flags == 0) {
334                 rte_errno = ENOENT;
335                 goto fail_unlock;
336         }
337         bitnum = rte_bsf64(shm->free_flags);
338
339         mbuf_dynflag_list = RTE_TAILQ_CAST(
340                 mbuf_dynflag_tailq.head, mbuf_dynflag_list);
341
342         te = rte_zmalloc("MBUF_DYNFLAG_TAILQ_ENTRY", sizeof(*te), 0);
343         if (te == NULL)
344                 goto fail_unlock;
345
346         mbuf_dynflag = rte_zmalloc("mbuf_dynflag", sizeof(*mbuf_dynflag), 0);
347         if (mbuf_dynflag == NULL)
348                 goto fail_unlock;
349
350         ret = strlcpy(mbuf_dynflag->name, name, sizeof(mbuf_dynflag->name));
351         if (ret < 0 || ret >= (int)sizeof(mbuf_dynflag->name)) {
352                 rte_errno = ENAMETOOLONG;
353                 goto fail_unlock;
354         }
355         mbuf_dynflag->bitnum = bitnum;
356         te->data = mbuf_dynflag;
357
358         TAILQ_INSERT_TAIL(mbuf_dynflag_list, te, next);
359
360         shm->free_flags &= ~(1ULL << bitnum);
361
362 out_unlock:
363         rte_mcfg_tailq_write_unlock();
364
365         return bitnum;
366
367 fail_unlock:
368         rte_mcfg_tailq_write_unlock();
369 fail:
370         rte_free(mbuf_dynflag);
371         rte_free(te);
372         return -1;
373 }