mirror of
				https://github.com/xdp-project/bpf-examples.git
				synced 2024-05-06 15:54:53 +00:00 
			
		
		
		
	
		
			
	
	
		
			241 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			241 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | // SPDX-License-Identifier: GPL-2.0+
 | ||
|  | static const char *__doc__ = | ||
|  | 	" XDP load-balancing with CPU-map"; | ||
|  | 
 | ||
|  | #include <errno.h>
 | ||
|  | #include <signal.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <stdbool.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <unistd.h>
 | ||
|  | #include <locale.h>
 | ||
|  | #include <sys/resource.h>
 | ||
|  | #include <sys/sysinfo.h>
 | ||
|  | #include <getopt.h>
 | ||
|  | #include <net/if.h>
 | ||
|  | #include <time.h>
 | ||
|  | #include <linux/limits.h>
 | ||
|  | 
 | ||
|  | #include <bpf/bpf.h>
 | ||
|  | #include <bpf/libbpf.h>
 | ||
|  | 
 | ||
|  | #include <linux/if_link.h> /* XDP defines */
 | ||
|  | 
 | ||
|  | static int ifindex = -1; | ||
|  | static char ifname_buf[IF_NAMESIZE]; | ||
|  | static char *ifname; | ||
|  | 
 | ||
|  | static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; | ||
|  | 
 | ||
|  | /* Exit return codes */ | ||
|  | #define EXIT_OK 		0
 | ||
|  | #define EXIT_FAIL		1
 | ||
|  | #define EXIT_FAIL_OPTION	2
 | ||
|  | #define EXIT_FAIL_XDP		3
 | ||
|  | #define EXIT_FAIL_BPF		4
 | ||
|  | #define EXIT_FAIL_MEM		5
 | ||
|  | #define EXIT_FAIL_FILE		6
 | ||
|  | 
 | ||
|  | static const struct option long_options[] = { | ||
|  | 	{"help",	no_argument,		NULL, 'h' }, | ||
|  | 	{"dev",		required_argument,	NULL, 'd' }, | ||
|  | 	{"qsize",	required_argument,	NULL, 'q' }, | ||
|  | 	{"force",	no_argument,		NULL, 'F' }, | ||
|  | 	{0, 0, NULL,  0 } | ||
|  | }; | ||
|  | 
 | ||
|  | static void usage(char *argv[]) | ||
|  | { | ||
|  | 	int i; | ||
|  | 
 | ||
|  | 	printf("\nDOCUMENTATION:\n%s\n", __doc__); | ||
|  | 	printf("\n"); | ||
|  | 	printf(" Usage: %s (options-see-below)\n", argv[0]); | ||
|  | 	printf(" Listing options:\n"); | ||
|  | 	for (i = 0; long_options[i].name != 0; i++) { | ||
|  | 		printf(" --%-12s", long_options[i].name); | ||
|  | 		if (long_options[i].flag != NULL) | ||
|  | 			printf(" flag (internal value:%d)", | ||
|  | 				*long_options[i].flag); | ||
|  | 		else | ||
|  | 			printf(" short-option: -%c", | ||
|  | 				long_options[i].val); | ||
|  | 		printf("\n"); | ||
|  | 	} | ||
|  | 	printf("\n"); | ||
|  | } | ||
|  | 
 | ||
