@ -28,6 +28,9 @@
# define HASH_MIN_SIZE 4UL
# define BUCKET_LOCKS_PER_CPU 128UL
/* Base bits plus 1 bit for nulls marker */
# define HASH_RESERVED_SPACE (RHT_BASE_BITS + 1)
enum {
RHT_LOCK_NORMAL ,
RHT_LOCK_NESTED ,
@ -86,7 +89,7 @@ static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr)
hash = ht - > p . hashfn ( ptr + ht - > p . key_offset , ht - > p . key_len ,
ht - > p . hash_rnd ) ;
return hash ;
return hash > > HASH_RESERVED_SPACE ;
}
static u32 key_hashfn ( struct rhashtable * ht , const void * key , u32 len )
@ -95,6 +98,7 @@ static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len)
u32 hash ;
hash = ht - > p . hashfn ( key , len , ht - > p . hash_rnd ) ;
hash > > = HASH_RESERVED_SPACE ;
return rht_bucket_index ( tbl , hash ) ;
}
@ -111,7 +115,7 @@ static struct rhash_head __rcu **bucket_tail(struct bucket_table *tbl, u32 n)
struct rhash_head __rcu * * pprev ;
for ( pprev = & tbl - > buckets [ n ] ;
rht_dereference_bucket ( * pprev , tbl , n ) ;
! rht_is_a_nulls ( rht_dereference_bucket ( * pprev , tbl , n ) ) ;
pprev = & rht_dereference_bucket ( * pprev , tbl , n ) - > next )
;
@ -164,6 +168,7 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
{
struct bucket_table * tbl ;
size_t size ;
int i ;
size = sizeof ( * tbl ) + nbuckets * sizeof ( tbl - > buckets [ 0 ] ) ;
tbl = kzalloc ( size , GFP_KERNEL | __GFP_NOWARN ) ;
@ -180,6 +185,9 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
return NULL ;
}
for ( i = 0 ; i < nbuckets ; i + + )
INIT_RHT_NULLS_HEAD ( tbl - > buckets [ i ] , ht , i ) ;
return tbl ;
}
@ -221,7 +229,7 @@ static void hashtable_chain_unzip(const struct rhashtable *ht,
/* Old bucket empty, no work needed. */
p = rht_dereference_bucket ( old_tbl - > buckets [ old_hash ] , old_tbl ,
old_hash ) ;
if ( ! p )
if ( rht_is_a_nulls ( p ) )
return ;
new_hash = new_hash2 = head_hashfn ( ht , new_tbl , p ) ;
@ -252,8 +260,8 @@ static void hashtable_chain_unzip(const struct rhashtable *ht,
/* Find the subsequent node which does hash to the same
* bucket as node P , or NULL if no such node exists .
*/
next = NULL ;
if ( he ) {
INIT_RHT_NULLS_HEAD ( next , ht , old_hash ) ;
if ( ! rht_is_a_nulls ( he ) ) {
rht_for_each_continue ( he , he - > next , old_tbl , old_hash ) {
if ( head_hashfn ( ht , new_tbl , he ) = = new_hash ) {
next = he ;
@ -369,11 +377,15 @@ int rhashtable_expand(struct rhashtable *ht)
*/
complete = true ;
for ( old_hash = 0 ; old_hash < old_tbl - > size ; old_hash + + ) {
struct rhash_head * head ;
old_bucket_lock = bucket_lock ( old_tbl , old_hash ) ;
spin_lock_bh ( old_bucket_lock ) ;
hashtable_chain_unzip ( ht , new_tbl , old_tbl , old_hash ) ;
if ( old_tbl - > buckets [ old_hash ] ! = NULL )
head = rht_dereference_bucket ( old_tbl - > buckets [ old_hash ] ,
old_tbl , old_hash ) ;
if ( ! rht_is_a_nulls ( head ) )
complete = false ;
spin_unlock_bh ( old_bucket_lock ) ;
@ -498,6 +510,7 @@ static void rht_deferred_worker(struct work_struct *work)
void rhashtable_insert ( struct rhashtable * ht , struct rhash_head * obj )
{
struct bucket_table * tbl ;
struct rhash_head * head ;
spinlock_t * lock ;
unsigned hash ;
@ -508,7 +521,12 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj)
lock = bucket_lock ( tbl , hash ) ;
spin_lock_bh ( lock ) ;
RCU_INIT_POINTER ( obj - > next , tbl - > buckets [ hash ] ) ;
head = rht_dereference_bucket ( tbl - > buckets [ hash ] , tbl , hash ) ;
if ( rht_is_a_nulls ( head ) )
INIT_RHT_NULLS_HEAD ( obj - > next , ht , hash ) ;
else
RCU_INIT_POINTER ( obj - > next , head ) ;
rcu_assign_pointer ( tbl - > buckets [ hash ] , obj ) ;
spin_unlock_bh ( lock ) ;
@ -709,6 +727,7 @@ static size_t rounded_hashtable_size(struct rhashtable_params *params)
* . key_offset = offsetof ( struct test_obj , key ) ,
* . key_len = sizeof ( int ) ,
* . hashfn = jhash ,
* . nulls_base = ( 1U < < RHT_BASE_SHIFT ) ,
* } ;
*
* Configuration Example 2 : Variable length keys
@ -741,6 +760,9 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params)
( ! params - > key_len & & ! params - > obj_hashfn ) )
return - EINVAL ;
if ( params - > nulls_base & & params - > nulls_base < ( 1U < < RHT_BASE_SHIFT ) )
return - EINVAL ;
params - > min_shift = max_t ( size_t , params - > min_shift ,
ilog2 ( HASH_MIN_SIZE ) ) ;
@ -974,6 +996,7 @@ static int __init test_rht_init(void)
. key_offset = offsetof ( struct test_obj , value ) ,
. key_len = sizeof ( int ) ,
. hashfn = jhash ,
. nulls_base = ( 3U < < RHT_BASE_SHIFT ) ,
. grow_decision = rht_grow_above_75 ,
. shrink_decision = rht_shrink_below_30 ,
} ;