| From foo@baz Tue Jan 26 21:35:03 PST 2016 |
| From: Sven Eckelmann <sven@narfation.org> |
| Date: Tue, 5 Jan 2016 12:06:20 +0100 |
| Subject: batman-adv: Drop immediate orig_node free function |
| |
| From: Sven Eckelmann <sven@narfation.org> |
| |
| [ Upstream commit 42eff6a617e23b691f8e4467f4687ed7245a92db ] |
| |
| It is not allowed to free the memory of an object which is part of a list |
| which is protected by rcu-read-side-critical sections without making sure |
| that no other context is accessing the object anymore. This usually happens |
| by removing the references to this object and then waiting until the rcu |
| grace period is over and no one (allowedly) accesses it anymore. |
| |
| But the _now functions ignore this completely. They free the object |
| directly even when a different context still tries to access it. This has |
| to be avoided and thus these functions must be removed and all functions |
| have to use batadv_orig_node_free_ref. |
| |
| Fixes: 72822225bd41 ("batman-adv: Fix rcu_barrier() miss due to double call_rcu() in TT code") |
| Signed-off-by: Sven Eckelmann <sven@narfation.org> |
| Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch> |
| Signed-off-by: Antonio Quartulli <a@unstable.cc> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/batman-adv/originator.c | 11 ----------- |
| net/batman-adv/originator.h | 1 - |
| net/batman-adv/translation-table.c | 28 +++++++++++++--------------- |
| 3 files changed, 13 insertions(+), 27 deletions(-) |
| |
| --- a/net/batman-adv/originator.c |
| +++ b/net/batman-adv/originator.c |
| @@ -601,17 +601,6 @@ void batadv_orig_node_free_ref(struct ba |
| batadv_orig_node_release(orig_node); |
| } |
| |
| -/** |
| - * 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; |
| --- a/net/batman-adv/originator.h |
| +++ b/net/batman-adv/originator.h |
| @@ -38,7 +38,6 @@ int batadv_originator_init(struct batadv |
| 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_orig_node_new(struct batadv_priv *bat_priv, |
| const u8 *addr); |
| struct batadv_neigh_node * |
| --- a/net/batman-adv/translation-table.c |
| +++ b/net/batman-adv/translation-table.c |
| @@ -238,20 +238,6 @@ int batadv_tt_global_hash_count(struct b |
| return count; |
| } |
| |
| -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); |
| - |
| - /* 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); |
| -} |
| - |
| /** |
| * batadv_tt_local_size_mod - change the size by v of the local table identified |
| * by vid |
| @@ -347,13 +333,25 @@ static void batadv_tt_global_size_dec(st |
| batadv_tt_global_size_mod(orig_node, vid, -1); |
| } |
| |
| +/** |
| + * batadv_tt_orig_list_entry_release - release tt orig entry from lists and |
| + * queue for free after rcu grace period |
| + * @orig_entry: tt orig entry to be free'd |
| + */ |
| +static void |
| +batadv_tt_orig_list_entry_release(struct batadv_tt_orig_list_entry *orig_entry) |
| +{ |
| + batadv_orig_node_free_ref(orig_entry->orig_node); |
| + kfree_rcu(orig_entry, rcu); |
| +} |
| + |
| static void |
| batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) |
| { |
| if (!atomic_dec_and_test(&orig_entry->refcount)) |
| return; |
| |
| - call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); |
| + batadv_tt_orig_list_entry_release(orig_entry); |
| } |
| |
| /** |