31cce80a88177af4dc73ac64abfb64e43a741423
[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                 free(ma);
864                 return -1;
865         }
866
867         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
868
869         /* check the tailq - maybe user has already mapped this address space */
870         rte_spinlock_lock(&mem_area_lock);
871
872         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
873                 if (overlap(tmp, arr->data, mmap_len)) {
874                         rte_errno = EEXIST;
875                         goto fail;
876                 }
877         }
878
879         /* we know this memory area is unique, so proceed */
880
881         data = eal_get_virtual_area(arr->data, &mmap_len, page_sz, 0, 0);
882         if (data == NULL)
883                 goto fail;
884
885         eal_get_fbarray_path(path, sizeof(path), arr->name);
886
887         fd = open(path, O_RDWR);
888         if (fd < 0) {
889                 rte_errno = errno;
890                 goto fail;
891         }
892
893         /* lock the file, to let others know we're using it */
894         if (flock(fd, LOCK_SH | LOCK_NB)) {
895                 rte_errno = errno;
896                 goto fail;
897         }
898
899         if (resize_and_map(fd, data, mmap_len))
900                 goto fail;
901
902         /* store our new memory area */
903         ma->addr = data;
904         ma->fd = fd; /* keep fd until detach/destroy */
905         ma->len = mmap_len;
906
907         TAILQ_INSERT_TAIL(&mem_area_tailq, ma, next);
908
909         /* we're done */
910
911         rte_spinlock_unlock(&mem_area_lock);
912         return 0;
913 fail:
914         if (data)
915                 munmap(data, mmap_len);
916         if (fd >= 0)
917                 close(fd);
918         free(ma);
919         rte_spinlock_unlock(&mem_area_lock);
920         return -1;
921 }
922
923 int __rte_experimental
924 rte_fbarray_detach(struct rte_fbarray *arr)
925 {
926         struct mem_area *tmp = NULL;
927         size_t mmap_len;
928         int ret = -1;
929
930         if (arr == NULL) {
931                 rte_errno = EINVAL;
932                 return -1;
933         }
934
935         /*
936          * we don't need to synchronize detach as two values we need (element
937          * size and total capacity) are constant for the duration of life of
938          * the array, so the parts we care about will not race. if the user is
939          * detaching while doing something else in the same process, we can't
940          * really do anything about it, things will blow up either way.
941          */
942
943         size_t page_sz = sysconf(_SC_PAGESIZE);
944
945         if (page_sz == (size_t)-1)
946                 return -1;
947
948         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
949
950         /* does this area exist? */
951         rte_spinlock_lock(&mem_area_lock);
952
953         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
954                 if (tmp->addr == arr->data && tmp->len == mmap_len)
955                         break;
956         }
957         if (tmp == NULL) {
958                 rte_errno = ENOENT;
959                 ret = -1;
960                 goto out;
961         }
962
963         munmap(arr->data, mmap_len);
964
965         /* area is unmapped, close fd and remove the tailq entry */
966         if (tmp->fd >= 0)
967                 close(tmp->fd);
968         TAILQ_REMOVE(&mem_area_tailq, tmp, next);
969         free(tmp);
970
971         ret = 0;
972 out:
973         rte_spinlock_unlock(&mem_area_lock);
974         return ret;
975 }
976
977 int __rte_experimental
978 rte_fbarray_destroy(struct rte_fbarray *arr)
979 {
980         struct mem_area *tmp = NULL;
981         size_t mmap_len;
982         int fd, ret;
983         char path[PATH_MAX];
984
985         if (arr == NULL) {
986                 rte_errno = EINVAL;
987                 return -1;
988         }
989
990         /*
991          * we don't need to synchronize detach as two values we need (element
992          * size and total capacity) are constant for the duration of life of
993          * the array, so the parts we care about will not race. if the user is
994          * detaching while doing something else in the same process, we can't
995          * really do anything about it, things will blow up either way.
996          */
997
998         size_t page_sz = sysconf(_SC_PAGESIZE);
999
1000         if (page_sz == (size_t)-1)
1001                 return -1;
1002
1003         mmap_len = calc_data_size(page_sz, arr->elt_sz, arr->len);
1004
1005         /* does this area exist? */
1006         rte_spinlock_lock(&mem_area_lock);
1007
1008         TAILQ_FOREACH(tmp, &mem_area_tailq, next) {
1009                 if (tmp->addr == arr->data && tmp->len == mmap_len)
1010                         break;
1011         }
1012         if (tmp == NULL) {
1013                 rte_errno = ENOENT;
1014                 ret = -1;
1015                 goto out;
1016         }
1017         /* with no shconf, there were never any files to begin with */
1018         if (!internal_config.no_shconf) {
1019                 /*
1020                  * attempt to get an exclusive lock on the file, to ensure it
1021                  * has been detached by all other processes
1022                  */
1023                 fd = tmp->fd;
1024                 if (flock(fd, LOCK_EX | LOCK_NB)) {
1025                         RTE_LOG(DEBUG, EAL, "Cannot destroy fbarray - another process is using it\n");
1026                         rte_errno = EBUSY;
1027                         ret = -1;
1028                         goto out;
1029                 }
1030
1031                 /* we're OK to destroy the file */
1032                 eal_get_fbarray_path(path, sizeof(path), arr->name);
1033                 if (unlink(path)) {
1034                         RTE_LOG(DEBUG, EAL, "Cannot unlink fbarray: %s\n",
1035                                 strerror(errno));
1036                         rte_errno = errno;
1037                         /*
1038                          * we're still holding an exclusive lock, so drop it to
1039                          * shared.
1040                          */
1041                         flock(fd, LOCK_SH | LOCK_NB);
1042
1043                         ret = -1;
1044                         goto out;
1045                 }
1046                 close(fd);
1047         }
1048         munmap(arr->data, mmap_len);
1049
1050         /* area is unmapped, remove the tailq entry */
1051         TAILQ_REMOVE(&mem_area_tailq, tmp, next);
1052         free(tmp);
1053         ret = 0;
1054 out:
1055         rte_spinlock_unlock(&mem_area_lock);
1056         return ret;
1057 }
1058
1059 void * __rte_experimental
1060 rte_fbarray_get(const struct rte_fbarray *arr, unsigned int idx)
1061 {
1062         void *ret = NULL;
1063         if (arr == NULL) {
1064                 rte_errno = EINVAL;
1065                 return NULL;
1066         }
1067
1068         if (idx >= arr->len) {
1069                 rte_errno = EINVAL;
1070                 return NULL;
1071         }
1072
1073         ret = RTE_PTR_ADD(arr->data, idx * arr->elt_sz);
1074
1075         return ret;
1076 }
1077
1078 int __rte_experimental
1079 rte_fbarray_set_used(struct rte_fbarray *arr, unsigned int idx)
1080 {
1081         return set_used(arr, idx, true);
1082 }
1083
1084 int __rte_experimental
1085 rte_fbarray_set_free(struct rte_fbarray *arr, unsigned int idx)
1086 {
1087         return set_used(arr, idx, false);
1088 }
1089
1090 int __rte_experimental
1091 rte_fbarray_is_used(struct rte_fbarray *arr, unsigned int idx)
1092 {
1093         struct used_mask *msk;
1094         int msk_idx;
1095         uint64_t msk_bit;
1096         int ret = -1;
1097
1098         if (arr == NULL || idx >= arr->len) {
1099                 rte_errno = EINVAL;
1100                 return -1;
1101         }
1102
1103         /* prevent array from changing under us */
1104         rte_rwlock_read_lock(&arr->rwlock);
1105
1106         msk = get_used_mask(arr->data, arr->elt_sz, arr->len);
1107         msk_idx = MASK_LEN_TO_IDX(idx);
1108         msk_bit = 1ULL << MASK_LEN_TO_MOD(idx);
1109
1110         ret = (msk->data[msk_idx] & msk_bit) != 0;
1111
1112         rte_rwlock_read_unlock(&arr->rwlock);
1113
1114         return ret;
1115 }
1116
1117 static int
1118 fbarray_find(struct rte_fbarray *arr, unsigned int start, bool next, bool used)
1119 {
1120         int ret = -1;
1121
1122         if (arr == NULL || start >= arr->len) {
1123                 rte_errno = EINVAL;
1124                 return -1;
1125         }
1126
1127         /* prevent array from changing under us */
1128         rte_rwlock_read_lock(&arr->rwlock);
1129
1130         /* cheap checks to prevent doing useless work */
1131         if (!used) {
1132                 if (arr->len == arr->count) {
1133                         rte_errno = ENOSPC;
1134                         goto out;
1135                 }
1136                 if (arr->count == 0) {
1137                         ret = start;
1138                         goto out;
1139                 }
1140         } else {
1141                 if (arr->count == 0) {
1142                         rte_errno = ENOENT;
1143                         goto out;
1144                 }
1145                 if (arr->len == arr->count) {
1146                         ret = start;
1147                         goto out;
1148                 }
1149         }
1150         if (next)
1151                 ret = find_next(arr, start, used);
1152         else
1153                 ret = find_prev(arr, start, used);
1154 out:
1155         rte_rwlock_read_unlock(&arr->rwlock);
1156         return ret;
1157 }
1158
1159 int __rte_experimental
1160 rte_fbarray_find_next_free(struct rte_fbarray *arr, unsigned int start)
1161 {
1162         return fbarray_find(arr, start, true, false);
1163 }
1164
1165 int __rte_experimental
1166 rte_fbarray_find_next_used(struct rte_fbarray *arr, unsigned int start)
1167 {
1168         return fbarray_find(arr, start, true, true);
1169 }
1170
1171 int __rte_experimental
1172 rte_fbarray_find_prev_free(struct rte_fbarray *arr, unsigned int start)
1173 {
1174         return fbarray_find(arr, start, false, false);
1175 }
1176
1177 int __rte_experimental
1178 rte_fbarray_find_prev_used(struct rte_fbarray *arr, unsigned int start)
1179 {
1180         return fbarray_find(arr, start, false, true);
1181 }
1182
1183 static int
1184 fbarray_find_n(struct rte_fbarray *arr, unsigned int start, unsigned int n,
1185                 bool next, bool used)
1186 {
1187         int ret = -1;
1188
1189         if (arr == NULL || start >= arr->len || n > arr->len || n == 0) {
1190                 rte_errno = EINVAL;
1191                 return -1;
1192         }
1193         if (next && (arr->len - start) < n) {
1194                 rte_errno = used ? ENOENT : ENOSPC;
1195                 return -1;
1196         }
1197         if (!next && start < (n - 1)) {
1198                 rte_errno = used ? ENOENT : ENOSPC;
1199                 return -1;
1200         }
1201
1202         /* prevent array from changing under us */
1203         rte_rwlock_read_lock(&arr->rwlock);
1204
1205         /* cheap checks to prevent doing useless work */
1206         if (!used) {
1207                 if (arr->len == arr->count || arr->len - arr->count < n) {
1208                         rte_errno = ENOSPC;
1209                         goto out;
1210                 }
1211                 if (arr->count == 0) {
1212                         ret = next ? start : start - n + 1;
1213                         goto out;
1214                 }
1215         } else {
1216                 if (arr->count < n) {
1217                         rte_errno = ENOENT;
1218                         goto out;
1219                 }
1220                 if (arr->count == arr->len) {
1221                         ret = next ? start : start - n + 1;
1222                         goto out;
1223                 }
1224         }
1225
1226         if (next)
1227                 ret = find_next_n(arr, start, n, used);
1228         else
1229                 ret = find_prev_n(arr, start, n, used);
1230 out:
1231         rte_rwlock_read_unlock(&arr->rwlock);
1232         return ret;
1233 }
1234
1235 int __rte_experimental
1236 rte_fbarray_find_next_n_free(struct rte_fbarray *arr, unsigned int start,
1237                 unsigned int n)
1238 {
1239         return fbarray_find_n(arr, start, n, true, false);
1240 }
1241
1242 int __rte_experimental
1243 rte_fbarray_find_next_n_used(struct rte_fbarray *arr, unsigned int start,
1244                 unsigned int n)
1245 {
1246         return fbarray_find_n(arr, start, n, true, true);
1247 }
1248
1249 int __rte_experimental
1250 rte_fbarray_find_prev_n_free(struct rte_fbarray *arr, unsigned int start,
1251                 unsigned int n)
1252 {
1253         return fbarray_find_n(arr, start, n, false, false);
1254 }
1255
1256 int __rte_experimental
1257 rte_fbarray_find_prev_n_used(struct rte_fbarray *arr, unsigned int start,
1258                 unsigned int n)
1259 {
1260         return fbarray_find_n(arr, start, n, false, true);
1261 }
1262
1263 static int
1264 fbarray_find_contig(struct rte_fbarray *arr, unsigned int start, bool next,
1265                 bool used)
1266 {
1267         int ret = -1;
1268
1269         if (arr == NULL || start >= arr->len) {
1270                 rte_errno = EINVAL;
1271                 return -1;
1272         }
1273
1274         /* prevent array from changing under us */
1275         rte_rwlock_read_lock(&arr->rwlock);
1276
1277         /* cheap checks to prevent doing useless work */
1278         if (used) {
1279                 if (arr->count == 0) {
1280                         ret = 0;
1281                         goto out;
1282                 }
1283                 if (next && arr->count == arr->len) {
1284                         ret = arr->len - start;
1285                         goto out;
1286                 }
1287                 if (!next && arr->count == arr->len) {
1288                         ret = start + 1;
1289                         goto out;
1290                 }
1291         } else {
1292                 if (arr->len == arr->count) {
1293                         ret = 0;
1294                         goto out;
1295                 }
1296                 if (next && arr->count == 0) {
1297                         ret = arr->len - start;
1298                         goto out;
1299                 }
1300                 if (!next && arr->count == 0) {
1301                         ret = start + 1;
1302                         goto out;
1303                 }
1304         }
1305
1306         if (next)
1307                 ret = find_contig(arr, start, used);
1308         else
1309                 ret = find_rev_contig(arr, start, used);
1310 out:
1311         rte_rwlock_read_unlock(&arr->rwlock);
1312         return ret;
1313 }
1314
1315 static int
1316 fbarray_find_biggest(struct rte_fbarray *arr, unsigned int start, bool used,
1317                 bool rev)
1318 {
1319         int cur_idx, next_idx, cur_len, biggest_idx, biggest_len;
1320         /* don't stack if conditions, use function pointers instead */
1321         int (*find_func)(struct rte_fbarray *, unsigned int);
1322         int (*find_contig_func)(struct rte_fbarray *, unsigned int);
1323
1324         if (arr == NULL || start >= arr->len) {
1325                 rte_errno = EINVAL;
1326                 return -1;
1327         }
1328         /* the other API calls already do their fair share of cheap checks, so
1329          * no need to do them here.
1330          */
1331
1332         /* the API's called are thread-safe, but something may still happen
1333          * inbetween the API calls, so lock the fbarray. all other API's are
1334          * read-locking the fbarray, so read lock here is OK.
1335          */
1336         rte_rwlock_read_lock(&arr->rwlock);
1337
1338         /* pick out appropriate functions */
1339         if (used) {
1340                 if (rev) {
1341                         find_func = rte_fbarray_find_prev_used;
1342                         find_contig_func = rte_fbarray_find_rev_contig_used;
1343                 } else {
1344                         find_func = rte_fbarray_find_next_used;
1345                         find_contig_func = rte_fbarray_find_contig_used;
1346                 }
1347         } else {
1348                 if (rev) {
1349                         find_func = rte_fbarray_find_prev_free;
1350                         find_contig_func = rte_fbarray_find_rev_contig_free;
1351                 } else {
1352                         find_func = rte_fbarray_find_next_free;
1353                         find_contig_func = rte_fbarray_find_contig_free;
1354                 }
1355         }
1356
1357         cur_idx = start;
1358         biggest_idx = -1; /* default is error */
1359         biggest_len = 0;
1360         for (;;) {
1361                 cur_idx = find_func(arr, cur_idx);
1362
1363                 /* block found, check its length */
1364                 if (cur_idx >= 0) {
1365                         cur_len = find_contig_func(arr, cur_idx);
1366                         /* decide where we go next */
1367                         next_idx = rev ? cur_idx - cur_len : cur_idx + cur_len;
1368                         /* move current index to start of chunk */
1369                         cur_idx = rev ? next_idx + 1 : cur_idx;
1370
1371                         if (cur_len > biggest_len) {
1372                                 biggest_idx = cur_idx;
1373                                 biggest_len = cur_len;
1374                         }
1375                         cur_idx = next_idx;
1376                         /* in reverse mode, next_idx may be -1 if chunk started
1377                          * at array beginning. this means there's no more work
1378                          * to do.
1379                          */
1380                         if (cur_idx < 0)
1381                                 break;
1382                 } else {
1383                         /* nothing more to find, stop. however, a failed API
1384                          * call has set rte_errno, which we want to ignore, as
1385                          * reaching the end of fbarray is not an error.
1386                          */
1387                         rte_errno = 0;
1388                         break;
1389                 }
1390         }
1391         /* if we didn't find anything at all, set rte_errno */
1392         if (biggest_idx < 0)
1393                 rte_errno = used ? ENOENT : ENOSPC;
1394
1395         rte_rwlock_read_unlock(&arr->rwlock);
1396         return biggest_idx;
1397 }
1398
1399 int __rte_experimental
1400 rte_fbarray_find_biggest_free(struct rte_fbarray *arr, unsigned int start)
1401 {
1402         return fbarray_find_biggest(arr, start, false, false);
1403 }
1404
1405 int __rte_experimental
1406 rte_fbarray_find_biggest_used(struct rte_fbarray *arr, unsigned int start)
1407 {
1408         return fbarray_find_biggest(arr, start, true, false);
1409 }
1410
1411 int __rte_experimental
1412 rte_fbarray_find_rev_biggest_free(struct rte_fbarray *arr, unsigned int start)
1413 {
1414         return fbarray_find_biggest(arr, start, false, true);
1415 }
1416
1417 int __rte_experimental
1418 rte_fbarray_find_rev_biggest_used(struct rte_fbarray *arr, unsigned int start)
1419 {
1420         return fbarray_find_biggest(arr, start, true, true);
1421 }
1422
1423
1424 int __rte_experimental
1425 rte_fbarray_find_contig_free(struct rte_fbarray *arr, unsigned int start)
1426 {
1427         return fbarray_find_contig(arr, start, true, false);
1428 }
1429
1430 int __rte_experimental
1431 rte_fbarray_find_contig_used(struct rte_fbarray *arr, unsigned int start)
1432 {
1433         return fbarray_find_contig(arr, start, true, true);
1434 }
1435
1436 int __rte_experimental
1437 rte_fbarray_find_rev_contig_free(struct rte_fbarray *arr, unsigned int start)
1438 {
1439         return fbarray_find_contig(arr, start, false, false);
1440 }
1441
1442 int __rte_experimental
1443 rte_fbarray_find_rev_contig_used(struct rte_fbarray *arr, unsigned int start)
1444 {
1445         return fbarray_find_contig(arr, start, false, true);
1446 }
1447
1448 int __rte_experimental
1449 rte_fbarray_find_idx(const struct rte_fbarray *arr, const void *elt)
1450 {
1451         void *end;
1452         int ret = -1;
1453
1454         /*
1455          * no need to synchronize as it doesn't matter if underlying data
1456          * changes - we're doing pointer arithmetic here.
1457          */
1458
1459         if (arr == NULL || elt == NULL) {
1460                 rte_errno = EINVAL;
1461                 return -1;
1462         }
1463         end = RTE_PTR_ADD(arr->data, arr->elt_sz * arr->len);
1464         if (elt < arr->data || elt >= end) {
1465                 rte_errno = EINVAL;
1466                 return -1;
1467         }
1468
1469         ret = RTE_PTR_DIFF(elt, arr->data) / arr->elt_sz;
1470
1471         return ret;
1472 }
1473
1474 void __rte_experimental
1475 rte_fbarray_dump_metadata(struct rte_fbarray *arr, FILE *f)
1476 {
1477         struct used_mask *msk;
1478         unsigned int i;
1479
1480         if (arr == NULL || f == NULL) {
1481                 rte_errno = EINVAL;
1482                 return;
1483         }
1484
1485         if (fully_validate(arr->name, arr->elt_sz, arr->len)) {
1486                 fprintf(f, "Invalid file-backed array\n");
1487                 goto out;
1488         }
1489
1490         /* prevent array from changing under us */
1491         rte_rwlock_read_lock(&arr->rwlock);
1492
1493         fprintf(f, "File-backed array: %s\n", arr->name);
1494         fprintf(f, "size: %i occupied: %i elt_sz: %i\n",
1495                         arr->len, arr->count, arr->elt_sz);
1496
1497         msk = get_used_mask(arr->data, arr->elt_sz, arr->len);
1498
1499         for (i = 0; i < msk->n_masks; i++)
1500                 fprintf(f, "msk idx %i: 0x%016" PRIx64 "\n", i, msk->data[i]);
1501 out:
1502         rte_rwlock_read_unlock(&arr->rwlock);
1503 }