]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
batman-adv: Fix rcu_barrier() miss due to double call_rcu() in TT code
authorLinus Lüssing <linus.luessing@web.de>
Mon, 15 Apr 2013 13:43:29 +0000 (21:43 +0800)
committerAntonio Quartulli <ordex@autistici.org>
Fri, 17 May 2013 07:54:28 +0000 (09:54 +0200)
rcu_barrier() only waits for the currently scheduled rcu functions
to finish - it won't wait for any function scheduled via another
call_rcu() within an rcu scheduled function.

Unfortunately our batadv_tt_orig_list_entry_free_ref() does just that,
via a batadv_orig_node_free_ref() call, leading to our rcu_barrier()
call potentially missing such a batadv_orig_node_free_ref().

This patch fixes this issue by calling the batadv_orig_node_free_rcu()
directly from the rcu callback, removing the unnecessary, additional
call_rcu() layer here.

Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Acked-by: Antonio Quartulli <ordex@autistici.org>
net/batman-adv/originator.c
net/batman-adv/originator.h
net/batman-adv/translation-table.c

index 2f3452546636ce71747ef9a8b1a949caae8d8f04..fad1a2093e15fee8889bb190fac96868143bd0f5 100644 (file)
@@ -156,12 +156,28 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
        kfree(orig_node);
 }
 
+/**
+ * batadv_orig_node_free_ref - decrement the orig node refcounter and possibly
+ * schedule an rcu callback for freeing it
+ * @orig_node: the orig node to free
+ */
 void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node)
 {
        if (atomic_dec_and_test(&orig_node->refcount))
                call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu);
 }
 
+/**
+ * batadv_orig_node_free_ref_now - decrement the orig node refcounter and
+ * possibly free it (without rcu callback)
+ * @orig_node: the orig node to free
+ */
+void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node)
+{
+       if (atomic_dec_and_test(&orig_node->refcount))
+               batadv_orig_node_free_rcu(&orig_node->rcu);
+}
+
 void batadv_originator_free(struct batadv_priv *bat_priv)
 {
        struct batadv_hashtable *hash = bat_priv->orig_hash;
index 7df48fa7669dd0ac173a51bafe3d6538fb049e4b..734e5a3d8a5b29fdf389b04db14e3a37504cf3cb 100644 (file)
@@ -26,6 +26,7 @@ int batadv_originator_init(struct batadv_priv *bat_priv);
 void batadv_originator_free(struct batadv_priv *bat_priv);
 void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
 void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
+void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
 struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
                                              const uint8_t *addr);
 struct batadv_neigh_node *
index 5e89deeb9542979c72ba635fb1c18d301a9c1621..9e87485758455743984bf51bf783c7bf61cee348 100644 (file)
@@ -144,7 +144,12 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
        struct batadv_tt_orig_list_entry *orig_entry;
 
        orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
-       batadv_orig_node_free_ref(orig_entry->orig_node);
+
+       /* We are in an rcu callback here, therefore we cannot use
+        * batadv_orig_node_free_ref() and its call_rcu():
+        * An rcu_barrier() wouldn't wait for that to finish
+        */
+       batadv_orig_node_free_ref_now(orig_entry->orig_node);
        kfree(orig_entry);
 }