[XFRM]: Speed up xfrm_policy and xfrm_state walking

Change xfrm_policy and xfrm_state walking algorithm from O(n^2) to O(n).
This is achieved adding the entries to one more list which is used
solely for walking the entries.

This also fixes some races where the dump can have duplicate or missing
entries when the SPD/SADB is modified during an ongoing dump.

Dumping SADB with 20000 entries using "time ip xfrm state" the sys
time dropped from 1.012s to 0.080s.

Signed-off-by: Timo Teras <timo.teras@iki.fi>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Timo Teras
2008-02-28 21:31:08 -08:00
committed by David S. Miller
parent 1e04d53070
commit 4c563f7669
6 changed files with 197 additions and 85 deletions

View File

@@ -50,6 +50,7 @@ static DEFINE_SPINLOCK(xfrm_state_lock);
* Main use is finding SA after policy selected tunnel or transport mode.
* Also, it can be used by ah/esp icmp error handler to find offending SA.
*/
static LIST_HEAD(xfrm_state_all);
static struct hlist_head *xfrm_state_bydst __read_mostly;
static struct hlist_head *xfrm_state_bysrc __read_mostly;
static struct hlist_head *xfrm_state_byspi __read_mostly;
@@ -510,6 +511,7 @@ struct xfrm_state *xfrm_state_alloc(void)
if (x) {
atomic_set(&x->refcnt, 1);
atomic_set(&x->tunnel_users, 0);
INIT_LIST_HEAD(&x->all);
INIT_HLIST_NODE(&x->bydst);
INIT_HLIST_NODE(&x->bysrc);
INIT_HLIST_NODE(&x->byspi);
@@ -533,6 +535,10 @@ void __xfrm_state_destroy(struct xfrm_state *x)
{
BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
spin_lock_bh(&xfrm_state_lock);
list_del(&x->all);
spin_unlock_bh(&xfrm_state_lock);
spin_lock_bh(&xfrm_state_gc_lock);
hlist_add_head(&x->bydst, &xfrm_state_gc_list);
spin_unlock_bh(&xfrm_state_gc_lock);
@@ -909,6 +915,8 @@ static void __xfrm_state_insert(struct xfrm_state *x)
x->genid = ++xfrm_state_genid;
list_add_tail(&x->all, &xfrm_state_all);
h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
x->props.reqid, x->props.family);
hlist_add_head(&x->bydst, xfrm_state_bydst+h);
@@ -1518,36 +1526,47 @@ unlock:
}
EXPORT_SYMBOL(xfrm_alloc_spi);
int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
int xfrm_state_walk(struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*),
void *data)
{
int i;
struct xfrm_state *x, *last = NULL;
struct hlist_node *entry;
int count = 0;
struct xfrm_state *old, *x, *last = NULL;
int err = 0;
if (walk->state == NULL && walk->count != 0)
return 0;
old = x = walk->state;
walk->state = NULL;
spin_lock_bh(&xfrm_state_lock);
for (i = 0; i <= xfrm_state_hmask; i++) {
hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
if (!xfrm_id_proto_match(x->id.proto, proto))
continue;
if (last) {
err = func(last, count, data);
if (err)
goto out;
if (x == NULL)
x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
list_for_each_entry_from(x, &xfrm_state_all, all) {
if (x->km.state == XFRM_STATE_DEAD)
continue;
if (!xfrm_id_proto_match(x->id.proto, walk->proto))
continue;
if (last) {
err = func(last, walk->count, data);
if (err) {
xfrm_state_hold(last);
walk->state = last;
goto out;
}
last = x;
count++;
}
last = x;
walk->count++;
}
if (count == 0) {
if (walk->count == 0) {
err = -ENOENT;
goto out;
}
err = func(last, 0, data);
if (last)
err = func(last, 0, data);
out:
spin_unlock_bh(&xfrm_state_lock);
if (old != NULL)
xfrm_state_put(old);
return err;
}
EXPORT_SYMBOL(xfrm_state_walk);