/* SPDX-License-Identifier: GPL-2.0 */1/* Copyright (C) B.A.T.M.A.N. contributors:2*3* Simon Wunderlich, Marek Lindner4*/56#ifndef _NET_BATMAN_ADV_HASH_H_7#define _NET_BATMAN_ADV_HASH_H_89#include "main.h"1011#include <linux/atomic.h>12#include <linux/compiler.h>13#include <linux/list.h>14#include <linux/lockdep.h>15#include <linux/rculist.h>16#include <linux/spinlock.h>17#include <linux/stddef.h>18#include <linux/types.h>1920/* callback to a compare function. should compare 2 element data for their21* keys22*23* Return: true if same and false if not same24*/25typedef bool (*batadv_hashdata_compare_cb)(const struct hlist_node *,26const void *);2728/* the hashfunction29*30* Return: an index based on the key in the data of the first argument and the31* size the second32*/33typedef u32 (*batadv_hashdata_choose_cb)(const void *, u32);34typedef void (*batadv_hashdata_free_cb)(struct hlist_node *, void *);3536/**37* struct batadv_hashtable - Wrapper of simple hlist based hashtable38*/39struct batadv_hashtable {40/** @table: the hashtable itself with the buckets */41struct hlist_head *table;4243/** @list_locks: spinlock for each hash list entry */44spinlock_t *list_locks;4546/** @size: size of hashtable */47u32 size;4849/** @generation: current (generation) sequence number */50atomic_t generation;51};5253/* allocates and clears the hash */54struct batadv_hashtable *batadv_hash_new(u32 size);5556/* set class key for all locks */57void batadv_hash_set_lock_class(struct batadv_hashtable *hash,58struct lock_class_key *key);5960/* free only the hashtable and the hash itself. */61void batadv_hash_destroy(struct batadv_hashtable *hash);6263/**64* batadv_hash_add() - adds data to the hashtable65* @hash: storage hash table66* @compare: callback to determine if 2 hash elements are identical67* @choose: callback calculating the hash index68* @data: data passed to the aforementioned callbacks as argument69* @data_node: to be added element70*71* Return: 0 on success, 1 if the element already is in the hash72* and -1 on error.73*/74static inline int batadv_hash_add(struct batadv_hashtable *hash,75batadv_hashdata_compare_cb compare,76batadv_hashdata_choose_cb choose,77const void *data,78struct hlist_node *data_node)79{80u32 index;81int ret = -1;82struct hlist_head *head;83struct hlist_node *node;84spinlock_t *list_lock; /* spinlock to protect write access */8586if (!hash)87goto out;8889index = choose(data, hash->size);90head = &hash->table[index];91list_lock = &hash->list_locks[index];9293spin_lock_bh(list_lock);9495hlist_for_each(node, head) {96if (!compare(node, data))97continue;9899ret = 1;100goto unlock;101}102103/* no duplicate found in list, add new element */104hlist_add_head_rcu(data_node, head);105atomic_inc(&hash->generation);106107ret = 0;108109unlock:110spin_unlock_bh(list_lock);111out:112return ret;113}114115/**116* batadv_hash_remove() - Removes data from hash, if found117* @hash: hash table118* @compare: callback to determine if 2 hash elements are identical119* @choose: callback calculating the hash index120* @data: data passed to the aforementioned callbacks as argument121*122* ata could be the structure you use with just the key filled, we just need123* the key for comparing.124*125* Return: returns pointer do data on success, so you can remove the used126* structure yourself, or NULL on error127*/128static inline void *batadv_hash_remove(struct batadv_hashtable *hash,129batadv_hashdata_compare_cb compare,130batadv_hashdata_choose_cb choose,131void *data)132{133u32 index;134struct hlist_node *node;135struct hlist_head *head;136void *data_save = NULL;137138index = choose(data, hash->size);139head = &hash->table[index];140141spin_lock_bh(&hash->list_locks[index]);142hlist_for_each(node, head) {143if (!compare(node, data))144continue;145146data_save = node;147hlist_del_rcu(node);148atomic_inc(&hash->generation);149break;150}151spin_unlock_bh(&hash->list_locks[index]);152153return data_save;154}155156#endif /* _NET_BATMAN_ADV_HASH_H_ */157158159