2010-12-13 11:19:28 +00:00
/*
2011-01-27 10:38:15 +01:00
* Copyright ( C ) 2006 - 2011 B . A . T . M . A . N . contributors :
2010-12-13 11:19:28 +00: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>
/* 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-14 23:14:50 +02:00
typedef int ( * hashdata_compare_cb ) ( const struct hlist_node * , const void * ) ;
2010-12-13 11:19:28 +00:00
/* the hashfunction, should return an index
* based on the key in the data of the first
* argument and the size the second */
2011-05-14 23:14:50 +02:00
typedef int ( * hashdata_choose_cb ) ( const void * , int ) ;
2011-02-18 12:28:09 +00:00
typedef void ( * hashdata_free_cb ) ( struct hlist_node * , void * ) ;
2010-12-13 11:19:28 +00:00
struct hashtable_t {
2011-01-19 20:01:40 +00:00
struct hlist_head * table ; /* the hashtable itself with the buckets */
spinlock_t * list_locks ; /* spinlock for each hash list entry */
2010-12-13 11:19:28 +00:00
int size ; /* size of hashtable */
} ;
/* allocates and clears the hash */
struct hashtable_t * hash_new ( int size ) ;
/* free only the hashtable and the hash itself. */
void hash_destroy ( struct hashtable_t * 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 )
{
struct hlist_head * head ;
2011-02-18 12:28:09 +00:00
struct hlist_node * node , * node_tmp ;
2011-01-19 20:01:40 +00:00
spinlock_t * list_lock ; /* spinlock to protect write access */
2010-12-13 11:19:28 +00:00
int i ;
for ( i = 0 ; i < hash - > size ; i + + ) {
head = & hash - > table [ i ] ;
2011-01-19 20:01:40 +00:00
list_lock = & hash - > list_locks [ i ] ;
2010-12-13 11:19:28 +00:00
2011-01-19 20:01:40 +00:00
spin_lock_bh ( list_lock ) ;
2011-02-18 12:28:09 +00:00
hlist_for_each_safe ( node , node_tmp , head ) {
hlist_del_rcu ( node ) ;
2010-12-13 11:19:28 +00:00
2011-02-18 12:28:09 +00:00
if ( free_cb )
free_cb ( node , arg ) ;
2010-12-13 11:19:28 +00:00
}
2011-01-19 20:01:40 +00:00
spin_unlock_bh ( list_lock ) ;
2010-12-13 11:19:28 +00:00
}
hash_destroy ( 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 ,
2011-02-18 12:28:09 +00:00
hashdata_choose_cb choose ,
2011-05-14 23:14:50 +02:00
const void * data , struct hlist_node * data_node )
2010-12-13 11:19:28 +00:00
{
int index ;
struct hlist_head * head ;
2011-02-18 12:28:09 +00:00
struct hlist_node * node ;
2011-01-19 20:01:40 +00:00
spinlock_t * list_lock ; /* spinlock to protect write access */
2010-12-13 11:19:28 +00:00
if ( ! hash )
2011-01-19 20:01:40 +00:00
goto err ;
2010-12-13 11:19:28 +00:00
index = choose ( data , hash - > size ) ;
head = & hash - > table [ index ] ;
2011-01-19 20:01:40 +00:00
list_lock = & hash - > list_locks [ index ] ;
2010-12-13 11:19:28 +00:00
2011-01-19 20:01:40 +00:00
rcu_read_lock ( ) ;
2011-02-18 12:28:09 +00:00
__hlist_for_each_rcu ( node , head ) {
if ( ! compare ( node , data ) )
continue ;
goto err_unlock ;
2010-12-13 11:19:28 +00:00
}
2011-01-19 20:01:40 +00:00
rcu_read_unlock ( ) ;
2010-12-13 11:19:28 +00:00
/* no duplicate found in list, add new element */
2011-01-19 20:01:40 +00:00
spin_lock_bh ( list_lock ) ;
2011-02-18 12:28:09 +00:00
hlist_add_head_rcu ( data_node , head ) ;
2011-01-19 20:01:40 +00:00
spin_unlock_bh ( list_lock ) ;
2010-12-13 11:19:28 +00:00
return 0 ;
2011-01-19 20:01:40 +00:00
err_unlock :
rcu_read_unlock ( ) ;
err :
return - 1 ;
2010-12-13 11:19:28 +00: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
* comparing . */
static inline void * hash_remove ( struct hashtable_t * hash ,
hashdata_compare_cb compare ,
hashdata_choose_cb choose , void * data )
{
size_t index ;
2011-02-18 12:28:09 +00:00
struct hlist_node * node ;
2010-12-13 11:19:28 +00:00
struct hlist_head * head ;
2011-01-19 20:01:40 +00:00
void * data_save = NULL ;
2010-12-13 11:19:28 +00:00
index = choose ( data , hash - > size ) ;
head = & hash - > table [ index ] ;
2011-01-19 20:01:40 +00:00
spin_lock_bh ( & hash - > list_locks [ index ] ) ;
2011-02-18 12:28:09 +00:00
hlist_for_each ( node , head ) {
if ( ! compare ( node , data ) )
continue ;
data_save = node ;
hlist_del_rcu ( node ) ;
break ;
2010-12-13 11:19:28 +00:00
}
2011-01-19 20:01:40 +00:00
spin_unlock_bh ( & hash - > list_locks [ index ] ) ;
2010-12-13 11:19:28 +00:00
2011-01-19 20:01:40 +00:00
return data_save ;
2010-12-13 11:19:28 +00:00
}
# endif /* _NET_BATMAN_ADV_HASH_H_ */