Message ID | 7fc1e77cc5156ba594858f3dcfe91fe8f56a3467.1376668723.git.joseph.salisbury@canonical.com |
---|---|
State | New |
Headers | show |
On Tue, Sep 10, 2013 at 04:26:42PM -0400, Joseph Salisbury wrote: > From: Eric Dumazet <edumazet@google.com> > > BugLink: http://bugs.launchpad.net/bugs/1205741 > > commit 5faa5df1fa2024 (inetpeer: Invalidate the inetpeer tree along with > the routing cache) added a race : > > Before freeing an inetpeer, we must respect a RCU grace period, and make > sure no user will attempt to increase refcnt. > > inetpeer_invalidate_tree() waits for a RCU grace period before inserting > inetpeer tree into gc_list and waking the worker. At that time, no > concurrent lookup can find a inetpeer in this tree. > > Signed-off-by: Eric Dumazet <edumazet@google.com> > Cc: Steffen Klassert <steffen.klassert@secunet.com> > Acked-by: Steffen Klassert <steffen.klassert@secunet.com> > Signed-off-by: David S. Miller <davem@davemloft.net> > Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com> > (cherry picked from commit 55432d2b543a4b6dfae54f5c432a566877a85d90) Strange ordering for your signed off relative to the cherry-picked, i would have expected: (cherry picked from commit 55432d2b543a4b6dfae54f5c432a566877a85d90) Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com> -apw
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index ac8a4ea..34b06da 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -41,7 +41,10 @@ struct inet_peer { u32 pmtu_orig; u32 pmtu_learned; struct inetpeer_addr_base redirect_learned; - struct list_head gc_list; + union { + struct list_head gc_list; + struct rcu_head gc_rcu; + }; /* * Once inet_peer is queued for deletion (refcnt == -1), following fields * are not available: rid, ip_id_count, tcp_ts, tcp_ts_stamp diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index d77a7dd..552e4f8 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -560,6 +560,17 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout) } EXPORT_SYMBOL(inet_peer_xrlim_allow); +static void inetpeer_inval_rcu(struct rcu_head *head) +{ + struct inet_peer *p = container_of(head, struct inet_peer, gc_rcu); + + spin_lock_bh(&gc_lock); + list_add_tail(&p->gc_list, &gc_list); + spin_unlock_bh(&gc_lock); + + schedule_delayed_work(&gc_work, gc_delay); +} + void inetpeer_invalidate_tree(int family) { struct inet_peer *old, *new, *prev; @@ -576,10 +587,7 @@ void inetpeer_invalidate_tree(int family) prev = cmpxchg(&base->root, old, new); if (prev == old) { base->total = 0; - spin_lock(&gc_lock); - list_add_tail(&prev->gc_list, &gc_list); - spin_unlock(&gc_lock); - schedule_delayed_work(&gc_work, gc_delay); + call_rcu(&prev->gc_rcu, inetpeer_inval_rcu); } out: