From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4817727147E for ; Mon, 14 Jul 2025 18:09:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752516584; cv=none; b=li4E0RxHL9JnVr+u/G6YRus6oLx951i3tpV7X3YmJ5Rc2w2g35SKyNfR+ep30XW2pvT05RnYfve66H7dEvDQ/XA8Re+RdvNa+4BTvIk5XVsQ+yyd13gAgTCpUcu1jXT/GRtZKDoVbNiBpp22pG8rrAj5ONzDsnPPR28c1KnMIq0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752516584; c=relaxed/simple; bh=gbeaiOCxmjmrG35++dBQFIH8o6yPXrtvBiMwvbhjors=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V2Anp8oT9cdI+ThS4O2wvXEhqr1liN3diF0LINOjU0PrZbyGN4a5dg4WwiuowZAlMCHE/cZYZboxCszdlPd9ytHr3UtjSuJ3f6tygEQDG4PRxqoxDFVLS4k3LZ4Y5S5SjlXaDi/PLrHJRCvDcPCzPwkJ1UTGo5AtrjpZpok5wI4= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=jrife.io; spf=none smtp.mailfrom=jrife.io; dkim=pass (2048-bit key) header.d=jrife-io.20230601.gappssmtp.com header.i=@jrife-io.20230601.gappssmtp.com header.b=PXbMwYd5; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=jrife.io Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=jrife.io Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=jrife-io.20230601.gappssmtp.com header.i=@jrife-io.20230601.gappssmtp.com header.b="PXbMwYd5" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-234d3103237so7592105ad.0 for ; Mon, 14 Jul 2025 11:09:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jrife-io.20230601.gappssmtp.com; s=20230601; t=1752516582; x=1753121382; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5MKluTiBvsE9AShLTc+dZ1fw+yxQau0Ijxi1CZX+dmQ=; b=PXbMwYd5uATti6KpNbQ96jO5dn0O/+0SB6ABxwW/gCQNHndAxh9BNMBbol4bc0Q6M8 WKlcsKym7YBYW81bZ3TkSukYLbN0/je6/jE+UDc3sENRt0+J5Co0Y+Vo869VC7J4Z1am c6UNi3PTIM5/vcHpcXfRGu9s9HAUhro6jIHQAuZvSlzRJDDIln1WEtgP0FcwnVnkE64V hAQCRMNTFH4QfIHowfM7kplDWRjHi+IDUHjU1eJMAsikcD0AIfi7bHSYZjXK8wgRuipk rCXY5PbkA4hQukTmSMEqHVY2jo6bBUDxo8gp21Sn6DQNg8FJI5RlVOZtZGiMpcavMxOc esMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752516582; x=1753121382; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5MKluTiBvsE9AShLTc+dZ1fw+yxQau0Ijxi1CZX+dmQ=; b=r9BqnPbPrswNOvaHiKLSb2vxXWJIs8OJJz2n5b/53VaGIoHpigw7ryNpW2Eo/+yMcs dulOnqN4xoFMTXrx9cRi2BmwGpEhcl5UMT7XjslxfIfSkWwMhQb5o1q8NEqqvQtBYUjw 0uS7vfEJO/BfAm/sB1GyC+xM7bJVRR7kPudwGa/CpcUiO8+MTXF8QAdQTqrlGXmGZWi4 o8rG/4AOM2hUNKVdK4OLd8EcqfdcatyHY+pzUEoBVRhzt98ZRn+Vnzzo9IlH7nW+1Oe7 w9mioDswN1nM/+HBLBt9SoJ4yJcWFA9dRRGue2bG9eRNTPBHyT7e17e3u/UIXiBmfpLY LAew== X-Forwarded-Encrypted: i=1; AJvYcCVDMukt0/UMctpD6bg1uDdEAS86tyZoa1eEqqGJy5vmv+FexGHII+R7Edb1i2QCllczFA0=@vger.kernel.org X-Gm-Message-State: AOJu0YwBazWNL0LU/KH1u+BvaR8kq5eM9EuZKtYyDaz5yNWGprfiEqR1 B0To34WEL9WC0ZNnPAPtajs3ErcUdNcfBfCyjoTGu35rkaMe02C2r4iJIDa7laUTKWs= X-Gm-Gg: ASbGncu16LShZRjPulu/f7nCPohaw/0L1sKxSKjTqUze4rwYMoSVj/A4695Rd7RcYoT f7A7Yqy+jQvQNFt+n5FhJFjZe2PYv1O5XmdqQvSuQsRg5ISF9XwIrgKBJS3b8sW/7PZQ+ETopHf 6Z5xx/7ivFuk08ZFXR4zyF9LSsWuNRqn+sgEDtYZRUTGBUNh/jV4BxKsPLP471rsUx0smTVGzZv yKIUt5vAqXVVQThefxS+m4baOqU5vwTm1OprhgcrU5ujWblzKiejIlp3h7lCxHdX2O0/ONGfXe8 nXlBkVHRarV8nbyh4foRA0GtbzoCsC+8uK4xKjM5BYOgYQrUJqySK27955jL1X14b4Q6dR67G5F z5RPURS64cA== X-Google-Smtp-Source: AGHT+IHtd9Zab7++YPpYhXvnzRBekp0ywwIe2Akw3gBFsdfyio5gN07xRKKx6wHGFJIIxdxZYR3cvg== X-Received: by 2002:a17:903:2f81:b0:236:7050:7464 with SMTP id d9443c01a7336-23defc8f256mr82944415ad.11.1752516581398; Mon, 14 Jul 2025 11:09:41 -0700 (PDT) Received: from t14.. ([2a00:79e1:abc:133:84d3:3b84:b221:e691]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-23de42aeadcsm98126405ad.78.2025.07.14.11.09.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Jul 2025 11:09:41 -0700 (PDT) From: Jordan Rife To: netdev@vger.kernel.org, bpf@vger.kernel.org Cc: Jordan Rife , Daniel Borkmann , Martin KaFai Lau , Willem de Bruijn , Kuniyuki Iwashima , Alexei Starovoitov , Stanislav Fomichev Subject: [PATCH v6 bpf-next 12/12] selftests/bpf: Add tests for bucket resume logic in established sockets Date: Mon, 14 Jul 2025 11:09:16 -0700 Message-ID: <20250714180919.127192-13-jordan@jrife.io> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250714180919.127192-1-jordan@jrife.io> References: <20250714180919.127192-1-jordan@jrife.io> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Replicate the set of test cases used for UDP socket iterators to test similar scenarios for TCP established sockets. Signed-off-by: Jordan Rife --- .../bpf/prog_tests/sock_iter_batch.c | 293 ++++++++++++++++++ 1 file changed, 293 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c index c8d0e23dbe03..7a89cc43d6ef 100644 --- a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c @@ -120,6 +120,45 @@ static int get_nth_socket(int *fds, int fds_len, struct bpf_link *link, int n) return nth_sock_idx; } +static void destroy(int fd) +{ + struct sock_iter_batch *skel = NULL; + __u64 cookie = socket_cookie(fd); + struct bpf_link *link = NULL; + int iter_fd = -1; + int nread; + __u64 out; + + skel = sock_iter_batch__open(); + if (!ASSERT_OK_PTR(skel, "sock_iter_batch__open")) + goto done; + + skel->rodata->destroy_cookie = cookie; + + if (!ASSERT_OK(sock_iter_batch__load(skel), "sock_iter_batch__load")) + goto done; + + link = bpf_program__attach_iter(skel->progs.iter_tcp_destroy, NULL); + if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter")) + goto done; + + iter_fd = bpf_iter_create(bpf_link__fd(link)); + if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create")) + goto done; + + /* Delete matching socket. */ + nread = read(iter_fd, &out, sizeof(out)); + ASSERT_GE(nread, 0, "nread"); + if (nread) + ASSERT_EQ(out, cookie, "cookie matches"); +done: + if (iter_fd >= 0) + close(iter_fd); + bpf_link__destroy(link); + sock_iter_batch__destroy(skel); + close(fd); +} + static int get_seen_count(int fd, struct sock_count counts[], int n) { __u64 cookie = socket_cookie(fd); @@ -250,6 +289,43 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port, counts_len); } +static void remove_seen_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int close_idx; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Leave one established socket. */ + read_n(iter_fd, established_socks_len - 1, counts, counts_len); + + /* Close a socket we've already seen to remove it from the bucket. */ + close_idx = get_nth_socket(established_socks, established_socks_len, + link, listen_socks_len + 1); + if (!ASSERT_GE(close_idx, 0, "close_idx")) + return; + destroy(established_socks[close_idx]); + established_socks[close_idx] = -1; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure the last socket wasn't skipped and that there were no + * repeats. + */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); +} + static void remove_unseen(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, int established_socks_len, @@ -283,6 +359,51 @@ static void remove_unseen(int family, int sock_type, const char *addr, counts_len); } +static void remove_unseen_established(int family, int sock_type, + const char *addr, __u16 port, + int *listen_socks, int listen_socks_len, + int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int close_idx; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established socket. */ + read_n(iter_fd, 1, counts, counts_len); + + /* Make sure we saw one established socks. */ + check_n_were_seen_once(established_socks, established_socks_len, 1, + counts, counts_len); + + /* Close what would be the next socket in the bucket to exercise the + * condition where we need to skip past the first cookie we remembered. + */ + close_idx = get_nth_socket(established_socks, established_socks_len, + link, listen_socks_len + 1); + if (!ASSERT_GE(close_idx, 0, "close_idx")) + return; + + destroy(established_socks[close_idx]); + established_socks[close_idx] = -1; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure the remaining sockets were seen exactly once and that we + * didn't repeat the socket that was already seen. + */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); +} + static void remove_all(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, int established_socks_len, @@ -312,6 +433,54 @@ static void remove_all(int family, int sock_type, const char *addr, ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n"); } +static void remove_all_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + int *close_idx = NULL; + int i; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established socket. */ + read_n(iter_fd, 1, counts, counts_len); + + /* Make sure we saw one established socks. */ + check_n_were_seen_once(established_socks, established_socks_len, 1, + counts, counts_len); + + /* Close all remaining sockets to exhaust the list of saved cookies and + * exit without putting any sockets into the batch on the next read. + */ + close_idx = malloc(sizeof(int) * (established_socks_len - 1)); + if (!ASSERT_OK_PTR(close_idx, "close_idx malloc")) + return; + for (i = 0; i < established_socks_len - 1; i++) { + close_idx[i] = get_nth_socket(established_socks, + established_socks_len, link, + listen_socks_len + i); + if (!ASSERT_GE(close_idx[i], 0, "close_idx")) + return; + } + + for (i = 0; i < established_socks_len - 1; i++) { + destroy(established_socks[close_idx[i]]); + established_socks[close_idx[i]] = -1; + } + + /* Make sure there are no more sockets returned */ + ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n"); + free(close_idx); +} + static void add_some(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, int established_socks_len, struct sock_count *counts, @@ -342,6 +511,49 @@ static void add_some(int family, int sock_type, const char *addr, __u16 port, free_fds(new_socks, socks_len); } +static void add_some_established(int family, int sock_type, const char *addr, + __u16 port, int *listen_socks, + int listen_socks_len, int *established_socks, + int established_socks_len, + struct sock_count *counts, + int counts_len, struct bpf_link *link, + int iter_fd) +{ + int *new_socks = NULL; + + /* Iterate through all listening sockets. */ + read_n(iter_fd, listen_socks_len, counts, counts_len); + + /* Make sure we saw all listening sockets exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + + /* Iterate through the first established_socks_len - 1 sockets. */ + read_n(iter_fd, established_socks_len - 1, counts, counts_len); + + /* Make sure we saw established_socks_len - 1 sockets exactly once. */ + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len - 1, counts, counts_len); + + /* Double the number of established sockets in the bucket. */ + new_socks = connect_to_server(family, sock_type, addr, port, + established_socks_len / 2, listen_socks, + listen_socks_len); + if (!ASSERT_OK_PTR(new_socks, "connect_to_server")) + goto done; + + /* Iterate through the rest of the sockets. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure each of the original sockets was seen exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len, counts, counts_len); +done: + free_fds(new_socks, established_socks_len); +} + static void force_realloc(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, int established_socks_len, @@ -371,6 +583,24 @@ static void force_realloc(int family, int sock_type, const char *addr, free_fds(new_socks, socks_len); } +static void force_realloc_established(int family, int sock_type, + const char *addr, __u16 port, + int *listen_socks, int listen_socks_len, + int *established_socks, + int established_socks_len, + struct sock_count *counts, int counts_len, + struct bpf_link *link, int iter_fd) +{ + /* Iterate through all sockets to trigger a realloc. */ + read_n(iter_fd, -1, counts, counts_len); + + /* Make sure each socket was seen exactly once. */ + check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, + counts, counts_len); + check_n_were_seen_once(established_socks, established_socks_len, + established_socks_len, counts, counts_len); +} + struct test_case { void (*test)(int family, int sock_type, const char *addr, __u16 port, int *socks, int socks_len, int *established_socks, @@ -480,6 +710,69 @@ static struct test_case resume_tests[] = { .family = AF_INET6, .test = force_realloc, }, + { + .description = "tcp: resume after removing a seen socket (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_seen_established, + }, + { + .description = "tcp: resume after removing one unseen socket (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_unseen_established, + }, + { + .description = "tcp: resume after removing all unseen sockets (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = remove_all_established, + }, + { + .description = "tcp: resume after adding a few sockets (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + .connections = nr_soreuse, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse * 3, + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = add_some_established, + }, + { + .description = "tcp: force a realloc to occur (established)", + /* Force all established sockets into one bucket */ + .ehash_buckets = 1, + /* Bucket size will need to double when going from listening to + * established sockets. + */ + .connections = init_batch_size, + .init_socks = nr_soreuse, + /* Room for connect()ed and accept()ed sockets */ + .max_socks = nr_soreuse + (init_batch_size * 2), + .sock_type = SOCK_STREAM, + .family = AF_INET6, + .test = force_realloc_established, + }, }; static void do_resume_test(struct test_case *tc) -- 2.43.0