Skip to content

Commit 92acdc5

Browse files
borkmannAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf, net: Rework cookie generator as per-cpu one
With its use in BPF, the cookie generator can be called very frequently in particular when used out of cgroup v2 hooks (e.g. connect / sendmsg) and attached to the root cgroup, for example, when used in v1/v2 mixed environments. In particular, when there's a high churn on sockets in the system there can be many parallel requests to the bpf_get_socket_cookie() and bpf_get_netns_cookie() helpers which then cause contention on the atomic counter. As similarly done in f991bd2 ("fs: introduce a per-cpu last_ino allocator"), add a small helper library that both can use for the 64 bit counters. Given this can be called from different contexts, we also need to deal with potential nested calls even though in practice they are considered extremely rare. One idea as suggested by Eric Dumazet was to use a reverse counter for this situation since we don't expect 64 bit overflows anyways; that way, we can avoid bigger gaps in the 64 bit counter space compared to just batch-wise increase. Even on machines with small number of cores (e.g. 4) the cookie generation shrinks from min/max/med/avg (ns) of 22/50/40/38.9 down to 10/35/14/17.3 when run in parallel from multiple CPUs. Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Cc: Eric Dumazet <[email protected]> Link: https://lore.kernel.org/bpf/8a80b8d27d3c49f9a14e1d5213c19d8be87d1dc8.1601477936.git.daniel@iogearbox.net
1 parent b426ce8 commit 92acdc5

File tree

8 files changed

+86
-18
lines changed

8 files changed

+86
-18
lines changed

include/linux/cookie.h

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef __LINUX_COOKIE_H
3+
#define __LINUX_COOKIE_H
4+
5+
#include <linux/atomic.h>
6+
#include <linux/percpu.h>
7+
#include <asm/local.h>
8+
9+
struct pcpu_gen_cookie {
10+
local_t nesting;
11+
u64 last;
12+
} __aligned(16);
13+
14+
struct gen_cookie {
15+
struct pcpu_gen_cookie __percpu *local;
16+
atomic64_t forward_last ____cacheline_aligned_in_smp;
17+
atomic64_t reverse_last;
18+
};
19+
20+
#define COOKIE_LOCAL_BATCH 4096
21+
22+
#define DEFINE_COOKIE(name) \
23+
static DEFINE_PER_CPU(struct pcpu_gen_cookie, __##name); \
24+
static struct gen_cookie name = { \
25+
.local = &__##name, \
26+
.forward_last = ATOMIC64_INIT(0), \
27+
.reverse_last = ATOMIC64_INIT(0), \
28+
}
29+
30+
static __always_inline u64 gen_cookie_next(struct gen_cookie *gc)
31+
{
32+
struct pcpu_gen_cookie *local = this_cpu_ptr(gc->local);
33+
u64 val;
34+
35+
if (likely(local_inc_return(&local->nesting) == 1)) {
36+
val = local->last;
37+
if (__is_defined(CONFIG_SMP) &&
38+
unlikely((val & (COOKIE_LOCAL_BATCH - 1)) == 0)) {
39+
s64 next = atomic64_add_return(COOKIE_LOCAL_BATCH,
40+
&gc->forward_last);
41+
val = next - COOKIE_LOCAL_BATCH;
42+
}
43+
local->last = ++val;
44+
} else {
45+
val = atomic64_dec_return(&gc->reverse_last);
46+
}
47+
local_dec(&local->nesting);
48+
return val;
49+
}
50+
51+
#endif /* __LINUX_COOKIE_H */

include/linux/sock_diag.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,19 @@ void sock_diag_unregister(const struct sock_diag_handler *h);
2525
void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
2626
void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh));
2727

28-
u64 sock_gen_cookie(struct sock *sk);
28+
u64 __sock_gen_cookie(struct sock *sk);
29+
30+
static inline u64 sock_gen_cookie(struct sock *sk)
31+
{
32+
u64 cookie;
33+
34+
preempt_disable();
35+
cookie = __sock_gen_cookie(sk);
36+
preempt_enable();
37+
38+
return cookie;
39+
}
40+
2941
int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie);
3042
void sock_diag_save_cookie(struct sock *sk, __u32 *cookie);
3143

include/net/net_namespace.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ extern struct list_head net_namespace_list;
230230
struct net *get_net_ns_by_pid(pid_t pid);
231231
struct net *get_net_ns_by_fd(int fd);
232232

233-
u64 net_gen_cookie(struct net *net);
233+
u64 __net_gen_cookie(struct net *net);
234234

235235
#ifdef CONFIG_SYSCTL
236236
void ipx_register_sysctl(void);

