fbarray: add internal tailq for mapped areas
[dpdk.git] / lib / librte_eal / common / eal_common_fbarray.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017-2018 Intel Corporation
3  */
4
5 #include <fcntl.h>
6 #include <inttypes.h>
7 #include <limits.h>
8 #include <sys/mman.h>
9 #include <stdint.h>
10 #include <errno.h>
11 #include <sys/file.h>
12 #include <string.h>
13
14 #include <rte_common.h>
15 #include <rte_log.h>
16 #include <rte_errno.h>
17 #include <rte_spinlock.h>
18 #include <rte_tailq.h>
19
20 #include "eal_filesystem.h"
21 #include "eal_private.h"
22
23 #include "rte_fbarray.h"
24
25 #define MASK_SHIFT 6ULL
26 #define MASK_ALIGN (1ULL << MASK_SHIFT)
27 #define MASK_LEN_TO_IDX(x) ((x) >> MASK_SHIFT)
28 #define MASK_LEN_TO_MOD(x) ((x) - RTE_ALIGN_FLOOR(x, MASK_ALIGN))
29 #define MASK_GET_IDX(idx, mod) ((idx << MASK_SHIFT) + mod)
30
31 /*
32  * We use this to keep track of created/attached memory areas to prevent user
33  * errors in API usage.
34  */
35 struct mem_area {
36         TAILQ_ENTRY(mem_area) next;
37         void *addr;
38         size_t len;
39         int fd;
40 };
41 TAILQ_HEAD(mem_area_head, mem_area);
42 /* local per-process tailq */
43 static struct mem_area_head mem_area_tailq =
44         TAILQ_HEAD_INITIALIZER(mem_area_tailq);
45 static rte_spinlock_t mem_area_lock = RTE_SPINLOCK_INITIALIZER;
46
47 /*
48  * This is a mask that is always stored at the end of array, to provide fast
49  * way of finding free/used spots without looping through each element.
50  */
51
52 struct used_mask {
53         unsigned int n_masks;
54         uint64_t data[];
55 };
56
57 static size_t
58 calc_mask_size(unsigned int len)
59 {
60         /* mask must be multiple of MASK_ALIGN, even though length of array
61          * itself may not be aligned on that boundary.
62          */
63         len = RTE_ALIGN_CEIL(len, MASK_ALIGN);
64         return sizeof(struct used_mask) +
65                         sizeof(uint64_t) * MASK_LEN_TO_IDX(len);
66 }
67
68 static size_t
69 calc_data_size(size_t page_sz, unsigned int elt_sz, unsigned int len)
70 {
71         size_t data_sz = elt_sz * len;
72         size_t msk_sz = calc_mask_size(len);
73         return RTE_ALIGN_CEIL(data_sz + msk_sz, page_sz);
74 }
75
76 static struct used_mask *
77 get_used_mask(void *data, unsigned int elt_sz, unsigned int len)
78 {
79         return (struct used_mask *) RTE_PTR_ADD(data, elt_sz * len);
80 }
81
82 static int
83 resize_and_map(int fd, void *addr, size_t len)
84 {
85         char path[PATH_MAX];
86         void *map_addr;
87
88         if (ftruncate(fd, len)) {
89                 RTE_LOG(ERR, EAL, "Cannot truncate %s\n", path);
90                 /* pass errno up the chain */
91                 rte_errno = errno;
92                 return -1;
93         }
94
95         map_addr = mmap(addr, len, PROT_READ | PROT_WRITE,
96                         MAP_SHARED | MAP_FIXED, fd, 0);
97         if (map_addr != addr) {
98                 RTE_LOG(ERR, EAL, "mmap() failed: %s\n", strerror(errno));
99                 /* pass errno up the chain */
100                 rte_errno = errno;
101                 return -1;
102         }
103         return 0;
104 }
105
106 static int
107 overlap(const struct mem_area *ma, const void *start, size_t len)
108 {
109         const void *end = RTE_PTR_ADD(start, len);
110         const void *ma_start = ma->addr;
111         const void *ma_end = RTE_PTR_ADD(ma->addr, ma->len);
112
113         /* start overlap? */
114         if (start >= ma_start && start < ma_end)
115                 return 1;
116         /* end overlap? */
117         if (end >= ma_start && end < ma_end)
118                 return 1;
119         return 0;
120 }
121
122 static int
123 find_next_n(const struct rte_fbarray *arr, unsigned int start, unsigned int n,
124             bool used)
125 {
126         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
127                         arr->len);
128         unsigned int msk_idx, lookahead_idx, first, first_mod;
129         unsigned int last, last_mod;
130         uint64_t last_msk, ignore_msk;
131
132         /*
133          * mask only has granularity of MASK_ALIGN, but start may not be aligned
134          * on that boundary, so construct a special mask to exclude anything we
135          * don't want to see to avoid confusing ctz.
136          */
137         first = MASK_LEN_TO_IDX(start);
138         first_mod = MASK_LEN_TO_MOD(start);
139         ignore_msk = ~((1ULL << first_mod) - 1);
140
141         /* array length may not be aligned, so calculate ignore mask for last
142          * mask index.
143          */
144         last = MASK_LEN_TO_IDX(arr->len);
145         last_mod = MASK_LEN_TO_MOD(arr->len);
146         last_msk = ~(-1ULL << last_mod);
147
148         for (msk_idx = first; msk_idx < msk->n_masks; msk_idx++) {
149                 uint64_t cur_msk, lookahead_msk;
150                 unsigned int run_start, clz, left;
151                 bool found = false;
152                 /*
153                  * The process of getting n consecutive bits for arbitrary n is
154                  * a bit involved, but here it is in a nutshell:
155                  *
156                  *  1. let n be the number of consecutive bits we're looking for
157                  *  2. check if n can fit in one mask, and if so, do n-1
158                  *     rshift-ands to see if there is an appropriate run inside
159                  *     our current mask
160                  *    2a. if we found a run, bail out early
161                  *    2b. if we didn't find a run, proceed
162                  *  3. invert the mask and count leading zeroes (that is, count
163                  *     how many consecutive set bits we had starting from the
164                  *     end of current mask) as k
165                  *    3a. if k is 0, continue to next mask
166                  *    3b. if k is not 0, we have a potential run
167                  *  4. to satisfy our requirements, next mask must have n-k
168                  *     consecutive set bits right at the start, so we will do
169                  *     (n-k-1) rshift-ands and check if first bit is set.
170                  *
171                  * Step 4 will need to be repeated if (n-k) > MASK_ALIGN until
172                  * we either run out of masks, lose the run, or find what we
173                  * were looking for.
174                  */
175                 cur_msk = msk->data[msk_idx];
176                 left = n;
177
178                 /* if we're looking for free spaces, invert the mask */
179                 if (!used)
180                         cur_msk = ~cur_msk;
181
182                 /* combine current ignore mask with last index ignore mask */
183                 if (msk_idx == last)
184                         ignore_msk |= last_msk;
185
186                 /* if we have an ignore mask, ignore once */
187                 if (ignore_msk) {
188                         cur_msk &= ignore_msk;
189                         ignore_msk = 0;
190                 }
191
192                 /* if n can fit in within a single mask, do a search */
193                 if (n <= MASK_ALIGN) {
194                         uint64_t tmp_msk = cur_msk;
195                         unsigned int s_idx;
196                         for (s_idx = 0; s_idx < n - 1; s_idx++)
197                                 tmp_msk &= tmp_msk >> 1ULL;
198                         /* we found what we were looking for */
199                         if (tmp_msk != 0) {
200                                 run_start = __builtin_ctzll(tmp_msk);
201                                 return MASK_GET_IDX(msk_idx, run_start);
202                         }
203                 }
204
205                 /*
206                  * we didn't find our run within the mask, or n > MASK_ALIGN,
207                  * so we're going for plan B.
208                  */
209
210                 /* count leading zeroes on inverted mask */
211                 if (~cur_msk == 0)
212                         clz = sizeof(cur_msk) * 8;
213                 else
214                         clz = __builtin_clzll(~cur_msk);
215
216                 /* if there aren't any runs at the end either, just continue */
217                 if (clz == 0)
218                         continue;
219
220                 /* we have a partial run at the end, so try looking ahead */
221                 run_start = MASK_ALIGN - clz;
222                 left -= clz;
223
224                 for (lookahead_idx = msk_idx + 1; lookahead_idx < msk->n_masks;
225                                 lookahead_idx++) {
226                         unsigned int s_idx, need;
227                         lookahead_msk = msk->data[lookahead_idx];
228
229                         /* if we're looking for free space, invert the mask */
230                         if (!used)
231                                 lookahead_msk = ~lookahead_msk;
232
233                         /* figure out how many consecutive bits we need here */
234                         need = RTE_MIN(left, MASK_ALIGN);
235
236                         for (s_idx = 0; s_idx < need - 1; s_idx++)
237                                 lookahead_msk &= lookahead_msk >> 1ULL;
238
239                         /* if first bit is not set, we've lost the run */
240                         if ((lookahead_msk & 1) == 0) {
241                                 /*
242                                  * we've scanned this far, so we know there are
243                                  * no runs in the space we've lookahead-scanned
244                                  * as well, so skip that on next iteration.
245                                  */
246                                 ignore_msk = ~((1ULL << need) - 1);
247                                 msk_idx = lookahead_idx;
248                                 break;
249                         }
250
251                         left -= need;
252
253                         /* check if we've found what we were looking for */
254                         if (left == 0) {
255                                 found = true;
256                                 break;
257                         }
258                 }
259
260                 /* we didn't find anything, so continue */
261                 if (!found)
262                         continue;
263
264                 return MASK_GET_IDX(msk_idx, run_start);
265         }
266         /* we didn't find anything */
267         rte_errno = used ? ENOENT : ENOSPC;
268         return -1;
269 }
270
271 static int
272 find_next(const struct rte_fbarray *arr, unsigned int start, bool used)
273 {
274         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
275                         arr->len);
276         unsigned int idx, first, first_mod;
277         unsigned int last, last_mod;
278         uint64_t last_msk, ignore_msk;
279
280         /*
281          * mask only has granularity of MASK_ALIGN, but start may not be aligned
282          * on that boundary, so construct a special mask to exclude anything we
283          * don't want to see to avoid confusing ctz.
284          */
285         first = MASK_LEN_TO_IDX(start);
286         first_mod = MASK_LEN_TO_MOD(start);
287         ignore_msk = ~((1ULL << first_mod) - 1ULL);
288
289         /* array length may not be aligned, so calculate ignore mask for last
290          * mask index.
291          */
292         last = MASK_LEN_TO_IDX(arr->len);
293         last_mod = MASK_LEN_TO_MOD(arr->len);
294         last_msk = ~(-(1ULL) << last_mod);
295
296         for (idx = first; idx < msk->n_masks; idx++) {
297                 uint64_t cur = msk->data[idx];
298                 int found;
299
300                 /* if we're looking for free entries, invert mask */
301                 if (!used)
302                         cur = ~cur;
303
304                 if (idx == last)
305                         cur &= last_msk;
306
307                 /* ignore everything before start on first iteration */
308                 if (idx == first)
309                         cur &= ignore_msk;
310
311                 /* check if we have any entries */
312                 if (cur == 0)
313                         continue;
314
315                 /*
316                  * find first set bit - that will correspond to whatever it is
317                  * that we're looking for.
318                  */
319                 found = __builtin_ctzll(cur);
320                 return MASK_GET_IDX(idx, found);
321         }
322         /* we didn't find anything */
323         rte_errno = used ? ENOENT : ENOSPC;
324         return -1;
325 }
326
327 static int
328 find_contig(const struct rte_fbarray *arr, unsigned int start, bool used)
329 {
330         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
331                         arr->len);
332         unsigned int idx, first, first_mod;
333         unsigned int last, last_mod;
334         uint64_t last_msk;
335         unsigned int need_len, result = 0;
336
337         /* array length may not be aligned, so calculate ignore mask for last
338          * mask index.
339          */
340         last = MASK_LEN_TO_IDX(arr->len);
341         last_mod = MASK_LEN_TO_MOD(arr->len);
342         last_msk = ~(-(1ULL) << last_mod);
343
344         first = MASK_LEN_TO_IDX(start);
345         first_mod = MASK_LEN_TO_MOD(start);
346         for (idx = first; idx < msk->n_masks; idx++, result += need_len) {
347                 uint64_t cur = msk->data[idx];
348                 unsigned int run_len;
349
350                 need_len = MASK_ALIGN;
351
352                 /* if we're looking for free entries, invert mask */
353                 if (!used)
354                         cur = ~cur;
355
356                 /* if this is last mask, ignore everything after last bit */
357                 if (idx == last)
358                         cur &= last_msk;
359
360                 /* ignore everything before start on first iteration */
361                 if (idx == first) {
362                         cur >>= first_mod;
363                         /* at the start, we don't need the full mask len */
364                         need_len -= first_mod;
365                 }
366
367                 /* we will be looking for zeroes, so invert the mask */
368                 cur = ~cur;
369
370                 /* if mask is zero, we have a complete run */
371                 if (cur == 0)
372                         continue;
373
374                 /*
375                  * see if current run ends before mask end.
376                  */
377                 run_len = __builtin_ctzll(cur);
378
379                 /* add however many zeroes we've had in the last run and quit */
380                 if (run_len < need_len) {
381                         result += run_len;
382                         break;
383                 }
384         }
385         return result;
386 }
387
388 static int
389 find_prev_n(const struct rte_fbarray *arr, unsigned int start, unsigned int n,
390                 bool used)
391 {
392         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
393                         arr->len);
394         unsigned int msk_idx, lookbehind_idx, first, first_mod;
395         uint64_t ignore_msk;
396
397         /*
398          * mask only has granularity of MASK_ALIGN, but start may not be aligned
399          * on that boundary, so construct a special mask to exclude anything we
400          * don't want to see to avoid confusing ctz.
401          */
402         first = MASK_LEN_TO_IDX(start);
403         first_mod = MASK_LEN_TO_MOD(start);
404         /* we're going backwards, so mask must start from the top */
405         ignore_msk = first_mod == MASK_ALIGN - 1 ?
406                                 -1ULL : /* prevent overflow */
407                                 ~(-1ULL << (first_mod + 1));
408
409         /* go backwards, include zero */
410         msk_idx = first;
411         do {
412                 uint64_t cur_msk, lookbehind_msk;
413                 unsigned int run_start, run_end, ctz, left;
414                 bool found = false;
415                 /*
416                  * The process of getting n consecutive bits from the top for
417                  * arbitrary n is a bit involved, but here it is in a nutshell:
418                  *
419                  *  1. let n be the number of consecutive bits we're looking for
420                  *  2. check if n can fit in one mask, and if so, do n-1
421                  *     lshift-ands to see if there is an appropriate run inside
422                  *     our current mask
423                  *    2a. if we found a run, bail out early
424                  *    2b. if we didn't find a run, proceed
425                  *  3. invert the mask and count trailing zeroes (that is, count
426                  *     how many consecutive set bits we had starting from the
427                  *     start of current mask) as k
428                  *    3a. if k is 0, continue to next mask
429                  *    3b. if k is not 0, we have a potential run
430                  *  4. to satisfy our requirements, next mask must have n-k
431                  *     consecutive set bits at the end, so we will do (n-k-1)
432                  *     lshift-ands and check if last bit is set.
433                  *
434                  * Step 4 will need to be repeated if (n-k) > MASK_ALIGN until
435                  * we either run out of masks, lose the run, or find what we
436                  * were looking for.
437                  */
438                 cur_msk = msk->data[msk_idx];
439                 left = n;
440
441                 /* if we're looking for free spaces, invert the mask */
442                 if (!used)
443                         cur_msk = ~cur_msk;
444
445                 /* if we have an ignore mask, ignore once */
446                 if (ignore_msk) {
447                         cur_msk &= ignore_msk;
448                         ignore_msk = 0;
449                 }
450
451                 /* if n can fit in within a single mask, do a search */
452                 if (n <= MASK_ALIGN) {
453                         uint64_t tmp_msk = cur_msk;
454                         unsigned int s_idx;
455                         for (s_idx = 0; s_idx < n - 1; s_idx++)
456                                 tmp_msk &= tmp_msk << 1ULL;
457                         /* we found what we were looking for */
458                         if (tmp_msk != 0) {
459                                 /* clz will give us offset from end of mask, and
460                                  * we only get the end of our run, not start,
461                                  * so adjust result to point to where start
462                                  * would have been.
463                                  */
464                                 run_start = MASK_ALIGN -
465                                                 __builtin_clzll(tmp_msk) - n;
466                                 return MASK_GET_IDX(msk_idx, run_start);
467                         }
468                 }
469
470                 /*
471                  * we didn't find our run within the mask, or n > MASK_ALIGN,
472                  * so we're going for plan B.
473                  */
474
475                 /* count trailing zeroes on inverted mask */
476                 if (~cur_msk == 0)
477                         ctz = sizeof(cur_msk) * 8;
478                 else
479                         ctz = __builtin_ctzll(~cur_msk);
480
481                 /* if there aren't any runs at the start either, just
482                  * continue
483                  */
484                 if (ctz == 0)
485                         continue;
486
487                 /* we have a partial run at the start, so try looking behind */
488                 run_end = MASK_GET_IDX(msk_idx, ctz);
489                 left -= ctz;
490
491                 /* go backwards, include zero */
492                 lookbehind_idx = msk_idx - 1;
493
494                 /* we can't lookbehind as we've run out of masks, so stop */
495                 if (msk_idx == 0)
496                         break;
497
498                 do {
499                         const uint64_t last_bit = 1ULL << (MASK_ALIGN - 1);
500                         unsigned int s_idx, need;
501
502                         lookbehind_msk = msk->data[lookbehind_idx];
503
504                         /* if we're looking for free space, invert the mask */
505                         if (!used)
506                                 lookbehind_msk = ~lookbehind_msk;
507
508                         /* figure out how many consecutive bits we need here */
509                         need = RTE_MIN(left, MASK_ALIGN);
510
511                         for (s_idx = 0; s_idx < need - 1; s_idx++)
512                                 lookbehind_msk &= lookbehind_msk << 1ULL;
513
514                         /* if last bit is not set, we've lost the run */
515                         if ((lookbehind_msk & last_bit) == 0) {
516                                 /*
517                                  * we've scanned this far, so we know there are
518                                  * no runs in the space we've lookbehind-scanned
519                                  * as well, so skip that on next iteration.
520                                  */
521                                 ignore_msk = -1ULL << need;
522                                 msk_idx = lookbehind_idx;
523                                 break;
524                         }
525
526                         left -= need;
527
528                         /* check if we've found what we were looking for */
529                         if (left == 0) {
530                                 found = true;
531                                 break;
532                         }
533                 } while ((lookbehind_idx--) != 0); /* decrement after check to
534                                                     * include zero
535                                                     */
536
537                 /* we didn't find anything, so continue */
538                 if (!found)
539                         continue;
540
541                 /* we've found what we were looking for, but we only know where
542                  * the run ended, so calculate start position.
543                  */
544                 return run_end - n;
545         } while (msk_idx-- != 0); /* decrement after check to include zero */
546         /* we didn't find anything */
547         rte_errno = used ? ENOENT : ENOSPC;
548         return -1;
549 }
550
551 static int
552 find_prev(const struct rte_fbarray *arr, unsigned int start, bool used)
553 {
554         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
555                         arr->len);
556         unsigned int idx, first, first_mod;
557         uint64_t ignore_msk;
558
559         /*
560          * mask only has granularity of MASK_ALIGN, but start may not be aligned
561          * on that boundary, so construct a special mask to exclude anything we
562          * don't want to see to avoid confusing clz.
563          */
564         first = MASK_LEN_TO_IDX(start);
565         first_mod = MASK_LEN_TO_MOD(start);
566         /* we're going backwards, so mask must start from the top */
567         ignore_msk = first_mod == MASK_ALIGN - 1 ?
568                                 -1ULL : /* prevent overflow */
569                                 ~(-1ULL << (first_mod + 1));
570
571         /* go backwards, include zero */
572         idx = first;
573         do {
574                 uint64_t cur = msk->data[idx];
575                 int found;
576
577                 /* if we're looking for free entries, invert mask */
578                 if (!used)
579                         cur = ~cur;
580
581                 /* ignore everything before start on first iteration */
582                 if (idx == first)
583                         cur &= ignore_msk;
584
585                 /* check if we have any entries */
586                 if (cur == 0)
587                         continue;
588
589                 /*
590                  * find last set bit - that will correspond to whatever it is
591                  * that we're looking for. we're counting trailing zeroes, thus
592                  * the value we get is counted from end of mask, so calculate
593                  * position from start of mask.
594                  */
595                 found = MASK_ALIGN - __builtin_clzll(cur) - 1;
596
597                 return MASK_GET_IDX(idx, found);
598         } while (idx-- != 0); /* decrement after check  to include zero*/
599
600         /* we didn't find anything */
601         rte_errno = used ? ENOENT : ENOSPC;
602         return -1;
603 }
604
605 static int
606 find_rev_contig(const struct rte_fbarray *arr, unsigned int start, bool used)
607 {
608         const struct used_mask *msk = get_used_mask(arr->data, arr->elt_sz,
609                         arr->len);
610         unsigned int idx, first, first_mod;
611         unsigned int need_len, result = 0;
612
613         first = MASK_LEN_TO_IDX(start);
614         first_mod = MASK_LEN_TO_MOD(start);
615
616         /* go backwards, include zero */
617         idx = first;
618         do {
619                 uint64_t cur = msk->data[idx];
620                 unsigned int run_len;
621
622                 need_len = MASK_ALIGN;
623
624                 /* if we're looking for free entries, invert mask */
625                 if (!used)
626                         cur = ~cur;
627
628                 /* ignore everything after start on first iteration */
629                 if (idx == first) {
630                         unsigned int end_len = MASK_ALIGN - first_mod - 1;
631                         cur <<= end_len;
632                         /* at the start, we don't need the full mask len */
633                         need_len -= end_len;
634                 }
635
636                 /* we will be looking for zeroes, so invert the mask */
637                 cur = ~cur;
638
639                 /* if mask is zero, we have a complete run */
640                 if (cur == 0)
641                         goto endloop;
642
643                 /*
644                  * see where run ends, starting from the end.
645                  */
646                 run_len = __builtin_clzll(cur);
647
648                 /* add however many zeroes we've had in the last run and quit */
649                 if (run_len < need_len) {
650                         result += run_len;
651                         break;
652                 }
653 endloop:
654                 result += need_len;
655         } while (idx-- != 0); /* decrement after check to include zero */
656         return result;
657 }
658
659 static int
660 set_used(struct rte_fbarray *arr, unsigned int idx, bool used)
661 {
662         struct used_mask *msk;
663         uint64_t msk_bit = 1ULL << MASK_LEN_TO_MOD(idx);
664         unsigned int msk_idx = MASK_LEN_TO_IDX(idx);
665         bool already_used;
666         int ret = -1;
667
668         if (arr == NULL || idx >= arr->len) {
669                 rte_errno = EINVAL;
670                 return -1;
671         }
672         msk = get_used_mask(arr->data, arr->elt_sz, arr->len);
673         ret = 0;
674
675         /* prevent array from changing under us */
676         rte_rwlock_write_lock(&arr->rwlock);
677
678         already_used = (msk->data[msk_idx] & msk_bit) != 0;
679
680         /* nothing to be done */
681         if (used == already_used)
682                 goto out;
683
684         if (used) {
685                 msk->data[msk_idx] |= msk_bit;
686                 arr->count++;
687         } else {
688                 msk->data[msk_idx] &= ~msk_bit;
689                 arr->count--;
690         }
691 out:
692         rte_rwlock_write_unlock(&arr->rwlock);
693
694         return ret;
695 }
696
697 static int
698 fully_validate(const char *name, unsigned int elt_sz, unsigned int len)
699 {
700         if (name == NULL || elt_sz == 0 || len == 0 || len > INT_MAX) {
701                 rte_errno = EINVAL;
702                 return -1;
703         }
704
705         if (strnlen(name, RTE_FBARRAY_NAME_LEN) == RTE_FBARRAY_NAME_LEN) {
706                 rte_errno = ENAMETOOLONG;
707                 return -1;
708         }
709         return 0;
710 }
711
712 int __rte_experimental
713 rte_fbarray_init(struct rte_fbarray *arr, const char *name, unsigned int len,
714                 unsigned int elt_sz)
715 {
716         size_t page_sz, mmap_len;
717         char path[PATH_MAX];
718         struct used_mask *msk;
719         struct mem_area *ma = NULL;
720         void *data = NULL;
721         int fd = -1;
722
723         if (arr == NULL) {
724                 rte_errno = EINVAL;
725                 return -1;
726         }
727
728         if (fully_validate(name, elt_sz, len))
729                 return -1;
730
731         /* allocate mem area before doing anything */
732         ma = malloc(sizeof(*ma));
733         if (ma == NULL) {
734                 rte_errno = ENOMEM;
735                 return -1;
736         }
737
738         page_sz = sysconf(_SC_PAGESIZE);
739         if (page_sz == (size_t)-1)
740                 goto fail;
741
742         /* calculate our memory limits */
743         mmap_len = calc_data_size(page_sz, elt_sz, len);
744
745         data = eal_get_virtual_area(NULL, &mmap_len, page_sz, 0, 0);
746         if (data == NULL)
747                 goto fail;
748
749         rte_spinlock_lock(&mem_area_lock);
750
751         fd = -1;
752
753         if (internal_config.no_shconf) {
754                 /* remap virtual area as writable */
755                 void *new_data = mmap(data, mmap_len, PROT_READ | PROT_WRITE,
756                                 MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
757                 if (new_data == MAP_FAILED) {
758                         RTE_LOG(DEBUG, EAL, "%s(): couldn't remap anonymous memory: %s\n",
759                                         __func__, strerror(errno));
760                         goto fail;
761                 }
762         } else {
763                 eal_get_fbarray_path(path, sizeof(path), name);
764
765                 /*
766                  * Each fbarray is unique to process namespace, i.e. the
767                  * filename depends on process prefix. Try to take out a lock
768                  * and see if we succeed. If we don't, someone else is using it
769                  * already.
770                  */
771                 fd = open(path, O_CREAT | O_RDWR, 0600);
772                 if (fd < 0) {
773                         RTE_LOG(DEBUG, EAL, "%s(): couldn't open %s: %s\n",
774                                         __func__, path, strerror(errno));
775                         rte_errno = errno;
776                         goto fail;
777                 } else if (flock(fd, LOCK_EX | LOCK_NB)) {
778                         RTE_LOG(DEBUG, EAL, "%s(): couldn't lock %s: %s\n",
779                                         __func__, path, strerror(errno));
780                         rte_errno = EBUSY;
781                         goto fail;
782                 }
783
784                 /* take out a non-exclusive lock, so that other processes could
785                  * still attach to it, but no other process could reinitialize
786                  * it.
787                  */
788                 if (flock(fd, LOCK_SH | LOCK_NB)) {
789                         rte_errno = errno;
790                         goto fail;
791                 }
792
793                 if (resize_and_map(fd, data, mmap_len))
794                         goto fail;
795         }
796         ma->addr = data;
797         ma->len = mmap_len;
798         ma->fd = fd;
799
800         /* do not close fd - keep it until detach/destroy */
801         TAILQ_INSERT_TAIL(&mem_area_tailq, ma, next);
802
803         /* initialize the data */
804         memset(data, 0, mmap_len);
805
806         /* populate data structure */
807         strlcpy(arr->name, name, sizeof(arr->name));
808         arr->data = data;
809         arr->len = len;
810         arr->elt_sz = elt_sz;
811         arr->count = 0;
812
813         msk = get_used_mask(data, elt_sz, len);
814         msk->n_masks = MASK_LEN_TO_IDX(RTE_ALIGN_CEIL(len, MASK_ALIGN));
815
816         rte_rwlock_init(&arr->rwlock);
817
818         rte_spinlock_unlock(&mem_area_lock);
819
820         return 0;
821 fail:
822         if (data)
823                 munmap(data, mmap_len);
824         if (fd >= 0)
825                 close(fd);
826         free(ma);
827
828         rte_spinlock_unlock(&mem_area_lock);
829         return -1;
830 }
831
832 int __rte_experimental
833 rte_fbarray_attach(struct rte_fbarray *arr)
834 {
835         struct mem_area *ma = NULL, *tmp = NULL;
836         size_t page_sz, mmap_len;
837         char path[PATH_MAX];
838         void *data = NULL;
839         int fd = -1;
840
841         if (arr == NULL) {
842                 rte_errno = EINVAL;
843                 return -1;
844         }
845
846         /*
847          * we don't need to synchronize attach as two values we need (element
848          * size and array length) are constant for the duration of life of
849          * the array, so the parts we care about will not race.
850          */
851
852         if (fully_validate(arr->name, arr->elt_sz, arr->len))
853                 return -1;
854
855         ma = malloc(sizeof(*ma));
856         if (ma == NULL) {
857                 rte_errno = ENOMEM;
858                 return -1;
859         }
860
861         page_sz = sysconf(_SC_PAGESIZE);
862         if (page_sz == (size_t)-1)
863                 goto fail;
864
865         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
866
867         /* check the tailq - maybe user has already mapped this address space */
868         rte_spinlock_lock(&mem_area_lock);
869
870         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
871                 if (overlap(tmp, arr->data, mmap_len)) {
872                         rte_errno = EEXIST;
873                         goto fail;
874                 }
875         }
876
877         /* we know this memory area is unique, so proceed */
878
879         data = eal_get_virtual_area(arr->data, &mmap_len, page_sz, 0, 0);
880         if (data == NULL)
881                 goto fail;
882
883         eal_get_fbarray_path(path, sizeof(path), arr->name);
884
885         fd = open(path, O_RDWR);
886         if (fd < 0) {
887                 rte_errno = errno;
888                 goto fail;
889         }
890
891         /* lock the file, to let others know we're using it */
892         if (flock(fd, LOCK_SH | LOCK_NB)) {
893                 rte_errno = errno;
894                 goto fail;
895         }
896
897         if (resize_and_map(fd, data, mmap_len))
898                 goto fail;
899
900         /* store our new memory area */
901         ma->addr = data;
902         ma->fd = fd; /* keep fd until detach/destroy */
903         ma->len = mmap_len;
904
905         TAILQ_INSERT_TAIL(&mem_area_tailq, ma, next);
906
907         /* we're done */
908
909         return 0;
910 fail:
911         if (data)
912                 munmap(data, mmap_len);
913         if (fd >= 0)
914                 close(fd);
915         free(ma);
916         return -1;
917 }
918
919 int __rte_experimental
920 rte_fbarray_detach(struct rte_fbarray *arr)
921 {
922         struct mem_area *tmp = NULL;
923         size_t mmap_len;
924         int ret = -1;
925
926         if (arr == NULL) {
927                 rte_errno = EINVAL;
928                 return -1;
929         }
930
931         /*
932          * we don't need to synchronize detach as two values we need (element
933          * size and total capacity) are constant for the duration of life of
934          * the array, so the parts we care about will not race. if the user is
935          * detaching while doing something else in the same process, we can't
936          * really do anything about it, things will blow up either way.
937          */
938
939         size_t page_sz = sysconf(_SC_PAGESIZE);
940
941         if (page_sz == (size_t)-1)
942                 return -1;
943
944         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
945
946         /* does this area exist? */
947         rte_spinlock_lock(&mem_area_lock);
948
949         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
950                 if (tmp->addr == arr->data && tmp->len == mmap_len)
951                         break;
952         }
953         if (tmp == NULL) {
954                 rte_errno = ENOENT;
955                 ret = -1;
956                 goto out;
957         }
958
959         munmap(arr->data, mmap_len);
960
961         /* area is unmapped, close fd and remove the tailq entry */
962         if (tmp->fd >= 0)
963                 close(tmp->fd);
964         TAILQ_REMOVE(&mem_area_tailq, tmp, next);
965         free(tmp);
966
967         ret = 0;
968 out:
969         rte_spinlock_unlock(&mem_area_lock);
970         return ret;
971 }
972
973 int __rte_experimental
974 rte_fbarray_destroy(struct rte_fbarray *arr)
975 {
976         struct mem_area *tmp = NULL;
977         size_t mmap_len;
978         int fd, ret;
979         char path[PATH_MAX];
980
981         if (arr == NULL) {
982                 rte_errno = EINVAL;
983                 return -1;
984         }
985
986         /*
987          * we don't need to synchronize detach as two values we need (element
988          * size and total capacity) are constant for the duration of life of
989          * the array, so the parts we care about will not race. if the user is
990          * detaching while doing something else in the same process, we can't
991          * really do anything about it, things will blow up either way.
992          */
993
994         size_t page_sz = sysconf(_SC_PAGESIZE);
995
996         if (page_sz == (size_t)-1)
997                 return -1;
998
999         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
1000
1001         /* does this area exist? */
1002         rte_spinlock_lock(&mem_area_lock);
1003
1004         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
1005                 if (tmp->addr == arr->data && tmp->len == mmap_len)
1006                         break;
1007         }
1008         if (tmp == NULL) {
1009                 rte_errno = ENOENT;
1010                 ret = -1;
1011                 goto out;
1012         }
1013         /* with no shconf, there were never any files to begin with */
1014         if (!internal_config.no_shconf) {
1015                 /*
1016                  * attempt to get an exclusive lock on the file, to ensure it
1017                  * has been detached by all other processes
1018                  */
1019                 fd = tmp->fd;
1020                 if (flock(fd, LOCK_EX | LOCK_NB)) {
1021                         RTE_LOG(DEBUG, EAL, "Cannot destroy fbarray - another process is using it\n");
1022                         rte_errno = EBUSY;
1023                         ret = -1;
1024                         goto out;
1025                 }
1026
1027                 /* we're OK to destroy the file */
1028                 eal_get_fbarray_path(path, sizeof(path), arr->name);
1029                 if (unlink(path)) {
1030                         RTE_LOG(DEBUG, EAL, "Cannot unlink fbarray: %s\n",
1031                                 strerror(errno));
1032                         rte_errno = errno;
1033                         /*
1034                          * we're still holding an exclusive lock, so drop it to
1035                          * shared.
1036                          */
1037                         flock(fd, LOCK_SH | LOCK_NB);
1038
1039                         ret = -1;
1040                         goto out;
1041                 }
1042                 close(fd);
1043         }
1044         munmap(arr->data, mmap_len);
1045
1046         /* area is unmapped, remove the tailq entry */
1047         TAILQ_REMOVE(&mem_area_tailq, tmp, next);
1048         free(tmp);
1049         ret = 0;
1050 out:
1051         rte_spinlock_unlock(&mem_area_lock);
1052         return ret;
1053 }
1054
1055 void * __rte_experimental
1056 rte_fbarray_get(const struct rte_fbarray *arr, unsigned int idx)
1057 {
1058         void *ret = NULL;
1059         if (arr == NULL) {
1060                 rte_errno = EINVAL;
1061                 return NULL;
1062         }
1063
1064         if (idx >= arr->len) {
1065                 rte_errno = EINVAL;
1066                 return NULL;
1067         }
1068
1069         ret = RTE_PTR_ADD(arr->data, idx * arr->elt_sz);
1070
1071         return ret;
1072 }
1073
1074 int __rte_experimental
1075 rte_fbarray_set_used(struct rte_fbarray *arr, unsigned int idx)
1076 {
1077         return set_used(arr, idx, true);
1078 }
1079
1080 int __rte_experimental
1081 rte_fbarray_set_free(struct rte_fbarray *arr, unsigned int idx)
1082 {
1083         return set_used(arr, idx, false);
1084 }
1085
1086 int __rte_experimental
1087 rte_fbarray_is_used(struct rte_fbarray *arr, unsigned int idx)
1088 {
1089         struct used_mask *msk;
1090         int msk_idx;
1091         uint64_t msk_bit;
1092         int ret = -1;
1093
1094         if (arr == NULL || idx >= arr->len) {
1095                 rte_errno = EINVAL;
1096                 return -1;
1097         }
1098
1099         /* prevent array from changing under us */
1100         rte_rwlock_read_lock(&arr->rwlock);
1101
1102         msk = get_used_mask(arr->data, arr->elt_sz, arr->len);
1103         msk_idx = MASK_LEN_TO_IDX(idx);
1104         msk_bit = 1ULL << MASK_LEN_TO_MOD(idx);
1105
1106         ret = (msk->data[msk_idx] & msk_bit) != 0;
1107
1108         rte_rwlock_read_unlock(&arr->rwlock);
1109
1110         return ret;
1111 }
1112
1113 static int
1114 fbarray_find(struct rte_fbarray *arr, unsigned int start, bool next, bool used)
1115 {
1116         int ret = -1;
1117
1118         if (arr == NULL || start >= arr->len) {
1119                 rte_errno = EINVAL;
1120                 return -1;
1121         }
1122
1123         /* prevent array from changing under us */
1124         rte_rwlock_read_lock(&arr->rwlock);
1125
1126         /* cheap checks to prevent doing useless work */
1127         if (!used) {
1128                 if (arr->len == arr->count) {
1129                         rte_errno = ENOSPC;
1130                         goto out;
1131                 }
1132                 if (arr->count == 0) {
1133                         ret = start;
1134                         goto out;
1135                 }
1136         } else {
1137                 if (arr->count == 0) {
1138                         rte_errno = ENOENT;
1139                         goto out;
1140                 }
1141                 if (arr->len == arr->count) {
1142                         ret = start;
1143                         goto out;
1144                 }
1145         }
1146         if (next)
1147                 ret = find_next(arr, start, used);
1148         else
1149                 ret = find_prev(arr, start, used);
1150 out:
1151         rte_rwlock_read_unlock(&arr->rwlock);
1152         return ret;
1153 }
1154
1155 int __rte_experimental
1156 rte_fbarray_find_next_free(struct rte_fbarray *arr, unsigned int start)
1157 {
1158         return fbarray_find(arr, start, true, false);
1159 }
1160
1161 int __rte_experimental
1162 rte_fbarray_find_next_used(struct rte_fbarray *arr, unsigned int start)
1163 {
1164         return fbarray_find(arr, start, true, true);
1165 }
1166
1167 int __rte_experimental
1168 rte_fbarray_find_prev_free(struct rte_fbarray *arr, unsigned int start)
1169 {
1170         return fbarray_find(arr, start, false, false);
1171 }
1172
1173 int __rte_experimental
1174 rte_fbarray_find_prev_used(struct rte_fbarray *arr, unsigned int start)
1175 {
1176         return fbarray_find(arr, start, false, true);
1177 }
1178
1179 static int
1180 fbarray_find_n(struct rte_fbarray *arr, unsigned int start, unsigned int n,
1181                 bool next, bool used)
1182 {
1183         int ret = -1;
1184
1185         if (arr == NULL || start >= arr->len || n > arr->len || n == 0) {
1186                 rte_errno = EINVAL;
1187                 return -1;
1188         }
1189         if (next && (arr->len - start) < n) {
1190                 rte_errno = used ? ENOENT : ENOSPC;
1191                 return -1;
1192         }
1193         if (!next && start < (n - 1)) {
1194                 rte_errno = used ? ENOENT : ENOSPC;
1195                 return -1;
1196         }
1197
1198         /* prevent array from changing under us */
1199         rte_rwlock_read_lock(&arr->rwlock);
1200
1201         /* cheap checks to prevent doing useless work */
1202         if (!used) {
1203                 if (arr->len == arr->count || arr->len - arr->count < n) {
1204                         rte_errno = ENOSPC;
1205                         goto out;
1206                 }
1207                 if (arr->count == 0) {
1208                         ret = next ? start : start - n + 1;
1209                         goto out;
1210                 }
1211         } else {
1212                 if (arr->count < n) {
1213                         rte_errno = ENOENT;
1214                         goto out;
1215                 }
1216                 if (arr->count == arr->len) {
1217                         ret = next ? start : start - n + 1;
1218                         goto out;
1219                 }
1220         }
1221
1222         if (next)
1223                 ret = find_next_n(arr, start, n, used);
1224         else
1225                 ret = find_prev_n(arr, start, n, used);
1226 out:
1227         rte_rwlock_read_unlock(&arr->rwlock);
1228         return ret;
1229 }
1230
1231 int __rte_experimental
1232 rte_fbarray_find_next_n_free(struct rte_fbarray *arr, unsigned int start,
1233                 unsigned int n)
1234 {
1235         return fbarray_find_n(arr, start, n, true, false);
1236 }
1237
1238 int __rte_experimental
1239 rte_fbarray_find_next_n_used(struct rte_fbarray *arr, unsigned int start,
1240                 unsigned int n)
1241 {
1242         return fbarray_find_n(arr, start, n, true, true);
1243 }
1244
1245 int __rte_experimental
1246 rte_fbarray_find_prev_n_free(struct rte_fbarray *arr, unsigned int start,
1247                 unsigned int n)
1248 {
1249         return fbarray_find_n(arr, start, n, false, false);
1250 }
1251
1252 int __rte_experimental
1253 rte_fbarray_find_prev_n_used(struct rte_fbarray *arr, unsigned int start,
1254                 unsigned int n)
1255 {
1256         return fbarray_find_n(arr, start, n, false, true);
1257 }
1258
1259 static int
1260 fbarray_find_contig(struct rte_fbarray *arr, unsigned int start, bool next,
1261                 bool used)
1262 {
1263         int ret = -1;
1264
1265         if (arr == NULL || start >= arr->len) {
1266                 rte_errno = EINVAL;
1267                 return -1;
1268         }
1269
1270         /* prevent array from changing under us */
1271         rte_rwlock_read_lock(&arr->rwlock);
1272
1273         /* cheap checks to prevent doing useless work */
1274         if (used) {
1275                 if (arr->count == 0) {
1276                         ret = 0;
1277                         goto out;
1278                 }
1279                 if (next && arr->count == arr->len) {
1280                         ret = arr->len - start;
1281                         goto out;
1282                 }
1283                 if (!next && arr->count == arr->len) {
1284                         ret = start + 1;
1285                         goto out;
1286                 }
1287         } else {
1288                 if (arr->len == arr->count) {
1289                         ret = 0;
1290                         goto out;
1291                 }
1292                 if (next && arr->count == 0) {
1293                         ret = arr->len - start;
1294                         goto out;
1295                 }
1296                 if (!next && arr->count == 0) {
1297                         ret = start + 1;
1298                         goto out;
1299                 }
1300         }
1301
1302         if (next)
1303                 ret = find_contig(arr, start, used);
1304         else
1305                 ret = find_rev_contig(arr, start, used);
1306 out:
1307         rte_rwlock_read_unlock(&arr->rwlock);
1308         return ret;
1309 }
1310
1311 int __rte_experimental
1312 rte_fbarray_find_contig_free(struct rte_fbarray *arr, unsigned int start)
1313 {
1314         return fbarray_find_contig(arr, start, true, false);
1315 }
1316
1317 int __rte_experimental
1318 rte_fbarray_find_contig_used(struct rte_fbarray *arr, unsigned int start)
1319 {
1320         return fbarray_find_contig(arr, start, true, true);
1321 }
1322
1323 int __rte_experimental
1324 rte_fbarray_find_rev_contig_free(struct rte_fbarray *arr, unsigned int start)
1325 {
1326         return fbarray_find_contig(arr, start, false, false);
1327 }
1328
1329 int __rte_experimental
1330 rte_fbarray_find_rev_contig_used(struct rte_fbarray *arr, unsigned int start)
1331 {
1332         return fbarray_find_contig(arr, start, false, true);
1333 }
1334
1335 int __rte_experimental
1336 rte_fbarray_find_idx(const struct rte_fbarray *arr, const void *elt)
1337 {
1338         void *end;
1339         int ret = -1;
1340
1341         /*
1342          * no need to synchronize as it doesn't matter if underlying data
1343          * changes - we're doing pointer arithmetic here.
1344          */
1345
1346         if (arr == NULL || elt == NULL) {
1347                 rte_errno = EINVAL;
1348                 return -1;
1349         }
1350         end = RTE_PTR_ADD(arr->data, arr->elt_sz * arr->len);
1351         if (elt < arr->data || elt >= end) {
1352                 rte_errno = EINVAL;
1353                 return -1;
1354         }
1355
1356         ret = RTE_PTR_DIFF(elt, arr->data) / arr->elt_sz;
1357
1358         return ret;
1359 }
1360
1361 void __rte_experimental
1362 rte_fbarray_dump_metadata(struct rte_fbarray *arr, FILE *f)
1363 {
1364         struct used_mask *msk;
1365         unsigned int i;
1366
1367         if (arr == NULL || f == NULL) {
1368                 rte_errno = EINVAL;
1369                 return;
1370         }
1371
1372         if (fully_validate(arr->name, arr->elt_sz, arr->len)) {
1373                 fprintf(f, "Invalid file-backed array\n");
1374                 goto out;
1375         }
1376
1377         /* prevent array from changing under us */
1378         rte_rwlock_read_lock(&arr->rwlock);
1379
1380         fprintf(f, "File-backed array: %s\n", arr->name);
1381         fprintf(f, "size: %i occupied: %i elt_sz: %i\n",
1382                         arr->len, arr->count, arr->elt_sz);
1383
1384         msk = get_used_mask(arr->data, arr->elt_sz, arr->len);
1385
1386         for (i = 0; i < msk->n_masks; i++)
1387                 fprintf(f, "msk idx %i: 0x%016" PRIx64 "\n", i, msk->data[i]);
1388 out:
1389         rte_rwlock_read_unlock(&arr->rwlock);
1390 }