| .. | .. | 
|---|
| 26 | 26 | #include <net/if.h> | 
|---|
| 27 | 27 | #include <time.h> | 
|---|
| 28 | 28 |  | 
|---|
|  | 29 | +#include <signal.h> | 
|---|
| 29 | 30 | #include <bpf/bpf.h> | 
|---|
| 30 |  | -#include "bpf_load.h" | 
|---|
|  | 31 | +#include <bpf/libbpf.h> | 
|---|
| 31 | 32 | #include "bpf_util.h" | 
|---|
| 32 | 33 |  | 
|---|
|  | 34 | +enum map_type { | 
|---|
|  | 35 | +	REDIRECT_ERR_CNT, | 
|---|
|  | 36 | +	EXCEPTION_CNT, | 
|---|
|  | 37 | +	CPUMAP_ENQUEUE_CNT, | 
|---|
|  | 38 | +	CPUMAP_KTHREAD_CNT, | 
|---|
|  | 39 | +	DEVMAP_XMIT_CNT, | 
|---|
|  | 40 | +}; | 
|---|
|  | 41 | + | 
|---|
|  | 42 | +static const char *const map_type_strings[] = { | 
|---|
|  | 43 | +	[REDIRECT_ERR_CNT] = "redirect_err_cnt", | 
|---|
|  | 44 | +	[EXCEPTION_CNT] = "exception_cnt", | 
|---|
|  | 45 | +	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt", | 
|---|
|  | 46 | +	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt", | 
|---|
|  | 47 | +	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt", | 
|---|
|  | 48 | +}; | 
|---|
|  | 49 | + | 
|---|
|  | 50 | +#define NUM_MAP 5 | 
|---|
|  | 51 | +#define NUM_TP 8 | 
|---|
|  | 52 | + | 
|---|
|  | 53 | +static int tp_cnt; | 
|---|
|  | 54 | +static int map_cnt; | 
|---|
| 33 | 55 | static int verbose = 1; | 
|---|
| 34 | 56 | static bool debug = false; | 
|---|
|  | 57 | +struct bpf_map *map_data[NUM_MAP] = {}; | 
|---|
|  | 58 | +struct bpf_link *tp_links[NUM_TP] = {}; | 
|---|
|  | 59 | +struct bpf_object *obj; | 
|---|
| 35 | 60 |  | 
|---|
| 36 | 61 | static const struct option long_options[] = { | 
|---|
| 37 | 62 | {"help",	no_argument,		NULL, 'h' }, | 
|---|
| .. | .. | 
|---|
| 40 | 65 | {"sec", 	required_argument,	NULL, 's' }, | 
|---|
| 41 | 66 | {0, 0, NULL,  0 } | 
|---|
| 42 | 67 | }; | 
|---|
|  | 68 | + | 
|---|
|  | 69 | +static void int_exit(int sig) | 
|---|
|  | 70 | +{ | 
|---|
|  | 71 | +	/* Detach tracepoints */ | 
|---|
|  | 72 | +	while (tp_cnt) | 
|---|
|  | 73 | +		bpf_link__destroy(tp_links[--tp_cnt]); | 
|---|
|  | 74 | + | 
|---|
|  | 75 | +	bpf_object__close(obj); | 
|---|
|  | 76 | +	exit(0); | 
|---|
|  | 77 | +} | 
|---|
| 43 | 78 |  | 
|---|
| 44 | 79 | /* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */ | 
|---|
| 45 | 80 | #define EXIT_FAIL_MEM	5 | 
|---|
| .. | .. | 
|---|
| 483 | 518 | * this can happen by someone running perf-record -e | 
|---|
| 484 | 519 | */ | 
|---|
| 485 | 520 |  | 
|---|
| 486 |  | -	fd = map_data[0].fd; /* map0: redirect_err_cnt */ | 
|---|
|  | 521 | +	fd = bpf_map__fd(map_data[REDIRECT_ERR_CNT]); | 
|---|
| 487 | 522 | for (i = 0; i < REDIR_RES_MAX; i++) | 
|---|
| 488 | 523 | map_collect_record_u64(fd, i, &rec->xdp_redirect[i]); | 
|---|
| 489 | 524 |  | 
|---|
| 490 |  | -	fd = map_data[1].fd; /* map1: exception_cnt */ | 
|---|
|  | 525 | +	fd = bpf_map__fd(map_data[EXCEPTION_CNT]); | 
|---|
| 491 | 526 | for (i = 0; i < XDP_ACTION_MAX; i++) { | 
|---|
| 492 | 527 | map_collect_record_u64(fd, i, &rec->xdp_exception[i]); | 
|---|
| 493 | 528 | } | 
|---|
| 494 | 529 |  | 
|---|
| 495 |  | -	fd = map_data[2].fd; /* map2: cpumap_enqueue_cnt */ | 
|---|
|  | 530 | +	fd = bpf_map__fd(map_data[CPUMAP_ENQUEUE_CNT]); | 
|---|
| 496 | 531 | for (i = 0; i < MAX_CPUS; i++) | 
|---|
| 497 | 532 | map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]); | 
|---|
| 498 | 533 |  | 
|---|
| 499 |  | -	fd = map_data[3].fd; /* map3: cpumap_kthread_cnt */ | 
|---|
|  | 534 | +	fd = bpf_map__fd(map_data[CPUMAP_KTHREAD_CNT]); | 
|---|
| 500 | 535 | map_collect_record(fd, 0, &rec->xdp_cpumap_kthread); | 
|---|
| 501 | 536 |  | 
|---|
| 502 |  | -	fd = map_data[4].fd; /* map4: devmap_xmit_cnt */ | 
|---|
|  | 537 | +	fd = bpf_map__fd(map_data[DEVMAP_XMIT_CNT]); | 
|---|
| 503 | 538 | map_collect_record(fd, 0, &rec->xdp_devmap_xmit); | 
|---|
| 504 | 539 |  | 
|---|
| 505 | 540 | return true; | 
|---|
| .. | .. | 
|---|
| 509 | 544 | { | 
|---|
| 510 | 545 | unsigned int nr_cpus = bpf_num_possible_cpus(); | 
|---|
| 511 | 546 | void *array; | 
|---|
| 512 |  | -	size_t size; | 
|---|
| 513 | 547 |  | 
|---|
| 514 |  | -	size = record_size * nr_cpus; | 
|---|
| 515 |  | -	array = malloc(size); | 
|---|
| 516 |  | -	memset(array, 0, size); | 
|---|
|  | 548 | +	array = calloc(nr_cpus, record_size); | 
|---|
| 517 | 549 | if (!array) { | 
|---|
| 518 | 550 | fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus); | 
|---|
| 519 | 551 | exit(EXIT_FAIL_MEM); | 
|---|
| .. | .. | 
|---|
| 528 | 560 | int i; | 
|---|
| 529 | 561 |  | 
|---|
| 530 | 562 | /* Alloc main stats_record structure */ | 
|---|
| 531 |  | -	rec = malloc(sizeof(*rec)); | 
|---|
| 532 |  | -	memset(rec, 0, sizeof(*rec)); | 
|---|
|  | 563 | +	rec = calloc(1, sizeof(*rec)); | 
|---|
| 533 | 564 | if (!rec) { | 
|---|
| 534 | 565 | fprintf(stderr, "Mem alloc error\n"); | 
|---|
| 535 | 566 | exit(EXIT_FAIL_MEM); | 
|---|
| .. | .. | 
|---|
| 602 | 633 |  | 
|---|
| 603 | 634 | /* TODO Need more advanced stats on error types */ | 
|---|
| 604 | 635 | if (verbose) { | 
|---|
| 605 |  | -		printf(" - Stats map0: %s\n", map_data[0].name); | 
|---|
| 606 |  | -		printf(" - Stats map1: %s\n", map_data[1].name); | 
|---|
|  | 636 | +		printf(" - Stats map0: %s\n", bpf_map__name(map_data[0])); | 
|---|
|  | 637 | +		printf(" - Stats map1: %s\n", bpf_map__name(map_data[1])); | 
|---|
| 607 | 638 | printf("\n"); | 
|---|
| 608 | 639 | } | 
|---|
| 609 | 640 | fflush(stdout); | 
|---|
| .. | .. | 
|---|
| 622 | 653 |  | 
|---|
| 623 | 654 | static void print_bpf_prog_info(void) | 
|---|
| 624 | 655 | { | 
|---|
| 625 |  | -	int i; | 
|---|
|  | 656 | +	struct bpf_program *prog; | 
|---|
|  | 657 | +	struct bpf_map *map; | 
|---|
|  | 658 | +	int i = 0; | 
|---|
| 626 | 659 |  | 
|---|
| 627 | 660 | /* Prog info */ | 
|---|
| 628 |  | -	printf("Loaded BPF prog have %d bpf program(s)\n", prog_cnt); | 
|---|
| 629 |  | -	for (i = 0; i < prog_cnt; i++) { | 
|---|
| 630 |  | -		printf(" - prog_fd[%d] = fd(%d)\n", i, prog_fd[i]); | 
|---|
|  | 661 | +	printf("Loaded BPF prog have %d bpf program(s)\n", tp_cnt); | 
|---|
|  | 662 | +	bpf_object__for_each_program(prog, obj) { | 
|---|
|  | 663 | +		printf(" - prog_fd[%d] = fd(%d)\n", i, bpf_program__fd(prog)); | 
|---|
|  | 664 | +		i++; | 
|---|
| 631 | 665 | } | 
|---|
| 632 | 666 |  | 
|---|
|  | 667 | +	i = 0; | 
|---|
| 633 | 668 | /* Maps info */ | 
|---|
| 634 |  | -	printf("Loaded BPF prog have %d map(s)\n", map_data_count); | 
|---|
| 635 |  | -	for (i = 0; i < map_data_count; i++) { | 
|---|
| 636 |  | -		char *name = map_data[i].name; | 
|---|
| 637 |  | -		int fd     = map_data[i].fd; | 
|---|
|  | 669 | +	printf("Loaded BPF prog have %d map(s)\n", map_cnt); | 
|---|
|  | 670 | +	bpf_object__for_each_map(map, obj) { | 
|---|
|  | 671 | +		const char *name = bpf_map__name(map); | 
|---|
|  | 672 | +		int fd		 = bpf_map__fd(map); | 
|---|
| 638 | 673 |  | 
|---|
| 639 | 674 | printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name); | 
|---|
|  | 675 | +		i++; | 
|---|
| 640 | 676 | } | 
|---|
| 641 | 677 |  | 
|---|
| 642 | 678 | /* Event info */ | 
|---|
| 643 |  | -	printf("Searching for (max:%d) event file descriptor(s)\n", prog_cnt); | 
|---|
| 644 |  | -	for (i = 0; i < prog_cnt; i++) { | 
|---|
| 645 |  | -		if (event_fd[i] != -1) | 
|---|
| 646 |  | -			printf(" - event_fd[%d] = fd(%d)\n", i, event_fd[i]); | 
|---|
|  | 679 | +	printf("Searching for (max:%d) event file descriptor(s)\n", tp_cnt); | 
|---|
|  | 680 | +	for (i = 0; i < tp_cnt; i++) { | 
|---|
|  | 681 | +		int fd = bpf_link__fd(tp_links[i]); | 
|---|
|  | 682 | + | 
|---|
|  | 683 | +		if (fd != -1) | 
|---|
|  | 684 | +			printf(" - event_fd[%d] = fd(%d)\n", i, fd); | 
|---|
| 647 | 685 | } | 
|---|
| 648 | 686 | } | 
|---|
| 649 | 687 |  | 
|---|
| 650 | 688 | int main(int argc, char **argv) | 
|---|
| 651 | 689 | { | 
|---|
| 652 | 690 | struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; | 
|---|
|  | 691 | +	struct bpf_program *prog; | 
|---|
| 653 | 692 | int longindex = 0, opt; | 
|---|
| 654 |  | -	int ret = EXIT_SUCCESS; | 
|---|
| 655 |  | -	char bpf_obj_file[256]; | 
|---|
|  | 693 | +	int ret = EXIT_FAILURE; | 
|---|
|  | 694 | +	enum map_type type; | 
|---|
|  | 695 | +	char filename[256]; | 
|---|
| 656 | 696 |  | 
|---|
| 657 | 697 | /* Default settings: */ | 
|---|
| 658 | 698 | bool errors_only = true; | 
|---|
| 659 | 699 | int interval = 2; | 
|---|
| 660 |  | - | 
|---|
| 661 |  | -	snprintf(bpf_obj_file, sizeof(bpf_obj_file), "%s_kern.o", argv[0]); | 
|---|
| 662 | 700 |  | 
|---|
| 663 | 701 | /* Parse commands line args */ | 
|---|
| 664 | 702 | while ((opt = getopt_long(argc, argv, "hDSs:", | 
|---|
| .. | .. | 
|---|
| 676 | 714 | case 'h': | 
|---|
| 677 | 715 | default: | 
|---|
| 678 | 716 | usage(argv); | 
|---|
| 679 |  | -			return EXIT_FAILURE; | 
|---|
|  | 717 | +			return ret; | 
|---|
| 680 | 718 | } | 
|---|
| 681 | 719 | } | 
|---|
| 682 | 720 |  | 
|---|
|  | 721 | +	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | 
|---|
| 683 | 722 | if (setrlimit(RLIMIT_MEMLOCK, &r)) { | 
|---|
| 684 | 723 | perror("setrlimit(RLIMIT_MEMLOCK)"); | 
|---|
| 685 |  | -		return EXIT_FAILURE; | 
|---|
|  | 724 | +		return ret; | 
|---|
| 686 | 725 | } | 
|---|
| 687 | 726 |  | 
|---|
| 688 |  | -	if (load_bpf_file(bpf_obj_file)) { | 
|---|
| 689 |  | -		printf("ERROR - bpf_log_buf: %s", bpf_log_buf); | 
|---|
| 690 |  | -		return EXIT_FAILURE; | 
|---|
|  | 727 | +	/* Remove tracepoint program when program is interrupted or killed */ | 
|---|
|  | 728 | +	signal(SIGINT, int_exit); | 
|---|
|  | 729 | +	signal(SIGTERM, int_exit); | 
|---|
|  | 730 | + | 
|---|
|  | 731 | +	obj = bpf_object__open_file(filename, NULL); | 
|---|
|  | 732 | +	if (libbpf_get_error(obj)) { | 
|---|
|  | 733 | +		printf("ERROR: opening BPF object file failed\n"); | 
|---|
|  | 734 | +		obj = NULL; | 
|---|
|  | 735 | +		goto cleanup; | 
|---|
| 691 | 736 | } | 
|---|
| 692 |  | -	if (!prog_fd[0]) { | 
|---|
| 693 |  | -		printf("ERROR - load_bpf_file: %s\n", strerror(errno)); | 
|---|
| 694 |  | -		return EXIT_FAILURE; | 
|---|
|  | 737 | + | 
|---|
|  | 738 | +	/* load BPF program */ | 
|---|
|  | 739 | +	if (bpf_object__load(obj)) { | 
|---|
|  | 740 | +		printf("ERROR: loading BPF object file failed\n"); | 
|---|
|  | 741 | +		goto cleanup; | 
|---|
|  | 742 | +	} | 
|---|
|  | 743 | + | 
|---|
|  | 744 | +	for (type = 0; type < NUM_MAP; type++) { | 
|---|
|  | 745 | +		map_data[type] = | 
|---|
|  | 746 | +			bpf_object__find_map_by_name(obj, map_type_strings[type]); | 
|---|
|  | 747 | + | 
|---|
|  | 748 | +		if (libbpf_get_error(map_data[type])) { | 
|---|
|  | 749 | +			printf("ERROR: finding a map in obj file failed\n"); | 
|---|
|  | 750 | +			goto cleanup; | 
|---|
|  | 751 | +		} | 
|---|
|  | 752 | +		map_cnt++; | 
|---|
|  | 753 | +	} | 
|---|
|  | 754 | + | 
|---|
|  | 755 | +	bpf_object__for_each_program(prog, obj) { | 
|---|
|  | 756 | +		tp_links[tp_cnt] = bpf_program__attach(prog); | 
|---|
|  | 757 | +		if (libbpf_get_error(tp_links[tp_cnt])) { | 
|---|
|  | 758 | +			printf("ERROR: bpf_program__attach failed\n"); | 
|---|
|  | 759 | +			tp_links[tp_cnt] = NULL; | 
|---|
|  | 760 | +			goto cleanup; | 
|---|
|  | 761 | +		} | 
|---|
|  | 762 | +		tp_cnt++; | 
|---|
| 695 | 763 | } | 
|---|
| 696 | 764 |  | 
|---|
| 697 | 765 | if (debug) { | 
|---|
| 698 | 766 | print_bpf_prog_info(); | 
|---|
| 699 | 767 | } | 
|---|
| 700 | 768 |  | 
|---|
| 701 |  | -	/* Unload/stop tracepoint event by closing fd's */ | 
|---|
|  | 769 | +	/* Unload/stop tracepoint event by closing bpf_link's */ | 
|---|
| 702 | 770 | if (errors_only) { | 
|---|
| 703 |  | -		/* The prog_fd[i] and event_fd[i] depend on the | 
|---|
| 704 |  | -		 * order the functions was defined in _kern.c | 
|---|
|  | 771 | +		/* The bpf_link[i] depend on the order of | 
|---|
|  | 772 | +		 * the functions was defined in _kern.c | 
|---|
| 705 | 773 | */ | 
|---|
| 706 |  | -		close(event_fd[2]); /* tracepoint/xdp/xdp_redirect */ | 
|---|
| 707 |  | -		close(prog_fd[2]);  /* func: trace_xdp_redirect */ | 
|---|
| 708 |  | -		close(event_fd[3]); /* tracepoint/xdp/xdp_redirect_map */ | 
|---|
| 709 |  | -		close(prog_fd[3]);  /* func: trace_xdp_redirect_map */ | 
|---|
|  | 774 | +		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */ | 
|---|
|  | 775 | +		tp_links[2] = NULL; | 
|---|
|  | 776 | + | 
|---|
|  | 777 | +		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */ | 
|---|
|  | 778 | +		tp_links[3] = NULL; | 
|---|
| 710 | 779 | } | 
|---|
| 711 | 780 |  | 
|---|
| 712 | 781 | stats_poll(interval, errors_only); | 
|---|
| 713 | 782 |  | 
|---|
|  | 783 | +	ret = EXIT_SUCCESS; | 
|---|
|  | 784 | + | 
|---|
|  | 785 | +cleanup: | 
|---|
|  | 786 | +	/* Detach tracepoints */ | 
|---|
|  | 787 | +	while (tp_cnt) | 
|---|
|  | 788 | +		bpf_link__destroy(tp_links[--tp_cnt]); | 
|---|
|  | 789 | + | 
|---|
|  | 790 | +	bpf_object__close(obj); | 
|---|
| 714 | 791 | return ret; | 
|---|
| 715 | 792 | } | 
|---|