|  | static int create_cpu_entry(int cpumap_fd, __u32 cpu, | ||
|  | 			    struct bpf_cpumap_val *value) | ||
|  | { | ||
|  | 	int err; | ||
|  | 
 | ||
|  | 	/* Add a CPU entry to cpumap, as this allocate a cpu entry in
 | ||
|  | 	 * the kernel for the cpu. | ||
|  | 	 */ | ||
|  | 	err = bpf_map_update_elem(cpumap_fd, &cpu, value, 0); | ||
|  | 	if (err) { | ||
|  | 		fprintf(stderr, "Create CPU entry failed (err:%d)\n", err); | ||
|  | 		exit(EXIT_FAIL_BPF); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | /* Userspace MUST create/populate CPUMAP entries for redirect to work
 | ||
|  |  */ | ||
|  | static void enable_all_cpus(int cpumap_fd, __u32 qsize) | ||
|  | { | ||
|  | 	struct bpf_cpumap_val value = { 0 }; | ||
|  |         int n_cpus = get_nprocs_conf(); | ||
|  | 	int i; | ||
|  | 
 | ||
|  | 	value.qsize = qsize; | ||
|  | 
 | ||
|  | 	for (i = 0; i < n_cpus; i++) { | ||
|  | 		printf("Enable CPU:%d\n", i); | ||
|  | 		create_cpu_entry(cpumap_fd, i, &value); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | struct bpf_object *do_load_bpf_obj(struct bpf_object *obj) | ||
|  | { | ||
|  | 	char buf[200]; | ||
|  | 	int err; | ||
|  | 
 | ||
|  | 	err = bpf_object__load(obj); | ||
|  | 	if (err) { | ||
|  | 		libbpf_strerror(err, buf, sizeof(buf)); | ||
|  | 		printf("Error loading: %s\n", buf); | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 	return obj; | ||
|  | } | ||
|  | 
 | ||
|  | int do_xdp_attach(int ifindex, struct bpf_program *prog, __u32 xdp_flags) | ||
|  | { | ||
|  | 	int prog_fd = bpf_program__fd(prog); | ||
|  | 	int err; | ||
|  | 
 | ||
|  | 	if (prog_fd < 0) { | ||
|  | 		fprintf(stderr, "bpf_program__fd failed\n"); | ||
|  | 		return EXIT_FAIL_BPF; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags); | ||
|  | 	if (err) { | ||
|  | 		fprintf(stderr, "link set xdp fd failed (err:%d)\n", err); | ||
|  | 		return EXIT_FAIL_XDP; | ||
|  | 	} | ||
|  | 	return EXIT_OK; | ||
|  | } | ||
|  | 
 | ||
|  | int main(int argc, char **argv) | ||
|  | { | ||
|  | 	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; | ||
|  | 	int opt, longindex = 0; | ||
|  | 	__u32 cfg_qsize = 512; | ||
|  | 	char buf[100]; | ||
|  | 	int err; | ||
|  | 
 | ||
|  | 	struct bpf_object *obj = NULL; | ||
|  | 	struct bpf_program *prog; | ||
|  | 	int cpumap_fd = -1; | ||
|  | 
 | ||
|  | 	int n_cpus = get_nprocs_conf(); | ||
|  | 
 | ||
|  |         obj = bpf_object__open_file("xdp_cpumap_qinq.o", NULL); | ||
|  | 	err = libbpf_get_error(obj); | ||
|  | 	if (err) { | ||
|  | 		libbpf_strerror(err, buf, sizeof(buf)); | ||
|  | 		printf("Error opening file: %s\n", buf); | ||
|  | 		return EXIT_FAIL_FILE; | ||
|  | 	} | ||
|  | 	err = EXIT_OK; | ||
|  | 
 | ||
|  | 	/* Parse commands line args */ | ||
|  | 	while ((opt = getopt_long(argc, argv, "hSd:s:p:q:c:xzFf:e:r:m:", | ||
|  | 				  long_options, &longindex)) != -1) { | ||
|  | 		switch (opt) { | ||
|  | 		case 'd': | ||
|  | 			if (strlen(optarg) >= IF_NAMESIZE) { | ||
|  | 				fprintf(stderr, "ERR: --dev name too long\n"); | ||
|  | 				goto error; | ||
|  | 			} | ||
|  | 			ifname = (char *)&ifname_buf; | ||
|  | 			strncpy(ifname, optarg, IF_NAMESIZE); | ||
|  | 			ifindex = if_nametoindex(ifname); | ||
|  | 			if (ifindex == 0) { | ||
|  | 				fprintf(stderr, | ||
|  | 					"ERR: --dev name unknown err(%d):%s\n", | ||
|  | 					errno, strerror(errno)); | ||
|  | 				goto error; | ||
|  | 			} | ||
|  | 			break; | ||
|  | 		case 'q': | ||
|  | 			cfg_qsize = strtol(optarg, NULL, 10); | ||
|  | 			break; | ||
|  | 		case 'F': | ||
|  | 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; | ||
|  | 			break; | ||
|  | 		case 'h': | ||
|  | 		error: | ||
|  | 		default: | ||
|  | 			usage(argv); | ||
|  | 			return EXIT_FAIL_OPTION; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	/* Required option */ | ||
|  | 	if (ifindex == -1) { | ||
|  | 		fprintf(stderr, "ERR: required option --dev missing\n"); | ||
|  | 		usage(argv); | ||
|  | 		return EXIT_FAIL_OPTION; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (setrlimit(RLIMIT_MEMLOCK, &r)) { | ||
|  | 		perror("setrlimit(RLIMIT_MEMLOCK)"); | ||
|  | 		return EXIT_FAIL_MEM; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* Always use XDP native driver mode */ | ||
|  | 	xdp_flags |= XDP_FLAGS_DRV_MODE; | ||
|  | 
 | ||
|  | 	obj = do_load_bpf_obj(obj); | ||
|  | 	if (!obj) | ||
|  | 		return EXIT_FAIL_BPF; | ||
|  | 
 | ||
|  | 	/* Pickup first BPF-program */ | ||
|  | 	prog = bpf_program__next(NULL, obj); | ||
|  | 	if (!prog) { | ||
|  | 		printf("No program!\n"); | ||
|  | 		err = EXIT_FAIL_BPF; | ||
|  | 		goto out; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* Get file descriptor to BPF-map */ | ||
|  | 	cpumap_fd = bpf_object__find_map_fd_by_name(obj, "cpumap"); | ||
|  | 	if (cpumap_fd < 0) { | ||
|  | 		printf("No cpumap found!\n"); | ||
|  | 		err = EXIT_FAIL_BPF; | ||
|  | 		goto out; | ||
|  | 	} | ||
|  | 	/* Configure cpumap */ | ||
|  | 	enable_all_cpus(cpumap_fd, cfg_qsize); | ||
|  | 
 | ||
|  | 	/* Attach XDP program */ | ||
|  | 	err = do_xdp_attach(ifindex, prog, xdp_flags); | ||
|  | 	if (err) | ||
|  | 		goto out; | ||
|  | 
 | ||
|  | 	printf("Attached XDP program:\"%s\" on netdev:%s (ifindex:%d)\n", | ||
|  | 	       bpf_program__name(prog), ifname, ifindex); | ||
|  | 	printf("CPUs: %d\n", n_cpus); | ||
|  | 
 | ||
|  | out: | ||
|  | 	if (obj) | ||
|  | 		bpf_object__close(obj); | ||
|  | 
 | ||
|  | 	return err; | ||
|  | } |