162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * test_ida.c: Test the IDA API 462306a36Sopenharmony_ci * Copyright (c) 2016-2018 Microsoft Corporation 562306a36Sopenharmony_ci * Copyright (c) 2018 Oracle Corporation 662306a36Sopenharmony_ci * Author: Matthew Wilcox <willy@infradead.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/idr.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic unsigned int tests_run; 1362306a36Sopenharmony_cistatic unsigned int tests_passed; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#ifdef __KERNEL__ 1662306a36Sopenharmony_civoid ida_dump(struct ida *ida) { } 1762306a36Sopenharmony_ci#endif 1862306a36Sopenharmony_ci#define IDA_BUG_ON(ida, x) do { \ 1962306a36Sopenharmony_ci tests_run++; \ 2062306a36Sopenharmony_ci if (x) { \ 2162306a36Sopenharmony_ci ida_dump(ida); \ 2262306a36Sopenharmony_ci dump_stack(); \ 2362306a36Sopenharmony_ci } else { \ 2462306a36Sopenharmony_ci tests_passed++; \ 2562306a36Sopenharmony_ci } \ 2662306a36Sopenharmony_ci} while (0) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Straightforward checks that allocating and freeing IDs work. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic void ida_check_alloc(struct ida *ida) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int i, id; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci for (i = 0; i < 10000; i++) 3662306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc(ida, GFP_KERNEL) != i); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ida_free(ida, 20); 3962306a36Sopenharmony_ci ida_free(ida, 21); 4062306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 4162306a36Sopenharmony_ci id = ida_alloc(ida, GFP_KERNEL); 4262306a36Sopenharmony_ci IDA_BUG_ON(ida, id < 0); 4362306a36Sopenharmony_ci if (i == 2) 4462306a36Sopenharmony_ci IDA_BUG_ON(ida, id != 10000); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (i = 0; i < 5000; i++) 4862306a36Sopenharmony_ci ida_free(ida, i); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, 5000, GFP_KERNEL) != 10001); 5162306a36Sopenharmony_ci ida_destroy(ida); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Destroy an IDA with a single entry at @base */ 5762306a36Sopenharmony_cistatic void ida_check_destroy_1(struct ida *ida, unsigned int base) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, base, GFP_KERNEL) != base); 6062306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_is_empty(ida)); 6162306a36Sopenharmony_ci ida_destroy(ida); 6262306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Check that ida_destroy and ida_is_empty work */ 6662306a36Sopenharmony_cistatic void ida_check_destroy(struct ida *ida) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci /* Destroy an already-empty IDA */ 6962306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 7062306a36Sopenharmony_ci ida_destroy(ida); 7162306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ida_check_destroy_1(ida, 0); 7462306a36Sopenharmony_ci ida_check_destroy_1(ida, 1); 7562306a36Sopenharmony_ci ida_check_destroy_1(ida, 1023); 7662306a36Sopenharmony_ci ida_check_destroy_1(ida, 1024); 7762306a36Sopenharmony_ci ida_check_destroy_1(ida, 12345678); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * Check what happens when we fill a leaf and then delete it. This may 8262306a36Sopenharmony_ci * discover mishandling of IDR_FREE. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic void ida_check_leaf(struct ida *ida, unsigned int base) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci unsigned long i; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (i = 0; i < IDA_BITMAP_BITS; i++) { 8962306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, base, GFP_KERNEL) != 9062306a36Sopenharmony_ci base + i); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ida_destroy(ida); 9462306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc(ida, GFP_KERNEL) != 0); 9762306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_is_empty(ida)); 9862306a36Sopenharmony_ci ida_free(ida, 0); 9962306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Check allocations up to and slightly above the maximum allowed (2^31-1) ID. 10462306a36Sopenharmony_ci * Allocating up to 2^31-1 should succeed, and then allocating the next one 10562306a36Sopenharmony_ci * should fail. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic void ida_check_max(struct ida *ida) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned long i, j; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci for (j = 1; j < 65537; j *= 2) { 11262306a36Sopenharmony_ci unsigned long base = (1UL << 31) - j; 11362306a36Sopenharmony_ci for (i = 0; i < j; i++) { 11462306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, base, GFP_KERNEL) != 11562306a36Sopenharmony_ci base + i); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, base, GFP_KERNEL) != 11862306a36Sopenharmony_ci -ENOSPC); 11962306a36Sopenharmony_ci ida_destroy(ida); 12062306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Check handling of conversions between exceptional entries and full bitmaps. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic void ida_check_conv(struct ida *ida) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned long i; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < IDA_BITMAP_BITS * 2; i += IDA_BITMAP_BITS) { 13262306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, i + 1, GFP_KERNEL) != i + 1); 13362306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, i + BITS_PER_LONG, 13462306a36Sopenharmony_ci GFP_KERNEL) != i + BITS_PER_LONG); 13562306a36Sopenharmony_ci ida_free(ida, i + 1); 13662306a36Sopenharmony_ci ida_free(ida, i + BITS_PER_LONG); 13762306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (i = 0; i < IDA_BITMAP_BITS * 2; i++) 14162306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc(ida, GFP_KERNEL) != i); 14262306a36Sopenharmony_ci for (i = IDA_BITMAP_BITS * 2; i > 0; i--) 14362306a36Sopenharmony_ci ida_free(ida, i - 1); 14462306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci for (i = 0; i < IDA_BITMAP_BITS + BITS_PER_LONG - 4; i++) 14762306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc(ida, GFP_KERNEL) != i); 14862306a36Sopenharmony_ci for (i = IDA_BITMAP_BITS + BITS_PER_LONG - 4; i > 0; i--) 14962306a36Sopenharmony_ci ida_free(ida, i - 1); 15062306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Check various situations where we attempt to free an ID we don't own. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cistatic void ida_check_bad_free(struct ida *ida) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci unsigned long i; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci printk("vvv Ignore \"not allocated\" warnings\n"); 16162306a36Sopenharmony_ci /* IDA is empty; all of these will fail */ 16262306a36Sopenharmony_ci ida_free(ida, 0); 16362306a36Sopenharmony_ci for (i = 0; i < 31; i++) 16462306a36Sopenharmony_ci ida_free(ida, 1 << i); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* IDA contains a single value entry */ 16762306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, 3, GFP_KERNEL) != 3); 16862306a36Sopenharmony_ci ida_free(ida, 0); 16962306a36Sopenharmony_ci for (i = 0; i < 31; i++) 17062306a36Sopenharmony_ci ida_free(ida, 1 << i); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* IDA contains a single bitmap */ 17362306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, 1023, GFP_KERNEL) != 1023); 17462306a36Sopenharmony_ci ida_free(ida, 0); 17562306a36Sopenharmony_ci for (i = 0; i < 31; i++) 17662306a36Sopenharmony_ci ida_free(ida, 1 << i); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* IDA contains a tree */ 17962306a36Sopenharmony_ci IDA_BUG_ON(ida, ida_alloc_min(ida, (1 << 20) - 1, GFP_KERNEL) != (1 << 20) - 1); 18062306a36Sopenharmony_ci ida_free(ida, 0); 18162306a36Sopenharmony_ci for (i = 0; i < 31; i++) 18262306a36Sopenharmony_ci ida_free(ida, 1 << i); 18362306a36Sopenharmony_ci printk("^^^ \"not allocated\" warnings over\n"); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ida_free(ida, 3); 18662306a36Sopenharmony_ci ida_free(ida, 1023); 18762306a36Sopenharmony_ci ida_free(ida, (1 << 20) - 1); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci IDA_BUG_ON(ida, !ida_is_empty(ida)); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic DEFINE_IDA(ida); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int ida_checks(void) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci IDA_BUG_ON(&ida, !ida_is_empty(&ida)); 19762306a36Sopenharmony_ci ida_check_alloc(&ida); 19862306a36Sopenharmony_ci ida_check_destroy(&ida); 19962306a36Sopenharmony_ci ida_check_leaf(&ida, 0); 20062306a36Sopenharmony_ci ida_check_leaf(&ida, 1024); 20162306a36Sopenharmony_ci ida_check_leaf(&ida, 1024 * 64); 20262306a36Sopenharmony_ci ida_check_max(&ida); 20362306a36Sopenharmony_ci ida_check_conv(&ida); 20462306a36Sopenharmony_ci ida_check_bad_free(&ida); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci printk("IDA: %u of %u tests passed\n", tests_passed, tests_run); 20762306a36Sopenharmony_ci return (tests_run != tests_passed) ? 0 : -EINVAL; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void ida_exit(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cimodule_init(ida_checks); 21562306a36Sopenharmony_cimodule_exit(ida_exit); 21662306a36Sopenharmony_ciMODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>"); 21762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 218