diff options
Diffstat (limited to 'net/batman-adv/gateway_client.c')
-rw-r--r-- | net/batman-adv/gateway_client.c | 37 |
1 files changed, 16 insertions, 21 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 429a013d2e0a..517e001605d9 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c | |||
@@ -28,20 +28,18 @@ | |||
28 | #include <linux/udp.h> | 28 | #include <linux/udp.h> |
29 | #include <linux/if_vlan.h> | 29 | #include <linux/if_vlan.h> |
30 | 30 | ||
31 | static void gw_node_free_ref(struct kref *refcount) | 31 | static void gw_node_free_rcu(struct rcu_head *rcu) |
32 | { | 32 | { |
33 | struct gw_node *gw_node; | 33 | struct gw_node *gw_node; |
34 | 34 | ||
35 | gw_node = container_of(refcount, struct gw_node, refcount); | 35 | gw_node = container_of(rcu, struct gw_node, rcu); |
36 | kfree(gw_node); | 36 | kfree(gw_node); |
37 | } | 37 | } |
38 | 38 | ||
39 | static void gw_node_free_rcu(struct rcu_head *rcu) | 39 | static void gw_node_free_ref(struct gw_node *gw_node) |
40 | { | 40 | { |
41 | struct gw_node *gw_node; | 41 | if (atomic_dec_and_test(&gw_node->refcount)) |
42 | 42 | call_rcu(&gw_node->rcu, gw_node_free_rcu); | |
43 | gw_node = container_of(rcu, struct gw_node, rcu); | ||
44 | kref_put(&gw_node->refcount, gw_node_free_ref); | ||
45 | } | 43 | } |
46 | 44 | ||
47 | void *gw_get_selected(struct bat_priv *bat_priv) | 45 | void *gw_get_selected(struct bat_priv *bat_priv) |
@@ -61,25 +59,26 @@ void gw_deselect(struct bat_priv *bat_priv) | |||
61 | bat_priv->curr_gw = NULL; | 59 | bat_priv->curr_gw = NULL; |
62 | 60 | ||
63 | if (gw_node) | 61 | if (gw_node) |
64 | kref_put(&gw_node->refcount, gw_node_free_ref); | 62 | gw_node_free_ref(gw_node); |
65 | } | 63 | } |
66 | 64 | ||
67 | static struct gw_node *gw_select(struct bat_priv *bat_priv, | 65 | static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) |
68 | struct gw_node *new_gw_node) | ||
69 | { | 66 | { |
70 | struct gw_node *curr_gw_node = bat_priv->curr_gw; | 67 | struct gw_node *curr_gw_node = bat_priv->curr_gw; |
71 | 68 | ||
72 | if (new_gw_node) | 69 | if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) |
73 | kref_get(&new_gw_node->refcount); | 70 | new_gw_node = NULL; |
74 | 71 | ||
75 | bat_priv->curr_gw = new_gw_node; | 72 | bat_priv->curr_gw = new_gw_node; |
76 | return curr_gw_node; | 73 | |
74 | if (curr_gw_node) | ||
75 | gw_node_free_ref(curr_gw_node); | ||
77 | } | 76 | } |
78 | 77 | ||
79 | void gw_election(struct bat_priv *bat_priv) | 78 | void gw_election(struct bat_priv *bat_priv) |
80 | { | 79 | { |
81 | struct hlist_node *node; | 80 | struct hlist_node *node; |
82 | struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; | 81 | struct gw_node *gw_node, *curr_gw_tmp = NULL; |
83 | uint8_t max_tq = 0; | 82 | uint8_t max_tq = 0; |
84 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; | 83 | uint32_t max_gw_factor = 0, tmp_gw_factor = 0; |
85 | int down, up; | 84 | int down, up; |
@@ -174,14 +173,10 @@ void gw_election(struct bat_priv *bat_priv) | |||
174 | curr_gw_tmp->orig_node->gw_flags, | 173 | curr_gw_tmp->orig_node->gw_flags, |
175 | curr_gw_tmp->orig_node->router->tq_avg); | 174 | curr_gw_tmp->orig_node->router->tq_avg); |
176 | 175 | ||
177 | old_gw_node = gw_select(bat_priv, curr_gw_tmp); | 176 | gw_select(bat_priv, curr_gw_tmp); |
178 | } | 177 | } |
179 | 178 | ||
180 | rcu_read_unlock(); | 179 | rcu_read_unlock(); |
181 | |||
182 | /* the kfree() has to be outside of the rcu lock */ | ||
183 | if (old_gw_node) | ||
184 | kref_put(&old_gw_node->refcount, gw_node_free_ref); | ||
185 | } | 180 | } |
186 | 181 | ||
187 | void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) | 182 | 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, | |||
242 | memset(gw_node, 0, sizeof(struct gw_node)); | 237 | memset(gw_node, 0, sizeof(struct gw_node)); |
243 | INIT_HLIST_NODE(&gw_node->list); | 238 | INIT_HLIST_NODE(&gw_node->list); |
244 | gw_node->orig_node = orig_node; | 239 | gw_node->orig_node = orig_node; |
245 | kref_init(&gw_node->refcount); | 240 | atomic_set(&gw_node->refcount, 1); |
246 | 241 | ||
247 | spin_lock_bh(&bat_priv->gw_list_lock); | 242 | spin_lock_bh(&bat_priv->gw_list_lock); |
248 | hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); | 243 | hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); |
@@ -325,7 +320,7 @@ void gw_node_purge(struct bat_priv *bat_priv) | |||
325 | gw_deselect(bat_priv); | 320 | gw_deselect(bat_priv); |
326 | 321 | ||
327 | hlist_del_rcu(&gw_node->list); | 322 | hlist_del_rcu(&gw_node->list); |
328 | call_rcu(&gw_node->rcu, gw_node_free_rcu); | 323 | gw_node_free_ref(gw_node); |
329 | } | 324 | } |
330 | 325 | ||
331 | 326 | ||