1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2016 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
12 # define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
14 # define dbg_printf(f, a...)
18 * Slab Arrays and Bags
20 * The slab array is a dynamically growable linear array. Internally it
21 * maintains a list of slabs of increasing size; when a slab fills up, another
22 * is allocated. Each slab is sorted individually, which means that one must
23 * use an iterator to walk the entire logical array, sorted order or otherwise.
24 * Array items can neither be removed nor accessed randomly, since (at the
25 * moment) the only user of them (storing reverse mappings) doesn't need either
26 * piece. Pointers are not stable across sort operations.
28 * A bag is a collection of pointers. The bag can be added to or removed from
29 * arbitrarily, and the bag items can be iterated. Bags are used to process
30 * rmaps into refcount btree entries.
34 * Slabs -- each slab_hdr holds an array of items; when a slab_hdr fills up, we
35 * allocate a new one and add to that one. The slab object coordinates the
39 /* Each slab holds at least 4096 items */
40 #define MIN_SLAB_NR 4096
41 /* and cannot be larger than 128M */
42 #define MAX_SLAB_SIZE (128 * 1048576)
45 size_t sh_inuse
; /* items in use */
46 struct xfs_slab_hdr
*sh_next
; /* next slab hdr */
51 size_t s_item_sz
; /* item size */
52 size_t s_nr_slabs
; /* # of slabs */
53 size_t s_nr_items
; /* # of items */
54 struct xfs_slab_hdr
*s_first
; /* first slab header */
55 struct xfs_slab_hdr
*s_last
; /* last sh_next pointer */
59 * Slab cursors -- each slab_hdr_cursor tracks a slab_hdr; the slab_cursor
60 * tracks the slab_hdr_cursors. If a compare_fn is specified, the cursor
61 * returns objects in increasing order (if you've previously sorted the
62 * slabs with qsort_slab()). If compare_fn == NULL, it returns slab items
65 struct xfs_slab_hdr_cursor
{
66 struct xfs_slab_hdr
*hdr
; /* a slab header */
67 size_t loc
; /* where we are in the slab */
70 typedef int (*xfs_slab_compare_fn
)(const void *, const void *);
72 struct xfs_slab_cursor
{
73 size_t nr
; /* # of per-slab cursors */
74 struct xfs_slab
*slab
; /* pointer to the slab */
75 struct xfs_slab_hdr_cursor
*last_hcur
; /* last header we took from */
76 xfs_slab_compare_fn compare_fn
; /* compare items */
77 struct xfs_slab_hdr_cursor hcur
[0]; /* per-slab cursors */
81 * Bags -- each bag is an array of pointers items; when a bag fills up, we
84 #define MIN_BAG_SIZE 4096
86 size_t bg_nr
; /* number of pointers */
87 size_t bg_inuse
; /* number of slots in use */
88 void **bg_ptrs
; /* pointers */
90 #define BAG_END(bag) (&(bag)->bg_ptrs[(bag)->bg_nr])
93 * Create a slab to hold some objects of a particular size.
97 struct xfs_slab
**slab
,
100 struct xfs_slab
*ptr
;
102 ptr
= calloc(1, sizeof(struct xfs_slab
));
105 ptr
->s_item_sz
= item_size
;
117 struct xfs_slab
**slab
)
119 struct xfs_slab
*ptr
;
120 struct xfs_slab_hdr
*hdr
;
121 struct xfs_slab_hdr
*nhdr
;
138 struct xfs_slab
*slab
,
139 struct xfs_slab_hdr
*hdr
,
144 ASSERT(idx
< hdr
->sh_inuse
);
145 p
= (char *)(hdr
+ 1);
146 p
+= slab
->s_item_sz
* idx
;
151 * Add an item to the slab.
155 struct xfs_slab
*slab
,
158 struct xfs_slab_hdr
*hdr
;
162 if (!hdr
|| hdr
->sh_inuse
== hdr
->sh_nr
) {
165 n
= (hdr
? hdr
->sh_nr
* 2 : MIN_SLAB_NR
);
166 if (n
* slab
->s_item_sz
> MAX_SLAB_SIZE
)
167 n
= MAX_SLAB_SIZE
/ slab
->s_item_sz
;
168 hdr
= malloc(sizeof(struct xfs_slab_hdr
) + (n
* slab
->s_item_sz
));
175 slab
->s_last
->sh_next
= hdr
;
182 p
= slab_ptr(slab
, hdr
, hdr
->sh_inuse
- 1);
183 memcpy(p
, item
, slab
->s_item_sz
);
192 struct xfs_slab
*slab
;
193 struct xfs_slab_hdr
*hdr
;
194 int (*compare_fn
)(const void *, const void *);
199 struct workqueue
*wq
,
203 struct qsort_slab
*qs
= arg
;
205 qsort(slab_ptr(qs
->slab
, qs
->hdr
, 0), qs
->hdr
->sh_inuse
,
206 qs
->slab
->s_item_sz
, qs
->compare_fn
);
211 * Sort the items in the slab. Do not run this method if there are any
212 * cursors holding on to the slab.
216 struct xfs_slab
*slab
,
217 int (*compare_fn
)(const void *, const void *))
220 struct xfs_slab_hdr
*hdr
;
221 struct qsort_slab
*qs
;
224 * If we don't have that many slabs, we're probably better
225 * off skipping all the thread overhead.
227 if (slab
->s_nr_slabs
<= 4) {
230 qsort(slab_ptr(slab
, hdr
, 0), hdr
->sh_inuse
,
231 slab
->s_item_sz
, compare_fn
);
237 create_work_queue(&wq
, NULL
, platform_nproc());
240 qs
= malloc(sizeof(struct qsort_slab
));
243 qs
->compare_fn
= compare_fn
;
244 queue_work(&wq
, qsort_slab_helper
, 0, qs
);
247 destroy_work_queue(&wq
);
251 * init_slab_cursor() -- Create a slab cursor to iterate the slab items.
254 * @compare_fn: If specified, use this function to return items in ascending order.
255 * @cur: The new cursor.
259 struct xfs_slab
*slab
,
260 int (*compare_fn
)(const void *, const void *),
261 struct xfs_slab_cursor
**cur
)
263 struct xfs_slab_cursor
*c
;
264 struct xfs_slab_hdr_cursor
*hcur
;
265 struct xfs_slab_hdr
*hdr
;
267 c
= malloc(sizeof(struct xfs_slab_cursor
) +
268 (sizeof(struct xfs_slab_hdr_cursor
) * slab
->s_nr_slabs
));
271 c
->nr
= slab
->s_nr_slabs
;
273 c
->compare_fn
= compare_fn
;
275 hcur
= (struct xfs_slab_hdr_cursor
*)(c
+ 1);
288 * Free the slab cursor.
292 struct xfs_slab_cursor
**cur
)
301 * Return the smallest item in the slab, without advancing the iterator.
302 * The slabs must be sorted prior to the creation of the cursor.
306 struct xfs_slab_cursor
*cur
)
308 struct xfs_slab_hdr_cursor
*hcur
;
313 cur
->last_hcur
= NULL
;
315 /* no compare function; inorder traversal */
316 if (!cur
->compare_fn
) {
318 cur
->last_hcur
= &cur
->hcur
[0];
319 hcur
= cur
->last_hcur
;
320 while (hcur
< &cur
->hcur
[cur
->nr
] &&
321 hcur
->loc
>= hcur
->hdr
->sh_inuse
)
323 if (hcur
== &cur
->hcur
[cur
->nr
])
325 p
= slab_ptr(cur
->slab
, hcur
->hdr
, hcur
->loc
);
326 cur
->last_hcur
= hcur
;
330 /* otherwise return things in increasing order */
331 for (i
= 0, hcur
= &cur
->hcur
[i
]; i
< cur
->nr
; i
++, hcur
++) {
332 if (hcur
->loc
>= hcur
->hdr
->sh_inuse
)
334 q
= slab_ptr(cur
->slab
, hcur
->hdr
, hcur
->loc
);
335 if (!p
|| cur
->compare_fn(p
, q
) > 0) {
337 cur
->last_hcur
= hcur
;
345 * After a peek operation, advance the cursor.
349 struct xfs_slab_cursor
*cur
)
351 ASSERT(cur
->last_hcur
);
352 cur
->last_hcur
->loc
++;
356 * Retrieve the next item in the slab and advance the cursor.
360 struct xfs_slab_cursor
*cur
)
364 p
= peek_slab_cursor(cur
);
366 advance_slab_cursor(cur
);
371 * Return the number of items in the slab.
375 struct xfs_slab
*slab
)
377 return slab
->s_nr_items
;
381 * Create a bag to point to some objects.
385 struct xfs_bag
**bag
)
389 ptr
= calloc(1, sizeof(struct xfs_bag
));
392 ptr
->bg_ptrs
= calloc(MIN_BAG_SIZE
, sizeof(void *));
397 ptr
->bg_nr
= MIN_BAG_SIZE
;
403 * Free a bag of pointers.
407 struct xfs_bag
**bag
)
420 * Add an object to the pointer bag.
429 p
= &bag
->bg_ptrs
[bag
->bg_inuse
];
430 if (p
== BAG_END(bag
)) {
431 /* No free space, alloc more pointers */
435 x
= realloc(bag
->bg_ptrs
, nr
* sizeof(void *));
439 memset(BAG_END(bag
), 0, bag
->bg_nr
* sizeof(void *));
442 bag
->bg_ptrs
[bag
->bg_inuse
] = ptr
;
448 * Remove a pointer from a bag.
455 ASSERT(nr
< bag
->bg_inuse
);
456 memmove(&bag
->bg_ptrs
[nr
], &bag
->bg_ptrs
[nr
+ 1],
457 (bag
->bg_inuse
- nr
- 1) * sizeof(void *));
463 * Return the number of items in a bag.
469 return bag
->bg_inuse
;
473 * Return the nth item in a bag.
480 if (nr
>= bag
->bg_inuse
)
482 return bag
->bg_ptrs
[nr
];