![Ying Xue](/assets/img/avatar_default.png)
Although rhashtable library allows user to specify a quiet big size for user's created hash table, the table may be shrunk to a very small size - HASH_MIN_SIZE(4) after object is removed from the table at the first time. Subsequently, even if the total amount of objects saved in the table is quite lower than user's initial setting in a long time, the hash table size is still dynamically adjusted by rhashtable_shrink() or rhashtable_expand() each time object is inserted or removed from the table. However, as synchronize_rcu() has to be called when table is shrunk or expanded by the two functions, we should permit user to set the minimum table size through configuring the minimum number of shifts according to user specific requirement, avoiding these expensive actions of shrinking or expanding because of calling synchronize_rcu(). Signed-off-by: Ying Xue <ying.xue@windriver.com> Acked-by: Thomas Graf <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
211 lines
6.9 KiB
C
211 lines
6.9 KiB
C
/*
|
|
* 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 by Josh Triplett, Paul E. McKenney
|
|
* and Jonathan Walpole:
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef _LINUX_RHASHTABLE_H
|
|
#define _LINUX_RHASHTABLE_H
|
|
|
|
#include <linux/rculist.h>
|
|
|
|
struct rhash_head {
|
|
struct rhash_head __rcu *next;
|
|
};
|
|
|
|
#define INIT_HASH_HEAD(ptr) ((ptr)->next = NULL)
|
|
|
|
struct bucket_table {
|
|
size_t size;
|
|
struct rhash_head __rcu *buckets[];
|
|
};
|
|
|
|
typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
|
|
typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed);
|
|
|
|
struct rhashtable;
|
|
|
|
/**
|
|
* struct rhashtable_params - Hash table construction parameters
|
|
* @nelem_hint: Hint on number of elements, should be 75% of desired size
|
|
* @key_len: Length of key
|
|
* @key_offset: Offset of key in struct to be hashed
|
|
* @head_offset: Offset of rhash_head in struct to be hashed
|
|
* @hash_rnd: Seed to use while hashing
|
|
* @max_shift: Maximum number of shifts while expanding
|
|
* @min_shift: Minimum number of shifts while shrinking
|
|
* @hashfn: Function to hash key
|
|
* @obj_hashfn: Function to hash object
|
|
* @grow_decision: If defined, may return true if table should expand
|
|
* @shrink_decision: If defined, may return true if table should shrink
|
|
* @mutex_is_held: Must return true if protecting mutex is held
|
|
*/
|
|
struct rhashtable_params {
|
|
size_t nelem_hint;
|
|
size_t key_len;
|
|
size_t key_offset;
|
|
size_t head_offset;
|
|
u32 hash_rnd;
|
|
size_t max_shift;
|
|
size_t min_shift;
|
|
rht_hashfn_t hashfn;
|
|
rht_obj_hashfn_t obj_hashfn;
|
|
bool (*grow_decision)(const struct rhashtable *ht,
|
|
size_t new_size);
|
|
bool (*shrink_decision)(const struct rhashtable *ht,
|
|
size_t new_size);
|
|
int (*mutex_is_held)(void);
|
|
};
|
|
|
|
/**
|
|
* struct rhashtable - Hash table handle
|
|
* @tbl: Bucket table
|
|
* @nelems: Number of elements in table
|
|
* @shift: Current size (1 << shift)
|
|
* @p: Configuration parameters
|
|
*/
|
|
struct rhashtable {
|
|
struct bucket_table __rcu *tbl;
|
|
size_t nelems;
|
|
size_t shift;
|
|
struct rhashtable_params p;
|
|
};
|
|
|
|
#ifdef CONFIG_PROVE_LOCKING
|
|
int lockdep_rht_mutex_is_held(const struct rhashtable *ht);
|
|
#else
|
|
static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht)
|
|
{
|
|
return 1;
|
|
}
|
|
#endif /* CONFIG_PROVE_LOCKING */
|
|
|
|
int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params);
|
|
|
|
u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len);
|
|
u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr);
|
|
|
|
void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node, gfp_t);
|
|
bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node, gfp_t);
|
|
void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj,
|
|
struct rhash_head __rcu **pprev, gfp_t flags);
|
|
|
|
bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size);
|
|
bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size);
|
|
|
|
int rhashtable_expand(struct rhashtable *ht, gfp_t flags);
|
|
int rhashtable_shrink(struct rhashtable *ht, gfp_t flags);
|
|
|
|
void *rhashtable_lookup(const struct rhashtable *ht, const void *key);
|
|
void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash,
|
|
bool (*compare)(void *, void *), void *arg);
|
|
|
|
void rhashtable_destroy(const struct rhashtable *ht);
|
|
|
|
#define rht_dereference(p, ht) \
|
|
rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht))
|
|
|
|
#define rht_dereference_rcu(p, ht) \
|
|
rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht))
|
|
|
|
#define rht_entry(ptr, type, member) container_of(ptr, type, member)
|
|
#define rht_entry_safe(ptr, type, member) \
|
|
({ \
|
|
typeof(ptr) __ptr = (ptr); \
|
|
__ptr ? rht_entry(__ptr, type, member) : NULL; \
|
|
})
|
|
|
|
#define rht_next_entry_safe(pos, ht, member) \
|
|
({ \
|
|
pos ? rht_entry_safe(rht_dereference((pos)->member.next, ht), \
|
|
typeof(*(pos)), member) : NULL; \
|
|
})
|
|
|
|
/**
|
|
* rht_for_each - iterate over hash chain
|
|
* @pos: &struct rhash_head to use as a loop cursor.
|
|
* @head: head of the hash chain (struct rhash_head *)
|
|
* @ht: pointer to your struct rhashtable
|
|
*/
|
|
#define rht_for_each(pos, head, ht) \
|
|
for (pos = rht_dereference(head, ht); \
|
|
pos; \
|
|
pos = rht_dereference((pos)->next, ht))
|
|
|
|
/**
|
|
* rht_for_each_entry - iterate over hash chain of given type
|
|
* @pos: type * to use as a loop cursor.
|
|
* @head: head of the hash chain (struct rhash_head *)
|
|
* @ht: pointer to your struct rhashtable
|
|
* @member: name of the rhash_head within the hashable struct.
|
|
*/
|
|
#define rht_for_each_entry(pos, head, ht, member) \
|
|
for (pos = rht_entry_safe(rht_dereference(head, ht), \
|
|
typeof(*(pos)), member); \
|
|
pos; \
|
|
pos = rht_next_entry_safe(pos, ht, member))
|
|
|
|
/**
|
|
* rht_for_each_entry_safe - safely iterate over hash chain of given type
|
|
* @pos: type * to use as a loop cursor.
|
|
* @n: type * to use for temporary next object storage
|
|
* @head: head of the hash chain (struct rhash_head *)
|
|
* @ht: pointer to your struct rhashtable
|
|
* @member: name of the rhash_head within the hashable struct.
|
|
*
|
|
* This hash chain list-traversal primitive allows for the looped code to
|
|
* remove the loop cursor from the list.
|
|
*/
|
|
#define rht_for_each_entry_safe(pos, n, head, ht, member) \
|
|
for (pos = rht_entry_safe(rht_dereference(head, ht), \
|
|
typeof(*(pos)), member), \
|
|
n = rht_next_entry_safe(pos, ht, member); \
|
|
pos; \
|
|
pos = n, \
|
|
n = rht_next_entry_safe(pos, ht, member))
|
|
|
|
/**
|
|
* rht_for_each_rcu - iterate over rcu hash chain
|
|
* @pos: &struct rhash_head to use as a loop cursor.
|
|
* @head: head of the hash chain (struct rhash_head *)
|
|
* @ht: pointer to your struct rhashtable
|
|
*
|
|
* This hash chain list-traversal primitive may safely run concurrently with
|
|
* the _rcu fkht mutation primitives such as rht_insert() as long as the
|
|
* traversal is guarded by rcu_read_lock().
|
|
*/
|
|
#define rht_for_each_rcu(pos, head, ht) \
|
|
for (pos = rht_dereference_rcu(head, ht); \
|
|
pos; \
|
|
pos = rht_dereference_rcu((pos)->next, ht))
|
|
|
|
/**
|
|
* rht_for_each_entry_rcu - iterate over rcu hash chain of given type
|
|
* @pos: type * to use as a loop cursor.
|
|
* @head: head of the hash chain (struct rhash_head *)
|
|
* @member: name of the rhash_head within the hashable struct.
|
|
*
|
|
* This hash chain list-traversal primitive may safely run concurrently with
|
|
* the _rcu fkht mutation primitives such as rht_insert() as long as the
|
|
* traversal is guarded by rcu_read_lock().
|
|
*/
|
|
#define rht_for_each_entry_rcu(pos, head, member) \
|
|
for (pos = rht_entry_safe(rcu_dereference_raw(head), \
|
|
typeof(*(pos)), member); \
|
|
pos; \
|
|
pos = rht_entry_safe(rcu_dereference_raw((pos)->member.next), \
|
|
typeof(*(pos)), member))
|
|
|
|
#endif /* _LINUX_RHASHTABLE_H */
|