Commit 575f4cd5 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by David S. Miller

net: Use rcu lookups in inet_twsk_purge.

While we are looking up entries to free there is no reason to take
the lock in inet_twsk_purge.  We have to drop locks and restart
occassionally anyway so adding a few more in case we get on the
wrong list because of a timewait move is no big deal.  At the
same time not taking the lock for long periods of time is much
more polite to the rest of the users of the hash table.

In my test configuration of killing 4k network namespaces
this change causes 4k back to back runs of inet_twsk_purge on an
empty hash table to go from roughly 20.7s to 3.3s, and the total
time to destroy 4k network namespaces goes from roughly 44s to
3.3s.
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Acked-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e9c5158a
...@@ -427,31 +427,40 @@ void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo, ...@@ -427,31 +427,40 @@ void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo,
struct inet_timewait_sock *tw; struct inet_timewait_sock *tw;
struct sock *sk; struct sock *sk;
struct hlist_nulls_node *node; struct hlist_nulls_node *node;
int h; unsigned int slot;
local_bh_disable(); for (slot = 0; slot <= hashinfo->ehash_mask; slot++) {
for (h = 0; h <= hashinfo->ehash_mask; h++) { struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
struct inet_ehash_bucket *head = restart_rcu:
inet_ehash_bucket(hashinfo, h); rcu_read_lock();
spinlock_t *lock = inet_ehash_lockp(hashinfo, h);
restart: restart:
spin_lock(lock); sk_nulls_for_each_rcu(sk, node, &head->twchain) {
sk_nulls_for_each(sk, node, &head->twchain) {
tw = inet_twsk(sk); tw = inet_twsk(sk);
if (!net_eq(twsk_net(tw), net) || if (!net_eq(twsk_net(tw), net) ||
tw->tw_family != family) tw->tw_family != family)
continue; continue;
atomic_inc(&tw->tw_refcnt); if (unlikely(!atomic_inc_not_zero(&tw->tw_refcnt)))
spin_unlock(lock); continue;
if (unlikely(!net_eq(twsk_net(tw), net) ||
tw->tw_family != family)) {
inet_twsk_put(tw);
goto restart;
}
rcu_read_unlock();
inet_twsk_deschedule(tw, twdr); inet_twsk_deschedule(tw, twdr);
inet_twsk_put(tw); inet_twsk_put(tw);
goto restart_rcu;
goto restart;
} }
spin_unlock(lock); /* If the nulls value we got at the end of this lookup is
* not the expected one, we must restart lookup.
* We probably met an item that was moved to another chain.
*/
if (get_nulls_value(node) != slot)
goto restart;
rcu_read_unlock();
} }
local_bh_enable();
} }
EXPORT_SYMBOL_GPL(inet_twsk_purge); EXPORT_SYMBOL_GPL(inet_twsk_purge);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment