xref: /linux/lib/region_alloc_benchmark.c (revision ae814200e8393fa504dd246e98fcba8f5493de28)
1*f4806cc6SYury Norov // SPDX-License-Identifier: GPL-2.0-only
2*f4806cc6SYury Norov /* Benchmark bitmap, IDA and Maple Tree allocation of variable-sized regions. */
3*f4806cc6SYury Norov 
4*f4806cc6SYury Norov #include <linux/bitmap.h>
5*f4806cc6SYury Norov #include <linux/idr.h>
6*f4806cc6SYury Norov #include <linux/kernel.h>
7*f4806cc6SYury Norov #include <linux/maple_tree.h>
8*f4806cc6SYury Norov #include <linux/module.h>
9*f4806cc6SYury Norov #include <linux/printk.h>
10*f4806cc6SYury Norov #include <linux/random.h>
11*f4806cc6SYury Norov #include <linux/slab.h>
12*f4806cc6SYury Norov #include <linux/xarray.h>
13*f4806cc6SYury Norov 
14*f4806cc6SYury Norov #define REGION_MAX_SIZE	32
15*f4806cc6SYury Norov 
16*f4806cc6SYury Norov static unsigned long *bitmap __initdata;
17*f4806cc6SYury Norov /* One more request guarantees that even an all-ones trace reaches ENOSPC. */
18*f4806cc6SYury Norov static u8 *reg_sz __initdata;
19*f4806cc6SYury Norov static unsigned long *reg_idx __initdata;
20*f4806cc6SYury Norov static unsigned long capacities[64] = { 1000000, 100000, 10000, 1000, 100, 10 };
21*f4806cc6SYury Norov static unsigned int cap_cnt = 6;
22*f4806cc6SYury Norov 
23*f4806cc6SYury Norov module_param_array(capacities, ulong, &cap_cnt, 0400);
24*f4806cc6SYury Norov MODULE_PARM_DESC(capacities, "Region capacities to benchmark");
25*f4806cc6SYury Norov 
benchmark_bitmap(unsigned long cap)26*f4806cc6SYury Norov static unsigned long __init benchmark_bitmap(unsigned long cap)
27*f4806cc6SYury Norov {
28*f4806cc6SYury Norov 	unsigned long cnt, idx;
29*f4806cc6SYury Norov 	ktime_t alloc_time, free_time;
30*f4806cc6SYury Norov 	size_t sz;
31*f4806cc6SYury Norov 
32*f4806cc6SYury Norov 	bitmap_zero(bitmap, cap);
33*f4806cc6SYury Norov 	alloc_time = ktime_get();
34*f4806cc6SYury Norov 	for (cnt = 0; cnt <= cap; cnt++) {
35*f4806cc6SYury Norov 		idx = bitmap_find_next_zero_area(bitmap, cap, 0, reg_sz[cnt], 0);
36*f4806cc6SYury Norov 		if (idx >= cap)
37*f4806cc6SYury Norov 			break;
38*f4806cc6SYury Norov 
39*f4806cc6SYury Norov 		reg_idx[cnt] = idx;
40*f4806cc6SYury Norov 		bitmap_set(bitmap, idx, reg_sz[cnt]);
41*f4806cc6SYury Norov 	}
42*f4806cc6SYury Norov 	alloc_time = ktime_get() - alloc_time;
43*f4806cc6SYury Norov 
44*f4806cc6SYury Norov 	idx = cnt;
45*f4806cc6SYury Norov 
46*f4806cc6SYury Norov 	free_time = ktime_get();
47*f4806cc6SYury Norov 	while (idx--)
48*f4806cc6SYury Norov 		bitmap_clear(bitmap, reg_idx[idx], reg_sz[idx]);
49*f4806cc6SYury Norov 	free_time = ktime_get() - free_time;
50*f4806cc6SYury Norov 
51*f4806cc6SYury Norov 	WARN_ON(!bitmap_empty(bitmap, cap));
52*f4806cc6SYury Norov 
53*f4806cc6SYury Norov 	sz = BITS_TO_LONGS(cap) * sizeof(unsigned long);
54*f4806cc6SYury Norov 	pr_err("Bitmap  %12llu  %12llu  %8lu  %8lu  %10zu\n",
55*f4806cc6SYury Norov 	       alloc_time, free_time, cnt, cap, sz);
56*f4806cc6SYury Norov 
57*f4806cc6SYury Norov 	return cnt;
58*f4806cc6SYury Norov }
59*f4806cc6SYury Norov 
ida_size(unsigned long nr_ids)60*f4806cc6SYury Norov static size_t __init ida_size(unsigned long nr_ids)
61*f4806cc6SYury Norov {
62*f4806cc6SYury Norov 	unsigned long entries = DIV_ROUND_UP(nr_ids, IDA_BITMAP_BITS);
63*f4806cc6SYury Norov 	unsigned long bitmaps = nr_ids / IDA_BITMAP_BITS;
64*f4806cc6SYury Norov 	unsigned long nodes = 0;
65*f4806cc6SYury Norov 
66*f4806cc6SYury Norov 	if (nr_ids % IDA_BITMAP_BITS > BITS_PER_XA_VALUE)
67*f4806cc6SYury Norov 		bitmaps++;
68*f4806cc6SYury Norov 
69*f4806cc6SYury Norov 	while (entries > 1) {
70*f4806cc6SYury Norov 		entries = DIV_ROUND_UP(entries, XA_CHUNK_SIZE);
71*f4806cc6SYury Norov 		nodes += entries;
72*f4806cc6SYury Norov 	}
73*f4806cc6SYury Norov 
74*f4806cc6SYury Norov 	return sizeof(struct ida) +
75*f4806cc6SYury Norov 		bitmaps * sizeof(struct ida_bitmap) +
76*f4806cc6SYury Norov 		nodes   * sizeof(struct xa_node);
77*f4806cc6SYury Norov }
78*f4806cc6SYury Norov 
benchmark_ida(unsigned long cap)79*f4806cc6SYury Norov static unsigned long __init benchmark_ida(unsigned long cap)
80*f4806cc6SYury Norov {
81*f4806cc6SYury Norov 	struct ida ida = IDA_INIT(ida);
82*f4806cc6SYury Norov 	unsigned long cnt, idx, off, nr_ids = 0;
83*f4806cc6SYury Norov 	ktime_t alloc_time, free_time;
84*f4806cc6SYury Norov 	int id = -ENOSPC;
85*f4806cc6SYury Norov 
86*f4806cc6SYury Norov 	alloc_time = ktime_get();
87*f4806cc6SYury Norov 	for (cnt = 0; cnt <= cap; cnt++) {
88*f4806cc6SYury Norov 		for (off = 0; off < reg_sz[cnt]; off++) {
89*f4806cc6SYury Norov 			id = ida_alloc_max(&ida, cap - 1, GFP_KERNEL);
90*f4806cc6SYury Norov 			if (id < 0)
91*f4806cc6SYury Norov 				break;
92*f4806cc6SYury Norov 
93*f4806cc6SYury Norov 			if (!off)
94*f4806cc6SYury Norov 				reg_idx[cnt] = id;
95*f4806cc6SYury Norov 		}
96*f4806cc6SYury Norov 		if (id < 0) {
97*f4806cc6SYury Norov 			while (off--)
98*f4806cc6SYury Norov 				ida_free(&ida, reg_idx[cnt] + off);
99*f4806cc6SYury Norov 			break;
100*f4806cc6SYury Norov 		}
101*f4806cc6SYury Norov 		WARN_ON(id != reg_idx[cnt] + reg_sz[cnt] - 1);
102*f4806cc6SYury Norov 		nr_ids += reg_sz[cnt];
103*f4806cc6SYury Norov 	}
104*f4806cc6SYury Norov 	alloc_time = ktime_get() - alloc_time;
105*f4806cc6SYury Norov 
106*f4806cc6SYury Norov 	WARN_ON(id != -ENOSPC);
107*f4806cc6SYury Norov 
108*f4806cc6SYury Norov 	idx = cnt;
109*f4806cc6SYury Norov 
110*f4806cc6SYury Norov 	free_time = ktime_get();
111*f4806cc6SYury Norov 	while (idx--) {
112*f4806cc6SYury Norov 		for (off = 0; off < reg_sz[idx]; off++)
113*f4806cc6SYury Norov 			ida_free(&ida, reg_idx[idx] + off);
114*f4806cc6SYury Norov 	}
115*f4806cc6SYury Norov 	free_time = ktime_get() - free_time;
116*f4806cc6SYury Norov 
117*f4806cc6SYury Norov 	WARN_ON(!ida_is_empty(&ida));
118*f4806cc6SYury Norov 
119*f4806cc6SYury Norov 	pr_err("IDA     %12llu  %12llu  %8lu  %8lu  %10zu\n",
120*f4806cc6SYury Norov 	       alloc_time, free_time, cnt, cap, ida_size(nr_ids));
121*f4806cc6SYury Norov 
122*f4806cc6SYury Norov 	ida_destroy(&ida);
123*f4806cc6SYury Norov 	return cnt;
124*f4806cc6SYury Norov }
125*f4806cc6SYury Norov 
benchmark_maple_tree(unsigned long cap)126*f4806cc6SYury Norov static unsigned long __init benchmark_maple_tree(unsigned long cap)
127*f4806cc6SYury Norov {
128*f4806cc6SYury Norov 	struct maple_tree mt = MTREE_INIT(mt, MT_FLAGS_ALLOC_RANGE);
129*f4806cc6SYury Norov 	unsigned long cnt, idx;
130*f4806cc6SYury Norov 	ktime_t alloc_time, free_time;
131*f4806cc6SYury Norov 	size_t sz;
132*f4806cc6SYury Norov 	int ret;
133*f4806cc6SYury Norov 
134*f4806cc6SYury Norov 	alloc_time = ktime_get();
135*f4806cc6SYury Norov 	for (cnt = 0; cnt <= cap; cnt++) {
136*f4806cc6SYury Norov 		ret = mtree_alloc_range(&mt, &idx, xa_mk_value(cnt + 1),
137*f4806cc6SYury Norov 					reg_sz[cnt], 0, cap - 1, GFP_KERNEL);
138*f4806cc6SYury Norov 		if (ret)
139*f4806cc6SYury Norov 			break;
140*f4806cc6SYury Norov 
141*f4806cc6SYury Norov 		reg_idx[cnt] = idx;
142*f4806cc6SYury Norov 	}
143*f4806cc6SYury Norov 	alloc_time = ktime_get() - alloc_time;
144*f4806cc6SYury Norov 
145*f4806cc6SYury Norov 	WARN_ON(ret != -EBUSY);
146*f4806cc6SYury Norov 
147*f4806cc6SYury Norov 	idx = cnt;
148*f4806cc6SYury Norov 
149*f4806cc6SYury Norov 	free_time = ktime_get();
150*f4806cc6SYury Norov 	while (idx--)
151*f4806cc6SYury Norov 		mtree_erase(&mt, reg_idx[idx]);
152*f4806cc6SYury Norov 	free_time = ktime_get() - free_time;
153*f4806cc6SYury Norov 
154*f4806cc6SYury Norov 	WARN_ON(!mtree_empty(&mt));
155*f4806cc6SYury Norov 
156*f4806cc6SYury Norov 	/* Minimum storage assuming fully occupied allocation-range leaf nodes. */
157*f4806cc6SYury Norov 	sz = sizeof(mt) + DIV_ROUND_UP(cnt, MAPLE_ARANGE64_SLOTS) * sizeof(struct maple_node);
158*f4806cc6SYury Norov 	pr_err("Maple   %12llu  %12llu  %8lu  %8lu  %10zu\n",
159*f4806cc6SYury Norov 	       alloc_time, free_time, cnt, cap, sz);
160*f4806cc6SYury Norov 
161*f4806cc6SYury Norov 	mtree_destroy(&mt);
162*f4806cc6SYury Norov 	return cnt;
163*f4806cc6SYury Norov }
164*f4806cc6SYury Norov 
region_alloc_benchmark(void)165*f4806cc6SYury Norov static int __init region_alloc_benchmark(void)
166*f4806cc6SYury Norov {
167*f4806cc6SYury Norov 	unsigned long bitmap_count, ida_count, maple_count;
168*f4806cc6SYury Norov 	unsigned long i, max_cap = 0;
169*f4806cc6SYury Norov 	int ret = -ENOMEM;
170*f4806cc6SYury Norov 
171*f4806cc6SYury Norov 	for (i = 0; i < cap_cnt; i++) {
172*f4806cc6SYury Norov 		if (capacities[i] == 0) {
173*f4806cc6SYury Norov 			pr_err("capacity must be nonzero\n");
174*f4806cc6SYury Norov 			return -EINVAL;
175*f4806cc6SYury Norov 		}
176*f4806cc6SYury Norov 		max_cap = max(max_cap, capacities[i]);
177*f4806cc6SYury Norov 	}
178*f4806cc6SYury Norov 
179*f4806cc6SYury Norov 	bitmap = kvmalloc_array(BITS_TO_LONGS(max_cap), sizeof(*bitmap), GFP_KERNEL);
180*f4806cc6SYury Norov 	reg_sz = kvmalloc_array(max_cap + 1, sizeof(*reg_sz), GFP_KERNEL);
181*f4806cc6SYury Norov 	reg_idx = kvmalloc_array(max_cap, sizeof(*reg_idx), GFP_KERNEL);
182*f4806cc6SYury Norov 	if (!bitmap || !reg_sz || !reg_idx)
183*f4806cc6SYury Norov 		goto out;
184*f4806cc6SYury Norov 
185*f4806cc6SYury Norov 	pr_err("\nStart testing bitmap vs IDA vs Maple Tree region allocation\n");
186*f4806cc6SYury Norov 	pr_err("memory: bitmap is exact; IDA and Maple Tree are lower bounds\n");
187*f4806cc6SYury Norov 	pr_err("Type      alloc (ns)     free (ns)   regions  capacity  memory (B)\n");
188*f4806cc6SYury Norov 
189*f4806cc6SYury Norov 	for (i = 0; i < cap_cnt; i++) {
190*f4806cc6SYury Norov 		unsigned long idx, max_size;
191*f4806cc6SYury Norov 
192*f4806cc6SYury Norov 		max_size = min(REGION_MAX_SIZE, capacities[i] / 10) ? : 1;
193*f4806cc6SYury Norov 		for (idx = 0; idx <= capacities[i]; idx++)
194*f4806cc6SYury Norov 			reg_sz[idx] = get_random_u32_below(max_size) + 1;
195*f4806cc6SYury Norov 
196*f4806cc6SYury Norov 		bitmap_count = benchmark_bitmap(capacities[i]);
197*f4806cc6SYury Norov 		maple_count  = benchmark_maple_tree(capacities[i]);
198*f4806cc6SYury Norov 		ida_count    = benchmark_ida(capacities[i]);
199*f4806cc6SYury Norov 
200*f4806cc6SYury Norov 		WARN_ON(bitmap_count != ida_count);
201*f4806cc6SYury Norov 		WARN_ON(bitmap_count != maple_count);
202*f4806cc6SYury Norov 	}
203*f4806cc6SYury Norov 
204*f4806cc6SYury Norov 	/* Return an error so the benchmark can run repeatedly without rmmod. */
205*f4806cc6SYury Norov 	pr_info("Region allocation benchmark complete\n");
206*f4806cc6SYury Norov 	ret = -EAGAIN;
207*f4806cc6SYury Norov out:
208*f4806cc6SYury Norov 	kvfree(reg_idx);
209*f4806cc6SYury Norov 	kvfree(reg_sz);
210*f4806cc6SYury Norov 	kvfree(bitmap);
211*f4806cc6SYury Norov 	return ret;
212*f4806cc6SYury Norov }
213*f4806cc6SYury Norov module_init(region_alloc_benchmark);
214*f4806cc6SYury Norov 
215*f4806cc6SYury Norov MODULE_AUTHOR("Yury Norov <ynorov@nvidia.com>");
216*f4806cc6SYury Norov MODULE_DESCRIPTION("Benchmark bitmap, IDA and Maple Tree region allocation");
217*f4806cc6SYury Norov MODULE_LICENSE("GPL");
218