From 25b6d3c17eaa92ae9700eb8235bc79782613354a Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Thu, 10 Feb 2011 14:33:49 +0000 Subject: batman-adv: Correct rcu refcounting for gw_node It might be possible that 2 threads access the same data in the same rcu grace period. The first thread calls call_rcu() to decrement the refcount and free the data while the second thread increases the refcount to use the data. To avoid this race condition all refcount operations have to be atomic. Reported-by: Sven Eckelmann Signed-off-by: Marek Lindner --- net/batman-adv/gateway_client.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'net/batman-adv/gateway_client.c') diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 429a013d2e0..517e001605d 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -28,20 +28,18 @@ #include #include -static void gw_node_free_ref(struct kref *refcount) +static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; - gw_node = container_of(refcount, struct gw_node, refcount); + gw_node = container_of(rcu, struct gw_node, rcu); kfree(gw_node); } -static void gw_node_free_rcu(struct rcu_head *rcu) +static void gw_node_free_ref(struct gw_node *gw_node) { - struct gw_node *gw_node; - - gw_node = container_of(rcu, struct gw_node, rcu); - kref_put(&gw_node->refcount, gw_node_free_ref); + if (atomic_dec_and_test(&gw_node->refcount)) + call_rcu(&gw_node->rcu, gw_node_free_rcu); } void *gw_get_selected(struct bat_priv *bat_priv) @@ -61,25 +59,26 @@ void gw_deselect(struct bat_priv *bat_priv) bat_priv->curr_gw = NULL; if (gw_node) - kref_put(&gw_node->refcount, gw_node_free_ref); + gw_node_free_ref(gw_node); } -static struct gw_node *gw_select(struct bat_priv *bat_priv, - struct gw_node *new_gw_node) +static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) { struct gw_node *curr_gw_node = bat_priv->curr_gw; - if (new_gw_node) - kref_get(&new_gw_node->refcount); + if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) + new_gw_node = NULL; bat_priv->curr_gw = new_gw_node; - return curr_gw_node; + + if (curr_gw_node) + gw_node_free_ref(curr_gw_node); } void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; - struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; + struct gw_node *gw_node, *curr_gw_tmp = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; @@ -174,14 +173,10 @@ void gw_election(struct bat_priv *bat_priv) curr_gw_tmp->orig_node->gw_flags, curr_gw_tmp->orig_node->router->tq_avg); - old_gw_node = gw_select(bat_priv, curr_gw_tmp); + gw_select(bat_priv, curr_gw_tmp); } rcu_read_unlock(); - - /* the kfree() has to be outside of the rcu lock */ - if (old_gw_node) - kref_put(&old_gw_node->refcount, gw_node_free_ref); } void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) @@ -242,7 +237,7 @@ static void gw_node_add(struct bat_priv *bat_priv, memset(gw_node, 0, sizeof(struct gw_node)); INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; - kref_init(&gw_node->refcount); + atomic_set(&gw_node->refcount, 1); spin_lock_bh(&bat_priv->gw_list_lock); hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); @@ -325,7 +320,7 @@ void gw_node_purge(struct bat_priv *bat_priv) gw_deselect(bat_priv); hlist_del_rcu(&gw_node->list); - call_rcu(&gw_node->rcu, gw_node_free_rcu); + gw_node_free_ref(gw_node); } -- cgit v1.2.3-70-g09d2 From 5d02b3cdfafeb23ab7cf43ef1d2118007370e8d0 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Sun, 13 Feb 2011 21:13:02 +0000 Subject: batman-adv: Make bat_priv->curr_gw an rcu protected pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rcu protected macros rcu_dereference() and rcu_assign_pointer() for the bat_priv->curr_gw need to be used, as well as spin/rcu locking. Otherwise we might end up using a curr_gw pointer pointing to already freed memory. Reported-by: Sven Eckelmann Signed-off-by: Linus Lüssing Signed-off-by: Marek Lindner --- net/batman-adv/gateway_client.c | 100 ++++++++++++++++++++++++++++------------ net/batman-adv/types.h | 4 +- 2 files changed, 72 insertions(+), 32 deletions(-) (limited to 'net/batman-adv/gateway_client.c') diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 517e001605d..a3e842fd0f9 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -44,19 +44,29 @@ static void gw_node_free_ref(struct gw_node *gw_node) void *gw_get_selected(struct bat_priv *bat_priv) { - struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + struct gw_node *curr_gateway_tmp; + struct orig_node *orig_node = NULL; + rcu_read_lock(); + curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); if (!curr_gateway_tmp) - return NULL; + goto out; + + orig_node = curr_gateway_tmp->orig_node; - return curr_gateway_tmp->orig_node; +out: + rcu_read_unlock(); + return orig_node; } void gw_deselect(struct bat_priv *bat_priv) { - struct gw_node *gw_node = bat_priv->curr_gw; + struct gw_node *gw_node; - bat_priv->curr_gw = NULL; + spin_lock_bh(&bat_priv->gw_list_lock); + gw_node = rcu_dereference(bat_priv->curr_gw); + rcu_assign_pointer(bat_priv->curr_gw, NULL); + spin_unlock_bh(&bat_priv->gw_list_lock); if (gw_node) gw_node_free_ref(gw_node); @@ -64,12 +74,15 @@ void gw_deselect(struct bat_priv *bat_priv) static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) { - struct gw_node *curr_gw_node = bat_priv->curr_gw; + struct gw_node *curr_gw_node; if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) new_gw_node = NULL; - bat_priv->curr_gw = new_gw_node; + spin_lock_bh(&bat_priv->gw_list_lock); + curr_gw_node = rcu_dereference(bat_priv->curr_gw); + rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); + spin_unlock_bh(&bat_priv->gw_list_lock); if (curr_gw_node) gw_node_free_ref(curr_gw_node); @@ -78,7 +91,7 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; - struct gw_node *gw_node, *curr_gw_tmp = NULL; + struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; @@ -92,19 +105,23 @@ void gw_election(struct bat_priv *bat_priv) if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) return; - if (bat_priv->curr_gw) + rcu_read_lock(); + curr_gw = rcu_dereference(bat_priv->curr_gw); + if (curr_gw) { + rcu_read_unlock(); return; + } - rcu_read_lock(); if (hlist_empty(&bat_priv->gw_list)) { - rcu_read_unlock(); - if (bat_priv->curr_gw) { + if (curr_gw) { + rcu_read_unlock(); bat_dbg(DBG_BATMAN, bat_priv, "Removing selected gateway - " "no gateway in range\n"); gw_deselect(bat_priv); - } + } else + rcu_read_unlock(); return; } @@ -153,12 +170,12 @@ void gw_election(struct bat_priv *bat_priv) max_gw_factor = tmp_gw_factor; } - if (bat_priv->curr_gw != curr_gw_tmp) { - if ((bat_priv->curr_gw) && (!curr_gw_tmp)) + if (curr_gw != curr_gw_tmp) { + if ((curr_gw) && (!curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, "Removing selected gateway - " "no gateway in range\n"); - else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) + else if ((!curr_gw) && (curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, "Adding route to gateway %pM " "(gw_flags: %i, tq: %i)\n", @@ -181,31 +198,35 @@ void gw_election(struct bat_priv *bat_priv) void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) { - struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + struct gw_node *curr_gateway_tmp; uint8_t gw_tq_avg, orig_tq_avg; + rcu_read_lock(); + curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); if (!curr_gateway_tmp) - return; + goto out_rcu; if (!curr_gateway_tmp->orig_node) - goto deselect; + goto deselect_rcu; if (!curr_gateway_tmp->orig_node->router) - goto deselect; + goto deselect_rcu; /* this node already is the gateway */ if (curr_gateway_tmp->orig_node == orig_node) - return; + goto out_rcu; if (!orig_node->router) - return; + goto out_rcu; gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; + rcu_read_unlock(); + orig_tq_avg = orig_node->router->tq_avg; /* the TQ value has to be better */ if (orig_tq_avg < gw_tq_avg) - return; + goto out; /** * if the routing class is greater than 3 the value tells us how much @@ -213,15 +234,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) **/ if ((atomic_read(&bat_priv->gw_sel_class) > 3) && (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) - return; + goto out; bat_dbg(DBG_BATMAN, bat_priv, "Restarting gateway selection: better gateway found (tq curr: " "%i, tq new: %i)\n", gw_tq_avg, orig_tq_avg); + goto deselect; +out_rcu: + rcu_read_unlock(); + goto out; +deselect_rcu: + rcu_read_unlock(); deselect: gw_deselect(bat_priv); +out: + return; } static void gw_node_add(struct bat_priv *bat_priv, @@ -278,7 +307,7 @@ void gw_node_update(struct bat_priv *bat_priv, "Gateway %pM removed from gateway list\n", orig_node->orig); - if (gw_node == bat_priv->curr_gw) { + if (gw_node == rcu_dereference(bat_priv->curr_gw)) { rcu_read_unlock(); gw_deselect(bat_priv); return; @@ -316,7 +345,7 @@ void gw_node_purge(struct bat_priv *bat_priv) atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) continue; - if (bat_priv->curr_gw == gw_node) + if (rcu_dereference(bat_priv->curr_gw) == gw_node) gw_deselect(bat_priv); hlist_del_rcu(&gw_node->list); @@ -330,12 +359,16 @@ void gw_node_purge(struct bat_priv *bat_priv) static int _write_buffer_text(struct bat_priv *bat_priv, struct seq_file *seq, struct gw_node *gw_node) { - int down, up; + struct gw_node *curr_gw; + int down, up, ret; gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); - return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", - (bat_priv->curr_gw == gw_node ? "=>" : " "), + rcu_read_lock(); + curr_gw = rcu_dereference(bat_priv->curr_gw); + + ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", + (curr_gw == gw_node ? "=>" : " "), gw_node->orig_node->orig, gw_node->orig_node->router->tq_avg, gw_node->orig_node->router->addr, @@ -345,6 +378,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv, (down > 2048 ? "MBit" : "KBit"), (up > 2048 ? up / 1024 : up), (up > 2048 ? "MBit" : "KBit")); + + rcu_read_unlock(); + return ret; } int gw_client_seq_print_text(struct seq_file *seq, void *offset) @@ -465,8 +501,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) return -1; - if (!bat_priv->curr_gw) + rcu_read_lock(); + if (!rcu_dereference(bat_priv->curr_gw)) { + rcu_read_unlock(); return 0; + } + rcu_read_unlock(); return 1; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index e1f3e5e7155..3dd5e77a0aa 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -162,7 +162,7 @@ struct bat_priv { spinlock_t forw_bcast_list_lock; /* protects */ spinlock_t hna_lhash_lock; /* protects hna_local_hash */ spinlock_t hna_ghash_lock; /* protects hna_global_hash */ - spinlock_t gw_list_lock; /* protects gw_list */ + spinlock_t gw_list_lock; /* protects gw_list and curr_gw */ spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */ spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ @@ -171,7 +171,7 @@ struct bat_priv { struct delayed_work hna_work; struct delayed_work orig_work; struct delayed_work vis_work; - struct gw_node *curr_gw; + struct gw_node __rcu *curr_gw; /* rcu protected pointer */ struct vis_info *my_vis_info; }; -- cgit v1.2.3-70-g09d2 From 43c70ad5ce5691cce24dae6610731694c0f3fcc8 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Sun, 13 Feb 2011 21:13:04 +0000 Subject: batman-adv: Increase orig_node refcount before releasing rcu read lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When unicast_send_skb() is increasing the orig_node's refcount another thread might have been freeing this orig_node already. We need to increase the refcount in the rcu read lock protected area to avoid that. Signed-off-by: Linus Lüssing Signed-off-by: Marek Lindner --- net/batman-adv/gateway_client.c | 3 +++ net/batman-adv/unicast.c | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'net/batman-adv/gateway_client.c') diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index a3e842fd0f9..41eba8a660f 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -54,6 +54,9 @@ void *gw_get_selected(struct bat_priv *bat_priv) orig_node = curr_gateway_tmp->orig_node; + if (orig_node) + kref_get(&orig_node->refcount); + out: rcu_read_unlock(); return orig_node; diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 7ca994ccac1..0603ceaeef6 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -293,10 +293,9 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) spin_lock_bh(&bat_priv->orig_hash_lock); /* get routing information */ - if (is_multicast_ether_addr(ethhdr->h_dest)) + if (is_multicast_ether_addr(ethhdr->h_dest)) { orig_node = (struct orig_node *)gw_get_selected(bat_priv); - if (orig_node) { - kref_get(&orig_node->refcount); + if (orig_node) goto find_router; } -- cgit v1.2.3-70-g09d2 From 7b36e8eef989fc59535b4f1d3fc0f83afaf419d4 Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Fri, 18 Feb 2011 12:28:10 +0000 Subject: batman-adv: Correct rcu refcounting for orig_node It might be possible that 2 threads access the same data in the same rcu grace period. The first thread calls call_rcu() to decrement the refcount and free the data while the second thread increases the refcount to use the data. To avoid this race condition all refcount operations have to be atomic. Reported-by: Sven Eckelmann Signed-off-by: Marek Lindner --- net/batman-adv/gateway_client.c | 6 ++++-- net/batman-adv/icmp_socket.c | 2 +- net/batman-adv/originator.c | 21 +++++++++++++-------- net/batman-adv/originator.h | 6 ++++-- net/batman-adv/routing.c | 24 ++++++++++++------------ net/batman-adv/translation-table.c | 15 +++++++++------ net/batman-adv/types.h | 3 ++- net/batman-adv/unicast.c | 6 +++--- net/batman-adv/vis.c | 2 +- 9 files changed, 49 insertions(+), 36 deletions(-) (limited to 'net/batman-adv/gateway_client.c') diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 41eba8a660f..3cc43558cf9 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -53,9 +53,11 @@ void *gw_get_selected(struct bat_priv *bat_priv) goto out; orig_node = curr_gateway_tmp->orig_node; + if (!orig_node) + goto out; - if (orig_node) - kref_get(&orig_node->refcount); + if (!atomic_inc_not_zero(&orig_node->refcount)) + orig_node = NULL; out: rcu_read_unlock(); diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 139b7336adf..a0a35b1af16 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -271,7 +271,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return len; } diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index bdcb399329d..a70debebfc5 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -102,13 +102,13 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node, return neigh_node; } -void orig_node_free_ref(struct kref *refcount) +static void orig_node_free_rcu(struct rcu_head *rcu) { struct hlist_node *node, *node_tmp; struct neigh_node *neigh_node, *tmp_neigh_node; struct orig_node *orig_node; - orig_node = container_of(refcount, struct orig_node, refcount); + orig_node = container_of(rcu, struct orig_node, rcu); spin_lock_bh(&orig_node->neigh_list_lock); @@ -137,6 +137,12 @@ void orig_node_free_ref(struct kref *refcount) kfree(orig_node); } +void orig_node_free_ref(struct orig_node *orig_node) +{ + if (atomic_dec_and_test(&orig_node->refcount)) + call_rcu(&orig_node->rcu, orig_node_free_rcu); +} + void originator_free(struct bat_priv *bat_priv) { struct hashtable_t *hash = bat_priv->orig_hash; @@ -163,7 +169,7 @@ void originator_free(struct bat_priv *bat_priv) head, hash_entry) { hlist_del_rcu(node); - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); } spin_unlock_bh(list_lock); } @@ -196,7 +202,9 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) spin_lock_init(&orig_node->ogm_cnt_lock); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); - kref_init(&orig_node->refcount); + + /* extra reference for return */ + atomic_set(&orig_node->refcount, 2); orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); @@ -229,8 +237,6 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) if (hash_added < 0) goto free_bcast_own_sum; - /* extra reference for return */ - kref_get(&orig_node->refcount); return orig_node; free_bcast_own_sum: kfree(orig_node->bcast_own_sum); @@ -348,8 +354,7 @@ static void _purge_orig(struct bat_priv *bat_priv) if (orig_node->gw_flags) gw_node_delete(bat_priv, orig_node); hlist_del_rcu(node); - kref_put(&orig_node->refcount, - orig_node_free_ref); + orig_node_free_ref(orig_node); continue; } diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index b4b9a09259f..3d7a39d4df0 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -27,7 +27,7 @@ int originator_init(struct bat_priv *bat_priv); void originator_free(struct bat_priv *bat_priv); void purge_orig_ref(struct bat_priv *bat_priv); -void orig_node_free_ref(struct kref *refcount); +void orig_node_free_ref(struct orig_node *orig_node); struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, @@ -88,8 +88,10 @@ static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv, if (!compare_eth(orig_node, data)) continue; + if (!atomic_inc_not_zero(&orig_node->refcount)) + continue; + orig_node_tmp = orig_node; - kref_get(&orig_node_tmp->refcount); break; } rcu_read_unlock(); diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index fc4c12a049d..9863c03a213 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -420,7 +420,7 @@ static void update_orig(struct bat_priv *bat_priv, neigh_node = create_neighbor(orig_node, orig_tmp, ethhdr->h_source, if_incoming); - kref_put(&orig_tmp->refcount, orig_node_free_ref); + orig_node_free_ref(orig_tmp); if (!neigh_node) goto unlock; @@ -604,7 +604,7 @@ static char count_real_packets(struct ethhdr *ethhdr, out: spin_unlock_bh(&orig_node->ogm_cnt_lock); - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -730,7 +730,7 @@ void receive_bat_packet(struct ethhdr *ethhdr, bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " "originator packet from myself (via neighbor)\n"); - kref_put(&orig_neigh_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_neigh_node); return; } @@ -835,10 +835,10 @@ void receive_bat_packet(struct ethhdr *ethhdr, 0, hna_buff_len, if_incoming); out_neigh: - if (!is_single_hop_neigh) - kref_put(&orig_neigh_node->refcount, orig_node_free_ref); + if ((orig_neigh_node) && (!is_single_hop_neigh)) + orig_node_free_ref(orig_neigh_node); out: - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); } int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) @@ -952,7 +952,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -1028,7 +1028,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -1134,7 +1134,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -1189,7 +1189,7 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, if (!primary_orig_node) goto return_router; - kref_put(&primary_orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(primary_orig_node); } /* with less than 2 candidates, we can't do any @@ -1401,7 +1401,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -1543,7 +1543,7 @@ spin_unlock: spin_unlock_bh(&bat_priv->orig_hash_lock); out: if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index cd8a58396d2..8d15b48d169 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -589,17 +589,20 @@ void hna_global_free(struct bat_priv *bat_priv) struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) { struct hna_global_entry *hna_global_entry; + struct orig_node *orig_node = NULL; spin_lock_bh(&bat_priv->hna_ghash_lock); hna_global_entry = hna_global_hash_find(bat_priv, addr); - if (hna_global_entry) - kref_get(&hna_global_entry->orig_node->refcount); + if (!hna_global_entry) + goto out; - spin_unlock_bh(&bat_priv->hna_ghash_lock); + if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount)) + goto out; - if (!hna_global_entry) - return NULL; + orig_node = hna_global_entry->orig_node; - return hna_global_entry->orig_node; +out: + spin_unlock_bh(&bat_priv->hna_ghash_lock); + return orig_node; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 40365b81bc4..1be76feddee 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -84,7 +84,8 @@ struct orig_node { struct hlist_head neigh_list; struct list_head frag_list; spinlock_t neigh_list_lock; /* protects neighbor list */ - struct kref refcount; + atomic_t refcount; + struct rcu_head rcu; struct hlist_node hash_entry; struct bat_priv *bat_priv; unsigned long last_frag_packet; diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 2d5daac5203..2ab81984123 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -211,7 +211,7 @@ unlock: spin_unlock_bh(&bat_priv->orig_hash_lock); out: if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return ret; } @@ -280,7 +280,7 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct unicast_packet *unicast_packet; - struct orig_node *orig_node = NULL; + struct orig_node *orig_node; struct batman_if *batman_if; struct neigh_node *neigh_node; int data_len = skb->len; @@ -347,7 +347,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); if (ret == 1) kfree_skb(skb); return ret; diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index d179acabb04..89722425dcb 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -826,7 +826,7 @@ out: if (neigh_node) neigh_node_free_ref(neigh_node); if (orig_node) - kref_put(&orig_node->refcount, orig_node_free_ref); + orig_node_free_ref(orig_node); return; } -- cgit v1.2.3-70-g09d2