[8/9] batman-adv: count election of gateway as reference

Message ID 1284738065-8715-9-git-send-email-sven.eckelmann@gmx.de (mailing list archive)
State Accepted, archived
Headers

Commit Message

Sven Eckelmann Sept. 17, 2010, 3:41 p.m. UTC
  We reference the gw_nodes as curr_gw for each bat_priv. They are tracked
inside a rcu protected list gw_list which may free the memory after it
gets removed from the list. Nevertheless it could still be referenced by
curr_gw and access to it would result in a kernel oops.

Signed-off-by: Sven Eckelmann <sven.eckelmann@gmx.de>
---
 batman-adv/gateway_client.c |   22 +++++++++++++++++++---
 1 files changed, 19 insertions(+), 3 deletions(-)
  

Patch

diff --git a/batman-adv/gateway_client.c b/batman-adv/gateway_client.c
index 16f0757..1cad4f8 100644
--- a/batman-adv/gateway_client.c
+++ b/batman-adv/gateway_client.c
@@ -51,13 +51,18 @@  void *gw_get_selected(struct bat_priv *bat_priv)
 
 void gw_deselect(struct bat_priv *bat_priv)
 {
+	struct gw_node *gw_node = bat_priv->curr_gw;
+
 	bat_priv->curr_gw = NULL;
+
+	if (gw_node)
+		gw_node_put(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_tmp = NULL, *old_gw_node = NULL;
 	uint8_t max_tq = 0;
 	uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
 	int down, up;
@@ -131,7 +136,6 @@  void gw_election(struct bat_priv *bat_priv)
 		if (tmp_gw_factor > max_gw_factor)
 			max_gw_factor = tmp_gw_factor;
 	}
-	rcu_read_unlock();
 
 	if (bat_priv->curr_gw != curr_gw_tmp) {
 		if ((bat_priv->curr_gw) && (!curr_gw_tmp))
@@ -153,8 +157,17 @@  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 = bat_priv->curr_gw;
+		if (curr_gw_tmp)
+			gw_node_hold(curr_gw_tmp);
+
 		bat_priv->curr_gw = curr_gw_tmp;
 	}
+
+	rcu_read_unlock();
+
+	if (old_gw_node)
+		gw_node_put(old_gw_node);
 }
 
 void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
@@ -258,8 +271,11 @@  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 == bat_priv->curr_gw) {
+				rcu_read_unlock();
 				gw_deselect(bat_priv);
+				return;
+			}
 		}
 
 		rcu_read_unlock();