Skip to content

Commit ff3f00a

Browse files
klassertmehmetb0
authored andcommitted
xfrm: Cache used outbound xfrm states at the policy.
BugLink: https://bugs.launchpad.net/bugs/2103829 [ Upstream commit 0045e3d80613cc7174dc15f189ee6fc4e73b9365 ] Now that we can have percpu xfrm states, the number of active states might increase. To get a better lookup performance, we cache the used xfrm states at the policy for outbound IPsec traffic. Signed-off-by: Steffen Klassert <[email protected]> Tested-by: Antony Antony <[email protected]> Tested-by: Tobias Brunner <[email protected]> Stable-dep-of: e952837f3ddb ("xfrm: state: fix out-of-bounds read during lookup") Signed-off-by: Sasha Levin <[email protected]> [nwager: Drop hunk #2 due to missing commit: 17163f23678c ("xfrm: minor update to sdb and xfrm_policy comments") and context change in hunk #4 due to missing commit: a54ad727f745 ("xfrm: policy: remove remaining use of inexact list"). I am keeping this change despite the backport effort becuase the Stable-dep-of commit fixes an in-tree patch.] Signed-off-by: Noah Wager <[email protected]> Signed-off-by: Mehmet Basaran <[email protected]>
1 parent e276c24 commit ff3f00a

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

include/net/xfrm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ struct xfrm_state {
184184
};
185185
struct hlist_node byspi;
186186
struct hlist_node byseq;
187+
struct hlist_node state_cache;
187188

