Skip to content

Commit 81a331a

Browse files
committed
xfrm: Add an inbound percpu state cache.
Now that we can have percpu xfrm states, the number of active states might increase. To get a better lookup performance, we add a percpu cache to cache the used inbound xfrm states. Signed-off-by: Steffen Klassert <[email protected]> Tested-by: Antony Antony <[email protected]> Tested-by: Tobias Brunner <[email protected]>
1 parent 0045e3d commit 81a331a

File tree

6 files changed

+70
-7
lines changed

6 files changed

+70
-7
lines changed

include/net/netns/xfrm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct netns_xfrm {
4343
struct hlist_head __rcu *state_bysrc;
4444
struct hlist_head __rcu *state_byspi;
4545
struct hlist_head __rcu *state_byseq;
46+
struct hlist_head __percpu *state_cache_input;
4647
unsigned int state_hmask;
4748
unsigned int state_num;
4849
struct work_struct state_hash_work;

include/net/xfrm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ struct xfrm_state {
185185
struct hlist_node byspi;
186186
struct hlist_node byseq;
187187
struct hlist_node state_cache;
188+
struct hlist_node state_cache_input;
188189

189190
refcount_t refcnt;
190191
spinlock_t lock;
@@ -1650,6 +1651,10 @@ int xfrm_state_update(struct xfrm_state *x);
16501651
struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark,
16511652
const xfrm_address_t *daddr, __be32 spi,
16521653
u8 proto, unsigned short family);
1654+
struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
1655+
const xfrm_address_t *daddr,
1656+
__be32 spi, u8 proto,
1657+
unsigned short family);
16531658
struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark,
16541659
const xfrm_address_t *daddr,
16551660
const xfrm_address_t *saddr,

net/ipv4/esp4_offload.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
5353
if (sp->len == XFRM_MAX_DEPTH)
5454
goto out_reset;
5555

56-
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
57-
(xfrm_address_t *)&ip_hdr(skb)->daddr,
58-
spi, IPPROTO_ESP, AF_INET);
56+
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
57+
(xfrm_address_t *)&ip_hdr(skb)->daddr,
58+
spi, IPPROTO_ESP, AF_INET);
5959

6060
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
6161
/* non-offload path will record the error and audit log */

net/ipv6/esp6_offload.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
8080
if (sp->len == XFRM_MAX_DEPTH)
8181
goto out_reset;
8282

83-
x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
84-
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
85-
spi, IPPROTO_ESP, AF_INET6);
83+
x = xfrm_input_state_lookup(dev_net(skb->dev), skb->mark,
84+
(xfrm_address_t *)&ipv6_hdr(skb)->daddr,
85+
spi, IPPROTO_ESP, AF_INET6);
8686

8787
if (unlikely(x && x->dir && x->dir != XFRM_SA_DIR_IN)) {
8888
/* non-offload path will record the error and audit log */

net/xfrm/xfrm_input.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
572572
goto drop;
573573
}
574574

575-
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
575+
x = xfrm_input_state_lookup(net, mark, daddr, spi, nexthdr, family);
576576
if (x == NULL) {
577577
secpath_reset(skb);
578578
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);

net/xfrm/xfrm_state.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,9 @@ int __xfrm_state_delete(struct xfrm_state *x)
754754
hlist_del_rcu(&x->byseq);
755755
if (!hlist_unhashed(&x->state_cache))
756756
hlist_del_rcu(&x->state_cache);
757+
if (!hlist_unhashed(&x->state_cache_input))
758+
hlist_del_rcu(&x->state_cache_input);
759+
757760
if (x->id.spi)
758761
hlist_del_rcu(&x->byspi);
759762
net->xfrm.state_num--;
@@ -1106,6 +1109,52 @@ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
11061109
return NULL;
11071110
}
11081111

1112+
struct xfrm_state *xfrm_input_state_lookup(struct net *net, u32 mark,
1113+
const xfrm_address_t *daddr,
1114+
__be32 spi, u8 proto,
1115+
unsigned short family)
1116+
{
1117+
struct hlist_head *state_cache_input;
1118+
struct xfrm_state *x = NULL;
1119+
int cpu = get_cpu();
1120+
1121+
state_cache_input = per_cpu_ptr(net->xfrm.state_cache_input, cpu);
1122+
1123+
rcu_read_lock();
1124+
hlist_for_each_entry_rcu(x, state_cache_input, state_cache_input) {
1125+
if (x->props.family != family ||
1126+
x->id.spi != spi ||
1127+
x->id.proto != proto ||
1128+
!xfrm_addr_equal(&x->id.daddr, daddr, family))
1129+
continue;
1130+
1131+
if ((mark & x->mark.m) != x->mark.v)
1132+
continue;
1133+
if (!xfrm_state_hold_rcu(x))
1134+
continue;
1135+
goto out;
1136+
}
1137+
1138+
x = __xfrm_state_lookup(net, mark, daddr, spi, proto, family);
1139+
1140+
if (x && x->km.state == XFRM_STATE_VALID) {
1141+
spin_lock_bh(&net->xfrm.xfrm_state_lock);
1142+
if (hlist_unhashed(&x->state_cache_input)) {
1143+
hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
1144+
} else {
1145+
hlist_del_rcu(&x->state_cache_input);
1146+
hlist_add_head_rcu(&x->state_cache_input, state_cache_input);
1147+
}
1148+
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
1149+
}
1150+
1151+
out:
1152+
rcu_read_unlock();
1153+
put_cpu();
1154+
return x;
1155+
}
1156+
EXPORT_SYMBOL(xfrm_input_state_lookup);
1157+
11091158
static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, u32 mark,
11101159
const xfrm_address_t *daddr,
11111160
const xfrm_address_t *saddr,
@@ -3079,6 +3128,11 @@ int __net_init xfrm_state_init(struct net *net)
30793128
net->xfrm.state_byseq = xfrm_hash_alloc(sz);
30803129
if (!net->xfrm.state_byseq)
30813130
goto out_byseq;
3131+
3132+
net->xfrm.state_cache_input = alloc_percpu(struct hlist_head);
3133+
if (!net->xfrm.state_cache_input)
3134+
goto out_state_cache_input;
3135+
30823136
net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
30833137

30843138
net->xfrm.state_num = 0;
@@ -3088,6 +3142,8 @@ int __net_init xfrm_state_init(struct net *net)
30883142
&net->xfrm.xfrm_state_lock);
30893143
return 0;
30903144

3145+
out_state_cache_input:
3146+
xfrm_hash_free(net->xfrm.state_byseq, sz);
30913147
out_byseq:
30923148
xfrm_hash_free(net->xfrm.state_byspi, sz);
30933149
out_byspi:
@@ -3117,6 +3173,7 @@ void xfrm_state_fini(struct net *net)
31173173
xfrm_hash_free(net->xfrm.state_bysrc, sz);
31183174
WARN_ON(!hlist_empty(net->xfrm.state_bydst));
31193175
xfrm_hash_free(net->xfrm.state_bydst, sz);
3176+
free_percpu(net->xfrm.state_cache_input);
31203177
}
31213178

31223179
#ifdef CONFIG_AUDITSYSCALL

0 commit comments

Comments
 (0)