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 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 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 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 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 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