blob: 37507b6d40068aa4f5f0fa2ebee3a497a45487ff [file] [log] [blame]
Sven Eckelmann7db7d9f2017-11-19 15:05:11 +01001/* SPDX-License-Identifier: GPL-2.0 */
Sven Eckelmann7a79d712018-12-31 23:59:59 +01002/* Copyright (C) 2006-2019 B.A.T.M.A.N. contributors:
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +00003 *
4 * Simon Wunderlich, Marek Lindner
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
Antonio Quartulliebf38fb2013-11-03 20:40:48 +010016 * along with this program; if not, see <http://www.gnu.org/licenses/>.
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000017 */
18
19#ifndef _NET_BATMAN_ADV_HASH_H_
20#define _NET_BATMAN_ADV_HASH_H_
21
Sven Eckelmann1e2c2a42015-04-17 19:40:28 +020022#include "main.h"
23
Sven Eckelmann05abd7b2018-10-30 22:01:25 +010024#include <linux/atomic.h>
Sven Eckelmann1e2c2a42015-04-17 19:40:28 +020025#include <linux/compiler.h>
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000026#include <linux/list.h>
Sven Eckelmann1e2c2a42015-04-17 19:40:28 +020027#include <linux/rculist.h>
28#include <linux/spinlock.h>
29#include <linux/stddef.h>
30#include <linux/types.h>
31
32struct lock_class_key;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000033
Sven Eckelmann9cfc7bd2012-05-12 02:09:43 +020034/* callback to a compare function. should compare 2 element datas for their
Sven Eckelmann62fe7102015-09-15 19:00:48 +020035 * keys
36 *
Sven Eckelmann4b426b12016-02-22 21:02:39 +010037 * Return: true if same and false if not same
Sven Eckelmann9cfc7bd2012-05-12 02:09:43 +020038 */
Sven Eckelmann4b426b12016-02-22 21:02:39 +010039typedef bool (*batadv_hashdata_compare_cb)(const struct hlist_node *,
40 const void *);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000041
Sven Eckelmann62fe7102015-09-15 19:00:48 +020042/* the hashfunction
43 *
44 * Return: an index based on the key in the data of the first argument and the
45 * size the second
Sven Eckelmann9cfc7bd2012-05-12 02:09:43 +020046 */
Sven Eckelmann6b5e9712015-05-26 18:34:26 +020047typedef u32 (*batadv_hashdata_choose_cb)(const void *, u32);
Sven Eckelmann5bf74e92012-06-05 22:31:28 +020048typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000049
Sven Eckelmannc93effc2017-12-02 19:51:50 +010050/**
51 * struct batadv_hashtable - Wrapper of simple hlist based hashtable
52 */
Sven Eckelmann5bf74e92012-06-05 22:31:28 +020053struct batadv_hashtable {
Sven Eckelmannc93effc2017-12-02 19:51:50 +010054 /** @table: the hashtable itself with the buckets */
55 struct hlist_head *table;
56
57 /** @list_locks: spinlock for each hash list entry */
58 spinlock_t *list_locks;
59
60 /** @size: size of hashtable */
61 u32 size;
Sven Eckelmann05abd7b2018-10-30 22:01:25 +010062
63 /** @generation: current (generation) sequence number */
64 atomic_t generation;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000065};
66
67/* allocates and clears the hash */
Sven Eckelmann6b5e9712015-05-26 18:34:26 +020068struct batadv_hashtable *batadv_hash_new(u32 size);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000069
Sven Eckelmann5d52dad2012-03-29 12:38:20 +020070/* set class key for all locks */
Sven Eckelmann5bf74e92012-06-05 22:31:28 +020071void batadv_hash_set_lock_class(struct batadv_hashtable *hash,
Sven Eckelmann5d52dad2012-03-29 12:38:20 +020072 struct lock_class_key *key);
73
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000074/* free only the hashtable and the hash itself. */
Sven Eckelmann5bf74e92012-06-05 22:31:28 +020075void batadv_hash_destroy(struct batadv_hashtable *hash);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000076
Ben Hutchings2c530402012-07-10 10:55:09 +000077/**
Sven Eckelmann7e9a8c22017-12-02 19:51:47 +010078 * batadv_hash_add() - adds data to the hashtable
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +020079 * @hash: storage hash table
80 * @compare: callback to determine if 2 hash elements are identical
81 * @choose: callback calculating the hash index
82 * @data: data passed to the aforementioned callbacks as argument
83 * @data_node: to be added element
84 *
Sven Eckelmann62fe7102015-09-15 19:00:48 +020085 * Return: 0 on success, 1 if the element already is in the hash
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +020086 * and -1 on error.
87 */
Sven Eckelmann5bf74e92012-06-05 22:31:28 +020088static inline int batadv_hash_add(struct batadv_hashtable *hash,
89 batadv_hashdata_compare_cb compare,
90 batadv_hashdata_choose_cb choose,
Sven Eckelmannc0a55922012-05-12 13:48:55 +020091 const void *data,
92 struct hlist_node *data_node)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000093{
Sven Eckelmann6b5e9712015-05-26 18:34:26 +020094 u32 index;
Antonio Quartullic90681b2011-10-05 17:05:25 +020095 int ret = -1;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000096 struct hlist_head *head;
Marek Lindner7aadf882011-02-18 12:28:09 +000097 struct hlist_node *node;
Marek Lindnerfb778ea2011-01-19 20:01:40 +000098 spinlock_t *list_lock; /* spinlock to protect write access */
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +000099
100 if (!hash)
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +0200101 goto out;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000102
103 index = choose(data, hash->size);
104 head = &hash->table[index];
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000105 list_lock = &hash->list_locks[index];
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000106
Matthias Schiffer75c5a2e2012-05-08 22:31:57 +0200107 spin_lock_bh(list_lock);
108
109 hlist_for_each(node, head) {
Marek Lindner7aadf882011-02-18 12:28:09 +0000110 if (!compare(node, data))
111 continue;
112
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +0200113 ret = 1;
Matthias Schiffer75c5a2e2012-05-08 22:31:57 +0200114 goto unlock;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000115 }
116
117 /* no duplicate found in list, add new element */
Marek Lindner7aadf882011-02-18 12:28:09 +0000118 hlist_add_head_rcu(data_node, head);
Sven Eckelmann05abd7b2018-10-30 22:01:25 +0100119 atomic_inc(&hash->generation);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000120
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +0200121 ret = 0;
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000122
Matthias Schiffer75c5a2e2012-05-08 22:31:57 +0200123unlock:
124 spin_unlock_bh(list_lock);
Antonio Quartulli1a1f37d2011-07-10 00:36:36 +0200125out:
126 return ret;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000127}
128
Sven Eckelmanne57acf82017-12-02 19:51:52 +0100129/**
130 * batadv_hash_remove() - Removes data from hash, if found
131 * @hash: hash table
132 * @compare: callback to determine if 2 hash elements are identical
133 * @choose: callback calculating the hash index
134 * @data: data passed to the aforementioned callbacks as argument
135 *
136 * ata could be the structure you use with just the key filled, we just need
137 * the key for comparing.
Sven Eckelmann62fe7102015-09-15 19:00:48 +0200138 *
139 * Return: returns pointer do data on success, so you can remove the used
140 * structure yourself, or NULL on error
Sven Eckelmann9cfc7bd2012-05-12 02:09:43 +0200141 */
Sven Eckelmann5bf74e92012-06-05 22:31:28 +0200142static inline void *batadv_hash_remove(struct batadv_hashtable *hash,
143 batadv_hashdata_compare_cb compare,
144 batadv_hashdata_choose_cb choose,
145 void *data)
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000146{
Sven Eckelmann6b5e9712015-05-26 18:34:26 +0200147 u32 index;
Marek Lindner7aadf882011-02-18 12:28:09 +0000148 struct hlist_node *node;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000149 struct hlist_head *head;
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000150 void *data_save = NULL;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000151
152 index = choose(data, hash->size);
153 head = &hash->table[index];
154
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000155 spin_lock_bh(&hash->list_locks[index]);
Marek Lindner7aadf882011-02-18 12:28:09 +0000156 hlist_for_each(node, head) {
157 if (!compare(node, data))
158 continue;
159
160 data_save = node;
161 hlist_del_rcu(node);
Sven Eckelmann05abd7b2018-10-30 22:01:25 +0100162 atomic_inc(&hash->generation);
Marek Lindner7aadf882011-02-18 12:28:09 +0000163 break;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000164 }
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000165 spin_unlock_bh(&hash->list_locks[index]);
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000166
Marek Lindnerfb778ea2011-01-19 20:01:40 +0000167 return data_save;
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000168}
169
Sven Eckelmannc6c8fea2010-12-13 11:19:28 +0000170#endif /* _NET_BATMAN_ADV_HASH_H_ */