| From foo@baz Tue Jan 26 21:31:27 PST 2016 |
| From: Sven Eckelmann <sven@narfation.org> |
| Date: Tue, 5 Jan 2016 12:06:19 +0100 |
| Subject: batman-adv: Avoid recursive call_rcu for batadv_nc_node |
| |
| From: Sven Eckelmann <sven@narfation.org> |
| |
| [ Upstream commit 44e8e7e91d6c7c7ab19688750f7257292640d1a0 ] |
| |
| The batadv_nc_node_free_ref function uses call_rcu to delay the free of the |
| batadv_nc_node object until no (already started) rcu_read_lock is enabled |
| anymore. This makes sure that no context is still trying to access the |
| object which should be removed. But batadv_nc_node also contains a |
| reference to orig_node which must be removed. |
| |
| The reference drop of orig_node was done in the call_rcu function |
| batadv_nc_node_free_rcu but should actually be done in the |
| batadv_nc_node_release function to avoid nested call_rcus. This is |
| important because rcu_barrier (e.g. batadv_softif_free or batadv_exit) will |
| not detect the inner call_rcu as relevant for its execution. Otherwise this |
| barrier will most likely be inserted in the queue before the callback of |
| the first call_rcu was executed. The caller of rcu_barrier will therefore |
| continue to run before the inner call_rcu callback finished. |
| |
| Fixes: d56b1705e28c ("batman-adv: network coding - detect coding nodes and remove these after timeout") |
| 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/network-coding.c | 19 ++++++++----------- |
| 1 file changed, 8 insertions(+), 11 deletions(-) |
| |
| --- a/net/batman-adv/network-coding.c |
| +++ b/net/batman-adv/network-coding.c |
| @@ -203,28 +203,25 @@ void batadv_nc_init_orig(struct batadv_o |
| } |
| |
| /** |
| - * batadv_nc_node_free_rcu - rcu callback to free an nc node and remove |
| - * its refcount on the orig_node |
| - * @rcu: rcu pointer of the nc node |
| + * batadv_nc_node_release - release nc_node from lists and queue for free after |
| + * rcu grace period |
| + * @nc_node: the nc node to free |
| */ |
| -static void batadv_nc_node_free_rcu(struct rcu_head *rcu) |
| +static void batadv_nc_node_release(struct batadv_nc_node *nc_node) |
| { |
| - struct batadv_nc_node *nc_node; |
| - |
| - nc_node = container_of(rcu, struct batadv_nc_node, rcu); |
| batadv_orig_node_free_ref(nc_node->orig_node); |
| - kfree(nc_node); |
| + kfree_rcu(nc_node, rcu); |
| } |
| |
| /** |
| - * batadv_nc_node_free_ref - decrements the nc node refcounter and possibly |
| - * frees it |
| + * batadv_nc_node_free_ref - decrement the nc node refcounter and possibly |
| + * release it |
| * @nc_node: the nc node to free |
| */ |
| static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node) |
| { |
| if (atomic_dec_and_test(&nc_node->refcount)) |
| - call_rcu(&nc_node->rcu, batadv_nc_node_free_rcu); |
| + batadv_nc_node_release(nc_node); |
| } |
| |
| /** |