2012-05-12 04:09:43 +04:00
/* Copyright (C) 2006-2012 B.A.T.M.A.N. contributors:
2010-12-13 14:19:28 +03:00
*
* Simon Wunderlich , Marek Lindner
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* 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
*/
# ifndef _NET_BATMAN_ADV_HASH_H_
# define _NET_BATMAN_ADV_HASH_H_
# include <linux/list.h>
2012-05-12 04:09:43 +04:00
/* callback to a compare function. should compare 2 element datas for their
* keys , return 0 if same and not 0 if not same
*/
2011-05-15 01:14:50 +04:00
typedef int ( * hashdata_compare_cb ) ( const struct hlist_node * , const void * ) ;
2010-12-13 14:19:28 +03:00
/* the hashfunction, should return an index
* based on the key in the data of the first
2012-05-12 04:09:43 +04:00
* argument and the size the second
*/
2011-10-05 19:05:25 +04:00
typedef uint32_t ( * hashdata_choose_cb ) ( const void * , uint32_t ) ;
2011-02-18 15:28:09 +03:00
typedef void ( * hashdata_free_cb ) ( struct hlist_node * , void * ) ;
2010-12-13 14:19:28 +03:00
struct hashtable_t {
2011-01-19 23:01:40 +03:00
struct hlist_head * table ; /* the hashtable itself with the buckets */
spinlock_t * list_locks ; /* spinlock for each hash list entry */
2011-10-05 19:05:25 +04:00
uint32_t size ; /* size of hashtable */
2010-12-13 14:19:28 +03:00
} ;
/* allocates and clears the hash */
2012-05-12 04:09:32 +04:00
struct hashtable_t * batadv_hash_new ( uint32_t size ) ;
2010-12-13 14:19:28 +03:00
2012-03-29 14:38:20 +04:00
/* set class key for all locks */
void batadv_hash_set_lock_class ( struct hashtable_t * hash ,
struct lock_class_key * key ) ;
2010-12-13 14:19:28 +03:00
/* free only the hashtable and the hash itself. */
2012-05-12 04:09:32 +04:00
void batadv_hash_destroy ( struct hashtable_t * hash ) ;
2010-12-13 14:19:28 +03:00
/* 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
2012-05-12 04:09:43 +04:00
* elements , memory might be leaked .
*/
2010-12-13 14:19:28 +03:00
static inline void hash_delete ( struct hashtable_t * hash ,
hashdata_free_cb free_cb , void * arg )
{
struct hlist_head * head ;
2011-02-18 15:28:09 +03:00
struct hlist_node * node , * node_tmp ;
2011-01-19 23:01:40 +03:00
spinlock_t * list_lock ; /* spinlock to protect write access */
2011-10-05 19:05:25 +04:00
uint32_t i ;
2010-12-13 14:19:28 +03:00
for ( i = 0 ; i < hash - > size ; i + + ) {
head = & hash - > table [ i ] ;
2011-01-19 23:01:40 +03:00
list_lock = & hash - > list_locks [ i ] ;
2010-12-13 14:19:28 +03:00
2011-01-19 23:01:40 +03:00
spin_lock_bh ( list_lock ) ;
2011-02-18 15:28:09 +03:00
hlist_for_each_safe ( node , node_tmp , head ) {
hlist_del_rcu ( node ) ;
2010-12-13 14:19:28 +03:00
2011-02-18 15:28:09 +03:00
if ( free_cb )
free_cb ( node , arg ) ;
2010-12-13 14:19:28 +03:00
}
2011-01-19 23:01:40 +03:00
spin_unlock_bh ( list_lock ) ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 04:09:32 +04:00
batadv_hash_destroy ( hash ) ;
2010-12-13 14:19:28 +03:00
}
2012-05-12 04:09:43 +04:00
/* hash_add - adds data to the hashtable
2011-07-10 02:36:36 +04:00
* @ 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 .
*/
2010-12-13 14:19:28 +03:00
static inline int hash_add ( struct hashtable_t * hash ,
hashdata_compare_cb compare ,
2011-02-18 15:28:09 +03:00
hashdata_choose_cb choose ,
2011-05-15 01:14:50 +04:00
const void * data , struct hlist_node * data_node )
2010-12-13 14:19:28 +03:00
{
2011-10-05 19:05:25 +04:00
uint32_t index ;
int ret = - 1 ;
2010-12-13 14:19:28 +03:00
struct hlist_head * head ;
2011-02-18 15:28:09 +03:00
struct hlist_node * node ;
2011-01-19 23:01:40 +03:00
spinlock_t * list_lock ; /* spinlock to protect write access */
2010-12-13 14:19:28 +03:00
if ( ! hash )
2011-07-10 02:36:36 +04:00
goto out ;
2010-12-13 14:19:28 +03:00
index = choose ( data , hash - > size ) ;
head = & hash - > table [ index ] ;
2011-01-19 23:01:40 +03:00
list_lock = & hash - > list_locks [ index ] ;
2010-12-13 14:19:28 +03:00
2012-05-09 00:31:57 +04:00
spin_lock_bh ( list_lock ) ;
hlist_for_each ( node , head ) {
2011-02-18 15:28:09 +03:00
if ( ! compare ( node , data ) )
continue ;
2011-07-10 02:36:36 +04:00
ret = 1 ;
2012-05-09 00:31:57 +04:00
goto unlock ;
2010-12-13 14:19:28 +03:00
}
/* no duplicate found in list, add new element */
2011-02-18 15:28:09 +03:00
hlist_add_head_rcu ( data_node , head ) ;
2010-12-13 14:19:28 +03:00
2011-07-10 02:36:36 +04:00
ret = 0 ;
2011-01-19 23:01:40 +03:00
2012-05-09 00:31:57 +04:00
unlock :
spin_unlock_bh ( list_lock ) ;
2011-07-10 02:36:36 +04:00
out :
return ret ;
2010-12-13 14:19:28 +03:00
}
/* 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
2012-05-12 04:09:43 +04:00
* comparing .
*/
2010-12-13 14:19:28 +03:00
static inline void * hash_remove ( struct hashtable_t * hash ,
hashdata_compare_cb compare ,
hashdata_choose_cb choose , void * data )
{
2011-10-05 19:05:25 +04:00
uint32_t index ;
2011-02-18 15:28:09 +03:00
struct hlist_node * node ;
2010-12-13 14:19:28 +03:00
struct hlist_head * head ;
2011-01-19 23:01:40 +03:00
void * data_save = NULL ;
2010-12-13 14:19:28 +03:00
index = choose ( data , hash - > size ) ;
head = & hash - > table [ index ] ;
2011-01-19 23:01:40 +03:00
spin_lock_bh ( & hash - > list_locks [ index ] ) ;
2011-02-18 15:28:09 +03:00
hlist_for_each ( node , head ) {
if ( ! compare ( node , data ) )
continue ;
data_save = node ;
hlist_del_rcu ( node ) ;
break ;
2010-12-13 14:19:28 +03:00
}
2011-01-19 23:01:40 +03:00
spin_unlock_bh ( & hash - > list_locks [ index ] ) ;
2010-12-13 14:19:28 +03:00
2011-01-19 23:01:40 +03:00
return data_save ;
2010-12-13 14:19:28 +03:00
}
# endif /* _NET_BATMAN_ADV_HASH_H_ */