xref: /openbmc/linux/net/batman-adv/hash.h (revision 791ad7f5)
17db7d9f3SSven Eckelmann /* SPDX-License-Identifier: GPL-2.0 */
2cfa55c6dSSven Eckelmann /* Copyright (C) B.A.T.M.A.N. contributors:
3c6c8fea2SSven Eckelmann  *
4c6c8fea2SSven Eckelmann  * Simon Wunderlich, Marek Lindner
5c6c8fea2SSven Eckelmann  */
6c6c8fea2SSven Eckelmann 
7c6c8fea2SSven Eckelmann #ifndef _NET_BATMAN_ADV_HASH_H_
8c6c8fea2SSven Eckelmann #define _NET_BATMAN_ADV_HASH_H_
9c6c8fea2SSven Eckelmann 
101e2c2a4fSSven Eckelmann #include "main.h"
111e2c2a4fSSven Eckelmann 
1205abd7bcSSven Eckelmann #include <linux/atomic.h>
131e2c2a4fSSven Eckelmann #include <linux/compiler.h>
14c6c8fea2SSven Eckelmann #include <linux/list.h>
1568a600deSSven Eckelmann #include <linux/lockdep.h>
161e2c2a4fSSven Eckelmann #include <linux/rculist.h>
171e2c2a4fSSven Eckelmann #include <linux/spinlock.h>
181e2c2a4fSSven Eckelmann #include <linux/stddef.h>
191e2c2a4fSSven Eckelmann #include <linux/types.h>
201e2c2a4fSSven Eckelmann 
21*791ad7f5SZheng Yongjun /* callback to a compare function.  should compare 2 element data for their
2262fe710fSSven Eckelmann  * keys
2362fe710fSSven Eckelmann  *
244b426b10SSven Eckelmann  * Return: true if same and false if not same
259cfc7bd6SSven Eckelmann  */
264b426b10SSven Eckelmann typedef bool (*batadv_hashdata_compare_cb)(const struct hlist_node *,
275bf74e9cSSven Eckelmann 					   const void *);
28c6c8fea2SSven Eckelmann 
2962fe710fSSven Eckelmann /* the hashfunction
3062fe710fSSven Eckelmann  *
3162fe710fSSven Eckelmann  * Return: an index based on the key in the data of the first argument and the
3262fe710fSSven Eckelmann  * size the second
339cfc7bd6SSven Eckelmann  */
346b5e971aSSven Eckelmann typedef u32 (*batadv_hashdata_choose_cb)(const void *, u32);
355bf74e9cSSven Eckelmann typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);
36c6c8fea2SSven Eckelmann 
37c93effcfSSven Eckelmann /**
38c93effcfSSven Eckelmann  * struct batadv_hashtable - Wrapper of simple hlist based hashtable
39c93effcfSSven Eckelmann  */
405bf74e9cSSven Eckelmann struct batadv_hashtable {
41c93effcfSSven Eckelmann 	/** @table: the hashtable itself with the buckets */
42c93effcfSSven Eckelmann 	struct hlist_head *table;
43c93effcfSSven Eckelmann 
44c93effcfSSven Eckelmann 	/** @list_locks: spinlock for each hash list entry */
45c93effcfSSven Eckelmann 	spinlock_t *list_locks;
46c93effcfSSven Eckelmann 
47c93effcfSSven Eckelmann 	/** @size: size of hashtable */
48c93effcfSSven Eckelmann 	u32 size;
4905abd7bcSSven Eckelmann 
5005abd7bcSSven Eckelmann 	/** @generation: current (generation) sequence number */
5105abd7bcSSven Eckelmann 	atomic_t generation;
52c6c8fea2SSven Eckelmann };
53c6c8fea2SSven Eckelmann 
54c6c8fea2SSven Eckelmann /* allocates and clears the hash */
556b5e971aSSven Eckelmann struct batadv_hashtable *batadv_hash_new(u32 size);
56c6c8fea2SSven Eckelmann 
575d52dad2SSven Eckelmann /* set class key for all locks */
585bf74e9cSSven Eckelmann void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
595d52dad2SSven Eckelmann 				struct lock_class_key *key);
605d52dad2SSven Eckelmann 
61c6c8fea2SSven Eckelmann /* free only the hashtable and the hash itself. */
625bf74e9cSSven Eckelmann void batadv_hash_destroy(struct batadv_hashtable *hash);
63c6c8fea2SSven Eckelmann 
642c53040fSBen Hutchings /**
657e9a8c2cSSven Eckelmann  *	batadv_hash_add() - adds data to the hashtable
661a1f37d9SAntonio Quartulli  *	@hash: storage hash table
671a1f37d9SAntonio Quartulli  *	@compare: callback to determine if 2 hash elements are identical
681a1f37d9SAntonio Quartulli  *	@choose: callback calculating the hash index
691a1f37d9SAntonio Quartulli  *	@data: data passed to the aforementioned callbacks as argument
701a1f37d9SAntonio Quartulli  *	@data_node: to be added element
711a1f37d9SAntonio Quartulli  *
7262fe710fSSven Eckelmann  *	Return: 0 on success, 1 if the element already is in the hash
731a1f37d9SAntonio Quartulli  *	and -1 on error.
741a1f37d9SAntonio Quartulli  */
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)755bf74e9cSSven Eckelmann static inline int batadv_hash_add(struct batadv_hashtable *hash,
765bf74e9cSSven Eckelmann 				  batadv_hashdata_compare_cb compare,
775bf74e9cSSven Eckelmann 				  batadv_hashdata_choose_cb choose,
78c0a55929SSven Eckelmann 				  const void *data,
79c0a55929SSven Eckelmann 				  struct hlist_node *data_node)
80c6c8fea2SSven Eckelmann {
816b5e971aSSven Eckelmann 	u32 index;
82c90681b8SAntonio Quartulli 	int ret = -1;
83c6c8fea2SSven Eckelmann 	struct hlist_head *head;
847aadf889SMarek Lindner 	struct hlist_node *node;
85fb778ea1SMarek Lindner 	spinlock_t *list_lock; /* spinlock to protect write access */
86c6c8fea2SSven Eckelmann 
87c6c8fea2SSven Eckelmann 	if (!hash)
881a1f37d9SAntonio Quartulli 		goto out;
89c6c8fea2SSven Eckelmann 
90c6c8fea2SSven Eckelmann 	index = choose(data, hash->size);
91c6c8fea2SSven Eckelmann 	head = &hash->table[index];
92fb778ea1SMarek Lindner 	list_lock = &hash->list_locks[index];
93c6c8fea2SSven Eckelmann 
9475c5a2e7SMatthias Schiffer 	spin_lock_bh(list_lock);
9575c5a2e7SMatthias Schiffer 
9675c5a2e7SMatthias Schiffer 	hlist_for_each(node, head) {
977aadf889SMarek Lindner 		if (!compare(node, data))
987aadf889SMarek Lindner 			continue;
997aadf889SMarek Lindner 
1001a1f37d9SAntonio Quartulli 		ret = 1;
10175c5a2e7SMatthias Schiffer 		goto unlock;
102c6c8fea2SSven Eckelmann 	}
103c6c8fea2SSven Eckelmann 
104c6c8fea2SSven Eckelmann 	/* no duplicate found in list, add new element */
1057aadf889SMarek Lindner 	hlist_add_head_rcu(data_node, head);
10605abd7bcSSven Eckelmann 	atomic_inc(&hash->generation);
107c6c8fea2SSven Eckelmann 
1081a1f37d9SAntonio Quartulli 	ret = 0;
109fb778ea1SMarek Lindner 
11075c5a2e7SMatthias Schiffer unlock:
11175c5a2e7SMatthias Schiffer 	spin_unlock_bh(list_lock);
1121a1f37d9SAntonio Quartulli out:
1131a1f37d9SAntonio Quartulli 	return ret;
114c6c8fea2SSven Eckelmann }
115c6c8fea2SSven Eckelmann 
116e57acf8eSSven Eckelmann /**
117e57acf8eSSven Eckelmann  * batadv_hash_remove() - Removes data from hash, if found
118e57acf8eSSven Eckelmann  * @hash: hash table
119e57acf8eSSven Eckelmann  * @compare: callback to determine if 2 hash elements are identical
120e57acf8eSSven Eckelmann  * @choose: callback calculating the hash index
121e57acf8eSSven Eckelmann  * @data: data passed to the aforementioned callbacks as argument
122e57acf8eSSven Eckelmann  *
123e57acf8eSSven Eckelmann  * ata could be the structure you use with  just the key filled, we just need
124e57acf8eSSven Eckelmann  * the key for comparing.
12562fe710fSSven Eckelmann  *
12662fe710fSSven Eckelmann  * Return: returns pointer do data on success, so you can remove the used
12762fe710fSSven Eckelmann  * structure yourself, or NULL on error
1289cfc7bd6SSven Eckelmann  */
batadv_hash_remove(struct batadv_hashtable * hash,batadv_hashdata_compare_cb compare,batadv_hashdata_choose_cb choose,void * data)1295bf74e9cSSven Eckelmann static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
1305bf74e9cSSven Eckelmann 				       batadv_hashdata_compare_cb compare,
1315bf74e9cSSven Eckelmann 				       batadv_hashdata_choose_cb choose,
1325bf74e9cSSven Eckelmann 				       void *data)
133c6c8fea2SSven Eckelmann {
1346b5e971aSSven Eckelmann 	u32 index;
1357aadf889SMarek Lindner 	struct hlist_node *node;
136c6c8fea2SSven Eckelmann 	struct hlist_head *head;
137fb778ea1SMarek Lindner 	void *data_save = NULL;
138c6c8fea2SSven Eckelmann 
139c6c8fea2SSven Eckelmann 	index = choose(data, hash->size);
140c6c8fea2SSven Eckelmann 	head = &hash->table[index];
141c6c8fea2SSven Eckelmann 
142fb778ea1SMarek Lindner 	spin_lock_bh(&hash->list_locks[index]);
1437aadf889SMarek Lindner 	hlist_for_each(node, head) {
1447aadf889SMarek Lindner 		if (!compare(node, data))
1457aadf889SMarek Lindner 			continue;
1467aadf889SMarek Lindner 
1477aadf889SMarek Lindner 		data_save = node;
1487aadf889SMarek Lindner 		hlist_del_rcu(node);
14905abd7bcSSven Eckelmann 		atomic_inc(&hash->generation);
150fb778ea1SMarek Lindner 		break;
151fb778ea1SMarek Lindner 	}
152fb778ea1SMarek Lindner 	spin_unlock_bh(&hash->list_locks[index]);
153fb778ea1SMarek Lindner 
154c6c8fea2SSven Eckelmann 	return data_save;
155c6c8fea2SSven Eckelmann }
156c6c8fea2SSven Eckelmann 
157c6c8fea2SSven Eckelmann #endif /* _NET_BATMAN_ADV_HASH_H_ */
158