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