Skip to content

Commit

Permalink
batman-adv: protect each hash row with rcu locks
Browse files Browse the repository at this point in the history
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
  • Loading branch information
Marek Lindner committed Mar 5, 2011
1 parent a775eb8 commit fb778ea
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 45 deletions.
34 changes: 25 additions & 9 deletions net/batman-adv/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ static void hash_init(struct hashtable_t *hash)
{
int i;

for (i = 0 ; i < hash->size; i++)
for (i = 0 ; i < hash->size; i++) {
INIT_HLIST_HEAD(&hash->table[i]);
spin_lock_init(&hash->list_locks[i]);
}
}

/* free only the hashtable and the hash itself. */
void hash_destroy(struct hashtable_t *hash)
{
kfree(hash->list_locks);
kfree(hash->table);
kfree(hash);
}
Expand All @@ -43,20 +46,33 @@ struct hashtable_t *hash_new(int size)
{
struct hashtable_t *hash;

hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC);

hash = kmalloc(sizeof(struct hashtable_t), GFP_ATOMIC);
if (!hash)
return NULL;

hash->size = size;
hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC);
if (!hash->table)
goto free_hash;

if (!hash->table) {
kfree(hash);
return NULL;
}
hash->list_locks = kmalloc(sizeof(spinlock_t) * size, GFP_ATOMIC);
if (!hash->list_locks)
goto free_table;

hash->size = size;
hash_init(hash);

return hash;

free_table:
kfree(hash->table);
free_hash:
kfree(hash);
return NULL;
}

void bucket_free_rcu(struct rcu_head *rcu)
{
struct element_t *bucket;

bucket = container_of(rcu, struct element_t, rcu);
kfree(bucket);
}
73 changes: 49 additions & 24 deletions net/batman-adv/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ typedef void (*hashdata_free_cb)(void *, void *);
struct element_t {
void *data; /* pointer to the data */
struct hlist_node hlist; /* bucket list pointer */
struct rcu_head rcu;
};

struct hashtable_t {
struct hlist_head *table; /* the hashtable itself, with the buckets */
struct hlist_head *table; /* the hashtable itself with the buckets */
spinlock_t *list_locks; /* spinlock for each hash list entry */
int size; /* size of hashtable */
};

Expand All @@ -52,6 +54,8 @@ struct hashtable_t *hash_new(int size);
/* free only the hashtable and the hash itself. */
void hash_destroy(struct hashtable_t *hash);

void bucket_free_rcu(struct rcu_head *rcu);

/* 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. */
Expand All @@ -61,19 +65,22 @@ static inline void hash_delete(struct hashtable_t *hash,
struct hlist_head *head;
struct hlist_node *walk, *safe;
struct element_t *bucket;
spinlock_t *list_lock; /* spinlock to protect write access */
int i;

for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
list_lock = &hash->list_locks[i];

hlist_for_each_safe(walk, safe, head) {
bucket = hlist_entry(walk, struct element_t, hlist);
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) {
if (free_cb)
free_cb(bucket->data, arg);

hlist_del(walk);
kfree(bucket);
hlist_del_rcu(walk);
call_rcu(&bucket->rcu, bucket_free_rcu);
}
spin_unlock_bh(list_lock);
}

hash_destroy(hash);
Expand All @@ -88,29 +95,39 @@ static inline int hash_add(struct hashtable_t *hash,
struct hlist_head *head;
struct hlist_node *walk, *safe;
struct element_t *bucket;
spinlock_t *list_lock; /* spinlock to protect write access */

if (!hash)
return -1;
goto err;

index = choose(data, hash->size);
head = &hash->table[index];
list_lock = &hash->list_locks[index];

hlist_for_each_safe(walk, safe, head) {
bucket = hlist_entry(walk, struct element_t, hlist);
rcu_read_lock();
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) {
if (compare(bucket->data, data))
return -1;
goto err_unlock;
}
rcu_read_unlock();

/* no duplicate found in list, add new element */
bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC);

if (!bucket)
return -1;
goto err;

bucket->data = data;
hlist_add_head(&bucket->hlist, head);

spin_lock_bh(list_lock);
hlist_add_head_rcu(&bucket->hlist, head);
spin_unlock_bh(list_lock);

return 0;

err_unlock:
rcu_read_unlock();
err:
return -1;
}

