mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	bpf, sockmap: Test shutdown() correctly exits epoll and recv()=0
When session gracefully shutdowns epoll needs to wake up and any recv() readers should return 0 not the -EAGAIN they previously returned. Note we use epoll instead of select to test the epoll wake on shutdown event as well. Signed-off-by: John Fastabend <john.fastabend@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com> Link: https://lore.kernel.org/bpf/20230523025618.113937-12-john.fastabend@gmail.com
This commit is contained in:
		
							parent
							
								
									298970c8af
								
							
						
					
					
						commit
						1fa1fe8ff1
					
				
					 2 changed files with 94 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
// Copyright (c) 2020 Cloudflare
 | 
			
		||||
#include <error.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
#include <sys/epoll.h>
 | 
			
		||||
 | 
			
		||||
#include "test_progs.h"
 | 
			
		||||
#include "test_skmsg_load_helpers.skel.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -9,8 +10,11 @@
 | 
			
		|||
#include "test_sockmap_invalid_update.skel.h"
 | 
			
		||||
#include "test_sockmap_skb_verdict_attach.skel.h"
 | 
			
		||||
#include "test_sockmap_progs_query.skel.h"
 | 
			
		||||
#include "test_sockmap_pass_prog.skel.h"
 | 
			
		||||
#include "bpf_iter_sockmap.skel.h"
 | 
			
		||||
 | 
			
		||||
#include "sockmap_helpers.h"
 | 
			
		||||
 | 
			
		||||
#define TCP_REPAIR		19	/* TCP sock is under repair right now */
 | 
			
		||||
 | 
			
		||||
#define TCP_REPAIR_ON		1
 | 
			
		||||
| 
						 | 
				
			
			@ -350,6 +354,62 @@ static void test_sockmap_progs_query(enum bpf_attach_type attach_type)
 | 
			
		|||
	test_sockmap_progs_query__destroy(skel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MAX_EVENTS 10
 | 
			
		||||
static void test_sockmap_skb_verdict_shutdown(void)
 | 
			
		||||
{
 | 
			
		||||
	struct epoll_event ev, events[MAX_EVENTS];
 | 
			
		||||
	int n, err, map, verdict, s, c1, p1;
 | 
			
		||||
	struct test_sockmap_pass_prog *skel;
 | 
			
		||||
	int epollfd;
 | 
			
		||||
	int zero = 0;
 | 
			
		||||
	char b;
 | 
			
		||||
 | 
			
		||||
	skel = test_sockmap_pass_prog__open_and_load();
 | 
			
		||||
	if (!ASSERT_OK_PTR(skel, "open_and_load"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	verdict = bpf_program__fd(skel->progs.prog_skb_verdict);
 | 
			
		||||
	map = bpf_map__fd(skel->maps.sock_map_rx);
 | 
			
		||||
 | 
			
		||||
	err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0);
 | 
			
		||||
	if (!ASSERT_OK(err, "bpf_prog_attach"))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	s = socket_loopback(AF_INET, SOCK_STREAM);
 | 
			
		||||
	if (s < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
	err = create_pair(s, AF_INET, SOCK_STREAM, &c1, &p1);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		goto out_close;
 | 
			
		||||
 | 
			
		||||
	shutdown(p1, SHUT_WR);
 | 
			
		||||
 | 
			
		||||
	ev.events = EPOLLIN;
 | 
			
		||||
	ev.data.fd = c1;
 | 
			
		||||
 | 
			
		||||
	epollfd = epoll_create1(0);
 | 
			
		||||
	if (!ASSERT_GT(epollfd, -1, "epoll_create(0)"))
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	err = epoll_ctl(epollfd, EPOLL_CTL_ADD, c1, &ev);
 | 
			
		||||
	if (!ASSERT_OK(err, "epoll_ctl(EPOLL_CTL_ADD)"))
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	err = epoll_wait(epollfd, events, MAX_EVENTS, -1);
 | 
			
		||||
	if (!ASSERT_EQ(err, 1, "epoll_wait(fd)"))
 | 
			
		||||
		goto out_close;
 | 
			
		||||
 | 
			
		||||
	n = recv(c1, &b, 1, SOCK_NONBLOCK);
 | 
			
		||||
	ASSERT_EQ(n, 0, "recv_timeout(fin)");
 | 
			
		||||
out_close:
 | 
			
		||||
	close(c1);
 | 
			
		||||
	close(p1);
 | 
			
		||||
out:
 | 
			
		||||
	test_sockmap_pass_prog__destroy(skel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_sockmap_basic(void)
 | 
			
		||||
{
 | 
			
		||||
	if (test__start_subtest("sockmap create_update_free"))
 | 
			
		||||
| 
						 | 
				
			
			@ -384,4 +444,6 @@ void test_sockmap_basic(void)
 | 
			
		|||
		test_sockmap_progs_query(BPF_SK_SKB_STREAM_VERDICT);
 | 
			
		||||
	if (test__start_subtest("sockmap skb_verdict progs query"))
 | 
			
		||||
		test_sockmap_progs_query(BPF_SK_SKB_VERDICT);
 | 
			
		||||
	if (test__start_subtest("sockmap skb_verdict shutdown"))
 | 
			
		||||
		test_sockmap_skb_verdict_shutdown();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								tools/testing/selftests/bpf/progs/test_sockmap_pass_prog.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tools/testing/selftests/bpf/progs/test_sockmap_pass_prog.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
#include <linux/bpf.h>
 | 
			
		||||
#include <bpf/bpf_helpers.h>
 | 
			
		||||
#include <bpf/bpf_endian.h>
 | 
			
		||||
 | 
			
		||||
struct {
 | 
			
		||||
	__uint(type, BPF_MAP_TYPE_SOCKMAP);
 | 
			
		||||
	__uint(max_entries, 20);
 | 
			
		||||
	__type(key, int);
 | 
			
		||||
	__type(value, int);
 | 
			
		||||
} sock_map_rx SEC(".maps");
 | 
			
		||||
 | 
			
		||||
struct {
 | 
			
		||||
	__uint(type, BPF_MAP_TYPE_SOCKMAP);
 | 
			
		||||
	__uint(max_entries, 20);
 | 
			
		||||
	__type(key, int);
 | 
			
		||||
	__type(value, int);
 | 
			
		||||
} sock_map_tx SEC(".maps");
 | 
			
		||||
 | 
			
		||||
struct {
 | 
			
		||||
	__uint(type, BPF_MAP_TYPE_SOCKMAP);
 | 
			
		||||
	__uint(max_entries, 20);
 | 
			
		||||
	__type(key, int);
 | 
			
		||||
	__type(value, int);
 | 
			
		||||
} sock_map_msg SEC(".maps");
 | 
			
		||||
 | 
			
		||||
SEC("sk_skb")
 | 
			
		||||
int prog_skb_verdict(struct __sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	return SK_PASS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char _license[] SEC("license") = "GPL";
 | 
			
		||||
		Loading…
	
		Reference in a new issue