diff options
Diffstat (limited to 'net/batman-adv/hash.h')
| -rw-r--r-- | net/batman-adv/hash.h | 213 |
1 files changed, 112 insertions, 101 deletions
diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index 09216ade16f..539fc126679 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -1,5 +1,4 @@ -/* - * Copyright (C) 2006-2010 B.A.T.M.A.N. contributors: +/* Copyright (C) 2006-2014 B.A.T.M.A.N. contributors: * * Simon Wunderlich, Marek Lindner * @@ -13,10 +12,7 @@ * General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - * + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _NET_BATMAN_ADV_HASH_H_ @@ -24,153 +20,168 @@ #include <linux/list.h> -/* callback to a compare function. should - * compare 2 element datas for their keys, - * return 0 if same and not 0 if not - * same */ -typedef int (*hashdata_compare_cb)(void *, void *); +/* callback to a compare function. should compare 2 element datas for their + * keys, return 0 if same and not 0 if not same + */ +typedef int (*batadv_hashdata_compare_cb)(const struct hlist_node *, + const void *); /* the hashfunction, should return an index * based on the key in the data of the first - * argument and the size the second */ -typedef int (*hashdata_choose_cb)(void *, int); -typedef void (*hashdata_free_cb)(void *, void *); - -struct element_t { - void *data; /* pointer to the data */ - struct hlist_node hlist; /* bucket list pointer */ -}; + * argument and the size the second + */ +typedef uint32_t (*batadv_hashdata_choose_cb)(const void *, uint32_t); +typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *); -struct hashtable_t { - struct hlist_head *table; /* the hashtable itself, with the buckets */ - int size; /* size of hashtable */ +struct batadv_hashtable { + struct hlist_head *table; /* the hashtable itself with the buckets */ + spinlock_t *list_locks; /* spinlock for each hash list entry */ + uint32_t size; /* size of hashtable */ }; /* allocates and clears the hash */ -struct hashtable_t *hash_new(int size); +struct batadv_hashtable *batadv_hash_new(uint32_t size); -/* remove element if you already found the element you want to delete and don't - * need the overhead to find it again with hash_remove(). But usually, you - * don't want to use this function, as it fiddles with hash-internals. */ -void *hash_remove_element(struct hashtable_t *hash, struct element_t *elem); +/* set class key for all locks */ +void batadv_hash_set_lock_class(struct batadv_hashtable *hash, + struct lock_class_key *key); /* free only the hashtable and the hash itself. */ -void hash_destroy(struct hashtable_t *hash); +void batadv_hash_destroy(struct batadv_hashtable *hash); /* remove the hash structure. if hashdata_free_cb != NULL, this function will be * called to remove the elements inside of the hash. if you don't remove the - * elements, memory might be leaked. */ -static inline void hash_delete(struct hashtable_t *hash, - hashdata_free_cb free_cb, void *arg) + * elements, memory might be leaked. + */ +static inline void batadv_hash_delete(struct batadv_hashtable *hash, + batadv_hashdata_free_cb free_cb, + void *arg) { struct hlist_head *head; - struct hlist_node *walk, *safe; - struct element_t *bucket; - int i; + struct hlist_node *node, *node_tmp; + spinlock_t *list_lock; /* spinlock to protect write access */ + uint32_t i; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; + list_lock = &hash->list_locks[i]; - hlist_for_each_safe(walk, safe, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (free_cb) - free_cb(bucket->data, arg); + spin_lock_bh(list_lock); + hlist_for_each_safe(node, node_tmp, head) { + hlist_del_rcu(node); - hlist_del(walk); - kfree(bucket); + if (free_cb) + free_cb(node, arg); } + spin_unlock_bh(list_lock); } - hash_destroy(hash); + batadv_hash_destroy(hash); +} + +/** + * batadv_hash_bytes - hash some bytes and add them to the previous hash + * @hash: previous hash value + * @data: data to be hashed + * @size: number of bytes to be hashed + * + * Returns the new hash value. + */ +static inline uint32_t batadv_hash_bytes(uint32_t hash, const void *data, + uint32_t size) +{ + const unsigned char *key = data; + int i; + + for (i = 0; i < size; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + return hash; } -/* adds data to the hashtable. returns 0 on success, -1 on error */ -static inline int hash_add(struct hashtable_t *hash, - hashdata_compare_cb compare, - hashdata_choose_cb choose, void *data) +/** + * batadv_hash_add - adds data to the hashtable + * @hash: storage hash table + * @compare: callback to determine if 2 hash elements are identical + * @choose: callback calculating the hash index + * @data: data passed to the aforementioned callbacks as argument + * @data_node: to be added element + * + * Returns 0 on success, 1 if the element already is in the hash + * and -1 on error. + */ +static inline int batadv_hash_add(struct batadv_hashtable *hash, + batadv_hashdata_compare_cb compare, + batadv_hashdata_choose_cb choose, + const void *data, + struct hlist_node *data_node) { - int index; + uint32_t index; + int ret = -1; struct hlist_head *head; - struct hlist_node *walk, *safe; - struct element_t *bucket; + struct hlist_node *node; + spinlock_t *list_lock; /* spinlock to protect write access */ if (!hash) - return -1; + goto out; index = choose(data, hash->size); head = &hash->table[index]; + list_lock = &hash->list_locks[index]; + + spin_lock_bh(list_lock); - hlist_for_each_safe(walk, safe, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (compare(bucket->data, data)) - return -1; + hlist_for_each(node, head) { + if (!compare(node, data)) + continue; + + ret = 1; + goto unlock; } /* no duplicate found in list, add new element */ - bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); - - if (!bucket) - return -1; + hlist_add_head_rcu(data_node, head); - bucket->data = data; - hlist_add_head(&bucket->hlist, head); + ret = 0; - return 0; +unlock: + spin_unlock_bh(list_lock); +out: + return ret; } /* removes data from hash, if found. returns pointer do data on success, so you * can remove the used structure yourself, or NULL on error . data could be the * structure you use with just the key filled, we just need the key for - * comparing. */ -static inline void *hash_remove(struct hashtable_t *hash, - hashdata_compare_cb compare, - hashdata_choose_cb choose, void *data) + * comparing. + */ +static inline void *batadv_hash_remove(struct batadv_hashtable *hash, + batadv_hashdata_compare_cb compare, + batadv_hashdata_choose_cb choose, + void *data) { - size_t index; - struct hlist_node *walk; - struct element_t *bucket; + uint32_t index; + struct hlist_node *node; struct hlist_head *head; - void *data_save; + void *data_save = NULL; index = choose(data, hash->size); head = &hash->table[index]; - hlist_for_each_entry(bucket, walk, head, hlist) { - if (compare(bucket->data, data)) { - data_save = bucket->data; - hlist_del(walk); - kfree(bucket); - return data_save; - } - } - - return NULL; -} - -/* finds data, based on the key in keydata. returns the found data on success, - * or NULL on error */ -static inline void *hash_find(struct hashtable_t *hash, - hashdata_compare_cb compare, - hashdata_choose_cb choose, void *keydata) -{ - int index; - struct hlist_head *head; - struct hlist_node *walk; - struct element_t *bucket; - - if (!hash) - return NULL; - - index = choose(keydata , hash->size); - head = &hash->table[index]; + spin_lock_bh(&hash->list_locks[index]); + hlist_for_each(node, head) { + if (!compare(node, data)) + continue; - hlist_for_each(walk, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (compare(bucket->data, keydata)) - return bucket->data; + data_save = node; + hlist_del_rcu(node); + break; } + spin_unlock_bh(&hash->list_locks[index]); - return NULL; + return data_save; } #endif /* _NET_BATMAN_ADV_HASH_H_ */ |