/* removes data from hash, if found. returns pointer do data on success, so you
Expand All @@ -125,25 +142,31 @@ static inline void *hash_remove(struct hashtable_t *hash,
struct hlist_node *walk;
struct element_t *bucket;
struct hlist_head *head;
void *data_save;
void *data_save = NULL;

index = choose(data, hash->size);
head = &hash->table[index];

spin_lock_bh(&hash->list_locks[index]);
hlist_for_each_entry(bucket, walk, head, hlist) {
if (compare(bucket->data, data)) {
data_save = bucket->data;
hlist_del(walk);
kfree(bucket);
return data_save;
hlist_del_rcu(walk);
call_rcu(&bucket->rcu, bucket_free_rcu);
break;
}
}
spin_unlock_bh(&hash->list_locks[index]);

return NULL;
return data_save;
}

/* finds data, based on the key in keydata. returns the found data on success,
* or NULL on error */
/**
* finds data, based on the key in keydata. returns the found data on success,
* or NULL on error
*
* caller must lock with rcu_read_lock() / rcu_read_unlock()
**/
static inline void *hash_find(struct hashtable_t *hash,
hashdata_compare_cb compare,
hashdata_choose_cb choose, void *keydata)
Expand All @@ -152,20 +175,22 @@ static inline void *hash_find(struct hashtable_t *hash,
struct hlist_head *head;
struct hlist_node *walk;
struct element_t *bucket;
void *bucket_data = NULL;

if (!hash)
return NULL;

index = choose(keydata , hash->size);
head = &hash->table[index];

hlist_for_each(walk, head) {
bucket = hlist_entry(walk, struct element_t, hlist);
if (compare(bucket->data, keydata))
return bucket->data;
hlist_for_each_entry(bucket, walk, head, hlist) {
if (compare(bucket->data, keydata)) {
bucket_data = bucket->data;
break;
}
}

return NULL;
return bucket_data;
}

#endif /* _NET_BATMAN_ADV_HASH_H_ */
2 changes: 2 additions & 0 deletions net/batman-adv/icmp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,11 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
goto dst_unreach;

spin_lock_bh(&bat_priv->orig_hash_lock);
rcu_read_lock();
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
compare_orig, choose_orig,
icmp_packet->dst));
rcu_read_unlock();

if (!orig_node)
goto unlock;
Expand Down
27 changes: 20 additions & 7 deletions net/batman-adv/originator.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,11 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
int size;
int hash_added;

rcu_read_lock();
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
compare_orig, choose_orig,
addr));
rcu_read_unlock();

if (orig_node)
return orig_node;
Expand Down Expand Up @@ -294,6 +296,7 @@ static void _purge_orig(struct bat_priv *bat_priv)
struct hlist_node *walk, *safe;
struct hlist_head *head;
struct element_t *bucket;
spinlock_t *list_lock; /* spinlock to protect write access */
struct orig_node *orig_node;
int i;

Expand All @@ -305,22 +308,26 @@ static void _purge_orig(struct bat_priv *bat_priv)
/* for all origins... */
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
list_lock = &hash->list_locks[i];

spin_lock_bh(list_lock);
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) {
orig_node = bucket->data;

if (purge_orig_node(bat_priv, orig_node)) {
if (orig_node->gw_flags)
gw_node_delete(bat_priv, orig_node);
hlist_del(walk);
kfree(bucket);
hlist_del_rcu(walk);
call_rcu(&bucket->rcu, bucket_free_rcu);
free_orig_node(orig_node, bat_priv);
continue;
}

if (time_after(jiffies, orig_node->last_frag_packet +
msecs_to_jiffies(FRAG_TIMEOUT)))
frag_list_free(&orig_node->frag_list);
}
spin_unlock_bh(list_lock);
}

spin_unlock_bh(&bat_priv->orig_hash_lock);
Expand Down Expand Up @@ -387,7 +394,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];

hlist_for_each_entry(bucket, walk, head, hlist) {
rcu_read_lock();
hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
orig_node = bucket->data;

if (!orig_node->router)
Expand All @@ -408,17 +416,16 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
neigh_node->addr,
neigh_node->if_incoming->net_dev->name);

rcu_read_lock();
hlist_for_each_entry_rcu(neigh_node, node,
&orig_node->neigh_list, list) {
seq_printf(seq, " %pM (%3i)", neigh_node->addr,
neigh_node->tq_avg);
}
rcu_read_unlock();

seq_printf(seq, "\n");
batman_count++;
}
rcu_read_unlock();
}

spin_unlock_bh(&bat_priv->orig_hash_lock);
Expand Down Expand Up @@ -476,18 +483,21 @@ int orig_hash_add_if(struct batman_if *batman_if, int max_if_num)
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];

hlist_for_each_entry(bucket, walk, head, hlist) {
rcu_read_lock();
hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
orig_node = bucket->data;

if (orig_node_add_if(orig_node, max_if_num) == -1)
goto err;
}
rcu_read_unlock();
}

spin_unlock_bh(&bat_priv->orig_hash_lock);
return 0;

err:
rcu_read_unlock();
spin_unlock_bh(&bat_priv->orig_hash_lock);
return -ENOMEM;
}
Expand Down Expand Up @@ -562,7 +572,8 @@ int orig_hash_del_if(struct batman_if *batman_if, int max_if_num)
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];

hlist_for_each_entry(bucket, walk, head, hlist) {
rcu_read_lock();
hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
orig_node = bucket->data;

ret = orig_node_del_if(orig_node, max_if_num,
Expand All @@ -571,6 +582,7 @@ int orig_hash_del_if(struct batman_if *batman_if, int max_if_num)
if (ret == -1)
goto err;
}
rcu_read_unlock();
}

/* renumber remaining batman interfaces _inside_ of orig_hash_lock */
Expand All @@ -595,6 +607,7 @@ int orig_hash_del_if(struct batman_if *batman_if, int max_if_num)
return 0;

err:
rcu_read_unlock();
spin_unlock_bh(&bat_priv->orig_hash_lock);
return -ENOMEM;
}
Loading

0 comments on commit fb778ea

Please sign in to comment.