* [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).