netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
@ 2012-06-05  9:28 Eric Dumazet
  2012-06-05 11:56 ` Steffen Klassert
  0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2012-06-05  9:28 UTC (permalink / raw)
  To: David Miller; +Cc: Steffen Klassert, netdev

From: Eric Dumazet <edumazet@google.com>

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.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
---
 net/ipv4/inetpeer.c |   14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index d4d61b6..f936e95 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -108,6 +108,11 @@ int inet_peer_threshold __read_mostly = 65536 + 128;	/* start to throw entries m
 int inet_peer_minttl __read_mostly = 120 * HZ;	/* TTL under high load: 120 sec */
 int inet_peer_maxttl __read_mostly = 10 * 60 * HZ;	/* usual time to live: 10 min */
 
+static void inetpeer_free_rcu(struct rcu_head *head)
+{
+	kmem_cache_free(peer_cachep, container_of(head, struct inet_peer, rcu));
+}
+
 static void inetpeer_gc_worker(struct work_struct *work)
 {
 	struct inet_peer *p, *n;
@@ -137,9 +142,9 @@ static void inetpeer_gc_worker(struct work_struct *work)
 
 		n = list_entry(p->gc_list.next, struct inet_peer, gc_list);
 
-		if (!atomic_read(&p->refcnt)) {
+		if (atomic_cmpxchg(&p->refcnt, 0, -1) == 0) {
 			list_del(&p->gc_list);
-			kmem_cache_free(peer_cachep, p);
+			call_rcu(&p->rcu, inetpeer_free_rcu);
 		}
 	}
 
@@ -364,11 +369,6 @@ do {								\
 	peer_avl_rebalance(stack, stackptr, base);		\
 } while (0)
 
-static void inetpeer_free_rcu(struct rcu_head *head)
-{
-	kmem_cache_free(peer_cachep, container_of(head, struct inet_peer, rcu));
-}
-
 static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base,
 			     struct inet_peer __rcu **stack[PEER_MAXDEPTH])
 {

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
  2012-06-05  9:28 [PATCH] inetpeer: fix a race in inetpeer_gc_worker() Eric Dumazet
@ 2012-06-05 11:56 ` Steffen Klassert
  2012-06-05 12:19   ` Eric Dumazet
  0 siblings, 1 reply; 6+ messages in thread
From: Steffen Klassert @ 2012-06-05 11:56 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev

On Tue, Jun 05, 2012 at 11:28:27AM +0200, Eric Dumazet wrote:
> From: Eric Dumazet <edumazet@google.com>
> 
> 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.
> 

As already mentioned in the other mail. In this case, I think
we can just delete the inetpeer once the refcount got zero.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
  2012-06-05 11:56 ` Steffen Klassert
@ 2012-06-05 12:19   ` Eric Dumazet
  2012-06-05 12:42     ` Steffen Klassert
  0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2012-06-05 12:19 UTC (permalink / raw)
  To: Steffen Klassert; +Cc: David Miller, netdev

On Tue, 2012-06-05 at 13:56 +0200, Steffen Klassert wrote:
> On Tue, Jun 05, 2012 at 11:28:27AM +0200, Eric Dumazet wrote:
> > From: Eric Dumazet <edumazet@google.com>
> > 
> > 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.
> > 
> 
> As already mentioned in the other mail. In this case, I think
> we can just delete the inetpeer once the refcount got zero.
> 

Nope, a concurrent lookup can find an entry about to be freed.

We must prevent it to increase the refcount from 0 to 1.

And we must wait a RCU grace period before freeing inetpeer.

Alternative would be the following patch :


diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index d4d61b6..6df9951 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, rcu);
+
+	spin_lock(&gc_lock);
+	list_add_tail(&p->gc_list, &gc_list);
+	spin_unlock(&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->rcu, inetpeer_inval_rcu);
 	}
 
 out:

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
  2012-06-05 12:19   ` Eric Dumazet
@ 2012-06-05 12:42     ` Steffen Klassert
  2012-06-05 12:56       ` Eric Dumazet
  0 siblings, 1 reply; 6+ messages in thread
From: Steffen Klassert @ 2012-06-05 12:42 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev

On Tue, Jun 05, 2012 at 02:19:12PM +0200, Eric Dumazet wrote:
> On Tue, 2012-06-05 at 13:56 +0200, Steffen Klassert wrote:
> > On Tue, Jun 05, 2012 at 11:28:27AM +0200, Eric Dumazet wrote:
> > > From: Eric Dumazet <edumazet@google.com>
> > > 
> > > 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.
> > > 
> > 
> > As already mentioned in the other mail. In this case, I think
> > we can just delete the inetpeer once the refcount got zero.
> > 
> 
> Nope, a concurrent lookup can find an entry about to be freed.

Hm, I agree that we need rcu protection when we remove single entries
from an inetpeer tree. But in this case we invalidate the entire tree.

The first lookup after inetpeer_invalidate_tree() was invoked should
find an empty tree, base->root initialized to peer_avl_empty_rcu.

Maybe I'm wrong, but I don't see how a lookup should find such an
old invalidated tree.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
  2012-06-05 12:42     ` Steffen Klassert
@ 2012-06-05 12:56       ` Eric Dumazet
  2012-06-05 13:01         ` Steffen Klassert
  0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2012-06-05 12:56 UTC (permalink / raw)
  To: Steffen Klassert; +Cc: David Miller, netdev

On Tue, 2012-06-05 at 14:42 +0200, Steffen Klassert wrote:
> On Tue, Jun 05, 2012 at 02:19:12PM +0200, Eric Dumazet wrote:
> > On Tue, 2012-06-05 at 13:56 +0200, Steffen Klassert wrote:
> > > On Tue, Jun 05, 2012 at 11:28:27AM +0200, Eric Dumazet wrote:
> > > > From: Eric Dumazet <edumazet@google.com>
> > > > 
> > > > 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.
> > > > 
> > > 
> > > As already mentioned in the other mail. In this case, I think
> > > we can just delete the inetpeer once the refcount got zero.
> > > 
> > 
> > Nope, a concurrent lookup can find an entry about to be freed.
> 
> Hm, I agree that we need rcu protection when we remove single entries
> from an inetpeer tree. But in this case we invalidate the entire tree.
> 
> The first lookup after inetpeer_invalidate_tree() was invoked should
> find an empty tree, base->root initialized to peer_avl_empty_rcu.
> 
> Maybe I'm wrong, but I don't see how a lookup should find such an
> old invalidated tree.
> 

You are absolutely wrong yes.

A concurrent lookup can read previous values of the root pointer, even
if you wrote a new value in it. Thats whole RCU point.

Only waiting a rcu grace period make sure all lookups can see the new
root pointer.

I'll send a v2 to avoid atomics in the worker itself.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] inetpeer: fix a race in inetpeer_gc_worker()
  2012-06-05 12:56       ` Eric Dumazet
@ 2012-06-05 13:01         ` Steffen Klassert
  0 siblings, 0 replies; 6+ messages in thread
From: Steffen Klassert @ 2012-06-05 13:01 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev

On Tue, Jun 05, 2012 at 02:56:06PM +0200, Eric Dumazet wrote:
> On Tue, 2012-06-05 at 14:42 +0200, Steffen Klassert wrote:
> > 
> > Hm, I agree that we need rcu protection when we remove single entries
> > from an inetpeer tree. But in this case we invalidate the entire tree.
> > 
> > The first lookup after inetpeer_invalidate_tree() was invoked should
> > find an empty tree, base->root initialized to peer_avl_empty_rcu.
> > 
> > Maybe I'm wrong, but I don't see how a lookup should find such an
> > old invalidated tree.
> > 
> 
> You are absolutely wrong yes.
> 
> A concurrent lookup can read previous values of the root pointer, even
> if you wrote a new value in it. Thats whole RCU point.

Argh, yes. You are absolutely right of course, now I got it :)
Thanks for your explaination.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2012-06-05 13:01 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-05  9:28 [PATCH] inetpeer: fix a race in inetpeer_gc_worker() Eric Dumazet
2012-06-05 11:56 ` Steffen Klassert
2012-06-05 12:19   ` Eric Dumazet
2012-06-05 12:42     ` Steffen Klassert
2012-06-05 12:56       ` Eric Dumazet
2012-06-05 13:01         ` Steffen Klassert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).