2015-01-29 17:40:25 +03:00
/*
* Resizable , Scalable , Concurrent Hash Table
*
* Copyright ( c ) 2014 Thomas Graf < tgraf @ suug . ch >
* Copyright ( c ) 2008 - 2014 Patrick McHardy < kaber @ trash . net >
*
* Based on the following paper :
* https : //www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf
*
* Code partially derived from nft_hash
*
* 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>
# define TEST_HT_SIZE 8
# define TEST_ENTRIES 2048
# define TEST_PTR ((void *) 0xdeadbeef)
# define TEST_NEXPANDS 4
struct test_obj {
void * ptr ;
int value ;
struct rhash_head node ;
} ;
static int __init test_rht_lookup ( struct rhashtable * ht )
{
unsigned int i ;
for ( i = 0 ; i < TEST_ENTRIES * 2 ; i + + ) {
struct test_obj * obj ;
bool expected = ! ( i % 2 ) ;
u32 key = i ;
obj = rhashtable_lookup ( ht , & key ) ;
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 ) {
if ( obj - > ptr ! = TEST_PTR | | obj - > value ! = i ) {
pr_warn ( " Test failed: Lookup value mismatch %p!=%p, %u!=%u \n " ,
obj - > ptr , TEST_PTR , obj - > value , i ) ;
return - EINVAL ;
}
}
}
return 0 ;
}
static void test_bucket_stats ( struct rhashtable * ht , bool quiet )
{
unsigned int cnt , rcu_cnt , i , total = 0 ;
struct rhash_head * pos ;
struct test_obj * obj ;
struct bucket_table * tbl ;
tbl = rht_dereference_rcu ( ht - > tbl , ht ) ;
for ( i = 0 ; i < tbl - > size ; i + + ) {
rcu_cnt = cnt = 0 ;
if ( ! quiet )
2015-03-14 05:57:24 +03:00
pr_info ( " [%#4x/%u] " , i , tbl - > size ) ;
2015-01-29 17:40:25 +03:00
rht_for_each_entry_rcu ( obj , pos , tbl , i , node ) {
cnt + + ;
total + + ;
if ( ! quiet )
pr_cont ( " [%p], " , obj ) ;
}
rht_for_each_entry_rcu ( obj , pos , tbl , i , node )
rcu_cnt + + ;
if ( rcu_cnt ! = cnt )
pr_warn ( " Test failed: Chain count mismach %d != %d " ,
cnt , rcu_cnt ) ;
if ( ! quiet )
pr_cont ( " \n [%#x] first element: %p, chain length: %u \n " ,
i , tbl - > buckets [ i ] , cnt ) ;
}
pr_info ( " Traversal complete: counted=%u, nelems=%u, entries=%d \n " ,
total , atomic_read ( & ht - > nelems ) , TEST_ENTRIES ) ;
if ( total ! = atomic_read ( & ht - > nelems ) | | total ! = TEST_ENTRIES )
pr_warn ( " Test failed: Total count mismatch ^^^ " ) ;
}
static int __init test_rhashtable ( struct rhashtable * ht )
{
struct bucket_table * tbl ;
struct test_obj * obj ;
struct rhash_head * pos , * next ;
int err ;
unsigned int i ;
/*
* Insertion Test :
* Insert TEST_ENTRIES into table with all keys even numbers
*/
pr_info ( " Adding %d keys \n " , TEST_ENTRIES ) ;
for ( i = 0 ; i < TEST_ENTRIES ; i + + ) {
struct test_obj * obj ;
obj = kzalloc ( sizeof ( * obj ) , GFP_KERNEL ) ;
if ( ! obj ) {
err = - ENOMEM ;
goto error ;
}
obj - > ptr = TEST_PTR ;
obj - > value = i * 2 ;
rhashtable_insert ( ht , & obj - > node ) ;
}
rcu_read_lock ( ) ;
test_bucket_stats ( ht , true ) ;
test_rht_lookup ( ht ) ;
rcu_read_unlock ( ) ;
for ( i = 0 ; i < TEST_NEXPANDS ; i + + ) {
pr_info ( " Table expansion iteration %u... \n " , i ) ;
mutex_lock ( & ht - > mutex ) ;
rhashtable_expand ( ht ) ;
mutex_unlock ( & ht - > mutex ) ;
rcu_read_lock ( ) ;
pr_info ( " Verifying lookups... \n " ) ;
test_rht_lookup ( ht ) ;
rcu_read_unlock ( ) ;
}
for ( i = 0 ; i < TEST_NEXPANDS ; i + + ) {
pr_info ( " Table shrinkage iteration %u... \n " , i ) ;
mutex_lock ( & ht - > mutex ) ;
rhashtable_shrink ( ht ) ;
mutex_unlock ( & ht - > mutex ) ;
rcu_read_lock ( ) ;
pr_info ( " Verifying lookups... \n " ) ;
test_rht_lookup ( ht ) ;
rcu_read_unlock ( ) ;
}
rcu_read_lock ( ) ;
test_bucket_stats ( ht , true ) ;
rcu_read_unlock ( ) ;
pr_info ( " Deleting %d keys \n " , TEST_ENTRIES ) ;
for ( i = 0 ; i < TEST_ENTRIES ; i + + ) {
u32 key = i * 2 ;
obj = rhashtable_lookup ( ht , & key ) ;
BUG_ON ( ! obj ) ;
rhashtable_remove ( ht , & obj - > node ) ;
kfree ( obj ) ;
}
return 0 ;
error :
tbl = rht_dereference_rcu ( ht - > tbl , ht ) ;
for ( i = 0 ; i < tbl - > size ; i + + )
rht_for_each_entry_safe ( obj , pos , next , tbl , i , node )
kfree ( obj ) ;
return err ;
}
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 )
{
struct rhashtable_params params = {
. nelem_hint = TEST_HT_SIZE ,
. head_offset = offsetof ( struct test_obj , node ) ,
. key_offset = offsetof ( struct test_obj , value ) ,
. key_len = sizeof ( int ) ,
. hashfn = jhash ,
2015-03-18 12:01:19 +03:00
. max_size = 2 , /* we expand/shrink manually here */
2015-01-29 17:40:25 +03:00
. nulls_base = ( 3U < < RHT_BASE_SHIFT ) ,
} ;
int err ;
pr_info ( " Running resizable hashtable tests... \n " ) ;
err = rhashtable_init ( & ht , & params ) ;
if ( err < 0 ) {
pr_warn ( " Test failed: Unable to initialize hashtable: %d \n " ,
err ) ;
return err ;
}
err = test_rhashtable ( & ht ) ;
rhashtable_destroy ( & ht ) ;
return err ;
}
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 " ) ;