188189
refcount_t refcnt;
189190
spinlock_t lock;
@@ -537,6 +538,8 @@ struct xfrm_policy {
537538
struct hlist_node bydst;
538539
struct hlist_node byidx;
539540

541+
struct hlist_head state_cache_list;
542+
540543
/* This lock only affects elements except for entry. */
541544
rwlock_t lock;
542545
refcount_t refcnt;

net/xfrm/xfrm_policy.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
431431
if (policy) {
432432
write_pnet(&policy->xp_net, net);
433433
INIT_LIST_HEAD(&policy->walk.all);
434+
INIT_HLIST_HEAD(&policy->state_cache_list);
434435
INIT_HLIST_NODE(&policy->bydst_inexact_list);
435436
INIT_HLIST_NODE(&policy->bydst);
436437
INIT_HLIST_NODE(&policy->byidx);
@@ -473,6 +474,9 @@ EXPORT_SYMBOL(xfrm_policy_destroy);
473474

474475
static void xfrm_policy_kill(struct xfrm_policy *policy)
475476
{
477+
struct net *net = xp_net(policy);
478+
struct xfrm_state *x;
479+
476480
xfrm_dev_policy_delete(policy);
477481

478482
write_lock_bh(&policy->lock);
@@ -488,6 +492,13 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
488492
if (del_timer(&policy->timer))
489493
xfrm_pol_put(policy);
490494

495+
/* XXX: Flush state cache */
496+
spin_lock_bh(&net->xfrm.xfrm_state_lock);
497+
hlist_for_each_entry_rcu(x, &policy->state_cache_list, state_cache) {
498+
hlist_del_init_rcu(&x->state_cache);
499+
}
500+
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
501+
491502
xfrm_pol_put(policy);
492503
}
493504

@@ -3283,6 +3294,7 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
32833294
dst_release(dst);
32843295
dst = dst_orig;
32853296
}
3297+
32863298
ok:
32873299
xfrm_pols_put(pols, drop_pols);
32883300
if (dst && dst->xfrm &&

net/xfrm/xfrm_state.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
665665
refcount_set(&x->refcnt, 1);
666666
atomic_set(&x->tunnel_users, 0);
667667
INIT_LIST_HEAD(&x->km.all);
668+
INIT_HLIST_NODE(&x->state_cache);
668669
INIT_HLIST_NODE(&x->bydst);
669670
INIT_HLIST_NODE(&x->bysrc);
670671
INIT_HLIST_NODE(&x->byspi);
@@ -744,12 +745,15 @@ int __xfrm_state_delete(struct xfrm_state *x)
744745

745746
if (x->km.state != XFRM_STATE_DEAD) {
746747
x->km.state = XFRM_STATE_DEAD;
748+
747749
spin_lock(&net->xfrm.xfrm_state_lock);
748750
list_del(&x->km.all);
749751
hlist_del_rcu(&x->bydst);
750752
hlist_del_rcu(&x->bysrc);
751753
if (x->km.seq)
752754
hlist_del_rcu(&x->byseq);
755+
if (!hlist_unhashed(&x->state_cache))
756+
hlist_del_rcu(&x->state_cache);
753757
if (x->id.spi)
754758
hlist_del_rcu(&x->byspi);
755759
net->xfrm.state_num--;
@@ -1222,6 +1226,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
12221226
unsigned int sequence;
12231227
struct km_event c;
12241228
unsigned int pcpu_id;
1229+
bool cached = false;
12251230

12261231
/* We need the cpu id just as a lookup key,
12271232
* we don't require it to be stable.
@@ -1234,6 +1239,46 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
12341239
sequence = read_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
12351240

12361241
rcu_read_lock();
1242+
hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
1243+
if (x->props.family == encap_family &&
1244+
x->props.reqid == tmpl->reqid &&
1245+
(mark & x->mark.m) == x->mark.v &&
1246+
x->if_id == if_id &&
1247+
!(x->props.flags & XFRM_STATE_WILDRECV) &&
1248+
xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
1249+
tmpl->mode == x->props.mode &&
1250+
tmpl->id.proto == x->id.proto &&
1251+
(tmpl->id.spi == x->id.spi || !tmpl->id.spi))
1252+
xfrm_state_look_at(pol, x, fl, encap_family,
1253+
&best, &acquire_in_progress, &error);
1254+
}
1255+
1256+
if (best)
1257+
goto cached;
1258+
1259+
hlist_for_each_entry_rcu(x, &pol->state_cache_list, state_cache) {
1260+
if (x->props.family == encap_family &&
1261+
x->props.reqid == tmpl->reqid &&
1262+
(mark & x->mark.m) == x->mark.v &&
1263+
x->if_id == if_id &&
1264+
!(x->props.flags & XFRM_STATE_WILDRECV) &&
1265+
xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
1266+
tmpl->mode == x->props.mode &&
1267+
tmpl->id.proto == x->id.proto &&
1268+
(tmpl->id.spi == x->id.spi || !tmpl->id.spi))
1269+
xfrm_state_look_at(pol, x, fl, family,
1270+
&best, &acquire_in_progress, &error);
1271+
}
1272+
1273+
cached:
1274+
cached = true;
1275+
if (best)
1276+
goto found;
1277+
else if (error)
1278+
best = NULL;
1279+
else if (acquire_in_progress) /* XXX: acquire_in_progress should not happen */
1280+
WARN_ON(1);
1281+
12371282
h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
12381283
hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
12391284
#ifdef CONFIG_XFRM_OFFLOAD
@@ -1383,6 +1428,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
13831428
XFRM_STATE_INSERT(bysrc, &x->bysrc,
13841429
net->xfrm.state_bysrc + h,
13851430
x->xso.type);
1431+
INIT_HLIST_NODE(&x->state_cache);
13861432
if (x->id.spi) {
13871433
h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
13881434
XFRM_STATE_INSERT(byspi, &x->byspi,
@@ -1431,6 +1477,15 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
14311477
} else {
14321478
*err = acquire_in_progress ? -EAGAIN : error;
14331479
}
1480+
1481+
if (x && x->km.state == XFRM_STATE_VALID && !cached &&
1482+
(!(pol->flags & XFRM_POLICY_CPU_ACQUIRE) || x->pcpu_num == pcpu_id)) {
1483+
spin_lock_bh(&net->xfrm.xfrm_state_lock);
1484+
if (hlist_unhashed(&x->state_cache))
1485+
hlist_add_head_rcu(&x->state_cache, &pol->state_cache_list);
1486+
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
1487+
}
1488+
14341489
rcu_read_unlock();
14351490
if (to_put)
14361491
xfrm_state_put(to_put);

0 commit comments

Comments
 (0)