2015-01-29 17:40:25 +03:00
/*
* Resizable , Scalable , Concurrent Hash Table
*
2015-05-01 01:37:41 +03:00
* Copyright ( c ) 2014 - 2015 Thomas Graf < tgraf @ suug . ch >
2015-01-29 17:40:25 +03:00
* Copyright ( c ) 2008 - 2014 Patrick McHardy < kaber @ trash . net >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
/**************************************************************************
* Self Test
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/init.h>
# include <linux/jhash.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/rcupdate.h>
# include <linux/rhashtable.h>
# include <linux/slab.h>
2015-05-01 01:37:41 +03:00
# define MAX_ENTRIES 1000000
2015-05-01 01:37:45 +03:00
# define TEST_INSERT_FAIL INT_MAX
2015-05-01 01:37:41 +03:00
static int entries = 50000 ;
module_param ( entries , int , 0 ) ;
MODULE_PARM_DESC ( entries , " Number of entries to add (default: 50000) " ) ;
static int runs = 4 ;
module_param ( runs , int , 0 ) ;
MODULE_PARM_DESC ( runs , " Number of test runs per variant (default: 4) " ) ;
static int max_size = 65536 ;
module_param ( max_size , int , 0 ) ;
MODULE_PARM_DESC ( runs , " Maximum table size (default: 65536) " ) ;
static bool shrinking = false ;
module_param ( shrinking , bool , 0 ) ;
MODULE_PARM_DESC ( shrinking , " Enable automatic shrinking (default: off) " ) ;
static int size = 8 ;
module_param ( size , int , 0 ) ;
MODULE_PARM_DESC ( size , " Initial size hint of table (default: 8) " ) ;
2015-01-29 17:40:25 +03:00
struct test_obj {
int value ;
struct rhash_head node ;
} ;
2015-05-01 01:37:43 +03:00
static struct test_obj array [ MAX_ENTRIES ] ;
2015-05-01 01:37:41 +03:00
static struct rhashtable_params test_rht_params = {
2015-03-20 13:57:04 +03:00
. head_offset = offsetof ( struct test_obj , node ) ,
. key_offset = offsetof ( struct test_obj , value ) ,
. key_len = sizeof ( int ) ,
. hashfn = jhash ,
. nulls_base = ( 3U < < RHT_BASE_SHIFT ) ,
} ;
2015-01-29 17:40:25 +03:00
static int __init test_rht_lookup ( struct rhashtable * ht )
{
unsigned int i ;
2015-05-01 01:37:41 +03:00
for ( i = 0 ; i < entries * 2 ; i + + ) {
2015-01-29 17:40:25 +03:00
struct test_obj * obj ;
bool expected = ! ( i % 2 ) ;
u32 key = i ;
2015-05-01 01:37:45 +03:00
if ( array [ i / 2 ] . value = = TEST_INSERT_FAIL )
expected = false ;
2015-03-20 13:57:04 +03:00
obj = rhashtable_lookup_fast ( ht , & key , test_rht_params ) ;
2015-01-29 17:40:25 +03:00
if ( expected & & ! obj ) {
pr_warn ( " Test failed: Could not find key %u \n " , key ) ;
return - ENOENT ;
} else if ( ! expected & & obj ) {
pr_warn ( " Test failed: Unexpected entry found for key %u \n " ,
key ) ;
return - EEXIST ;
} else if ( expected & & obj ) {
2015-05-01 01:37:42 +03:00
if ( obj - > value ! = i ) {
pr_warn ( " Test failed: Lookup value mismatch %u!=%u \n " ,
obj - > value , i ) ;
2015-01-29 17:40:25 +03:00
return - EINVAL ;
}
}
}
return 0 ;
}
2015-05-01 01:37:44 +03:00
static void test_bucket_stats ( struct rhashtable * ht )
2015-01-29 17:40:25 +03:00
{
2015-05-01 01:37:44 +03:00
unsigned int err , total = 0 , chain_len = 0 ;
struct rhashtable_iter hti ;
2015-01-29 17:40:25 +03:00
struct rhash_head * pos ;
2015-05-01 01:37:44 +03:00
err = rhashtable_walk_init ( ht , & hti ) ;
if ( err ) {
pr_warn ( " Test failed: allocation error " ) ;
return ;
}
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:44 +03:00
err = rhashtable_walk_start ( & hti ) ;
if ( err & & err ! = - EAGAIN ) {
pr_warn ( " Test failed: iterator failed: %d \n " , err ) ;
return ;
}
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:44 +03:00
while ( ( pos = rhashtable_walk_next ( & hti ) ) ) {
if ( PTR_ERR ( pos ) = = - EAGAIN ) {
pr_info ( " Info: encountered resize \n " ) ;
chain_len + + ;
continue ;
} else if ( IS_ERR ( pos ) ) {
pr_warn ( " Test failed: rhashtable_walk_next() error: %ld \n " ,
PTR_ERR ( pos ) ) ;
break ;
2015-01-29 17:40:25 +03:00
}
2015-05-01 01:37:44 +03:00
total + + ;
2015-01-29 17:40:25 +03:00
}
2015-05-01 01:37:44 +03:00
rhashtable_walk_stop ( & hti ) ;
rhashtable_walk_exit ( & hti ) ;
pr_info ( " Traversal complete: counted=%u, nelems=%u, entries=%d, table-jumps=%u \n " ,
total , atomic_read ( & ht - > nelems ) , entries , chain_len ) ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
if ( total ! = atomic_read ( & ht - > nelems ) | | total ! = entries )
2015-01-29 17:40:25 +03:00
pr_warn ( " Test failed: Total count mismatch ^^^ " ) ;
}
2015-05-01 01:37:41 +03:00
static s64 __init test_rhashtable ( struct rhashtable * ht )
2015-01-29 17:40:25 +03:00
{
struct test_obj * obj ;
int err ;
2015-05-01 01:37:45 +03:00
unsigned int i , insert_fails = 0 ;
2015-05-01 01:37:41 +03:00
s64 start , end ;
2015-01-29 17:40:25 +03:00
/*
* Insertion Test :
2015-05-01 01:37:41 +03:00
* Insert entries into table with all keys even numbers
2015-01-29 17:40:25 +03:00
*/
2015-05-01 01:37:41 +03:00
pr_info ( " Adding %d keys \n " , entries ) ;
start = ktime_get_ns ( ) ;
for ( i = 0 ; i < entries ; i + + ) {
2015-05-01 01:37:43 +03:00
struct test_obj * obj = & array [ i ] ;
2015-01-29 17:40:25 +03:00
obj - > value = i * 2 ;
2015-03-20 13:57:04 +03:00
err = rhashtable_insert_fast ( ht , & obj - > node , test_rht_params ) ;
2015-05-01 01:37:45 +03:00
if ( err = = - ENOMEM | | err = = - EBUSY ) {
/* Mark failed inserts but continue */
obj - > value = TEST_INSERT_FAIL ;
insert_fails + + ;
} else if ( err ) {
2015-05-01 01:37:43 +03:00
return err ;
2015-05-01 01:37:45 +03:00
}
2015-01-29 17:40:25 +03:00
}
2015-05-01 01:37:45 +03:00
if ( insert_fails )
pr_info ( " %u insertions failed due to memory pressure \n " ,
insert_fails ) ;
2015-05-01 01:37:44 +03:00
test_bucket_stats ( ht ) ;
2015-01-29 17:40:25 +03:00
rcu_read_lock ( ) ;
test_rht_lookup ( ht ) ;
rcu_read_unlock ( ) ;
2015-05-01 01:37:44 +03:00
test_bucket_stats ( ht ) ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
pr_info ( " Deleting %d keys \n " , entries ) ;
for ( i = 0 ; i < entries ; i + + ) {
2015-01-29 17:40:25 +03:00
u32 key = i * 2 ;
2015-05-01 01:37:45 +03:00
if ( array [ i ] . value ! = TEST_INSERT_FAIL ) {
obj = rhashtable_lookup_fast ( ht , & key , test_rht_params ) ;
BUG_ON ( ! obj ) ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:45 +03:00
rhashtable_remove_fast ( ht , & obj - > node , test_rht_params ) ;
}
2015-01-29 17:40:25 +03:00
}
2015-05-01 01:37:41 +03:00
end = ktime_get_ns ( ) ;
pr_info ( " Duration of test: %lld ns \n " , end - start ) ;
return end - start ;
2015-01-29 17:40:25 +03:00
}
rhashtable: don't allocate ht structure on stack in test_rht_init
With object runtime debugging enabled, the rhashtable test suite
will rightfully throw a warning "ODEBUG: object is on stack, but
not annotated" from rhashtable_init().
This is because run_work is (correctly) being initialized via
INIT_WORK(), and not annotated by INIT_WORK_ONSTACK(). Meaning,
rhashtable_init() is okay as is, we just need to move ht e.g.,
into global scope.
It never triggered anything, since test_rhashtable is rather a
controlled environment and effectively runs to completion, so
that stack memory is not vanishing underneath us, we shouldn't
confuse any testers with it though.
Fixes: 7e1e77636e36 ("lib: Resizable, Scalable, Concurrent Hash Table")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-02-20 23:14:21 +03:00
static struct rhashtable ht ;
2015-01-29 17:40:25 +03:00
static int __init test_rht_init ( void )
{
2015-05-01 01:37:41 +03:00
int i , err ;
u64 total_time = 0 ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
entries = min ( entries , MAX_ENTRIES ) ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
test_rht_params . automatic_shrinking = shrinking ;
test_rht_params . max_size = max_size ;
test_rht_params . nelem_hint = size ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
pr_info ( " Running rhashtable test nelem=%d, max_size=%d, shrinking=%d \n " ,
size , max_size , shrinking ) ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
for ( i = 0 ; i < runs ; i + + ) {
s64 time ;
2015-01-29 17:40:25 +03:00
2015-05-01 01:37:41 +03:00
pr_info ( " Test %02d: \n " , i ) ;
2015-05-01 01:37:43 +03:00
memset ( & array , 0 , sizeof ( array ) ) ;
2015-05-01 01:37:41 +03:00
err = rhashtable_init ( & ht , & test_rht_params ) ;
if ( err < 0 ) {
pr_warn ( " Test failed: Unable to initialize hashtable: %d \n " ,
err ) ;
continue ;
}
time = test_rhashtable ( & ht ) ;
rhashtable_destroy ( & ht ) ;
if ( time < 0 ) {
pr_warn ( " Test failed: return code %lld \n " , time ) ;
return - EINVAL ;
}
total_time + = time ;
}
2015-05-05 03:27:02 +03:00
do_div ( total_time , runs ) ;
pr_info ( " Average test time: %llu \n " , total_time ) ;
2015-05-01 01:37:41 +03:00
return 0 ;
2015-01-29 17:40:25 +03:00
}
2015-02-20 02:53:39 +03:00
static void __exit test_rht_exit ( void )
{
}
2015-01-29 17:40:25 +03:00
module_init ( test_rht_init ) ;
2015-02-20 02:53:39 +03:00
module_exit ( test_rht_exit ) ;
2015-01-29 17:40:25 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;