Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: selftests: Add dctcp fallback test

This patch makes the bpf_dctcp test to fallback to cubic by
using setsockopt(TCP_CONGESTION) when the tcp flow is not
ecn ready.

It also checks setsockopt() is not available to release().

The settimeo() from the network_helpers.h is used, so the local
one is removed.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20210824173026.3979130-1-kafai@fb.com

authored by

Martin KaFai Lau and committed by
Alexei Starovoitov
574ee209 3d778983

+134 -23
+83 -23
tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c
··· 4 4 #include <linux/err.h> 5 5 #include <netinet/tcp.h> 6 6 #include <test_progs.h> 7 + #include "network_helpers.h" 7 8 #include "bpf_dctcp.skel.h" 8 9 #include "bpf_cubic.skel.h" 9 10 #include "bpf_tcp_nogpl.skel.h" 11 + #include "bpf_dctcp_release.skel.h" 10 12 11 13 #define min(a, b) ((a) < (b) ? (a) : (b)) 12 14 15 + #ifndef ENOTSUPP 16 + #define ENOTSUPP 524 17 + #endif 18 + 13 19 static const unsigned int total_bytes = 10 * 1024 * 1024; 14 - static const struct timeval timeo_sec = { .tv_sec = 10 }; 15 - static const size_t timeo_optlen = sizeof(timeo_sec); 16 20 static int expected_stg = 0xeB9F; 17 21 static int stop, duration; 18 - 19 - static int settimeo(int fd) 20 - { 21 - int err; 22 - 23 - err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec, 24 - timeo_optlen); 25 - if (CHECK(err == -1, "setsockopt(fd, SO_RCVTIMEO)", "errno:%d\n", 26 - errno)) 27 - return -1; 28 - 29 - err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec, 30 - timeo_optlen); 31 - if (CHECK(err == -1, "setsockopt(fd, SO_SNDTIMEO)", "errno:%d\n", 32 - errno)) 33 - return -1; 34 - 35 - return 0; 36 - } 37 22 38 23 static int settcpca(int fd, const char *tcp_ca) 39 24 { ··· 46 61 goto done; 47 62 } 48 63 49 - if (settimeo(fd)) { 64 + if (settimeo(fd, 0)) { 50 65 err = -errno; 51 66 goto done; 52 67 } ··· 99 114 } 100 115 101 116 if (settcpca(lfd, tcp_ca) || settcpca(fd, tcp_ca) || 102 - settimeo(lfd) || settimeo(fd)) 117 + settimeo(lfd, 0) || settimeo(fd, 0)) 103 118 goto done; 104 119 105 120 /* bind, listen and start server thread to accept */ ··· 252 267 libbpf_set_print(old_print_fn); 253 268 } 254 269 270 + static void test_dctcp_fallback(void) 271 + { 272 + int err, lfd = -1, cli_fd = -1, srv_fd = -1; 273 + struct network_helper_opts opts = { 274 + .cc = "cubic", 275 + }; 276 + struct bpf_dctcp *dctcp_skel; 277 + struct bpf_link *link = NULL; 278 + char srv_cc[16]; 279 + socklen_t cc_len = sizeof(srv_cc); 280 + 281 + dctcp_skel = bpf_dctcp__open(); 282 + if (!ASSERT_OK_PTR(dctcp_skel, "dctcp_skel")) 283 + return; 284 + strcpy(dctcp_skel->rodata->fallback, "cubic"); 285 + if (!ASSERT_OK(bpf_dctcp__load(dctcp_skel), "bpf_dctcp__load")) 286 + goto done; 287 + 288 + link = bpf_map__attach_struct_ops(dctcp_skel->maps.dctcp); 289 + if (!ASSERT_OK_PTR(link, "dctcp link")) 290 + goto done; 291 + 292 + lfd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 293 + if (!ASSERT_GE(lfd, 0, "lfd") || 294 + !ASSERT_OK(settcpca(lfd, "bpf_dctcp"), "lfd=>bpf_dctcp")) 295 + goto done; 296 + 297 + cli_fd = connect_to_fd_opts(lfd, &opts); 298 + if (!ASSERT_GE(cli_fd, 0, "cli_fd")) 299 + goto done; 300 + 301 + srv_fd = accept(lfd, NULL, 0); 302 + if (!ASSERT_GE(srv_fd, 0, "srv_fd")) 303 + goto done; 304 + ASSERT_STREQ(dctcp_skel->bss->cc_res, "cubic", "cc_res"); 305 + ASSERT_EQ(dctcp_skel->bss->tcp_cdg_res, -ENOTSUPP, "tcp_cdg_res"); 306 + 307 + err = getsockopt(srv_fd, SOL_TCP, TCP_CONGESTION, srv_cc, &cc_len); 308 + if (!ASSERT_OK(err, "getsockopt(srv_fd, TCP_CONGESTION)")) 309 + goto done; 310 + ASSERT_STREQ(srv_cc, "cubic", "srv_fd cc"); 311 + 312 + done: 313 + bpf_link__destroy(link); 314 + bpf_dctcp__destroy(dctcp_skel); 315 + if (lfd != -1) 316 + close(lfd); 317 + if (srv_fd != -1) 318 + close(srv_fd); 319 + if (cli_fd != -1) 320 + close(cli_fd); 321 + } 322 + 323 + static void test_rel_setsockopt(void) 324 + { 325 + struct bpf_dctcp_release *rel_skel; 326 + libbpf_print_fn_t old_print_fn; 327 + 328 + err_str = "unknown func bpf_setsockopt"; 329 + found = false; 330 + 331 + old_print_fn = libbpf_set_print(libbpf_debug_print); 332 + rel_skel = bpf_dctcp_release__open_and_load(); 333 + libbpf_set_print(old_print_fn); 334 + 335 + ASSERT_ERR_PTR(rel_skel, "rel_skel"); 336 + ASSERT_TRUE(found, "expected_err_msg"); 337 + 338 + bpf_dctcp_release__destroy(rel_skel); 339 + } 340 + 255 341 void test_bpf_tcp_ca(void) 256 342 { 257 343 if (test__start_subtest("dctcp")) ··· 331 275 test_cubic(); 332 276 if (test__start_subtest("invalid_license")) 333 277 test_invalid_license(); 278 + if (test__start_subtest("dctcp_fallback")) 279 + test_dctcp_fallback(); 280 + if (test__start_subtest("rel_setsockopt")) 281 + test_rel_setsockopt(); 334 282 }
+25
tools/testing/selftests/bpf/progs/bpf_dctcp.c
··· 17 17 18 18 char _license[] SEC("license") = "GPL"; 19 19 20 + volatile const char fallback[TCP_CA_NAME_MAX]; 21 + const char bpf_dctcp[] = "bpf_dctcp"; 22 + const char tcp_cdg[] = "cdg"; 23 + char cc_res[TCP_CA_NAME_MAX]; 24 + int tcp_cdg_res = 0; 20 25 int stg_result = 0; 21 26 22 27 struct { ··· 61 56 const struct tcp_sock *tp = tcp_sk(sk); 62 57 struct dctcp *ca = inet_csk_ca(sk); 63 58 int *stg; 59 + 60 + if (!(tp->ecn_flags & TCP_ECN_OK) && fallback[0]) { 61 + /* Switch to fallback */ 62 + bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 63 + (void *)fallback, sizeof(fallback)); 64 + /* Switch back to myself which the bpf trampoline 65 + * stopped calling dctcp_init recursively. 66 + */ 67 + bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 68 + (void *)bpf_dctcp, sizeof(bpf_dctcp)); 69 + /* Switch back to fallback */ 70 + bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 71 + (void *)fallback, sizeof(fallback)); 72 + /* Expecting -ENOTSUPP for tcp_cdg_res */ 73 + tcp_cdg_res = bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 74 + (void *)tcp_cdg, sizeof(tcp_cdg)); 75 + bpf_getsockopt(sk, SOL_TCP, TCP_CONGESTION, 76 + (void *)cc_res, sizeof(cc_res)); 77 + return; 78 + } 64 79 65 80 ca->prior_rcv_nxt = tp->rcv_nxt; 66 81 ca->dctcp_alpha = min(dctcp_alpha_on_init, DCTCP_MAX_ALPHA);
+26
tools/testing/selftests/bpf/progs/bpf_dctcp_release.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2021 Facebook */ 3 + 4 + #include <stddef.h> 5 + #include <linux/bpf.h> 6 + #include <linux/types.h> 7 + #include <linux/stddef.h> 8 + #include <linux/tcp.h> 9 + #include <bpf/bpf_helpers.h> 10 + #include <bpf/bpf_tracing.h> 11 + #include "bpf_tcp_helpers.h" 12 + 13 + char _license[] SEC("license") = "GPL"; 14 + const char cubic[] = "cubic"; 15 + 16 + void BPF_STRUCT_OPS(dctcp_nouse_release, struct sock *sk) 17 + { 18 + bpf_setsockopt(sk, SOL_TCP, TCP_CONGESTION, 19 + (void *)cubic, sizeof(cubic)); 20 + } 21 + 22 + SEC(".struct_ops") 23 + struct tcp_congestion_ops dctcp_rel = { 24 + .release = (void *)dctcp_nouse_release, 25 + .name = "bpf_dctcp_rel", 26 + };