2018-06-18 23:59:29 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* test_ida . c : Test the IDA API
* Copyright ( c ) 2016 - 2018 Microsoft Corporation
* Copyright ( c ) 2018 Oracle Corporation
* Author : Matthew Wilcox < willy @ infradead . org >
*/
# include <linux/idr.h>
# include <linux/module.h>
static unsigned int tests_run ;
static unsigned int tests_passed ;
# ifdef __KERNEL__
void ida_dump ( struct ida * ida ) { }
# endif
# define IDA_BUG_ON(ida, x) do { \
tests_run + + ; \
if ( x ) { \
ida_dump ( ida ) ; \
dump_stack ( ) ; \
} else { \
tests_passed + + ; \
} \
} while ( 0 )
2018-06-19 01:39:28 +03:00
/*
* Straightforward checks that allocating and freeing IDs work .
*/
static void ida_check_alloc ( struct ida * ida )
{
int i , id ;
for ( i = 0 ; i < 10000 ; i + + )
IDA_BUG_ON ( ida , ida_alloc ( ida , GFP_KERNEL ) ! = i ) ;
ida_free ( ida , 20 ) ;
ida_free ( ida , 21 ) ;
for ( i = 0 ; i < 3 ; i + + ) {
id = ida_alloc ( ida , GFP_KERNEL ) ;
IDA_BUG_ON ( ida , id < 0 ) ;
if ( i = = 2 )
IDA_BUG_ON ( ida , id ! = 10000 ) ;
}
for ( i = 0 ; i < 5000 ; i + + )
ida_free ( ida , i ) ;
IDA_BUG_ON ( ida , ida_alloc_min ( ida , 5000 , GFP_KERNEL ) ! = 10001 ) ;
ida_destroy ( ida ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
/* Destroy an IDA with a single entry at @base */
static void ida_check_destroy_1 ( struct ida * ida , unsigned int base )
{
IDA_BUG_ON ( ida , ida_alloc_min ( ida , base , GFP_KERNEL ) ! = base ) ;
IDA_BUG_ON ( ida , ida_is_empty ( ida ) ) ;
ida_destroy ( ida ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
/* Check that ida_destroy and ida_is_empty work */
static void ida_check_destroy ( struct ida * ida )
{
/* Destroy an already-empty IDA */
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
ida_destroy ( ida ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
ida_check_destroy_1 ( ida , 0 ) ;
ida_check_destroy_1 ( ida , 1 ) ;
ida_check_destroy_1 ( ida , 1023 ) ;
ida_check_destroy_1 ( ida , 1024 ) ;
ida_check_destroy_1 ( ida , 12345678 ) ;
}
2018-06-19 00:23:37 +03:00
/*
* Check what happens when we fill a leaf and then delete it . This may
* discover mishandling of IDR_FREE .
*/
static void ida_check_leaf ( struct ida * ida , unsigned int base )
{
unsigned long i ;
for ( i = 0 ; i < IDA_BITMAP_BITS ; i + + ) {
IDA_BUG_ON ( ida , ida_alloc_min ( ida , base , GFP_KERNEL ) ! =
base + i ) ;
}
ida_destroy ( ida ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
IDA_BUG_ON ( ida , ida_alloc ( ida , GFP_KERNEL ) ! = 0 ) ;
IDA_BUG_ON ( ida , ida_is_empty ( ida ) ) ;
ida_free ( ida , 0 ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
2018-06-19 00:25:20 +03:00
/*
* Check allocations up to and slightly above the maximum allowed ( 2 ^ 31 - 1 ) ID .
* Allocating up to 2 ^ 31 - 1 should succeed , and then allocating the next one
* should fail .
*/
static void ida_check_max ( struct ida * ida )
{
unsigned long i , j ;
for ( j = 1 ; j < 65537 ; j * = 2 ) {
unsigned long base = ( 1UL < < 31 ) - j ;
for ( i = 0 ; i < j ; i + + ) {
IDA_BUG_ON ( ida , ida_alloc_min ( ida , base , GFP_KERNEL ) ! =
base + i ) ;
}
IDA_BUG_ON ( ida , ida_alloc_min ( ida , base , GFP_KERNEL ) ! =
- ENOSPC ) ;
ida_destroy ( ida ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
}
2018-06-19 01:10:32 +03:00
/*
* Check handling of conversions between exceptional entries and full bitmaps .
*/
static void ida_check_conv ( struct ida * ida )
{
unsigned long i ;
for ( i = 0 ; i < IDA_BITMAP_BITS * 2 ; i + = IDA_BITMAP_BITS ) {
IDA_BUG_ON ( ida , ida_alloc_min ( ida , i + 1 , GFP_KERNEL ) ! = i + 1 ) ;
IDA_BUG_ON ( ida , ida_alloc_min ( ida , i + BITS_PER_LONG ,
GFP_KERNEL ) ! = i + BITS_PER_LONG ) ;
ida_free ( ida , i + 1 ) ;
ida_free ( ida , i + BITS_PER_LONG ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
for ( i = 0 ; i < IDA_BITMAP_BITS * 2 ; i + + )
IDA_BUG_ON ( ida , ida_alloc ( ida , GFP_KERNEL ) ! = i ) ;
for ( i = IDA_BITMAP_BITS * 2 ; i > 0 ; i - - )
ida_free ( ida , i - 1 ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
for ( i = 0 ; i < IDA_BITMAP_BITS + BITS_PER_LONG - 4 ; i + + )
IDA_BUG_ON ( ida , ida_alloc ( ida , GFP_KERNEL ) ! = i ) ;
for ( i = IDA_BITMAP_BITS + BITS_PER_LONG - 4 ; i > 0 ; i - - )
ida_free ( ida , i - 1 ) ;
IDA_BUG_ON ( ida , ! ida_is_empty ( ida ) ) ;
}
2018-10-15 23:23:08 +03:00
static DEFINE_IDA ( ida ) ;
2018-06-18 23:59:29 +03:00
static int ida_checks ( void )
{
IDA_BUG_ON ( & ida , ! ida_is_empty ( & ida ) ) ;
2018-06-19 01:39:28 +03:00
ida_check_alloc ( & ida ) ;
ida_check_destroy ( & ida ) ;
2018-06-19 00:23:37 +03:00
ida_check_leaf ( & ida , 0 ) ;
ida_check_leaf ( & ida , 1024 ) ;
ida_check_leaf ( & ida , 1024 * 64 ) ;
2018-06-19 00:25:20 +03:00
ida_check_max ( & ida ) ;
2018-06-19 01:10:32 +03:00
ida_check_conv ( & ida ) ;
2018-06-18 23:59:29 +03:00
printk ( " IDA: %u of %u tests passed \n " , tests_passed , tests_run ) ;
return ( tests_run ! = tests_passed ) ? 0 : - EINVAL ;
}
static void ida_exit ( void )
{
}
module_init ( ida_checks ) ;
module_exit ( ida_exit ) ;
MODULE_AUTHOR ( " Matthew Wilcox <willy@infradead.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;