-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bpf: selftest: Test batching and bpf_(get|set)sockopt in bpf tcp iter
This patch adds tests for the batching and bpf_(get|set)sockopt in bpf tcp iter. It first creates: a) 1 non SO_REUSEPORT listener in lhash2. b) 256 passive and active fds connected to the listener in (a). c) 256 SO_REUSEPORT listeners in one of the lhash2 bucket. The test sets all listeners and connections to bpf_cubic before running the bpf iter. The bpf iter then calls setsockopt(TCP_CONGESTION) to switch each listener and connection from bpf_cubic to bpf_dctcp. The bpf iter has a random_retry mode such that it can return EAGAIN to the usespace in the middle of a batch. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Reviewed-by: Eric Dumazet <edumazet@google.com> Acked-by: Kuniyuki Iwashima <kuniyu@amazon.co.jp> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20210701200625.1036874-1-kafai@fb.com
- Loading branch information
Martin KaFai Lau
authored and
Andrii Nakryiko
committed
Jul 23, 2021
1 parent
3cee6fb
commit eed92af
Showing
5 changed files
with
384 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
226 changes: 226 additions & 0 deletions
226
tools/testing/selftests/bpf/prog_tests/bpf_iter_setsockopt.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
#define _GNU_SOURCE | ||
#include <sched.h> | ||
#include <test_progs.h> | ||
#include "network_helpers.h" | ||
#include "bpf_dctcp.skel.h" | ||
#include "bpf_cubic.skel.h" | ||
#include "bpf_iter_setsockopt.skel.h" | ||
|
||
static int create_netns(void) | ||
{ | ||
if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns")) | ||
return -1; | ||
|
||
if (!ASSERT_OK(system("ip link set dev lo up"), "bring up lo")) | ||
return -1; | ||
|
||
return 0; | ||
} | ||
|
||
static unsigned int set_bpf_cubic(int *fds, unsigned int nr_fds) | ||
{ | ||
unsigned int i; | ||
|
||
for (i = 0; i < nr_fds; i++) { | ||
if (setsockopt(fds[i], SOL_TCP, TCP_CONGESTION, "bpf_cubic", | ||
sizeof("bpf_cubic"))) | ||
return i; | ||
} | ||
|
||
return nr_fds; | ||
} | ||
|
||
static unsigned int check_bpf_dctcp(int *fds, unsigned int nr_fds) | ||
{ | ||
char tcp_cc[16]; | ||
socklen_t optlen = sizeof(tcp_cc); | ||
unsigned int i; | ||
|
||
for (i = 0; i < nr_fds; i++) { | ||
if (getsockopt(fds[i], SOL_TCP, TCP_CONGESTION, | ||
tcp_cc, &optlen) || | ||
strcmp(tcp_cc, "bpf_dctcp")) | ||
return i; | ||
} | ||
|
||
return nr_fds; | ||
} | ||
|
||
static int *make_established(int listen_fd, unsigned int nr_est, | ||
int **paccepted_fds) | ||
{ | ||
int *est_fds, *accepted_fds; | ||
unsigned int i; | ||
|
||
est_fds = malloc(sizeof(*est_fds) * nr_est); | ||
if (!est_fds) | ||
return NULL; | ||
|
||
accepted_fds = malloc(sizeof(*accepted_fds) * nr_est); | ||
if (!accepted_fds) { | ||
free(est_fds); | ||
return NULL; | ||
} | ||
|
||
for (i = 0; i < nr_est; i++) { | ||
est_fds[i] = connect_to_fd(listen_fd, 0); | ||
if (est_fds[i] == -1) | ||
break; | ||
if (set_bpf_cubic(&est_fds[i], 1) != 1) { | ||
close(est_fds[i]); | ||
break; | ||
} | ||
|
||
accepted_fds[i] = accept(listen_fd, NULL, 0); | ||
if (accepted_fds[i] == -1) { | ||
close(est_fds[i]); | ||
break; | ||
} | ||
} | ||
|
||
if (!ASSERT_EQ(i, nr_est, "create established fds")) { | ||
free_fds(accepted_fds, i); | ||
free_fds(est_fds, i); | ||
return NULL; | ||
} | ||
|
||
*paccepted_fds = accepted_fds; | ||
return est_fds; | ||
} | ||
|
||
static unsigned short get_local_port(int fd) | ||
{ | ||
struct sockaddr_in6 addr; | ||
socklen_t addrlen = sizeof(addr); | ||
|
||
if (!getsockname(fd, &addr, &addrlen)) | ||
return ntohs(addr.sin6_port); | ||
|
||
return 0; | ||
} | ||
|
||
static void do_bpf_iter_setsockopt(struct bpf_iter_setsockopt *iter_skel, | ||
bool random_retry) | ||
{ | ||
int *reuse_listen_fds = NULL, *accepted_fds = NULL, *est_fds = NULL; | ||
unsigned int nr_reuse_listens = 256, nr_est = 256; | ||
int err, iter_fd = -1, listen_fd = -1; | ||
char buf; | ||
|
||
/* Prepare non-reuseport listen_fd */ | ||
listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); | ||
if (!ASSERT_GE(listen_fd, 0, "start_server")) | ||
return; | ||
if (!ASSERT_EQ(set_bpf_cubic(&listen_fd, 1), 1, | ||
"set listen_fd to cubic")) | ||
goto done; | ||
iter_skel->bss->listen_hport = get_local_port(listen_fd); | ||
if (!ASSERT_NEQ(iter_skel->bss->listen_hport, 0, | ||
"get_local_port(listen_fd)")) | ||
goto done; | ||
|
||
/* Connect to non-reuseport listen_fd */ | ||
est_fds = make_established(listen_fd, nr_est, &accepted_fds); | ||
if (!ASSERT_OK_PTR(est_fds, "create established")) | ||
goto done; | ||
|
||
/* Prepare reuseport listen fds */ | ||
reuse_listen_fds = start_reuseport_server(AF_INET6, SOCK_STREAM, | ||
"::1", 0, 0, | ||
nr_reuse_listens); | ||
if (!ASSERT_OK_PTR(reuse_listen_fds, "start_reuseport_server")) | ||
goto done; | ||
if (!ASSERT_EQ(set_bpf_cubic(reuse_listen_fds, nr_reuse_listens), | ||
nr_reuse_listens, "set reuse_listen_fds to cubic")) | ||
goto done; | ||
iter_skel->bss->reuse_listen_hport = get_local_port(reuse_listen_fds[0]); | ||
if (!ASSERT_NEQ(iter_skel->bss->reuse_listen_hport, 0, | ||
"get_local_port(reuse_listen_fds[0])")) | ||
goto done; | ||
|
||
/* Run bpf tcp iter to switch from bpf_cubic to bpf_dctcp */ | ||
iter_skel->bss->random_retry = random_retry; | ||
iter_fd = bpf_iter_create(bpf_link__fd(iter_skel->links.change_tcp_cc)); | ||
if (!ASSERT_GE(iter_fd, 0, "create iter_fd")) | ||
goto done; | ||
|
||
while ((err = read(iter_fd, &buf, sizeof(buf))) == -1 && | ||
errno == EAGAIN) | ||
; | ||
if (!ASSERT_OK(err, "read iter error")) | ||
goto done; | ||
|
||
/* Check reuseport listen fds for dctcp */ | ||
ASSERT_EQ(check_bpf_dctcp(reuse_listen_fds, nr_reuse_listens), | ||
nr_reuse_listens, | ||
"check reuse_listen_fds dctcp"); | ||
|
||
/* Check non reuseport listen fd for dctcp */ | ||
ASSERT_EQ(check_bpf_dctcp(&listen_fd, 1), 1, | ||
"check listen_fd dctcp"); | ||
|
||
/* Check established fds for dctcp */ | ||
ASSERT_EQ(check_bpf_dctcp(est_fds, nr_est), nr_est, | ||
"check est_fds dctcp"); | ||
|
||
/* Check accepted fds for dctcp */ | ||
ASSERT_EQ(check_bpf_dctcp(accepted_fds, nr_est), nr_est, | ||
"check accepted_fds dctcp"); | ||
|
||
done: | ||
if (iter_fd != -1) | ||
close(iter_fd); | ||
if (listen_fd != -1) | ||
close(listen_fd); | ||
free_fds(reuse_listen_fds, nr_reuse_listens); | ||
free_fds(accepted_fds, nr_est); | ||
free_fds(est_fds, nr_est); | ||
} | ||
|
||
void test_bpf_iter_setsockopt(void) | ||
{ | ||
struct bpf_iter_setsockopt *iter_skel = NULL; | ||
struct bpf_cubic *cubic_skel = NULL; | ||
struct bpf_dctcp *dctcp_skel = NULL; | ||
struct bpf_link *cubic_link = NULL; | ||
struct bpf_link *dctcp_link = NULL; | ||
|
||
if (create_netns()) | ||
return; | ||
|
||
/* Load iter_skel */ | ||
iter_skel = bpf_iter_setsockopt__open_and_load(); | ||
if (!ASSERT_OK_PTR(iter_skel, "iter_skel")) | ||
return; | ||
iter_skel->links.change_tcp_cc = bpf_program__attach_iter(iter_skel->progs.change_tcp_cc, NULL); | ||
if (!ASSERT_OK_PTR(iter_skel->links.change_tcp_cc, "attach iter")) | ||
goto done; | ||
|
||
/* Load bpf_cubic */ | ||
cubic_skel = bpf_cubic__open_and_load(); | ||
if (!ASSERT_OK_PTR(cubic_skel, "cubic_skel")) | ||
goto done; | ||
cubic_link = bpf_map__attach_struct_ops(cubic_skel->maps.cubic); | ||
if (!ASSERT_OK_PTR(cubic_link, "cubic_link")) | ||
goto done; | ||
|
||
/* Load bpf_dctcp */ | ||
dctcp_skel = bpf_dctcp__open_and_load(); | ||
if (!ASSERT_OK_PTR(dctcp_skel, "dctcp_skel")) | ||
goto done; | ||
dctcp_link = bpf_map__attach_struct_ops(dctcp_skel->maps.dctcp); | ||
if (!ASSERT_OK_PTR(dctcp_link, "dctcp_link")) | ||
goto done; | ||
|
||
do_bpf_iter_setsockopt(iter_skel, true); | ||
do_bpf_iter_setsockopt(iter_skel, false); | ||
|
||
done: | ||
bpf_link__destroy(cubic_link); | ||
bpf_link__destroy(dctcp_link); | ||
bpf_cubic__destroy(cubic_skel); | ||
bpf_dctcp__destroy(dctcp_skel); | ||
bpf_iter_setsockopt__destroy(iter_skel); | ||
} |
Oops, something went wrong.