kernel/bpf/reuseport_array.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key,
191191
rcu_read_lock();
192192
sk = reuseport_array_lookup_elem(map, key);
193193
if (sk) {
194-
*(u64 *)value = sock_gen_cookie(sk);
194+
*(u64 *)value = __sock_gen_cookie(sk);
195195
err = 0;
196196
} else {
197197
err = -ENOENT;

net/core/filter.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -4232,7 +4232,7 @@ const struct bpf_func_proto bpf_xdp_output_proto = {
42324232

42334233
BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb)
42344234
{
4235-
return skb->sk ? sock_gen_cookie(skb->sk) : 0;
4235+
return skb->sk ? __sock_gen_cookie(skb->sk) : 0;
42364236
}
42374237

42384238
static const struct bpf_func_proto bpf_get_socket_cookie_proto = {
@@ -4244,7 +4244,7 @@ static const struct bpf_func_proto bpf_get_socket_cookie_proto = {
42444244

42454245
BPF_CALL_1(bpf_get_socket_cookie_sock_addr, struct bpf_sock_addr_kern *, ctx)
42464246
{
4247-
return sock_gen_cookie(ctx->sk);
4247+
return __sock_gen_cookie(ctx->sk);
42484248
}
42494249

42504250
static const struct bpf_func_proto bpf_get_socket_cookie_sock_addr_proto = {
@@ -4256,7 +4256,7 @@ static const struct bpf_func_proto bpf_get_socket_cookie_sock_addr_proto = {
42564256

42574257
BPF_CALL_1(bpf_get_socket_cookie_sock, struct sock *, ctx)
42584258
{
4259-
return sock_gen_cookie(ctx);
4259+
return __sock_gen_cookie(ctx);
42604260
}
42614261

42624262
static const struct bpf_func_proto bpf_get_socket_cookie_sock_proto = {
@@ -4268,7 +4268,7 @@ static const struct bpf_func_proto bpf_get_socket_cookie_sock_proto = {
42684268

42694269
BPF_CALL_1(bpf_get_socket_cookie_sock_ops, struct bpf_sock_ops_kern *, ctx)
42704270
{
4271-
return sock_gen_cookie(ctx->sk);
4271+
return __sock_gen_cookie(ctx->sk);
42724272
}
42734273

42744274
static const struct bpf_func_proto bpf_get_socket_cookie_sock_ops_proto = {
@@ -4281,7 +4281,7 @@ static const struct bpf_func_proto bpf_get_socket_cookie_sock_ops_proto = {
42814281
static u64 __bpf_get_netns_cookie(struct sock *sk)
42824282
{
42834283
#ifdef CONFIG_NET_NS
4284-
return net_gen_cookie(sk ? sk->sk_net.net : &init_net);
4284+
return __net_gen_cookie(sk ? sk->sk_net.net : &init_net);
42854285
#else
42864286
return 0;
42874287
#endif

net/core/net_namespace.c

+8-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/net_namespace.h>
2020
#include <linux/sched/task.h>
2121
#include <linux/uidgid.h>
22+
#include <linux/cookie.h>
2223

2324
#include <net/sock.h>
2425
#include <net/netlink.h>
@@ -69,16 +70,16 @@ EXPORT_SYMBOL_GPL(pernet_ops_rwsem);
6970

7071
static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS;
7172

72-
static atomic64_t cookie_gen;
73+
DEFINE_COOKIE(net_cookie);
7374

74-
u64 net_gen_cookie(struct net *net)
75+
u64 __net_gen_cookie(struct net *net)
7576
{
7677
while (1) {
7778
u64 res = atomic64_read(&net->net_cookie);
7879

7980
if (res)
8081
return res;
81-
res = atomic64_inc_return(&cookie_gen);
82+
res = gen_cookie_next(&net_cookie);
8283
atomic64_cmpxchg(&net->net_cookie, 0, res);
8384
}
8485
}
@@ -1101,7 +1102,10 @@ static int __init net_ns_init(void)
11011102
panic("Could not allocate generic netns");
11021103

11031104
rcu_assign_pointer(init_net.gen, ng);
1104-
net_gen_cookie(&init_net);
1105+
1106+
preempt_disable();
1107+
__net_gen_cookie(&init_net);
1108+
preempt_enable();
11051109

11061110
down_write(&pernet_ops_rwsem);
11071111
if (setup_net(&init_net, &init_user_ns))

net/core/sock_diag.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,25 @@
1111
#include <linux/tcp.h>
1212
#include <linux/workqueue.h>
1313
#include <linux/nospec.h>
14-
14+
#include <linux/cookie.h>
1515
#include <linux/inet_diag.h>
1616
#include <linux/sock_diag.h>
1717

1818
static const struct sock_diag_handler *sock_diag_handlers[AF_MAX];
1919
static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh);
2020
static DEFINE_MUTEX(sock_diag_table_mutex);
2121
static struct workqueue_struct *broadcast_wq;
22-
static atomic64_t cookie_gen;
2322

24-
u64 sock_gen_cookie(struct sock *sk)
23+
DEFINE_COOKIE(sock_cookie);
24+
25+
u64 __sock_gen_cookie(struct sock *sk)
2526
{
2627
while (1) {
2728
u64 res = atomic64_read(&sk->sk_cookie);
2829

2930
if (res)
3031
return res;
31-
res = atomic64_inc_return(&cookie_gen);
32+
res = gen_cookie_next(&sock_cookie);
3233
atomic64_cmpxchg(&sk->sk_cookie, 0, res);
3334
}
3435
}

net/core/sock_map.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ static void *sock_map_lookup_sys(struct bpf_map *map, void *key)
401401
if (!sk)
402402
return ERR_PTR(-ENOENT);
403403

404-
sock_gen_cookie(sk);
404+
__sock_gen_cookie(sk);
405405
return &sk->sk_cookie;
406406
}
407407

@@ -1209,7 +1209,7 @@ static void *sock_hash_lookup_sys(struct bpf_map *map, void *key)
12091209
if (!sk)
12101210
return ERR_PTR(-ENOENT);
12111211

1212-
sock_gen_cookie(sk);
1212+
__sock_gen_cookie(sk);
12131213
return &sk->sk_cookie;
12141214
}
12151215

0 commit comments

Comments
 (0)