diff options
Diffstat (limited to 'samples')
94 files changed, 1732 insertions, 4404 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index 43d2e9aa557f..0d81c00289ee 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -14,7 +14,13 @@ config SAMPLE_TRACE_EVENTS tristate "Build trace_events examples -- loadable modules only" depends on EVENT_TRACING && m help - This build trace event example modules. + This builds the trace event example module. + +config SAMPLE_TRACE_CUSTOM_EVENTS + tristate "Build custom trace event example -- loadable modules only" + depends on EVENT_TRACING && m + help + This builds the custom trace event example module. config SAMPLE_TRACE_PRINTK tristate "Build trace_printk module - tests various trace_printk formats" @@ -73,6 +79,13 @@ config SAMPLE_HW_BREAKPOINT help This builds kernel hardware breakpoint example modules. +config SAMPLE_FPROBE + tristate "Build fprobe examples -- loadable modules only" + depends on FPROBE && m + help + This builds a fprobe example module. This module has an option 'symbol'. + You can specify a probed symbol or symbols separated with ','. + config SAMPLE_KFIFO tristate "Build kfifo examples -- loadable modules only" depends on m @@ -241,6 +254,17 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function. +config SAMPLE_CORESIGHT_SYSCFG + tristate "Build example loadable module for CoreSight config" + depends on CORESIGHT && m + help + Build an example loadable module that adds new CoreSight features + and configuration using the CoreSight system configuration API. + This demonstrates how a user may create their own CoreSight + configurations and easily load them into the system at runtime. + +source "samples/rust/Kconfig" + endif # SAMPLES config HAVE_SAMPLE_FTRACE_DIRECT diff --git a/samples/Makefile b/samples/Makefile index 4bcd6b93bffa..9832ef3f8fcb 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg/ subdir-$(CONFIG_SAMPLE_SECCOMP) += seccomp subdir-$(CONFIG_SAMPLE_TIMER) += timers obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace_events/ +obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_events/ obj-$(CONFIG_SAMPLE_TRACE_PRINTK) += trace_printk/ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace/ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace/ @@ -32,3 +33,6 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ +obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/ +obj-$(CONFIG_SAMPLES_RUST) += rust/ diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 38638845db9d..727da3c5879b 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -45,9 +45,6 @@ tprogs-y += xdp_rxq_info tprogs-y += syscall_tp tprogs-y += cpustat tprogs-y += xdp_adjust_tail -tprogs-y += xdpsock -tprogs-y += xdpsock_ctrl_proc -tprogs-y += xsk_fwd tprogs-y += xdp_fwd tprogs-y += task_fd_query tprogs-y += xdp_sample_pkts @@ -96,7 +93,6 @@ test_cgrp2_sock2-objs := test_cgrp2_sock2.o xdp1-objs := xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := xdp1_user.o -xdp_router_ipv4-objs := xdp_router_ipv4_user.o test_current_task_under_cgroup-objs := $(CGROUP_HELPERS) \ test_current_task_under_cgroup_user.o trace_event-objs := trace_event_user.o $(TRACE_HELPERS) @@ -110,9 +106,6 @@ xdp_rxq_info-objs := xdp_rxq_info_user.o syscall_tp-objs := syscall_tp_user.o cpustat-objs := cpustat_user.o xdp_adjust_tail-objs := xdp_adjust_tail_user.o -xdpsock-objs := xdpsock_user.o -xdpsock_ctrl_proc-objs := xdpsock_ctrl_proc.o -xsk_fwd-objs := xsk_fwd.o xdp_fwd-objs := xdp_fwd_user.o task_fd_query-objs := task_fd_query_user.o $(TRACE_HELPERS) xdp_sample_pkts-objs := xdp_sample_pkts_user.o @@ -124,6 +117,7 @@ xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o $(XDP_SAMPLE) xdp_redirect_map-objs := xdp_redirect_map_user.o $(XDP_SAMPLE) xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE) xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE) +xdp_router_ipv4-objs := xdp_router_ipv4_user.o $(XDP_SAMPLE) # Tell kbuild to always build the programs always-y := $(tprogs-y) @@ -153,7 +147,6 @@ always-y += parse_varlen.o parse_simple.o parse_ldabs.o always-y += test_cgrp2_tc_kern.o always-y += xdp1_kern.o always-y += xdp2_kern.o -always-y += xdp_router_ipv4_kern.o always-y += test_current_task_under_cgroup_kern.o always-y += trace_event_kern.o always-y += sampleip_kern.o @@ -180,7 +173,6 @@ always-y += xdp_sample_pkts_kern.o always-y += ibumad_kern.o always-y += hbm_out_kern.o always-y += hbm_edt_kern.o -always-y += xdpsock_kern.o ifeq ($(ARCH), arm) # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux @@ -220,12 +212,11 @@ TPROGLDLIBS_xdp_redirect += -lm TPROGLDLIBS_xdp_redirect_cpu += -lm TPROGLDLIBS_xdp_redirect_map += -lm TPROGLDLIBS_xdp_redirect_map_multi += -lm +TPROGLDLIBS_xdp_router_ipv4 += -lm -pthread TPROGLDLIBS_tracex4 += -lrt TPROGLDLIBS_trace_output += -lrt TPROGLDLIBS_map_perf_test += -lrt TPROGLDLIBS_test_overhead += -lrt -TPROGLDLIBS_xdpsock += -pthread -lcap -TPROGLDLIBS_xsk_fwd += -pthread # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang @@ -291,12 +282,10 @@ $(LIBBPF): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OU BPFTOOLDIR := $(TOOLS_PATH)/bpf/bpftool BPFTOOL_OUTPUT := $(abspath $(BPF_SAMPLES_PATH))/bpftool -BPFTOOL := $(BPFTOOL_OUTPUT)/bpftool -$(BPFTOOL): $(LIBBPF) $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) | $(BPFTOOL_OUTPUT) - $(MAKE) -C $(BPFTOOLDIR) srctree=$(BPF_SAMPLES_PATH)/../../ \ - OUTPUT=$(BPFTOOL_OUTPUT)/ \ - LIBBPF_OUTPUT=$(LIBBPF_OUTPUT)/ \ - LIBBPF_DESTDIR=$(LIBBPF_DESTDIR)/ +BPFTOOL := $(BPFTOOL_OUTPUT)/bootstrap/bpftool +$(BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) | $(BPFTOOL_OUTPUT) + $(MAKE) -C $(BPFTOOLDIR) srctree=$(BPF_SAMPLES_PATH)/../../ \ + OUTPUT=$(BPFTOOL_OUTPUT)/ bootstrap $(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT): $(call msg,MKDIR,$@) @@ -342,6 +331,7 @@ $(obj)/xdp_redirect_map_multi_user.o: $(obj)/xdp_redirect_map_multi.skel.h $(obj)/xdp_redirect_map_user.o: $(obj)/xdp_redirect_map.skel.h $(obj)/xdp_redirect_user.o: $(obj)/xdp_redirect.skel.h $(obj)/xdp_monitor_user.o: $(obj)/xdp_monitor.skel.h +$(obj)/xdp_router_ipv4_user.o: $(obj)/xdp_router_ipv4.skel.h $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h $(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h @@ -368,16 +358,15 @@ VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS)))) $(obj)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) ifeq ($(VMLINUX_H),) +ifeq ($(VMLINUX_BTF),) + $(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)",\ + build the kernel or set VMLINUX_BTF or VMLINUX_H variable) +endif $(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@ else $(Q)cp "$(VMLINUX_H)" $@ endif -ifeq ($(VMLINUX_BTF),) - $(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)",\ - build the kernel or set VMLINUX_BTF variable) -endif - clean-files += vmlinux.h # Get Clang's default includes on this system, as opposed to those seen by @@ -399,6 +388,7 @@ $(obj)/xdp_redirect_map_multi.bpf.o: $(obj)/xdp_sample.bpf.o $(obj)/xdp_redirect_map.bpf.o: $(obj)/xdp_sample.bpf.o $(obj)/xdp_redirect.bpf.o: $(obj)/xdp_sample.bpf.o $(obj)/xdp_monitor.bpf.o: $(obj)/xdp_sample.bpf.o +$(obj)/xdp_router_ipv4.bpf.o: $(obj)/xdp_sample.bpf.o $(obj)/%.bpf.o: $(src)/%.bpf.c $(obj)/vmlinux.h $(src)/xdp_sample.bpf.h $(src)/xdp_sample_shared.h @echo " CLANG-BPF " $@ @@ -409,7 +399,8 @@ $(obj)/%.bpf.o: $(src)/%.bpf.c $(obj)/vmlinux.h $(src)/xdp_sample.bpf.h $(src)/x -c $(filter %.bpf.c,$^) -o $@ LINKED_SKELS := xdp_redirect_cpu.skel.h xdp_redirect_map_multi.skel.h \ - xdp_redirect_map.skel.h xdp_redirect.skel.h xdp_monitor.skel.h + xdp_redirect_map.skel.h xdp_redirect.skel.h xdp_monitor.skel.h \ + xdp_router_ipv4.skel.h clean-files += $(LINKED_SKELS) xdp_redirect_cpu.skel.h-deps := xdp_redirect_cpu.bpf.o xdp_sample.bpf.o @@ -417,6 +408,7 @@ xdp_redirect_map_multi.skel.h-deps := xdp_redirect_map_multi.bpf.o xdp_sample.bp xdp_redirect_map.skel.h-deps := xdp_redirect_map.bpf.o xdp_sample.bpf.o xdp_redirect.skel.h-deps := xdp_redirect.bpf.o xdp_sample.bpf.o xdp_monitor.skel.h-deps := xdp_monitor.bpf.o xdp_sample.bpf.o +xdp_router_ipv4.skel.h-deps := xdp_router_ipv4.bpf.o xdp_sample.bpf.o LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.bpf.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps))) diff --git a/samples/bpf/cpustat_user.c b/samples/bpf/cpustat_user.c index 96675985e9e0..ab90bb08a2b4 100644 --- a/samples/bpf/cpustat_user.c +++ b/samples/bpf/cpustat_user.c @@ -13,7 +13,6 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> -#include <sys/resource.h> #include <sys/wait.h> #include <bpf/bpf.h> diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c index 16dbf49e0f19..88a26f3ce201 100644 --- a/samples/bpf/fds_example.c +++ b/samples/bpf/fds_example.c @@ -17,6 +17,7 @@ #include <bpf/libbpf.h> #include "bpf_insn.h" #include "sock_example.h" +#include "bpf_util.h" #define BPF_F_PIN (1 << 0) #define BPF_F_GET (1 << 1) @@ -52,7 +53,7 @@ static int bpf_prog_create(const char *object) BPF_MOV64_IMM(BPF_REG_0, 1), BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(insns) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(insns); struct bpf_object *obj; int err; diff --git a/samples/bpf/hbm.c b/samples/bpf/hbm.c index 1fe5bcafb3bc..516fbac28b71 100644 --- a/samples/bpf/hbm.c +++ b/samples/bpf/hbm.c @@ -34,7 +34,6 @@ #include <stdio.h> #include <stdlib.h> #include <assert.h> -#include <sys/resource.h> #include <sys/time.h> #include <unistd.h> #include <errno.h> @@ -46,7 +45,6 @@ #include <bpf/bpf.h> #include <getopt.h> -#include "bpf_rlimit.h" #include "cgroup_helpers.h" #include "hbm.h" #include "bpf_util.h" @@ -510,5 +508,8 @@ int main(int argc, char **argv) prog = argv[optind]; printf("HBM prog: %s\n", prog != NULL ? prog : "NULL"); + /* Use libbpf 1.0 API mode */ + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + return run_bpf_prog(prog, cg_id); } diff --git a/samples/bpf/ibumad_user.c b/samples/bpf/ibumad_user.c index 0746ca516097..d074c978aac7 100644 --- a/samples/bpf/ibumad_user.c +++ b/samples/bpf/ibumad_user.c @@ -19,7 +19,6 @@ #include <sys/types.h> #include <limits.h> -#include <sys/resource.h> #include <getopt.h> #include <net/if.h> diff --git a/samples/bpf/map_perf_test_kern.c b/samples/bpf/map_perf_test_kern.c index 8773f22b6a98..7342c5b2f278 100644 --- a/samples/bpf/map_perf_test_kern.c +++ b/samples/bpf/map_perf_test_kern.c @@ -108,11 +108,14 @@ int stress_hmap(struct pt_regs *ctx) u32 key = bpf_get_current_pid_tgid(); long init_val = 1; long *value; + int i; - bpf_map_update_elem(&hash_map, &key, &init_val, BPF_ANY); - value = bpf_map_lookup_elem(&hash_map, &key); - if (value) - bpf_map_delete_elem(&hash_map, &key); + for (i = 0; i < 10; i++) { + bpf_map_update_elem(&hash_map, &key, &init_val, BPF_ANY); + value = bpf_map_lookup_elem(&hash_map, &key); + if (value) + bpf_map_delete_elem(&hash_map, &key); + } return 0; } @@ -123,11 +126,14 @@ int stress_percpu_hmap(struct pt_regs *ctx) u32 key = bpf_get_current_pid_tgid(); long init_val = 1; long *value; + int i; - bpf_map_update_elem(&percpu_hash_map, &key, &init_val, BPF_ANY); - value = bpf_map_lookup_elem(&percpu_hash_map, &key); - if (value) - bpf_map_delete_elem(&percpu_hash_map, &key); + for (i = 0; i < 10; i++) { + bpf_map_update_elem(&percpu_hash_map, &key, &init_val, BPF_ANY); + value = bpf_map_lookup_elem(&percpu_hash_map, &key); + if (value) + bpf_map_delete_elem(&percpu_hash_map, &key); + } return 0; } @@ -137,11 +143,14 @@ int stress_hmap_alloc(struct pt_regs *ctx) u32 key = bpf_get_current_pid_tgid(); long init_val = 1; long *value; + int i; - bpf_map_update_elem(&hash_map_alloc, &key, &init_val, BPF_ANY); - value = bpf_map_lookup_elem(&hash_map_alloc, &key); - if (value) - bpf_map_delete_elem(&hash_map_alloc, &key); + for (i = 0; i < 10; i++) { + bpf_map_update_elem(&hash_map_alloc, &key, &init_val, BPF_ANY); + value = bpf_map_lookup_elem(&hash_map_alloc, &key); + if (value) + bpf_map_delete_elem(&hash_map_alloc, &key); + } return 0; } @@ -151,11 +160,14 @@ int stress_percpu_hmap_alloc(struct pt_regs *ctx) u32 key = bpf_get_current_pid_tgid(); long init_val = 1; long *value; + int i; - bpf_map_update_elem(&percpu_hash_map_alloc, &key, &init_val, BPF_ANY); - value = bpf_map_lookup_elem(&percpu_hash_map_alloc, &key); - if (value) - bpf_map_delete_elem(&percpu_hash_map_alloc, &key); + for (i = 0; i < 10; i++) { + bpf_map_update_elem(&percpu_hash_map_alloc, &key, &init_val, BPF_ANY); + value = bpf_map_lookup_elem(&percpu_hash_map_alloc, &key); + if (value) + bpf_map_delete_elem(&percpu_hash_map_alloc, &key); + } return 0; } diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 319fd31522f3..1bb53f4b29e1 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -13,7 +13,6 @@ #include <signal.h> #include <string.h> #include <time.h> -#include <sys/resource.h> #include <arpa/inet.h> #include <errno.h> @@ -73,7 +72,7 @@ static int test_flags = ~0; static uint32_t num_map_entries; static uint32_t inner_lru_hash_size; static int lru_hash_lookup_test_entries = 32; -static uint32_t max_cnt = 1000000; +static uint32_t max_cnt = 10000; static int check_test_flags(enum test_type t) { @@ -413,7 +412,7 @@ static void fixup_map(struct bpf_object *obj) for (i = 0; i < NR_TESTS; i++) { if (!strcmp(test_map_names[i], name) && (check_test_flags(i))) { - bpf_map__resize(map, num_map_entries); + bpf_map__set_max_entries(map, num_map_entries); continue; } } diff --git a/samples/bpf/offwaketime_kern.c b/samples/bpf/offwaketime_kern.c index 4866afd054da..eb4d94742e6b 100644 --- a/samples/bpf/offwaketime_kern.c +++ b/samples/bpf/offwaketime_kern.c @@ -113,11 +113,11 @@ static inline int update_counts(void *ctx, u32 pid, u64 delta) /* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */ struct sched_switch_args { unsigned long long pad; - char prev_comm[16]; + char prev_comm[TASK_COMM_LEN]; int prev_pid; int prev_prio; long long prev_state; - char next_comm[16]; + char next_comm[TASK_COMM_LEN]; int next_pid; int next_prio; }; diff --git a/samples/bpf/offwaketime_user.c b/samples/bpf/offwaketime_user.c index 73a986876c1a..b6eedcb98fb9 100644 --- a/samples/bpf/offwaketime_user.c +++ b/samples/bpf/offwaketime_user.c @@ -8,7 +8,6 @@ #include <linux/perf_event.h> #include <errno.h> #include <stdbool.h> -#include <sys/resource.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> #include "trace_helpers.h" diff --git a/samples/bpf/sock_example.c b/samples/bpf/sock_example.c index a88f69504c08..5b66f2401b96 100644 --- a/samples/bpf/sock_example.c +++ b/samples/bpf/sock_example.c @@ -29,6 +29,7 @@ #include <bpf/bpf.h> #include "bpf_insn.h" #include "sock_example.h" +#include "bpf_util.h" char bpf_log_buf[BPF_LOG_BUF_SIZE]; @@ -58,7 +59,7 @@ static int test_sock(void) BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */ BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); LIBBPF_OPTS(bpf_prog_load_opts, opts, .log_buf = bpf_log_buf, .log_size = BPF_LOG_BUF_SIZE, diff --git a/samples/bpf/sockex2_user.c b/samples/bpf/sockex2_user.c index 6a3fd369d3fc..2c18471336f0 100644 --- a/samples/bpf/sockex2_user.c +++ b/samples/bpf/sockex2_user.c @@ -7,7 +7,6 @@ #include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> -#include <sys/resource.h> struct pair { __u64 packets; diff --git a/samples/bpf/sockex3_user.c b/samples/bpf/sockex3_user.c index 6ae99ecc766c..cd6fa79df900 100644 --- a/samples/bpf/sockex3_user.c +++ b/samples/bpf/sockex3_user.c @@ -6,7 +6,6 @@ #include "sock_example.h" #include <unistd.h> #include <arpa/inet.h> -#include <sys/resource.h> struct flow_key_record { __be32 src; diff --git a/samples/bpf/spintest_user.c b/samples/bpf/spintest_user.c index 0d7e1e5a8658..aadac14f748a 100644 --- a/samples/bpf/spintest_user.c +++ b/samples/bpf/spintest_user.c @@ -3,7 +3,6 @@ #include <unistd.h> #include <string.h> #include <assert.h> -#include <sys/resource.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> #include "trace_helpers.h" diff --git a/samples/bpf/syscall_tp_user.c b/samples/bpf/syscall_tp_user.c index a0ebf1833ed3..7a788bb837fc 100644 --- a/samples/bpf/syscall_tp_user.c +++ b/samples/bpf/syscall_tp_user.c @@ -8,7 +8,6 @@ #include <string.h> #include <linux/perf_event.h> #include <errno.h> -#include <sys/resource.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> @@ -36,6 +35,9 @@ static void verify_map(int map_id) fprintf(stderr, "failed: map #%d returns value 0\n", map_id); return; } + + printf("verify map:%d val: %d\n", map_id, val); + val = 0; if (bpf_map_update_elem(map_id, &key, &val, BPF_ANY) != 0) { fprintf(stderr, "map_update failed: %s\n", strerror(errno)); diff --git a/samples/bpf/task_fd_query_kern.c b/samples/bpf/task_fd_query_kern.c index c821294e1774..186ac0a79c0a 100644 --- a/samples/bpf/task_fd_query_kern.c +++ b/samples/bpf/task_fd_query_kern.c @@ -10,7 +10,7 @@ int bpf_prog1(struct pt_regs *ctx) return 0; } -SEC("kretprobe/blk_account_io_done") +SEC("kretprobe/__blk_account_io_done") int bpf_prog2(struct pt_regs *ctx) { return 0; diff --git a/samples/bpf/task_fd_query_user.c b/samples/bpf/task_fd_query_user.c index c9a0ca8351fd..a33d74bd3a4b 100644 --- a/samples/bpf/task_fd_query_user.c +++ b/samples/bpf/task_fd_query_user.c @@ -10,7 +10,6 @@ #include <fcntl.h> #include <linux/bpf.h> #include <sys/ioctl.h> -#include <sys/resource.h> #include <sys/types.h> #include <sys/stat.h> #include <linux/perf_event.h> @@ -349,7 +348,7 @@ int main(int argc, char **argv) /* test two functions in the corresponding *_kern.c file */ CHECK_AND_RET(test_debug_fs_kprobe(0, "blk_mq_start_request", BPF_FD_TYPE_KPROBE)); - CHECK_AND_RET(test_debug_fs_kprobe(1, "blk_account_io_done", + CHECK_AND_RET(test_debug_fs_kprobe(1, "__blk_account_io_done", BPF_FD_TYPE_KRETPROBE)); /* test nondebug fs kprobe */ diff --git a/samples/bpf/test_cgrp2_attach.c b/samples/bpf/test_cgrp2_attach.c index 6d90874b09c3..68ce69457afe 100644 --- a/samples/bpf/test_cgrp2_attach.c +++ b/samples/bpf/test_cgrp2_attach.c @@ -31,6 +31,7 @@ #include <bpf/bpf.h> #include "bpf_insn.h" +#include "bpf_util.h" enum { MAP_KEY_PACKETS, @@ -70,7 +71,7 @@ static int prog_load(int map_fd, int verdict) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ BPF_EXIT_INSN(), }; - size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); + size_t insns_cnt = ARRAY_SIZE(prog); LIBBPF_OPTS(bpf_prog_load_opts, opts, .log_buf = bpf_log_buf, .log_size = BPF_LOG_BUF_SIZE, diff --git a/samples/bpf/test_lru_dist.c b/samples/bpf/test_lru_dist.c index 75e877853596..5efb91763d65 100644 --- a/samples/bpf/test_lru_dist.c +++ b/samples/bpf/test_lru_dist.c @@ -13,7 +13,6 @@ #include <sched.h> #include <sys/wait.h> #include <sys/stat.h> -#include <sys/resource.h> #include <fcntl.h> #include <stdlib.h> #include <time.h> @@ -524,7 +523,7 @@ int main(int argc, char **argv) return -1; } - for (f = 0; f < sizeof(map_flags) / sizeof(*map_flags); f++) { + for (f = 0; f < ARRAY_SIZE(map_flags); f++) { test_lru_loss0(BPF_MAP_TYPE_LRU_HASH, map_flags[f]); test_lru_loss1(BPF_MAP_TYPE_LRU_HASH, map_flags[f]); test_parallel_lru_loss(BPF_MAP_TYPE_LRU_HASH, map_flags[f], diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c index 472d65c70354..652ec720533d 100644 --- a/samples/bpf/test_map_in_map_user.c +++ b/samples/bpf/test_map_in_map_user.c @@ -2,7 +2,6 @@ /* * Copyright (c) 2017 Facebook */ -#include <sys/resource.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdint.h> @@ -13,6 +12,8 @@ #include <bpf/bpf.h> #include <bpf/libbpf.h> +#include "bpf_util.h" + static int map_fd[7]; #define PORT_A (map_fd[0]) @@ -29,7 +30,7 @@ static const char * const test_names[] = { "Hash of Hash", }; -#define NR_TESTS (sizeof(test_names) / sizeof(*test_names)) +#define NR_TESTS ARRAY_SIZE(test_names) static void check_map_id(int inner_map_fd, int map_in_map_fd, uint32_t key) { diff --git a/samples/bpf/test_overhead_kprobe_kern.c b/samples/bpf/test_overhead_kprobe_kern.c index f6d593e47037..8fdd2c9c56b2 100644 --- a/samples/bpf/test_overhead_kprobe_kern.c +++ b/samples/bpf/test_overhead_kprobe_kern.c @@ -6,6 +6,7 @@ */ #include <linux/version.h> #include <linux/ptrace.h> +#include <linux/sched.h> #include <uapi/linux/bpf.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> @@ -22,17 +23,17 @@ int prog(struct pt_regs *ctx) { struct signal_struct *signal; struct task_struct *tsk; - char oldcomm[16] = {}; - char newcomm[16] = {}; + char oldcomm[TASK_COMM_LEN] = {}; + char newcomm[TASK_COMM_LEN] = {}; u16 oom_score_adj; u32 pid; tsk = (void *)PT_REGS_PARM1(ctx); pid = _(tsk->pid); - bpf_probe_read_kernel(oldcomm, sizeof(oldcomm), &tsk->comm); - bpf_probe_read_kernel(newcomm, sizeof(newcomm), - (void *)PT_REGS_PARM2(ctx)); + bpf_probe_read_kernel_str(oldcomm, sizeof(oldcomm), &tsk->comm); + bpf_probe_read_kernel_str(newcomm, sizeof(newcomm), + (void *)PT_REGS_PARM2(ctx)); signal = _(tsk->signal); oom_score_adj = _(signal->oom_score_adj); return 0; diff --git a/samples/bpf/test_overhead_tp_kern.c b/samples/bpf/test_overhead_tp_kern.c index eaa32693f8fc..80edadacb692 100644 --- a/samples/bpf/test_overhead_tp_kern.c +++ b/samples/bpf/test_overhead_tp_kern.c @@ -4,6 +4,7 @@ * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ +#include <linux/sched.h> #include <uapi/linux/bpf.h> #include <bpf/bpf_helpers.h> @@ -11,8 +12,8 @@ struct task_rename { __u64 pad; __u32 pid; - char oldcomm[16]; - char newcomm[16]; + char oldcomm[TASK_COMM_LEN]; + char newcomm[TASK_COMM_LEN]; __u16 oom_score_adj; }; SEC("tracepoint/task/task_rename") diff --git a/samples/bpf/test_overhead_user.c b/samples/bpf/test_overhead_user.c index 4821f9d99c1f..88717f8ec6ac 100644 --- a/samples/bpf/test_overhead_user.c +++ b/samples/bpf/test_overhead_user.c @@ -16,7 +16,6 @@ #include <linux/bpf.h> #include <string.h> #include <time.h> -#include <sys/resource.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> diff --git a/samples/bpf/tracex2_user.c b/samples/bpf/tracex2_user.c index 1626d51dfffd..dd6205c6b6a7 100644 --- a/samples/bpf/tracex2_user.c +++ b/samples/bpf/tracex2_user.c @@ -4,7 +4,6 @@ #include <stdlib.h> #include <signal.h> #include <string.h> -#include <sys/resource.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> diff --git a/samples/bpf/tracex3_kern.c b/samples/bpf/tracex3_kern.c index 710a4410b2fb..bde6591cb20c 100644 --- a/samples/bpf/tracex3_kern.c +++ b/samples/bpf/tracex3_kern.c @@ -49,7 +49,7 @@ struct { __uint(max_entries, SLOTS); } lat_map SEC(".maps"); -SEC("kprobe/blk_account_io_done") +SEC("kprobe/__blk_account_io_done") int bpf_prog2(struct pt_regs *ctx) { long rq = PT_REGS_PARM1(ctx); diff --git a/samples/bpf/tracex3_user.c b/samples/bpf/tracex3_user.c index 33e16ba39f25..d5eebace31e6 100644 --- a/samples/bpf/tracex3_user.c +++ b/samples/bpf/tracex3_user.c @@ -7,7 +7,6 @@ #include <unistd.h> #include <stdbool.h> #include <string.h> -#include <sys/resource.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> diff --git a/samples/bpf/tracex4_user.c b/samples/bpf/tracex4_user.c index 566e6440e8c2..227b05a0bc88 100644 --- a/samples/bpf/tracex4_user.c +++ b/samples/bpf/tracex4_user.c @@ -8,7 +8,6 @@ #include <stdbool.h> #include <string.h> #include <time.h> -#include <sys/resource.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> diff --git a/samples/bpf/tracex5_user.c b/samples/bpf/tracex5_user.c index 08dfdc77ad2a..9d7d79f0d47d 100644 --- a/samples/bpf/tracex5_user.c +++ b/samples/bpf/tracex5_user.c @@ -7,8 +7,8 @@ #include <sys/prctl.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> -#include <sys/resource.h> #include "trace_helpers.h" +#include "bpf_util.h" #ifdef __mips__ #define MAX_ENTRIES 6000 /* MIPS n64 syscalls start at 5000 */ @@ -25,7 +25,7 @@ static void install_accept_all_seccomp(void) BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog = { - .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .len = (unsigned short)ARRAY_SIZE(filter), .filter = filter, }; if (prctl(PR_SET_SECCOMP, 2, &prog)) diff --git a/samples/bpf/tracex6_user.c b/samples/bpf/tracex6_user.c index 28296f40c133..8e83bf2a84a4 100644 --- a/samples/bpf/tracex6_user.c +++ b/samples/bpf/tracex6_user.c @@ -8,7 +8,6 @@ #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> -#include <sys/resource.h> #include <sys/time.h> #include <sys/types.h> #include <sys/wait.h> diff --git a/samples/bpf/xdp1_kern.c b/samples/bpf/xdp1_kern.c index f0c5d95084de..0a5c704badd0 100644 --- a/samples/bpf/xdp1_kern.c +++ b/samples/bpf/xdp1_kern.c @@ -39,11 +39,13 @@ static int parse_ipv6(void *data, u64 nh_off, void *data_end) return ip6h->nexthdr; } -SEC("xdp1") +#define XDPBUFSIZE 64 +SEC("xdp.frags") int xdp_prog1(struct xdp_md *ctx) { - void *data_end = (void *)(long)ctx->data_end; - void *data = (void *)(long)ctx->data; + __u8 pkt[XDPBUFSIZE] = {}; + void *data_end = &pkt[XDPBUFSIZE-1]; + void *data = pkt; struct ethhdr *eth = data; int rc = XDP_DROP; long *value; @@ -51,6 +53,9 @@ int xdp_prog1(struct xdp_md *ctx) u64 nh_off; u32 ipproto; + if (bpf_xdp_load_bytes(ctx, 0, pkt, sizeof(pkt))) + return rc; + nh_off = sizeof(*eth); if (data + nh_off > data_end) return rc; diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index 8675fa5273df..ac370e638fa3 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -11,7 +11,6 @@ #include <string.h> #include <unistd.h> #include <libgen.h> -#include <sys/resource.h> #include <net/if.h> #include "bpf_util.h" @@ -26,12 +25,12 @@ static void int_exit(int sig) { __u32 curr_prog_id = 0; - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given interface\n"); else @@ -79,13 +78,11 @@ static void usage(const char *prog) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); const char *optstr = "FSN"; int prog_fd, map_fd, opt; + struct bpf_program *prog; struct bpf_object *obj; struct bpf_map *map; char filename[256]; @@ -123,11 +120,19 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) + return 1; + + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + err = bpf_object__load(obj); + if (err) return 1; + prog_fd = bpf_program__fd(prog); + map = bpf_object__next_map(obj, NULL); if (!map) { printf("finding a map in obj file failed\n"); @@ -143,7 +148,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } @@ -155,7 +160,7 @@ int main(int argc, char **argv) } prog_id = info.id; - poll_stats(map_fd, 2); + poll_stats(map_fd, 1); return 0; } diff --git a/samples/bpf/xdp2_kern.c b/samples/bpf/xdp2_kern.c index d8a64ab077b0..3332ba6bb95f 100644 --- a/samples/bpf/xdp2_kern.c +++ b/samples/bpf/xdp2_kern.c @@ -55,11 +55,13 @@ static int parse_ipv6(void *data, u64 nh_off, void *data_end) return ip6h->nexthdr; } -SEC("xdp1") +#define XDPBUFSIZE 64 +SEC("xdp.frags") int xdp_prog1(struct xdp_md *ctx) { - void *data_end = (void *)(long)ctx->data_end; - void *data = (void *)(long)ctx->data; + __u8 pkt[XDPBUFSIZE] = {}; + void *data_end = &pkt[XDPBUFSIZE-1]; + void *data = pkt; struct ethhdr *eth = data; int rc = XDP_DROP; long *value; @@ -67,6 +69,9 @@ int xdp_prog1(struct xdp_md *ctx) u64 nh_off; u32 ipproto; + if (bpf_xdp_load_bytes(ctx, 0, pkt, sizeof(pkt))) + return rc; + nh_off = sizeof(*eth); if (data + nh_off > data_end) return rc; diff --git a/samples/bpf/xdp_adjust_tail_user.c b/samples/bpf/xdp_adjust_tail_user.c index a70b094c8ec5..167646077c8f 100644 --- a/samples/bpf/xdp_adjust_tail_user.c +++ b/samples/bpf/xdp_adjust_tail_user.c @@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> #include <net/if.h> -#include <sys/resource.h> #include <arpa/inet.h> #include <netinet/ether.h> #include <unistd.h> @@ -34,12 +33,12 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given iface\n"); else @@ -82,15 +81,13 @@ static void usage(const char *cmd) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; unsigned char opt_flags[256] = {}; const char *optstr = "i:T:P:SNFh"; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); unsigned int kill_after_s = 0; int i, prog_fd, map_fd, opt; + struct bpf_program *prog; struct bpf_object *obj; __u32 max_pckt_size = 0; __u32 key = 0; @@ -148,11 +145,20 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) + return 1; + + prog_fd = bpf_program__fd(prog); + /* static global var 'max_pcktsz' is accessible from .data section */ if (max_pckt_size) { map_fd = bpf_object__find_map_fd_by_name(obj, "xdp_adju.data"); @@ -173,7 +179,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } diff --git a/samples/bpf/xdp_fwd_user.c b/samples/bpf/xdp_fwd_user.c index 4ad896782f77..84f57f1209ce 100644 --- a/samples/bpf/xdp_fwd_user.c +++ b/samples/bpf/xdp_fwd_user.c @@ -33,7 +33,7 @@ static int do_attach(int idx, int prog_fd, int map_fd, const char *name) { int err; - err = bpf_set_link_xdp_fd(idx, prog_fd, xdp_flags); + err = bpf_xdp_attach(idx, prog_fd, xdp_flags, NULL); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; @@ -47,17 +47,60 @@ static int do_attach(int idx, int prog_fd, int map_fd, const char *name) return err; } -static int do_detach(int idx, const char *name) +static int do_detach(int ifindex, const char *ifname, const char *app_name) { - int err; + LIBBPF_OPTS(bpf_xdp_attach_opts, opts); + struct bpf_prog_info prog_info = {}; + char prog_name[BPF_OBJ_NAME_LEN]; + __u32 info_len, curr_prog_id; + int prog_fd; + int err = 1; + + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("ERROR: bpf_xdp_query_id failed (%s)\n", + strerror(errno)); + return err; + } - err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); - if (err < 0) - printf("ERROR: failed to detach program from %s\n", name); + if (!curr_prog_id) { + printf("ERROR: flags(0x%x) xdp prog is not attached to %s\n", + xdp_flags, ifname); + return err; + } + + info_len = sizeof(prog_info); + prog_fd = bpf_prog_get_fd_by_id(curr_prog_id); + if (prog_fd < 0) { + printf("ERROR: bpf_prog_get_fd_by_id failed (%s)\n", + strerror(errno)); + return prog_fd; + } + err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len); + if (err) { + printf("ERROR: bpf_obj_get_info_by_fd failed (%s)\n", + strerror(errno)); + goto close_out; + } + snprintf(prog_name, sizeof(prog_name), "%s_prog", app_name); + prog_name[BPF_OBJ_NAME_LEN - 1] = '\0'; + + if (strcmp(prog_info.name, prog_name)) { + printf("ERROR: %s isn't attached to %s\n", app_name, ifname); + err = 1; + goto close_out; + } + + opts.old_prog_fd = prog_fd; + err = bpf_xdp_detach(ifindex, xdp_flags, &opts); + if (err < 0) + printf("ERROR: failed to detach program from %s (%s)\n", + ifname, strerror(errno)); /* TODO: Remember to cleanup map, when adding use of shared map * bpf_map_delete_elem((map_fd, &idx); */ +close_out: + close(prog_fd); return err; } @@ -75,14 +118,11 @@ static void usage(const char *prog) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; const char *prog_name = "xdp_fwd"; struct bpf_program *prog = NULL; struct bpf_program *pos; const char *sec_name; - int prog_fd, map_fd = -1; + int prog_fd = -1, map_fd = -1; char filename[PATH_MAX]; struct bpf_object *obj; int opt, i, idx, err; @@ -119,7 +159,6 @@ int main(int argc, char **argv) if (attach) { snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; if (access(filename, O_RDONLY) < 0) { printf("error accessing file %s: %s\n", @@ -127,7 +166,14 @@ int main(int argc, char **argv) return 1; } - err = bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd); + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) + return 1; + + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); if (err) { printf("Does kernel support devmap lookup?\n"); /* If not, the error message will be: @@ -166,7 +212,7 @@ int main(int argc, char **argv) return 1; } if (!attach) { - err = do_detach(idx, argv[i]); + err = do_detach(idx, argv[i], prog_name); if (err) ret = err; } else { diff --git a/samples/bpf/xdp_monitor_user.c b/samples/bpf/xdp_monitor_user.c index fb9391a5ec62..58015eb2ffae 100644 --- a/samples/bpf/xdp_monitor_user.c +++ b/samples/bpf/xdp_monitor_user.c @@ -17,7 +17,6 @@ static const char *__doc_err_only__= #include <ctype.h> #include <unistd.h> #include <locale.h> -#include <sys/resource.h> #include <getopt.h> #include <net/if.h> #include <time.h> diff --git a/samples/bpf/xdp_redirect_cpu.bpf.c b/samples/bpf/xdp_redirect_cpu.bpf.c index 25e3a405375f..87c54bfdbb70 100644 --- a/samples/bpf/xdp_redirect_cpu.bpf.c +++ b/samples/bpf/xdp_redirect_cpu.bpf.c @@ -491,7 +491,7 @@ int xdp_prognum5_lb_hash_ip_pairs(struct xdp_md *ctx) return bpf_redirect_map(&cpu_map, cpu_dest, 0); } -SEC("xdp_cpumap/redirect") +SEC("xdp/cpumap") int xdp_redirect_cpu_devmap(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; @@ -507,19 +507,19 @@ int xdp_redirect_cpu_devmap(struct xdp_md *ctx) return bpf_redirect_map(&tx_port, 0, 0); } -SEC("xdp_cpumap/pass") +SEC("xdp/cpumap") int xdp_redirect_cpu_pass(struct xdp_md *ctx) { return XDP_PASS; } -SEC("xdp_cpumap/drop") +SEC("xdp/cpumap") int xdp_redirect_cpu_drop(struct xdp_md *ctx) { return XDP_DROP; } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_redirect_egress_prog(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/samples/bpf/xdp_redirect_cpu_user.c b/samples/bpf/xdp_redirect_cpu_user.c index a81704d3317b..a12381c37d2b 100644 --- a/samples/bpf/xdp_redirect_cpu_user.c +++ b/samples/bpf/xdp_redirect_cpu_user.c @@ -21,7 +21,6 @@ static const char *__doc__ = #include <string.h> #include <unistd.h> #include <locale.h> -#include <sys/resource.h> #include <sys/sysinfo.h> #include <getopt.h> #include <net/if.h> @@ -70,7 +69,7 @@ static void print_avail_progs(struct bpf_object *obj) printf(" Programs to be used for -p/--progname:\n"); bpf_object__for_each_program(pos, obj) { - if (bpf_program__is_xdp(pos)) { + if (bpf_program__type(pos) == BPF_PROG_TYPE_XDP) { if (!strncmp(bpf_program__name(pos), "xdp_prognum", sizeof("xdp_prognum") - 1)) printf(" %s\n", bpf_program__name(pos)); diff --git a/samples/bpf/xdp_redirect_map.bpf.c b/samples/bpf/xdp_redirect_map.bpf.c index 59efd656e1b2..8557c278df77 100644 --- a/samples/bpf/xdp_redirect_map.bpf.c +++ b/samples/bpf/xdp_redirect_map.bpf.c @@ -33,7 +33,7 @@ struct { } tx_port_native SEC(".maps"); /* store egress interface mac address */ -const volatile char tx_mac_addr[ETH_ALEN]; +const volatile __u8 tx_mac_addr[ETH_ALEN]; static __always_inline int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map) { @@ -68,11 +68,12 @@ int xdp_redirect_map_native(struct xdp_md *ctx) return xdp_redirect_map(ctx, &tx_port_native); } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_redirect_map_egress(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; + u8 *mac_addr = (u8 *) tx_mac_addr; struct ethhdr *eth = data; u64 nh_off; @@ -80,7 +81,8 @@ int xdp_redirect_map_egress(struct xdp_md *ctx) if (data + nh_off > data_end) return XDP_DROP; - __builtin_memcpy(eth->h_source, (const char *)tx_mac_addr, ETH_ALEN); + barrier_var(mac_addr); /* prevent optimizing out memcpy */ + __builtin_memcpy(eth->h_source, mac_addr, ETH_ALEN); return XDP_PASS; } diff --git a/samples/bpf/xdp_redirect_map_multi.bpf.c b/samples/bpf/xdp_redirect_map_multi.bpf.c index bb0a5a3bfcf0..8b2fd4ec2c76 100644 --- a/samples/bpf/xdp_redirect_map_multi.bpf.c +++ b/samples/bpf/xdp_redirect_map_multi.bpf.c @@ -53,7 +53,7 @@ int xdp_redirect_map_native(struct xdp_md *ctx) return xdp_redirect_map(ctx, &forward_map_native); } -SEC("xdp_devmap/egress") +SEC("xdp/devmap") int xdp_devmap_prog(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; diff --git a/samples/bpf/xdp_redirect_map_multi_user.c b/samples/bpf/xdp_redirect_map_multi_user.c index 315314716121..9e24f2705b67 100644 --- a/samples/bpf/xdp_redirect_map_multi_user.c +++ b/samples/bpf/xdp_redirect_map_multi_user.c @@ -15,7 +15,6 @@ static const char *__doc__ = #include <net/if.h> #include <unistd.h> #include <libgen.h> -#include <sys/resource.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c index b6e4fc849577..c889a1394dc1 100644 --- a/samples/bpf/xdp_redirect_map_user.c +++ b/samples/bpf/xdp_redirect_map_user.c @@ -40,6 +40,8 @@ static const struct option long_options[] = { {} }; +static int verbose = 0; + int main(int argc, char **argv) { struct bpf_devmap_val devmap_val = {}; @@ -79,6 +81,7 @@ int main(int argc, char **argv) break; case 'v': sample_switch_mode(); + verbose = 1; break; case 's': mask |= SAMPLE_REDIRECT_MAP_CNT; @@ -134,6 +137,12 @@ int main(int argc, char **argv) ret = EXIT_FAIL; goto end_destroy; } + if (verbose) + printf("Egress ifindex:%d using src MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + ifindex_out, + skel->rodata->tx_mac_addr[0], skel->rodata->tx_mac_addr[1], + skel->rodata->tx_mac_addr[2], skel->rodata->tx_mac_addr[3], + skel->rodata->tx_mac_addr[4], skel->rodata->tx_mac_addr[5]); } skel->rodata->from_match[0] = ifindex_in; diff --git a/samples/bpf/xdp_redirect_user.c b/samples/bpf/xdp_redirect_user.c index 7af5b07a7523..8663dd631b6e 100644 --- a/samples/bpf/xdp_redirect_user.c +++ b/samples/bpf/xdp_redirect_user.c @@ -18,7 +18,6 @@ static const char *__doc__ = #include <unistd.h> #include <libgen.h> #include <getopt.h> -#include <sys/resource.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> #include "bpf_util.h" diff --git a/samples/bpf/xdp_router_ipv4.bpf.c b/samples/bpf/xdp_router_ipv4.bpf.c new file mode 100644 index 000000000000..0643330d1d2e --- /dev/null +++ b/samples/bpf/xdp_router_ipv4.bpf.c @@ -0,0 +1,189 @@ +/* Copyright (C) 2017 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include "vmlinux.h" +#include "xdp_sample.bpf.h" +#include "xdp_sample_shared.h" + +#define ETH_ALEN 6 +#define ETH_P_8021Q 0x8100 +#define ETH_P_8021AD 0x88A8 + +struct trie_value { + __u8 prefix[4]; + __be64 value; + int ifindex; + int metric; + __be32 gw; +}; + +/* Key for lpm_trie */ +union key_4 { + u32 b32[2]; + u8 b8[8]; +}; + +struct arp_entry { + __be64 mac; + __be32 dst; +}; + +struct direct_map { + struct arp_entry arp; + int ifindex; + __be64 mac; +}; + +/* Map for trie implementation */ +struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __uint(key_size, 8); + __uint(value_size, sizeof(struct trie_value)); + __uint(max_entries, 50); + __uint(map_flags, BPF_F_NO_PREALLOC); +} lpm_map SEC(".maps"); + +/* Map for ARP table */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __be32); + __type(value, __be64); + __uint(max_entries, 50); +} arp_table SEC(".maps"); + +/* Map to keep the exact match entries in the route table */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __be32); + __type(value, struct direct_map); + __uint(max_entries, 50); +} exact_match SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_DEVMAP); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); + __uint(max_entries, 100); +} tx_port SEC(".maps"); + +SEC("xdp") +int xdp_router_ipv4_prog(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth = data; + u64 nh_off = sizeof(*eth); + struct datarec *rec; + __be16 h_proto; + u32 key = 0; + + rec = bpf_map_lookup_elem(&rx_cnt, &key); + if (rec) + NO_TEAR_INC(rec->processed); + + if (data + nh_off > data_end) + goto drop; + + h_proto = eth->h_proto; + if (h_proto == bpf_htons(ETH_P_8021Q) || + h_proto == bpf_htons(ETH_P_8021AD)) { + struct vlan_hdr *vhdr; + + vhdr = data + nh_off; + nh_off += sizeof(struct vlan_hdr); + if (data + nh_off > data_end) + goto drop; + + h_proto = vhdr->h_vlan_encapsulated_proto; + } + + switch (bpf_ntohs(h_proto)) { + case ETH_P_ARP: + if (rec) + NO_TEAR_INC(rec->xdp_pass); + return XDP_PASS; + case ETH_P_IP: { + struct iphdr *iph = data + nh_off; + struct direct_map *direct_entry; + __be64 *dest_mac, *src_mac; + int forward_to; + + if (iph + 1 > data_end) + goto drop; + + direct_entry = bpf_map_lookup_elem(&exact_match, &iph->daddr); + + /* Check for exact match, this would give a faster lookup */ + if (direct_entry && direct_entry->mac && + direct_entry->arp.mac) { + src_mac = &direct_entry->mac; + dest_mac = &direct_entry->arp.mac; + forward_to = direct_entry->ifindex; + } else { + struct trie_value *prefix_value; + union key_4 key4; + + /* Look up in the trie for lpm */ + key4.b32[0] = 32; + key4.b8[4] = iph->daddr & 0xff; + key4.b8[5] = (iph->daddr >> 8) & 0xff; + key4.b8[6] = (iph->daddr >> 16) & 0xff; + key4.b8[7] = (iph->daddr >> 24) & 0xff; + + prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); + if (!prefix_value) + goto drop; + + forward_to = prefix_value->ifindex; + src_mac = &prefix_value->value; + if (!src_mac) + goto drop; + + dest_mac = bpf_map_lookup_elem(&arp_table, &iph->daddr); + if (!dest_mac) { + if (!prefix_value->gw) + goto drop; + + dest_mac = bpf_map_lookup_elem(&arp_table, + &prefix_value->gw); + if (!dest_mac) { + /* Forward the packet to the kernel in + * order to trigger ARP discovery for + * the default gw. + */ + if (rec) + NO_TEAR_INC(rec->xdp_pass); + return XDP_PASS; + } + } + } + + if (src_mac && dest_mac) { + int ret; + + __builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN); + __builtin_memcpy(eth->h_source, src_mac, ETH_ALEN); + + ret = bpf_redirect_map(&tx_port, forward_to, 0); + if (ret == XDP_REDIRECT) { + if (rec) + NO_TEAR_INC(rec->xdp_redirect); + return ret; + } + } + } + default: + break; + } +drop: + if (rec) + NO_TEAR_INC(rec->xdp_drop); + + return XDP_DROP; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp_router_ipv4_kern.c b/samples/bpf/xdp_router_ipv4_kern.c deleted file mode 100644 index b37ca2b13063..000000000000 --- a/samples/bpf/xdp_router_ipv4_kern.c +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright (C) 2017 Cavium, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ -#define KBUILD_MODNAME "foo" -#include <uapi/linux/bpf.h> -#include <linux/in.h> -#include <linux/if_ether.h> -#include <linux/if_packet.h> -#include <linux/if_vlan.h> -#include <linux/ip.h> -#include <linux/ipv6.h> -#include <bpf/bpf_helpers.h> -#include <linux/slab.h> -#include <net/ip_fib.h> - -struct trie_value { - __u8 prefix[4]; - __be64 value; - int ifindex; - int metric; - __be32 gw; -}; - -/* Key for lpm_trie*/ -union key_4 { - u32 b32[2]; - u8 b8[8]; -}; - -struct arp_entry { - __be64 mac; - __be32 dst; -}; - -struct direct_map { - struct arp_entry arp; - int ifindex; - __be64 mac; -}; - -/* Map for trie implementation*/ -struct { - __uint(type, BPF_MAP_TYPE_LPM_TRIE); - __uint(key_size, 8); - __uint(value_size, sizeof(struct trie_value)); - __uint(max_entries, 50); - __uint(map_flags, BPF_F_NO_PREALLOC); -} lpm_map SEC(".maps"); - -/* Map for counter*/ -struct { - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __type(key, u32); - __type(value, u64); - __uint(max_entries, 256); -} rxcnt SEC(".maps"); - -/* Map for ARP table*/ -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __type(key, __be32); - __type(value, __be64); - __uint(max_entries, 50); -} arp_table SEC(".maps"); - -/* Map to keep the exact match entries in the route table*/ -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __type(key, __be32); - __type(value, struct direct_map); - __uint(max_entries, 50); -} exact_match SEC(".maps"); - -struct { - __uint(type, BPF_MAP_TYPE_DEVMAP); - __uint(key_size, sizeof(int)); - __uint(value_size, sizeof(int)); - __uint(max_entries, 100); -} tx_port SEC(".maps"); - -/* Function to set source and destination mac of the packet */ -static inline void set_src_dst_mac(void *data, void *src, void *dst) -{ - unsigned short *source = src; - unsigned short *dest = dst; - unsigned short *p = data; - - __builtin_memcpy(p, dest, 6); - __builtin_memcpy(p + 3, source, 6); -} - -/* Parse IPV4 packet to get SRC, DST IP and protocol */ -static inline int parse_ipv4(void *data, u64 nh_off, void *data_end, - __be32 *src, __be32 *dest) -{ - struct iphdr *iph = data + nh_off; - - if (iph + 1 > data_end) - return 0; - *src = iph->saddr; - *dest = iph->daddr; - return iph->protocol; -} - -SEC("xdp_router_ipv4") -int xdp_router_ipv4_prog(struct xdp_md *ctx) -{ - void *data_end = (void *)(long)ctx->data_end; - __be64 *dest_mac = NULL, *src_mac = NULL; - void *data = (void *)(long)ctx->data; - struct trie_value *prefix_value; - int rc = XDP_DROP, forward_to; - struct ethhdr *eth = data; - union key_4 key4; - long *value; - u16 h_proto; - u32 ipproto; - u64 nh_off; - - nh_off = sizeof(*eth); - if (data + nh_off > data_end) - return rc; - - h_proto = eth->h_proto; - - if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { - struct vlan_hdr *vhdr; - - vhdr = data + nh_off; - nh_off += sizeof(struct vlan_hdr); - if (data + nh_off > data_end) - return rc; - h_proto = vhdr->h_vlan_encapsulated_proto; - } - if (h_proto == htons(ETH_P_ARP)) { - return XDP_PASS; - } else if (h_proto == htons(ETH_P_IP)) { - struct direct_map *direct_entry; - __be32 src_ip = 0, dest_ip = 0; - - ipproto = parse_ipv4(data, nh_off, data_end, &src_ip, &dest_ip); - direct_entry = bpf_map_lookup_elem(&exact_match, &dest_ip); - /* Check for exact match, this would give a faster lookup*/ - if (direct_entry && direct_entry->mac && direct_entry->arp.mac) { - src_mac = &direct_entry->mac; - dest_mac = &direct_entry->arp.mac; - forward_to = direct_entry->ifindex; - } else { - /* Look up in the trie for lpm*/ - key4.b32[0] = 32; - key4.b8[4] = dest_ip & 0xff; - key4.b8[5] = (dest_ip >> 8) & 0xff; - key4.b8[6] = (dest_ip >> 16) & 0xff; - key4.b8[7] = (dest_ip >> 24) & 0xff; - prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); - if (!prefix_value) - return XDP_DROP; - src_mac = &prefix_value->value; - if (!src_mac) - return XDP_DROP; - dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); - if (!dest_mac) { - if (!prefix_value->gw) - return XDP_DROP; - dest_ip = prefix_value->gw; - dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); - } - forward_to = prefix_value->ifindex; - } - } else { - ipproto = 0; - } - if (src_mac && dest_mac) { - set_src_dst_mac(data, src_mac, dest_mac); - value = bpf_map_lookup_elem(&rxcnt, &ipproto); - if (value) - *value += 1; - return bpf_redirect_map(&tx_port, forward_to, 0); - } - return rc; -} - -char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp_router_ipv4_user.c b/samples/bpf/xdp_router_ipv4_user.c index cfaf7e50e431..683913bbf279 100644 --- a/samples/bpf/xdp_router_ipv4_user.c +++ b/samples/bpf/xdp_router_ipv4_user.c @@ -22,72 +22,41 @@ #include <sys/syscall.h> #include "bpf_util.h" #include <bpf/libbpf.h> -#include <sys/resource.h> #include <libgen.h> +#include <getopt.h> +#include <pthread.h> +#include "xdp_sample_user.h" +#include "xdp_router_ipv4.skel.h" -int sock, sock_arp, flags = XDP_FLAGS_UPDATE_IF_NOEXIST; -static int total_ifindex; -static int *ifindex_list; -static __u32 *prog_id_list; -char buf[8192]; +static const char *__doc__ = +"XDP IPv4 router implementation\n" +"Usage: xdp_router_ipv4 <IFNAME-0> ... <IFNAME-N>\n"; + +static char buf[8192]; static int lpm_map_fd; -static int rxcnt_map_fd; static int arp_table_map_fd; static int exact_match_map_fd; static int tx_port_map_fd; -static int get_route_table(int rtm_family); -static void int_exit(int sig) -{ - __u32 prog_id = 0; - int i = 0; +static bool routes_thread_exit; +static int interval = 5; - for (i = 0; i < total_ifindex; i++) { - if (bpf_get_link_xdp_id(ifindex_list[i], &prog_id, flags)) { - printf("bpf_get_link_xdp_id on iface %d failed\n", - ifindex_list[i]); - exit(1); - } - if (prog_id_list[i] == prog_id) - bpf_set_link_xdp_fd(ifindex_list[i], -1, flags); - else if (!prog_id) - printf("couldn't find a prog id on iface %d\n", - ifindex_list[i]); - else - printf("program on iface %d changed, not removing\n", - ifindex_list[i]); - prog_id = 0; - } - exit(0); -} +static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT | + SAMPLE_DEVMAP_XMIT_CNT_MULTI | SAMPLE_EXCEPTION_CNT; -static void close_and_exit(int sig) -{ - close(sock); - close(sock_arp); +DEFINE_SAMPLE_INIT(xdp_router_ipv4); - int_exit(0); -} +static const struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "skb-mode", no_argument, NULL, 'S' }, + { "force", no_argument, NULL, 'F' }, + { "interval", required_argument, NULL, 'i' }, + { "verbose", no_argument, NULL, 'v' }, + { "stats", no_argument, NULL, 's' }, + {} +}; -/* Get the mac address of the interface given interface name */ -static __be64 getmac(char *iface) -{ - struct ifreq ifr; - __be64 mac = 0; - int fd, i; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - ifr.ifr_addr.sa_family = AF_INET; - strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - printf("ioctl failed leaving....\n"); - return -1; - } - for (i = 0; i < 6 ; i++) - *((__u8 *)&mac + i) = (__u8)ifr.ifr_hwaddr.sa_data[i]; - close(fd); - return mac; -} +static int get_route_table(int rtm_family); static int recv_msg(struct sockaddr_nl sock_addr, int sock) { @@ -130,7 +99,6 @@ static void read_route(struct nlmsghdr *nh, int nll) int i; struct route_table { int dst_len, iface, metric; - char *iface_name; __be32 dst, gw; __be64 mac; } route; @@ -145,17 +113,7 @@ static void read_route(struct nlmsghdr *nh, int nll) __be64 mac; } direct_entry; - if (nh->nlmsg_type == RTM_DELROUTE) - printf("DELETING Route entry\n"); - else if (nh->nlmsg_type == RTM_GETROUTE) - printf("READING Route entry\n"); - else if (nh->nlmsg_type == RTM_NEWROUTE) - printf("NEW Route entry\n"); - else - printf("%d\n", nh->nlmsg_type); - memset(&route, 0, sizeof(route)); - printf("Destination Gateway Genmask Metric Iface\n"); for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) { rt_msg = (struct rtmsg *)NLMSG_DATA(nh); rtm_family = rt_msg->rtm_family; @@ -192,11 +150,7 @@ static void read_route(struct nlmsghdr *nh, int nll) route.gw = atoi(gws); route.iface = atoi(ifs); route.metric = atoi(metrics); - route.iface_name = alloca(sizeof(char *) * IFNAMSIZ); - route.iface_name = if_indextoname(route.iface, route.iface_name); - route.mac = getmac(route.iface_name); - if (route.mac == -1) - int_exit(0); + assert(get_mac_addr(route.iface, &route.mac) == 0); assert(bpf_map_update_elem(tx_port_map_fd, &route.iface, &route.iface, 0) == 0); if (rtm_family == AF_INET) { @@ -207,7 +161,6 @@ static void read_route(struct nlmsghdr *nh, int nll) int metric; __be32 gw; } *prefix_value; - struct in_addr dst_addr, gw_addr, mask_addr; prefix_key = alloca(sizeof(*prefix_key) + 3); prefix_value = alloca(sizeof(*prefix_value)); @@ -235,17 +188,6 @@ static void read_route(struct nlmsghdr *nh, int nll) for (i = 0; i < 4; i++) prefix_key->data[i] = (route.dst >> i * 8) & 0xff; - dst_addr.s_addr = route.dst; - printf("%-16s", inet_ntoa(dst_addr)); - - gw_addr.s_addr = route.gw; - printf("%-16s", inet_ntoa(gw_addr)); - - mask_addr.s_addr = htonl(~(0xffffffffU >> route.dst_len)); - printf("%-16s%-7d%s\n", inet_ntoa(mask_addr), - route.metric, - route.iface_name); - if (bpf_map_lookup_elem(lpm_map_fd, prefix_key, prefix_value) < 0) { for (i = 0; i < 4; i++) @@ -261,20 +203,13 @@ static void read_route(struct nlmsghdr *nh, int nll) ) == 0); } else { if (nh->nlmsg_type == RTM_DELROUTE) { - printf("deleting entry\n"); - printf("prefix key=%d.%d.%d.%d/%d", - prefix_key->data[0], - prefix_key->data[1], - prefix_key->data[2], - prefix_key->data[3], - prefix_key->prefixlen); assert(bpf_map_delete_elem(lpm_map_fd, prefix_key ) == 0); /* Rereading the route table to check if * there is an entry with the same * prefix but a different metric as the - * deleted enty. + * deleted entry. */ get_route_table(AF_INET); } else if (prefix_key->data[0] == @@ -331,14 +266,14 @@ static int get_route_table(int rtm_family) sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { - printf("open netlink socket: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "open netlink socket: %s\n", strerror(errno)); + return -errno; } memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - printf("bind to netlink: %s\n", strerror(errno)); - ret = -1; + fprintf(stderr, "bind netlink socket: %s\n", strerror(errno)); + ret = -errno; goto cleanup; } memset(&req, 0, sizeof(req)); @@ -357,15 +292,15 @@ static int get_route_table(int rtm_family) msg.msg_iovlen = 1; ret = sendmsg(sock, &msg, 0); if (ret < 0) { - printf("send to netlink: %s\n", strerror(errno)); - ret = -1; + fprintf(stderr, "send to netlink: %s\n", strerror(errno)); + ret = -errno; goto cleanup; } memset(buf, 0, sizeof(buf)); nll = recv_msg(sa, sock); if (nll < 0) { - printf("recv from netlink: %s\n", strerror(nll)); - ret = -1; + fprintf(stderr, "recv from netlink: %s\n", strerror(nll)); + ret = nll; goto cleanup; } nh = (struct nlmsghdr *)buf; @@ -395,14 +330,7 @@ static void read_arp(struct nlmsghdr *nh, int nll) __be64 mac; } direct_entry; - if (nh->nlmsg_type == RTM_GETNEIGH) - printf("READING arp entry\n"); - printf("Address HwAddress\n"); for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) { - struct in_addr dst_addr; - char mac_str[18]; - int len = 0, i; - rt_msg = (struct ndmsg *)NLMSG_DATA(nh); rt_attr = (struct rtattr *)RTM_RTA(rt_msg); ndm_family = rt_msg->ndm_family; @@ -424,13 +352,6 @@ static void read_arp(struct nlmsghdr *nh, int nll) arp_entry.dst = atoi(dsts); arp_entry.mac = atol(mac); - dst_addr.s_addr = arp_entry.dst; - for (i = 0; i < 6; i++) - len += snprintf(mac_str + len, 18 - len, "%02llx%s", - ((arp_entry.mac >> i * 8) & 0xff), - i < 5 ? ":" : ""); - printf("%-16s%s\n", inet_ntoa(dst_addr), mac_str); - if (ndm_family == AF_INET) { if (bpf_map_lookup_elem(exact_match_map_fd, &arp_entry.dst, @@ -481,14 +402,14 @@ static int get_arp_table(int rtm_family) sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { - printf("open netlink socket: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "open netlink socket: %s\n", strerror(errno)); + return -errno; } memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { - printf("bind to netlink: %s\n", strerror(errno)); - ret = -1; + fprintf(stderr, "bind netlink socket: %s\n", strerror(errno)); + ret = -errno; goto cleanup; } memset(&req, 0, sizeof(req)); @@ -506,15 +427,15 @@ static int get_arp_table(int rtm_family) msg.msg_iovlen = 1; ret = sendmsg(sock, &msg, 0); if (ret < 0) { - printf("send to netlink: %s\n", strerror(errno)); - ret = -1; + fprintf(stderr, "send to netlink: %s\n", strerror(errno)); + ret = -errno; goto cleanup; } memset(buf, 0, sizeof(buf)); nll = recv_msg(sa, sock); if (nll < 0) { - printf("recv from netlink: %s\n", strerror(nll)); - ret = -1; + fprintf(stderr, "recv from netlink: %s\n", strerror(nll)); + ret = nll; goto cleanup; } nh = (struct nlmsghdr *)buf; @@ -527,24 +448,17 @@ cleanup: /* Function to keep track and update changes in route and arp table * Give regular statistics of packets forwarded */ -static int monitor_route(void) +static void *monitor_routes_thread(void *arg) { - unsigned int nr_cpus = bpf_num_possible_cpus(); - const unsigned int nr_keys = 256; struct pollfd fds_route, fds_arp; - __u64 prev[nr_keys][nr_cpus]; struct sockaddr_nl la, lr; - __u64 values[nr_cpus]; + int sock, sock_arp, nll; struct nlmsghdr *nh; - int nll, ret = 0; - int interval = 5; - __u32 key; - int i; sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { - printf("open netlink socket: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "open netlink socket: %s\n", strerror(errno)); + return NULL; } fcntl(sock, F_SETFL, O_NONBLOCK); @@ -552,17 +466,19 @@ static int monitor_route(void) lr.nl_family = AF_NETLINK; lr.nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY; if (bind(sock, (struct sockaddr *)&lr, sizeof(lr)) < 0) { - printf("bind to netlink: %s\n", strerror(errno)); - ret = -1; - goto cleanup; + fprintf(stderr, "bind netlink socket: %s\n", strerror(errno)); + close(sock); + return NULL; } + fds_route.fd = sock; fds_route.events = POLL_IN; sock_arp = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock_arp < 0) { - printf("open netlink socket: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "open netlink socket: %s\n", strerror(errno)); + close(sock); + return NULL; } fcntl(sock_arp, F_SETFL, O_NONBLOCK); @@ -570,51 +486,44 @@ static int monitor_route(void) la.nl_family = AF_NETLINK; la.nl_groups = RTMGRP_NEIGH | RTMGRP_NOTIFY; if (bind(sock_arp, (struct sockaddr *)&la, sizeof(la)) < 0) { - printf("bind to netlink: %s\n", strerror(errno)); - ret = -1; + fprintf(stderr, "bind netlink socket: %s\n", strerror(errno)); goto cleanup; } + fds_arp.fd = sock_arp; fds_arp.events = POLL_IN; - memset(prev, 0, sizeof(prev)); - do { - signal(SIGINT, close_and_exit); - signal(SIGTERM, close_and_exit); + /* dump route and arp tables */ + if (get_arp_table(AF_INET) < 0) { + fprintf(stderr, "Failed reading arp table\n"); + goto cleanup; + } - sleep(interval); - for (key = 0; key < nr_keys; key++) { - __u64 sum = 0; - - assert(bpf_map_lookup_elem(rxcnt_map_fd, - &key, values) == 0); - for (i = 0; i < nr_cpus; i++) - sum += (values[i] - prev[key][i]); - if (sum) - printf("proto %u: %10llu pkt/s\n", - key, sum / interval); - memcpy(prev[key], values, sizeof(values)); - } + if (get_route_table(AF_INET) < 0) { + fprintf(stderr, "Failed reading route table\n"); + goto cleanup; + } + while (!routes_thread_exit) { memset(buf, 0, sizeof(buf)); if (poll(&fds_route, 1, 3) == POLL_IN) { nll = recv_msg(lr, sock); if (nll < 0) { - printf("recv from netlink: %s\n", strerror(nll)); - ret = -1; + fprintf(stderr, "recv from netlink: %s\n", + strerror(nll)); goto cleanup; } nh = (struct nlmsghdr *)buf; - printf("Routing table updated.\n"); read_route(nh, nll); } + memset(buf, 0, sizeof(buf)); if (poll(&fds_arp, 1, 3) == POLL_IN) { nll = recv_msg(la, sock_arp); if (nll < 0) { - printf("recv from netlink: %s\n", strerror(nll)); - ret = -1; + fprintf(stderr, "recv from netlink: %s\n", + strerror(nll)); goto cleanup; } @@ -622,129 +531,169 @@ static int monitor_route(void) read_arp(nh, nll); } - } while (1); + sleep(interval); + } + cleanup: + close(sock_arp); close(sock); - return ret; + return NULL; } -static void usage(const char *prog) +static void usage(char *argv[], const struct option *long_options, + const char *doc, int mask, bool error, + struct bpf_object *obj) { - fprintf(stderr, - "%s: %s [OPTS] interface name list\n\n" - "OPTS:\n" - " -S use skb-mode\n" - " -F force loading prog\n", - __func__, prog); + sample_usage(argv, long_options, doc, mask, error); } -int main(int ac, char **argv) +int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; - struct bpf_prog_info info = {}; - __u32 info_len = sizeof(info); - const char *optstr = "SF"; - struct bpf_object *obj; - char filename[256]; - char **ifname_list; - int prog_fd, opt; - int err, i = 1; - - snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - - total_ifindex = ac - 1; - ifname_list = (argv + 1); - - while ((opt = getopt(ac, argv, optstr)) != -1) { + bool error = true, generic = false, force = false; + int opt, ret = EXIT_FAIL_BPF; + struct xdp_router_ipv4 *skel; + int i, total_ifindex = argc - 1; + char **ifname_list = argv + 1; + pthread_t routes_thread; + int longindex = 0; + + if (libbpf_set_strict_mode(LIBBPF_STRICT_ALL) < 0) { + fprintf(stderr, "Failed to set libbpf strict mode: %s\n", + strerror(errno)); + goto end; + } + + skel = xdp_router_ipv4__open(); + if (!skel) { + fprintf(stderr, "Failed to xdp_router_ipv4__open: %s\n", + strerror(errno)); + goto end; + } + + ret = sample_init_pre_load(skel); + if (ret < 0) { + fprintf(stderr, "Failed to sample_init_pre_load: %s\n", + strerror(-ret)); + ret = EXIT_FAIL_BPF; + goto end_destroy; + } + + ret = xdp_router_ipv4__load(skel); + if (ret < 0) { + fprintf(stderr, "Failed to xdp_router_ipv4__load: %s\n", + strerror(errno)); + goto end_destroy; + } + + ret = sample_init(skel, mask); + if (ret < 0) { + fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret)); + ret = EXIT_FAIL; + goto end_destroy; + } + + while ((opt = getopt_long(argc, argv, "si:SFvh", + long_options, &longindex)) != -1) { switch (opt) { + case 's': + mask |= SAMPLE_REDIRECT_MAP_CNT; + total_ifindex--; + ifname_list++; + break; + case 'i': + interval = strtoul(optarg, NULL, 0); + total_ifindex -= 2; + ifname_list += 2; + break; case 'S': - flags |= XDP_FLAGS_SKB_MODE; + generic = true; total_ifindex--; ifname_list++; break; case 'F': - flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; + force = true; total_ifindex--; ifname_list++; break; + case 'v': + sample_switch_mode(); + total_ifindex--; + ifname_list++; + break; + case 'h': + error = false; default: - usage(basename(argv[0])); - return 1; + usage(argv, long_options, __doc__, mask, error, skel->obj); + goto end_destroy; } } - if (!(flags & XDP_FLAGS_SKB_MODE)) - flags |= XDP_FLAGS_DRV_MODE; - - if (optind == ac) { - usage(basename(argv[0])); - return 1; + ret = EXIT_FAIL_OPTION; + if (optind == argc) { + usage(argv, long_options, __doc__, mask, true, skel->obj); + goto end_destroy; } - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) - return 1; - - printf("\n******************loading bpf file*********************\n"); - if (!prog_fd) { - printf("bpf_prog_load_xattr: %s\n", strerror(errno)); - return 1; + lpm_map_fd = bpf_map__fd(skel->maps.lpm_map); + if (lpm_map_fd < 0) { + fprintf(stderr, "Failed loading lpm_map %s\n", + strerror(-lpm_map_fd)); + goto end_destroy; } - - lpm_map_fd = bpf_object__find_map_fd_by_name(obj, "lpm_map"); - rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); - arp_table_map_fd = bpf_object__find_map_fd_by_name(obj, "arp_table"); - exact_match_map_fd = bpf_object__find_map_fd_by_name(obj, - "exact_match"); - tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port"); - if (lpm_map_fd < 0 || rxcnt_map_fd < 0 || arp_table_map_fd < 0 || - exact_match_map_fd < 0 || tx_port_map_fd < 0) { - printf("bpf_object__find_map_fd_by_name failed\n"); - return 1; + arp_table_map_fd = bpf_map__fd(skel->maps.arp_table); + if (arp_table_map_fd < 0) { + fprintf(stderr, "Failed loading arp_table_map_fd %s\n", + strerror(-arp_table_map_fd)); + goto end_destroy; } - - ifindex_list = (int *)calloc(total_ifindex, sizeof(int *)); - for (i = 0; i < total_ifindex; i++) { - ifindex_list[i] = if_nametoindex(ifname_list[i]); - if (!ifindex_list[i]) { - printf("Couldn't translate interface name: %s", - strerror(errno)); - return 1; - } + exact_match_map_fd = bpf_map__fd(skel->maps.exact_match); + if (exact_match_map_fd < 0) { + fprintf(stderr, "Failed loading exact_match_map_fd %s\n", + strerror(-exact_match_map_fd)); + goto end_destroy; + } + tx_port_map_fd = bpf_map__fd(skel->maps.tx_port); + if (tx_port_map_fd < 0) { + fprintf(stderr, "Failed loading tx_port_map_fd %s\n", + strerror(-tx_port_map_fd)); + goto end_destroy; } - prog_id_list = (__u32 *)calloc(total_ifindex, sizeof(__u32 *)); - for (i = 0; i < total_ifindex; i++) { - if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) { - printf("link set xdp fd failed\n"); - int recovery_index = i; - for (i = 0; i < recovery_index; i++) - bpf_set_link_xdp_fd(ifindex_list[i], -1, flags); + ret = EXIT_FAIL_XDP; + for (i = 0; i < total_ifindex; i++) { + int index = if_nametoindex(ifname_list[i]); - return 1; + if (!index) { + fprintf(stderr, "Interface %s not found %s\n", + ifname_list[i], strerror(-tx_port_map_fd)); + goto end_destroy; } - err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len); - if (err) { - printf("can't get prog info - %s\n", strerror(errno)); - return err; - } - prog_id_list[i] = info.id; - memset(&info, 0, sizeof(info)); - printf("Attached to %d\n", ifindex_list[i]); + if (sample_install_xdp(skel->progs.xdp_router_ipv4_prog, + index, generic, force) < 0) + goto end_destroy; } - signal(SIGINT, int_exit); - signal(SIGTERM, int_exit); - printf("\n*******************ROUTE TABLE*************************\n"); - get_route_table(AF_INET); - printf("\n*******************ARP TABLE***************************\n"); - get_arp_table(AF_INET); - if (monitor_route() < 0) { - printf("Error in receiving route update"); - return 1; + ret = pthread_create(&routes_thread, NULL, monitor_routes_thread, NULL); + if (ret) { + fprintf(stderr, "Failed creating routes_thread: %s\n", strerror(-ret)); + ret = EXIT_FAIL; + goto end_destroy; } - return 0; + ret = sample_run(interval, NULL, NULL); + routes_thread_exit = true; + + if (ret < 0) { + fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret)); + ret = EXIT_FAIL; + goto end_thread_wait; + } + ret = EXIT_OK; + +end_thread_wait: + pthread_join(routes_thread, NULL); +end_destroy: + xdp_router_ipv4__destroy(skel); +end: + sample_exit(ret); } diff --git a/samples/bpf/xdp_rxq_info_user.c b/samples/bpf/xdp_rxq_info_user.c index 74a2926eba08..08f5331d2b00 100644 --- a/samples/bpf/xdp_rxq_info_user.c +++ b/samples/bpf/xdp_rxq_info_user.c @@ -14,11 +14,10 @@ static const char *__doc__ = " XDP RX-queue info extract example\n\n" #include <string.h> #include <unistd.h> #include <locale.h> -#include <sys/resource.h> #include <getopt.h> #include <net/if.h> #include <time.h> - +#include <limits.h> #include <arpa/inet.h> #include <linux/if_link.h> @@ -44,6 +43,9 @@ static struct bpf_map *rx_queue_index_map; #define EXIT_FAIL_BPF 4 #define EXIT_FAIL_MEM 5 +#define FAIL_MEM_SIG INT_MAX +#define FAIL_STAT_SIG (INT_MAX - 1) + static const struct option long_options[] = { {"help", no_argument, NULL, 'h' }, {"dev", required_argument, NULL, 'd' }, @@ -62,21 +64,27 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(EXIT_FAIL); } if (prog_id == curr_prog_id) { fprintf(stderr, "Interrupted: Removing XDP program on ifindex:%d device:%s\n", ifindex, ifname); - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); } else if (!curr_prog_id) { printf("couldn't find a prog id on a given iface\n"); } else { printf("program on interface changed, not removing\n"); } } + + if (sig == FAIL_MEM_SIG) + exit(EXIT_FAIL_MEM); + else if (sig == FAIL_STAT_SIG) + exit(EXIT_FAIL); + exit(EXIT_OK); } @@ -141,7 +149,8 @@ static char* options2str(enum cfg_options_flags flag) if (flag & READ_MEM) return "read"; fprintf(stderr, "ERR: Unknown config option flags"); - exit(EXIT_FAIL); + int_exit(FAIL_STAT_SIG); + return "unknown"; } static void usage(char *argv[]) @@ -174,7 +183,7 @@ static __u64 gettime(void) res = clock_gettime(CLOCK_MONOTONIC, &t); if (res < 0) { fprintf(stderr, "Error with gettimeofday! (%i)\n", res); - exit(EXIT_FAIL); + int_exit(FAIL_STAT_SIG); } return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec; } @@ -202,34 +211,34 @@ static struct datarec *alloc_record_per_cpu(void) array = calloc(nr_cpus, sizeof(struct datarec)); if (!array) { fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus); - exit(EXIT_FAIL_MEM); + int_exit(FAIL_MEM_SIG); } return array; } static struct record *alloc_record_per_rxq(void) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); struct record *array; array = calloc(nr_rxqs, sizeof(struct record)); if (!array) { fprintf(stderr, "Mem alloc error (nr_rxqs:%u)\n", nr_rxqs); - exit(EXIT_FAIL_MEM); + int_exit(FAIL_MEM_SIG); } return array; } static struct stats_record *alloc_stats_record(void) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); struct stats_record *rec; int i; rec = calloc(1, sizeof(struct stats_record)); if (!rec) { fprintf(stderr, "Mem alloc error\n"); - exit(EXIT_FAIL_MEM); + int_exit(FAIL_MEM_SIG); } rec->rxq = alloc_record_per_rxq(); for (i = 0; i < nr_rxqs; i++) @@ -241,7 +250,7 @@ static struct stats_record *alloc_stats_record(void) static void free_stats_record(struct stats_record *r) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); int i; for (i = 0; i < nr_rxqs; i++) @@ -289,7 +298,7 @@ static void stats_collect(struct stats_record *rec) map_collect_percpu(fd, 0, &rec->stats); fd = bpf_map__fd(rx_queue_index_map); - max_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + max_rxqs = bpf_map__max_entries(rx_queue_index_map); for (i = 0; i < max_rxqs; i++) map_collect_percpu(fd, i, &rec->rxq[i]); } @@ -335,7 +344,7 @@ static void stats_print(struct stats_record *stats_rec, struct stats_record *stats_prev, int action, __u32 cfg_opt) { - unsigned int nr_rxqs = bpf_map__def(rx_queue_index_map)->max_entries; + unsigned int nr_rxqs = bpf_map__max_entries(rx_queue_index_map); unsigned int nr_cpus = bpf_num_possible_cpus(); double pps = 0, err = 0; struct record *rec, *prev; @@ -450,14 +459,12 @@ static void stats_poll(int interval, int action, __u32 cfg_opt) int main(int argc, char **argv) { __u32 cfg_options= NO_TOUCH ; /* Default: Don't touch packet memory */ - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; struct bpf_prog_info info = {}; __u32 info_len = sizeof(info); int prog_fd, map_fd, opt, err; bool use_separators = true; struct config cfg = { 0 }; + struct bpf_program *prog; struct bpf_object *obj; struct bpf_map *map; char filename[256]; @@ -471,10 +478,18 @@ int main(int argc, char **argv) char *action_str = NULL; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) + return EXIT_FAIL; + + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) return EXIT_FAIL; + prog_fd = bpf_program__fd(prog); map = bpf_object__find_map_by_name(obj, "config_map"); stats_global_map = bpf_object__find_map_by_name(obj, "stats_global_map"); @@ -582,7 +597,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); signal(SIGTERM, int_exit); - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { fprintf(stderr, "link set xdp fd failed\n"); return EXIT_FAIL_XDP; } diff --git a/samples/bpf/xdp_sample_pkts_user.c b/samples/bpf/xdp_sample_pkts_user.c index 587eacb49103..7df7163239ac 100644 --- a/samples/bpf/xdp_sample_pkts_user.c +++ b/samples/bpf/xdp_sample_pkts_user.c @@ -12,7 +12,6 @@ #include <signal.h> #include <bpf/libbpf.h> #include <bpf/bpf.h> -#include <sys/resource.h> #include <libgen.h> #include <linux/if_link.h> @@ -30,7 +29,7 @@ static int do_attach(int idx, int fd, const char *name) __u32 info_len = sizeof(info); int err; - err = bpf_set_link_xdp_fd(idx, fd, xdp_flags); + err = bpf_xdp_attach(idx, fd, xdp_flags, NULL); if (err < 0) { printf("ERROR: failed to attach program to %s\n", name); return err; @@ -51,13 +50,13 @@ static int do_detach(int idx, const char *name) __u32 curr_prog_id = 0; int err = 0; - err = bpf_get_link_xdp_id(idx, &curr_prog_id, xdp_flags); + err = bpf_xdp_query_id(idx, xdp_flags, &curr_prog_id); if (err) { - printf("bpf_get_link_xdp_id failed\n"); + printf("bpf_xdp_query_id failed\n"); return err; } if (prog_id == curr_prog_id) { - err = bpf_set_link_xdp_fd(idx, -1, xdp_flags); + err = bpf_xdp_detach(idx, xdp_flags, NULL); if (err < 0) printf("ERROR: failed to detach prog from %s\n", name); } else if (!curr_prog_id) { diff --git a/samples/bpf/xdp_sample_user.c b/samples/bpf/xdp_sample_user.c index 8740838e7767..158682852162 100644 --- a/samples/bpf/xdp_sample_user.c +++ b/samples/bpf/xdp_sample_user.c @@ -25,7 +25,6 @@ #include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> -#include <sys/resource.h> #include <sys/signalfd.h> #include <sys/sysinfo.h> #include <sys/timerfd.h> @@ -1218,7 +1217,7 @@ int sample_setup_maps(struct bpf_map **maps) default: return -EINVAL; } - if (bpf_map__resize(sample_map[i], sample_map_count[i]) < 0) + if (bpf_map__set_max_entries(sample_map[i], sample_map_count[i]) < 0) return -errno; } sample_map[MAP_DEVMAP_XMIT_MULTI] = maps[MAP_DEVMAP_XMIT_MULTI]; @@ -1265,7 +1264,7 @@ static int __sample_remove_xdp(int ifindex, __u32 prog_id, int xdp_flags) int ret; if (prog_id) { - ret = bpf_get_link_xdp_id(ifindex, &cur_prog_id, xdp_flags); + ret = bpf_xdp_query_id(ifindex, xdp_flags, &cur_prog_id); if (ret < 0) return -errno; @@ -1278,7 +1277,7 @@ static int __sample_remove_xdp(int ifindex, __u32 prog_id, int xdp_flags) } } - return bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + return bpf_xdp_detach(ifindex, xdp_flags, NULL); } int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, @@ -1295,8 +1294,7 @@ int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, xdp_flags |= !force ? XDP_FLAGS_UPDATE_IF_NOEXIST : 0; xdp_flags |= generic ? XDP_FLAGS_SKB_MODE : XDP_FLAGS_DRV_MODE; - ret = bpf_set_link_xdp_fd(ifindex, bpf_program__fd(xdp_prog), - xdp_flags); + ret = bpf_xdp_attach(ifindex, bpf_program__fd(xdp_prog), xdp_flags, NULL); if (ret < 0) { ret = -errno; fprintf(stderr, @@ -1308,7 +1306,7 @@ int sample_install_xdp(struct bpf_program *xdp_prog, int ifindex, bool generic, return ret; } - ret = bpf_get_link_xdp_id(ifindex, &prog_id, xdp_flags); + ret = bpf_xdp_query_id(ifindex, xdp_flags, &prog_id); if (ret < 0) { ret = -errno; fprintf(stderr, diff --git a/samples/bpf/xdp_sample_user.h b/samples/bpf/xdp_sample_user.h index 5f44b877ecf5..f45051679977 100644 --- a/samples/bpf/xdp_sample_user.h +++ b/samples/bpf/xdp_sample_user.h @@ -61,7 +61,7 @@ static inline char *safe_strncpy(char *dst, const char *src, size_t size) #define __attach_tp(name) \ ({ \ - if (!bpf_program__is_tracing(skel->progs.name)) \ + if (bpf_program__type(skel->progs.name) != BPF_PROG_TYPE_TRACING)\ return -EINVAL; \ skel->links.name = bpf_program__attach(skel->progs.name); \ if (!skel->links.name) \ diff --git a/samples/bpf/xdp_tx_iptunnel_kern.c b/samples/bpf/xdp_tx_iptunnel_kern.c index 575d57e4b8d6..0e2bca3a3fff 100644 --- a/samples/bpf/xdp_tx_iptunnel_kern.c +++ b/samples/bpf/xdp_tx_iptunnel_kern.c @@ -212,7 +212,7 @@ static __always_inline int handle_ipv6(struct xdp_md *xdp) return XDP_TX; } -SEC("xdp_tx_iptunnel") +SEC("xdp.frags") int _xdp_tx_iptunnel(struct xdp_md *xdp) { void *data_end = (void *)(long)xdp->data_end; diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 1d4f305d02aa..307baef6861a 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -10,7 +10,6 @@ #include <stdlib.h> #include <string.h> #include <net/if.h> -#include <sys/resource.h> #include <arpa/inet.h> #include <netinet/ether.h> #include <unistd.h> @@ -32,12 +31,12 @@ static void int_exit(int sig) __u32 curr_prog_id = 0; if (ifindex > -1) { - if (bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); + if (bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id)) { + printf("bpf_xdp_query_id failed\n"); exit(1); } if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); else if (!curr_prog_id) printf("couldn't find a prog id on a given iface\n"); else @@ -152,9 +151,6 @@ static int parse_ports(const char *port_str, int *min_port, int *max_port) int main(int argc, char **argv) { - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; int min_port = 0, max_port = 0, vip2tnl_map_fd; const char *optstr = "i:a:p:s:d:m:T:P:FSNh"; unsigned char opt_flags[256] = {}; @@ -162,6 +158,7 @@ int main(int argc, char **argv) __u32 info_len = sizeof(info); unsigned int kill_after_s = 0; struct iptnl_info tnl = {}; + struct bpf_program *prog; struct bpf_object *obj; struct vip vip = {}; char filename[256]; @@ -259,15 +256,20 @@ int main(int argc, char **argv) } snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); - prog_load_attr.file = filename; - if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) + obj = bpf_object__open_file(filename, NULL); + if (libbpf_get_error(obj)) return 1; - if (!prog_fd) { - printf("bpf_prog_load_xattr: %s\n", strerror(errno)); + prog = bpf_object__next_program(obj, NULL); + bpf_program__set_type(prog, BPF_PROG_TYPE_XDP); + + err = bpf_object__load(obj); + if (err) { + printf("bpf_object__load(): %s\n", strerror(errno)); return 1; } + prog_fd = bpf_program__fd(prog); rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); vip2tnl_map_fd = bpf_object__find_map_fd_by_name(obj, "vip2tnl"); @@ -288,7 +290,7 @@ int main(int argc, char **argv) } } - if (bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags) < 0) { + if (bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL) < 0) { printf("link set xdp fd failed\n"); return 1; } @@ -302,7 +304,7 @@ int main(int argc, char **argv) poll_stats(kill_after_s); - bpf_set_link_xdp_fd(ifindex, -1, xdp_flags); + bpf_xdp_detach(ifindex, xdp_flags, NULL); return 0; } diff --git a/samples/bpf/xdpsock.h b/samples/bpf/xdpsock.h deleted file mode 100644 index fd70cce60712..000000000000 --- a/samples/bpf/xdpsock.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright(c) 2019 Intel Corporation. - */ - -#ifndef XDPSOCK_H_ -#define XDPSOCK_H_ - -#define MAX_SOCKS 4 - -#define SOCKET_NAME "sock_cal_bpf_fd" -#define MAX_NUM_OF_CLIENTS 10 - -#define CLOSE_CONN 1 - -typedef __u64 u64; -typedef __u32 u32; - -#endif /* XDPSOCK_H */ diff --git a/samples/bpf/xdpsock_ctrl_proc.c b/samples/bpf/xdpsock_ctrl_proc.c deleted file mode 100644 index cc4408797ab7..000000000000 --- a/samples/bpf/xdpsock_ctrl_proc.c +++ /dev/null @@ -1,190 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2017 - 2018 Intel Corporation. */ - -#include <errno.h> -#include <getopt.h> -#include <libgen.h> -#include <net/if.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - -#include <bpf/bpf.h> -#include <bpf/xsk.h> -#include "xdpsock.h" - -/* libbpf APIs for AF_XDP are deprecated starting from v0.7 */ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -static const char *opt_if = ""; - -static struct option long_options[] = { - {"interface", required_argument, 0, 'i'}, - {0, 0, 0, 0} -}; - -static void usage(const char *prog) -{ - const char *str = - " Usage: %s [OPTIONS]\n" - " Options:\n" - " -i, --interface=n Run on interface n\n" - "\n"; - fprintf(stderr, "%s\n", str); - - exit(0); -} - -static void parse_command_line(int argc, char **argv) -{ - int option_index, c; - - opterr = 0; - - for (;;) { - c = getopt_long(argc, argv, "i:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'i': - opt_if = optarg; - break; - default: - usage(basename(argv[0])); - } - } -} - -static int send_xsks_map_fd(int sock, int fd) -{ - char cmsgbuf[CMSG_SPACE(sizeof(int))]; - struct msghdr msg; - struct iovec iov; - int value = 0; - - if (fd == -1) { - fprintf(stderr, "Incorrect fd = %d\n", fd); - return -1; - } - iov.iov_base = &value; - iov.iov_len = sizeof(int); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = cmsgbuf; - msg.msg_controllen = CMSG_LEN(sizeof(int)); - - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - - *(int *)CMSG_DATA(cmsg) = fd; - int ret = sendmsg(sock, &msg, 0); - - if (ret == -1) { - fprintf(stderr, "Sendmsg failed with %s", strerror(errno)); - return -errno; - } - - return ret; -} - -int -main(int argc, char **argv) -{ - struct sockaddr_un server; - int listening = 1; - int rval, msgsock; - int ifindex = 0; - int flag = 1; - int cmd = 0; - int sock; - int err; - int xsks_map_fd; - - parse_command_line(argc, argv); - - ifindex = if_nametoindex(opt_if); - if (ifindex == 0) { - fprintf(stderr, "Unable to get ifindex for Interface %s. Reason:%s", - opt_if, strerror(errno)); - return -errno; - } - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - fprintf(stderr, "Opening socket stream failed: %s", strerror(errno)); - return -errno; - } - - server.sun_family = AF_UNIX; - strcpy(server.sun_path, SOCKET_NAME); - - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)); - - if (bind(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un))) { - fprintf(stderr, "Binding to socket stream failed: %s", strerror(errno)); - return -errno; - } - - listen(sock, MAX_NUM_OF_CLIENTS); - - err = xsk_setup_xdp_prog(ifindex, &xsks_map_fd); - if (err) { - fprintf(stderr, "Setup of xdp program failed\n"); - goto close_sock; - } - - while (listening) { - msgsock = accept(sock, 0, 0); - if (msgsock == -1) { - fprintf(stderr, "Error accepting connection: %s", strerror(errno)); - err = -errno; - goto close_sock; - } - err = send_xsks_map_fd(msgsock, xsks_map_fd); - if (err <= 0) { - fprintf(stderr, "Error %d sending xsks_map_fd\n", err); - goto cleanup; - } - do { - rval = read(msgsock, &cmd, sizeof(int)); - if (rval < 0) { - fprintf(stderr, "Error reading stream message"); - } else { - if (cmd != CLOSE_CONN) - fprintf(stderr, "Recv unknown cmd = %d\n", cmd); - listening = 0; - break; - } - } while (rval > 0); - } - close(msgsock); - close(sock); - unlink(SOCKET_NAME); - - /* Unset fd for given ifindex */ - err = bpf_set_link_xdp_fd(ifindex, -1, 0); - if (err) { - fprintf(stderr, "Error when unsetting bpf prog_fd for ifindex(%d)\n", ifindex); - return err; - } - - return 0; - -cleanup: - close(msgsock); -close_sock: - close(sock); - unlink(SOCKET_NAME); - return err; -} diff --git a/samples/bpf/xdpsock_kern.c b/samples/bpf/xdpsock_kern.c deleted file mode 100644 index 05430484375c..000000000000 --- a/samples/bpf/xdpsock_kern.c +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/bpf.h> -#include <bpf/bpf_helpers.h> -#include "xdpsock.h" - -/* This XDP program is only needed for the XDP_SHARED_UMEM mode. - * If you do not use this mode, libbpf can supply an XDP program for you. - */ - -struct { - __uint(type, BPF_MAP_TYPE_XSKMAP); - __uint(max_entries, MAX_SOCKS); - __uint(key_size, sizeof(int)); - __uint(value_size, sizeof(int)); -} xsks_map SEC(".maps"); - -static unsigned int rr; - -SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx) -{ - rr = (rr + 1) & (MAX_SOCKS - 1); - - return bpf_redirect_map(&xsks_map, rr, XDP_DROP); -} diff --git a/samples/bpf/xdpsock_user.c b/samples/bpf/xdpsock_user.c deleted file mode 100644 index aa50864e4415..000000000000 --- a/samples/bpf/xdpsock_user.c +++ /dev/null @@ -1,2024 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2017 - 2018 Intel Corporation. */ - -#include <errno.h> -#include <getopt.h> -#include <libgen.h> -#include <linux/bpf.h> -#include <linux/if_link.h> -#include <linux/if_xdp.h> -#include <linux/if_ether.h> -#include <linux/ip.h> -#include <linux/limits.h> -#include <linux/udp.h> -#include <arpa/inet.h> -#include <locale.h> -#include <net/ethernet.h> -#include <netinet/ether.h> -#include <net/if.h> -#include <poll.h> -#include <pthread.h> -#include <signal.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/capability.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <time.h> -#include <unistd.h> -#include <sched.h> - -#include <bpf/libbpf.h> -#include <bpf/xsk.h> -#include <bpf/bpf.h> -#include "xdpsock.h" - -/* libbpf APIs for AF_XDP are deprecated starting from v0.7 */ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -#ifndef SOL_XDP -#define SOL_XDP 283 -#endif - -#ifndef AF_XDP -#define AF_XDP 44 -#endif - -#ifndef PF_XDP -#define PF_XDP AF_XDP -#endif - -#define NUM_FRAMES (4 * 1024) -#define MIN_PKT_SIZE 64 - -#define DEBUG_HEXDUMP 0 - -#define VLAN_PRIO_MASK 0xe000 /* Priority Code Point */ -#define VLAN_PRIO_SHIFT 13 -#define VLAN_VID_MASK 0x0fff /* VLAN Identifier */ -#define VLAN_VID__DEFAULT 1 -#define VLAN_PRI__DEFAULT 0 - -#define NSEC_PER_SEC 1000000000UL -#define NSEC_PER_USEC 1000 - -#define SCHED_PRI__DEFAULT 0 - -typedef __u64 u64; -typedef __u32 u32; -typedef __u16 u16; -typedef __u8 u8; - -static unsigned long prev_time; -static long tx_cycle_diff_min; -static long tx_cycle_diff_max; -static double tx_cycle_diff_ave; -static long tx_cycle_cnt; - -enum benchmark_type { - BENCH_RXDROP = 0, - BENCH_TXONLY = 1, - BENCH_L2FWD = 2, -}; - -static enum benchmark_type opt_bench = BENCH_RXDROP; -static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; -static const char *opt_if = ""; -static int opt_ifindex; -static int opt_queue; -static unsigned long opt_duration; -static unsigned long start_time; -static bool benchmark_done; -static u32 opt_batch_size = 64; -static int opt_pkt_count; -static u16 opt_pkt_size = MIN_PKT_SIZE; -static u32 opt_pkt_fill_pattern = 0x12345678; -static bool opt_vlan_tag; -static u16 opt_pkt_vlan_id = VLAN_VID__DEFAULT; -static u16 opt_pkt_vlan_pri = VLAN_PRI__DEFAULT; -static struct ether_addr opt_txdmac = {{ 0x3c, 0xfd, 0xfe, - 0x9e, 0x7f, 0x71 }}; -static struct ether_addr opt_txsmac = {{ 0xec, 0xb1, 0xd7, - 0x98, 0x3a, 0xc0 }}; -static bool opt_extra_stats; -static bool opt_quiet; -static bool opt_app_stats; -static const char *opt_irq_str = ""; -static u32 irq_no; -static int irqs_at_init = -1; -static u32 sequence; -static int opt_poll; -static int opt_interval = 1; -static int opt_retries = 3; -static u32 opt_xdp_bind_flags = XDP_USE_NEED_WAKEUP; -static u32 opt_umem_flags; -static int opt_unaligned_chunks; -static int opt_mmap_flags; -static int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; -static int opt_timeout = 1000; -static bool opt_need_wakeup = true; -static u32 opt_num_xsks = 1; -static u32 prog_id; -static bool opt_busy_poll; -static bool opt_reduced_cap; -static clockid_t opt_clock = CLOCK_MONOTONIC; -static unsigned long opt_tx_cycle_ns; -static int opt_schpolicy = SCHED_OTHER; -static int opt_schprio = SCHED_PRI__DEFAULT; -static bool opt_tstamp; - -struct vlan_ethhdr { - unsigned char h_dest[6]; - unsigned char h_source[6]; - __be16 h_vlan_proto; - __be16 h_vlan_TCI; - __be16 h_vlan_encapsulated_proto; -}; - -#define PKTGEN_MAGIC 0xbe9be955 -struct pktgen_hdr { - __be32 pgh_magic; - __be32 seq_num; - __be32 tv_sec; - __be32 tv_usec; -}; - -struct xsk_ring_stats { - unsigned long rx_npkts; - unsigned long tx_npkts; - unsigned long rx_dropped_npkts; - unsigned long rx_invalid_npkts; - unsigned long tx_invalid_npkts; - unsigned long rx_full_npkts; - unsigned long rx_fill_empty_npkts; - unsigned long tx_empty_npkts; - unsigned long prev_rx_npkts; - unsigned long prev_tx_npkts; - unsigned long prev_rx_dropped_npkts; - unsigned long prev_rx_invalid_npkts; - unsigned long prev_tx_invalid_npkts; - unsigned long prev_rx_full_npkts; - unsigned long prev_rx_fill_empty_npkts; - unsigned long prev_tx_empty_npkts; -}; - -struct xsk_driver_stats { - unsigned long intrs; - unsigned long prev_intrs; -}; - -struct xsk_app_stats { - unsigned long rx_empty_polls; - unsigned long fill_fail_polls; - unsigned long copy_tx_sendtos; - unsigned long tx_wakeup_sendtos; - unsigned long opt_polls; - unsigned long prev_rx_empty_polls; - unsigned long prev_fill_fail_polls; - unsigned long prev_copy_tx_sendtos; - unsigned long prev_tx_wakeup_sendtos; - unsigned long prev_opt_polls; -}; - -struct xsk_umem_info { - struct xsk_ring_prod fq; - struct xsk_ring_cons cq; - struct xsk_umem *umem; - void *buffer; -}; - -struct xsk_socket_info { - struct xsk_ring_cons rx; - struct xsk_ring_prod tx; - struct xsk_umem_info *umem; - struct xsk_socket *xsk; - struct xsk_ring_stats ring_stats; - struct xsk_app_stats app_stats; - struct xsk_driver_stats drv_stats; - u32 outstanding_tx; -}; - -static const struct clockid_map { - const char *name; - clockid_t clockid; -} clockids_map[] = { - { "REALTIME", CLOCK_REALTIME }, - { "TAI", CLOCK_TAI }, - { "BOOTTIME", CLOCK_BOOTTIME }, - { "MONOTONIC", CLOCK_MONOTONIC }, - { NULL } -}; - -static const struct sched_map { - const char *name; - int policy; -} schmap[] = { - { "OTHER", SCHED_OTHER }, - { "FIFO", SCHED_FIFO }, - { NULL } -}; - -static int num_socks; -struct xsk_socket_info *xsks[MAX_SOCKS]; -int sock; - -static int get_clockid(clockid_t *id, const char *name) -{ - const struct clockid_map *clk; - - for (clk = clockids_map; clk->name; clk++) { - if (strcasecmp(clk->name, name) == 0) { - *id = clk->clockid; - return 0; - } - } - - return -1; -} - -static int get_schpolicy(int *policy, const char *name) -{ - const struct sched_map *sch; - - for (sch = schmap; sch->name; sch++) { - if (strcasecmp(sch->name, name) == 0) { - *policy = sch->policy; - return 0; - } - } - - return -1; -} - -static unsigned long get_nsecs(void) -{ - struct timespec ts; - - clock_gettime(opt_clock, &ts); - return ts.tv_sec * 1000000000UL + ts.tv_nsec; -} - -static void print_benchmark(bool running) -{ - const char *bench_str = "INVALID"; - - if (opt_bench == BENCH_RXDROP) - bench_str = "rxdrop"; - else if (opt_bench == BENCH_TXONLY) - bench_str = "txonly"; - else if (opt_bench == BENCH_L2FWD) - bench_str = "l2fwd"; - - printf("%s:%d %s ", opt_if, opt_queue, bench_str); - if (opt_xdp_flags & XDP_FLAGS_SKB_MODE) - printf("xdp-skb "); - else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE) - printf("xdp-drv "); - else - printf(" "); - - if (opt_poll) - printf("poll() "); - - if (running) { - printf("running..."); - fflush(stdout); - } -} - -static int xsk_get_xdp_stats(int fd, struct xsk_socket_info *xsk) -{ - struct xdp_statistics stats; - socklen_t optlen; - int err; - - optlen = sizeof(stats); - err = getsockopt(fd, SOL_XDP, XDP_STATISTICS, &stats, &optlen); - if (err) - return err; - - if (optlen == sizeof(struct xdp_statistics)) { - xsk->ring_stats.rx_dropped_npkts = stats.rx_dropped; - xsk->ring_stats.rx_invalid_npkts = stats.rx_invalid_descs; - xsk->ring_stats.tx_invalid_npkts = stats.tx_invalid_descs; - xsk->ring_stats.rx_full_npkts = stats.rx_ring_full; - xsk->ring_stats.rx_fill_empty_npkts = stats.rx_fill_ring_empty_descs; - xsk->ring_stats.tx_empty_npkts = stats.tx_ring_empty_descs; - return 0; - } - - return -EINVAL; -} - -static void dump_app_stats(long dt) -{ - int i; - - for (i = 0; i < num_socks && xsks[i]; i++) { - char *fmt = "%-18s %'-14.0f %'-14lu\n"; - double rx_empty_polls_ps, fill_fail_polls_ps, copy_tx_sendtos_ps, - tx_wakeup_sendtos_ps, opt_polls_ps; - - rx_empty_polls_ps = (xsks[i]->app_stats.rx_empty_polls - - xsks[i]->app_stats.prev_rx_empty_polls) * 1000000000. / dt; - fill_fail_polls_ps = (xsks[i]->app_stats.fill_fail_polls - - xsks[i]->app_stats.prev_fill_fail_polls) * 1000000000. / dt; - copy_tx_sendtos_ps = (xsks[i]->app_stats.copy_tx_sendtos - - xsks[i]->app_stats.prev_copy_tx_sendtos) * 1000000000. / dt; - tx_wakeup_sendtos_ps = (xsks[i]->app_stats.tx_wakeup_sendtos - - xsks[i]->app_stats.prev_tx_wakeup_sendtos) - * 1000000000. / dt; - opt_polls_ps = (xsks[i]->app_stats.opt_polls - - xsks[i]->app_stats.prev_opt_polls) * 1000000000. / dt; - - printf("\n%-18s %-14s %-14s\n", "", "calls/s", "count"); - printf(fmt, "rx empty polls", rx_empty_polls_ps, xsks[i]->app_stats.rx_empty_polls); - printf(fmt, "fill fail polls", fill_fail_polls_ps, - xsks[i]->app_stats.fill_fail_polls); - printf(fmt, "copy tx sendtos", copy_tx_sendtos_ps, - xsks[i]->app_stats.copy_tx_sendtos); - printf(fmt, "tx wakeup sendtos", tx_wakeup_sendtos_ps, - xsks[i]->app_stats.tx_wakeup_sendtos); - printf(fmt, "opt polls", opt_polls_ps, xsks[i]->app_stats.opt_polls); - - xsks[i]->app_stats.prev_rx_empty_polls = xsks[i]->app_stats.rx_empty_polls; - xsks[i]->app_stats.prev_fill_fail_polls = xsks[i]->app_stats.fill_fail_polls; - xsks[i]->app_stats.prev_copy_tx_sendtos = xsks[i]->app_stats.copy_tx_sendtos; - xsks[i]->app_stats.prev_tx_wakeup_sendtos = xsks[i]->app_stats.tx_wakeup_sendtos; - xsks[i]->app_stats.prev_opt_polls = xsks[i]->app_stats.opt_polls; - } - - if (opt_tx_cycle_ns) { - printf("\n%-18s %-10s %-10s %-10s %-10s %-10s\n", - "", "period", "min", "ave", "max", "cycle"); - printf("%-18s %-10lu %-10lu %-10lu %-10lu %-10lu\n", - "Cyclic TX", opt_tx_cycle_ns, tx_cycle_diff_min, - (long)(tx_cycle_diff_ave / tx_cycle_cnt), - tx_cycle_diff_max, tx_cycle_cnt); - } -} - -static bool get_interrupt_number(void) -{ - FILE *f_int_proc; - char line[4096]; - bool found = false; - - f_int_proc = fopen("/proc/interrupts", "r"); - if (f_int_proc == NULL) { - printf("Failed to open /proc/interrupts.\n"); - return found; - } - - while (!feof(f_int_proc) && !found) { - /* Make sure to read a full line at a time */ - if (fgets(line, sizeof(line), f_int_proc) == NULL || - line[strlen(line) - 1] != '\n') { - printf("Error reading from interrupts file\n"); - break; - } - - /* Extract interrupt number from line */ - if (strstr(line, opt_irq_str) != NULL) { - irq_no = atoi(line); - found = true; - break; - } - } - - fclose(f_int_proc); - - return found; -} - -static int get_irqs(void) -{ - char count_path[PATH_MAX]; - int total_intrs = -1; - FILE *f_count_proc; - char line[4096]; - - snprintf(count_path, sizeof(count_path), - "/sys/kernel/irq/%i/per_cpu_count", irq_no); - f_count_proc = fopen(count_path, "r"); - if (f_count_proc == NULL) { - printf("Failed to open %s\n", count_path); - return total_intrs; - } - - if (fgets(line, sizeof(line), f_count_proc) == NULL || - line[strlen(line) - 1] != '\n') { - printf("Error reading from %s\n", count_path); - } else { - static const char com[2] = ","; - char *token; - - total_intrs = 0; - token = strtok(line, com); - while (token != NULL) { - /* sum up interrupts across all cores */ - total_intrs += atoi(token); - token = strtok(NULL, com); - } - } - - fclose(f_count_proc); - - return total_intrs; -} - -static void dump_driver_stats(long dt) -{ - int i; - - for (i = 0; i < num_socks && xsks[i]; i++) { - char *fmt = "%-18s %'-14.0f %'-14lu\n"; - double intrs_ps; - int n_ints = get_irqs(); - - if (n_ints < 0) { - printf("error getting intr info for intr %i\n", irq_no); - return; - } - xsks[i]->drv_stats.intrs = n_ints - irqs_at_init; - - intrs_ps = (xsks[i]->drv_stats.intrs - xsks[i]->drv_stats.prev_intrs) * - 1000000000. / dt; - - printf("\n%-18s %-14s %-14s\n", "", "intrs/s", "count"); - printf(fmt, "irqs", intrs_ps, xsks[i]->drv_stats.intrs); - - xsks[i]->drv_stats.prev_intrs = xsks[i]->drv_stats.intrs; - } -} - -static void dump_stats(void) -{ - unsigned long now = get_nsecs(); - long dt = now - prev_time; - int i; - - prev_time = now; - - for (i = 0; i < num_socks && xsks[i]; i++) { - char *fmt = "%-18s %'-14.0f %'-14lu\n"; - double rx_pps, tx_pps, dropped_pps, rx_invalid_pps, full_pps, fill_empty_pps, - tx_invalid_pps, tx_empty_pps; - - rx_pps = (xsks[i]->ring_stats.rx_npkts - xsks[i]->ring_stats.prev_rx_npkts) * - 1000000000. / dt; - tx_pps = (xsks[i]->ring_stats.tx_npkts - xsks[i]->ring_stats.prev_tx_npkts) * - 1000000000. / dt; - - printf("\n sock%d@", i); - print_benchmark(false); - printf("\n"); - - printf("%-18s %-14s %-14s %-14.2f\n", "", "pps", "pkts", - dt / 1000000000.); - printf(fmt, "rx", rx_pps, xsks[i]->ring_stats.rx_npkts); - printf(fmt, "tx", tx_pps, xsks[i]->ring_stats.tx_npkts); - - xsks[i]->ring_stats.prev_rx_npkts = xsks[i]->ring_stats.rx_npkts; - xsks[i]->ring_stats.prev_tx_npkts = xsks[i]->ring_stats.tx_npkts; - - if (opt_extra_stats) { - if (!xsk_get_xdp_stats(xsk_socket__fd(xsks[i]->xsk), xsks[i])) { - dropped_pps = (xsks[i]->ring_stats.rx_dropped_npkts - - xsks[i]->ring_stats.prev_rx_dropped_npkts) * - 1000000000. / dt; - rx_invalid_pps = (xsks[i]->ring_stats.rx_invalid_npkts - - xsks[i]->ring_stats.prev_rx_invalid_npkts) * - 1000000000. / dt; - tx_invalid_pps = (xsks[i]->ring_stats.tx_invalid_npkts - - xsks[i]->ring_stats.prev_tx_invalid_npkts) * - 1000000000. / dt; - full_pps = (xsks[i]->ring_stats.rx_full_npkts - - xsks[i]->ring_stats.prev_rx_full_npkts) * - 1000000000. / dt; - fill_empty_pps = (xsks[i]->ring_stats.rx_fill_empty_npkts - - xsks[i]->ring_stats.prev_rx_fill_empty_npkts) * - 1000000000. / dt; - tx_empty_pps = (xsks[i]->ring_stats.tx_empty_npkts - - xsks[i]->ring_stats.prev_tx_empty_npkts) * - 1000000000. / dt; - - printf(fmt, "rx dropped", dropped_pps, - xsks[i]->ring_stats.rx_dropped_npkts); - printf(fmt, "rx invalid", rx_invalid_pps, - xsks[i]->ring_stats.rx_invalid_npkts); - printf(fmt, "tx invalid", tx_invalid_pps, - xsks[i]->ring_stats.tx_invalid_npkts); - printf(fmt, "rx queue full", full_pps, - xsks[i]->ring_stats.rx_full_npkts); - printf(fmt, "fill ring empty", fill_empty_pps, - xsks[i]->ring_stats.rx_fill_empty_npkts); - printf(fmt, "tx ring empty", tx_empty_pps, - xsks[i]->ring_stats.tx_empty_npkts); - - xsks[i]->ring_stats.prev_rx_dropped_npkts = - xsks[i]->ring_stats.rx_dropped_npkts; - xsks[i]->ring_stats.prev_rx_invalid_npkts = - xsks[i]->ring_stats.rx_invalid_npkts; - xsks[i]->ring_stats.prev_tx_invalid_npkts = - xsks[i]->ring_stats.tx_invalid_npkts; - xsks[i]->ring_stats.prev_rx_full_npkts = - xsks[i]->ring_stats.rx_full_npkts; - xsks[i]->ring_stats.prev_rx_fill_empty_npkts = - xsks[i]->ring_stats.rx_fill_empty_npkts; - xsks[i]->ring_stats.prev_tx_empty_npkts = - xsks[i]->ring_stats.tx_empty_npkts; - } else { - printf("%-15s\n", "Error retrieving extra stats"); - } - } - } - - if (opt_app_stats) - dump_app_stats(dt); - if (irq_no) - dump_driver_stats(dt); -} - -static bool is_benchmark_done(void) -{ - if (opt_duration > 0) { - unsigned long dt = (get_nsecs() - start_time); - - if (dt >= opt_duration) - benchmark_done = true; - } - return benchmark_done; -} - -static void *poller(void *arg) -{ - (void)arg; - while (!is_benchmark_done()) { - sleep(opt_interval); - dump_stats(); - } - - return NULL; -} - -static void remove_xdp_program(void) -{ - u32 curr_prog_id = 0; - - if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) { - printf("bpf_get_link_xdp_id failed\n"); - exit(EXIT_FAILURE); - } - - if (prog_id == curr_prog_id) - bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags); - else if (!curr_prog_id) - printf("couldn't find a prog id on a given interface\n"); - else - printf("program on interface changed, not removing\n"); -} - -static void int_exit(int sig) -{ - benchmark_done = true; -} - -static void __exit_with_error(int error, const char *file, const char *func, - int line) -{ - fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func, - line, error, strerror(error)); - - if (opt_num_xsks > 1) - remove_xdp_program(); - exit(EXIT_FAILURE); -} - -#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, __LINE__) - -static void xdpsock_cleanup(void) -{ - struct xsk_umem *umem = xsks[0]->umem->umem; - int i, cmd = CLOSE_CONN; - - dump_stats(); - for (i = 0; i < num_socks; i++) - xsk_socket__delete(xsks[i]->xsk); - (void)xsk_umem__delete(umem); - - if (opt_reduced_cap) { - if (write(sock, &cmd, sizeof(int)) < 0) - exit_with_error(errno); - } - - if (opt_num_xsks > 1) - remove_xdp_program(); -} - -static void swap_mac_addresses(void *data) -{ - struct ether_header *eth = (struct ether_header *)data; - struct ether_addr *src_addr = (struct ether_addr *)ð->ether_shost; - struct ether_addr *dst_addr = (struct ether_addr *)ð->ether_dhost; - struct ether_addr tmp; - - tmp = *src_addr; - *src_addr = *dst_addr; - *dst_addr = tmp; -} - -static void hex_dump(void *pkt, size_t length, u64 addr) -{ - const unsigned char *address = (unsigned char *)pkt; - const unsigned char *line = address; - size_t line_size = 32; - unsigned char c; - char buf[32]; - int i = 0; - - if (!DEBUG_HEXDUMP) - return; - - sprintf(buf, "addr=%llu", addr); - printf("length = %zu\n", length); - printf("%s | ", buf); - while (length-- > 0) { - printf("%02X ", *address++); - if (!(++i % line_size) || (length == 0 && i % line_size)) { - if (length == 0) { - while (i++ % line_size) - printf("__ "); - } - printf(" | "); /* right close */ - while (line < address) { - c = *line++; - printf("%c", (c < 33 || c == 255) ? 0x2E : c); - } - printf("\n"); - if (length > 0) - printf("%s | ", buf); - } - } - printf("\n"); -} - -static void *memset32_htonl(void *dest, u32 val, u32 size) -{ - u32 *ptr = (u32 *)dest; - int i; - - val = htonl(val); - - for (i = 0; i < (size & (~0x3)); i += 4) - ptr[i >> 2] = val; - - for (; i < size; i++) - ((char *)dest)[i] = ((char *)&val)[i & 3]; - - return dest; -} - -/* - * This function code has been taken from - * Linux kernel lib/checksum.c - */ -static inline unsigned short from32to16(unsigned int x) -{ - /* add up 16-bit and 16-bit for 16+c bit */ - x = (x & 0xffff) + (x >> 16); - /* add up carry.. */ - x = (x & 0xffff) + (x >> 16); - return x; -} - -/* - * This function code has been taken from - * Linux kernel lib/checksum.c - */ -static unsigned int do_csum(const unsigned char *buff, int len) -{ - unsigned int result = 0; - int odd; - - if (len <= 0) - goto out; - odd = 1 & (unsigned long)buff; - if (odd) { -#ifdef __LITTLE_ENDIAN - result += (*buff << 8); -#else - result = *buff; -#endif - len--; - buff++; - } - if (len >= 2) { - if (2 & (unsigned long)buff) { - result += *(unsigned short *)buff; - len -= 2; - buff += 2; - } - if (len >= 4) { - const unsigned char *end = buff + - ((unsigned int)len & ~3); - unsigned int carry = 0; - - do { - unsigned int w = *(unsigned int *)buff; - - buff += 4; - result += carry; - result += w; - carry = (w > result); - } while (buff < end); - result += carry; - result = (result & 0xffff) + (result >> 16); - } - if (len & 2) { - result += *(unsigned short *)buff; - buff += 2; - } - } - if (len & 1) -#ifdef __LITTLE_ENDIAN - result += *buff; -#else - result += (*buff << 8); -#endif - result = from32to16(result); - if (odd) - result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); -out: - return result; -} - -/* - * This is a version of ip_compute_csum() optimized for IP headers, - * which always checksum on 4 octet boundaries. - * This function code has been taken from - * Linux kernel lib/checksum.c - */ -static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl) -{ - return (__sum16)~do_csum(iph, ihl * 4); -} - -/* - * Fold a partial checksum - * This function code has been taken from - * Linux kernel include/asm-generic/checksum.h - */ -static inline __sum16 csum_fold(__wsum csum) -{ - u32 sum = (u32)csum; - - sum = (sum & 0xffff) + (sum >> 16); - sum = (sum & 0xffff) + (sum >> 16); - return (__sum16)~sum; -} - -/* - * This function code has been taken from - * Linux kernel lib/checksum.c - */ -static inline u32 from64to32(u64 x) -{ - /* add up 32-bit and 32-bit for 32+c bit */ - x = (x & 0xffffffff) + (x >> 32); - /* add up carry.. */ - x = (x & 0xffffffff) + (x >> 32); - return (u32)x; -} - -__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, - __u32 len, __u8 proto, __wsum sum); - -/* - * This function code has been taken from - * Linux kernel lib/checksum.c - */ -__wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, - __u32 len, __u8 proto, __wsum sum) -{ - unsigned long long s = (u32)sum; - - s += (u32)saddr; - s += (u32)daddr; -#ifdef __BIG_ENDIAN__ - s += proto + len; -#else - s += (proto + len) << 8; -#endif - return (__wsum)from64to32(s); -} - -/* - * This function has been taken from - * Linux kernel include/asm-generic/checksum.h - */ -static inline __sum16 -csum_tcpudp_magic(__be32 saddr, __be32 daddr, __u32 len, - __u8 proto, __wsum sum) -{ - return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); -} - -static inline u16 udp_csum(u32 saddr, u32 daddr, u32 len, - u8 proto, u16 *udp_pkt) -{ - u32 csum = 0; - u32 cnt = 0; - - /* udp hdr and data */ - for (; cnt < len; cnt += 2) - csum += udp_pkt[cnt >> 1]; - - return csum_tcpudp_magic(saddr, daddr, len, proto, csum); -} - -#define ETH_FCS_SIZE 4 - -#define ETH_HDR_SIZE (opt_vlan_tag ? sizeof(struct vlan_ethhdr) : \ - sizeof(struct ethhdr)) -#define PKTGEN_HDR_SIZE (opt_tstamp ? sizeof(struct pktgen_hdr) : 0) -#define PKT_HDR_SIZE (ETH_HDR_SIZE + sizeof(struct iphdr) + \ - sizeof(struct udphdr) + PKTGEN_HDR_SIZE) -#define PKTGEN_HDR_OFFSET (ETH_HDR_SIZE + sizeof(struct iphdr) + \ - sizeof(struct udphdr)) -#define PKTGEN_SIZE_MIN (PKTGEN_HDR_OFFSET + sizeof(struct pktgen_hdr) + \ - ETH_FCS_SIZE) - -#define PKT_SIZE (opt_pkt_size - ETH_FCS_SIZE) -#define IP_PKT_SIZE (PKT_SIZE - ETH_HDR_SIZE) -#define UDP_PKT_SIZE (IP_PKT_SIZE - sizeof(struct iphdr)) -#define UDP_PKT_DATA_SIZE (UDP_PKT_SIZE - \ - (sizeof(struct udphdr) + PKTGEN_HDR_SIZE)) - -static u8 pkt_data[XSK_UMEM__DEFAULT_FRAME_SIZE]; - -static void gen_eth_hdr_data(void) -{ - struct pktgen_hdr *pktgen_hdr; - struct udphdr *udp_hdr; - struct iphdr *ip_hdr; - - if (opt_vlan_tag) { - struct vlan_ethhdr *veth_hdr = (struct vlan_ethhdr *)pkt_data; - u16 vlan_tci = 0; - - udp_hdr = (struct udphdr *)(pkt_data + - sizeof(struct vlan_ethhdr) + - sizeof(struct iphdr)); - ip_hdr = (struct iphdr *)(pkt_data + - sizeof(struct vlan_ethhdr)); - pktgen_hdr = (struct pktgen_hdr *)(pkt_data + - sizeof(struct vlan_ethhdr) + - sizeof(struct iphdr) + - sizeof(struct udphdr)); - /* ethernet & VLAN header */ - memcpy(veth_hdr->h_dest, &opt_txdmac, ETH_ALEN); - memcpy(veth_hdr->h_source, &opt_txsmac, ETH_ALEN); - veth_hdr->h_vlan_proto = htons(ETH_P_8021Q); - vlan_tci = opt_pkt_vlan_id & VLAN_VID_MASK; - vlan_tci |= (opt_pkt_vlan_pri << VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK; - veth_hdr->h_vlan_TCI = htons(vlan_tci); - veth_hdr->h_vlan_encapsulated_proto = htons(ETH_P_IP); - } else { - struct ethhdr *eth_hdr = (struct ethhdr *)pkt_data; - - udp_hdr = (struct udphdr *)(pkt_data + - sizeof(struct ethhdr) + - sizeof(struct iphdr)); - ip_hdr = (struct iphdr *)(pkt_data + - sizeof(struct ethhdr)); - pktgen_hdr = (struct pktgen_hdr *)(pkt_data + - sizeof(struct ethhdr) + - sizeof(struct iphdr) + - sizeof(struct udphdr)); - /* ethernet header */ - memcpy(eth_hdr->h_dest, &opt_txdmac, ETH_ALEN); - memcpy(eth_hdr->h_source, &opt_txsmac, ETH_ALEN); - eth_hdr->h_proto = htons(ETH_P_IP); - } - - - /* IP header */ - ip_hdr->version = IPVERSION; - ip_hdr->ihl = 0x5; /* 20 byte header */ - ip_hdr->tos = 0x0; - ip_hdr->tot_len = htons(IP_PKT_SIZE); - ip_hdr->id = 0; - ip_hdr->frag_off = 0; - ip_hdr->ttl = IPDEFTTL; - ip_hdr->protocol = IPPROTO_UDP; - ip_hdr->saddr = htonl(0x0a0a0a10); - ip_hdr->daddr = htonl(0x0a0a0a20); - - /* IP header checksum */ - ip_hdr->check = 0; - ip_hdr->check = ip_fast_csum((const void *)ip_hdr, ip_hdr->ihl); - - /* UDP header */ - udp_hdr->source = htons(0x1000); - udp_hdr->dest = htons(0x1000); - udp_hdr->len = htons(UDP_PKT_SIZE); - - if (opt_tstamp) - pktgen_hdr->pgh_magic = htonl(PKTGEN_MAGIC); - - /* UDP data */ - memset32_htonl(pkt_data + PKT_HDR_SIZE, opt_pkt_fill_pattern, - UDP_PKT_DATA_SIZE); - - /* UDP header checksum */ - udp_hdr->check = 0; - udp_hdr->check = udp_csum(ip_hdr->saddr, ip_hdr->daddr, UDP_PKT_SIZE, - IPPROTO_UDP, (u16 *)udp_hdr); -} - -static void gen_eth_frame(struct xsk_umem_info *umem, u64 addr) -{ - memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, - PKT_SIZE); -} - -static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size) -{ - struct xsk_umem_info *umem; - struct xsk_umem_config cfg = { - /* We recommend that you set the fill ring size >= HW RX ring size + - * AF_XDP RX ring size. Make sure you fill up the fill ring - * with buffers at regular intervals, and you will with this setting - * avoid allocation failures in the driver. These are usually quite - * expensive since drivers have not been written to assume that - * allocation failures are common. For regular sockets, kernel - * allocated memory is used that only runs out in OOM situations - * that should be rare. - */ - .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, - .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, - .frame_size = opt_xsk_frame_size, - .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, - .flags = opt_umem_flags - }; - int ret; - - umem = calloc(1, sizeof(*umem)); - if (!umem) - exit_with_error(errno); - - ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, - &cfg); - if (ret) - exit_with_error(-ret); - - umem->buffer = buffer; - return umem; -} - -static void xsk_populate_fill_ring(struct xsk_umem_info *umem) -{ - int ret, i; - u32 idx; - - ret = xsk_ring_prod__reserve(&umem->fq, - XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, &idx); - if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS * 2) - exit_with_error(-ret); - for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS * 2; i++) - *xsk_ring_prod__fill_addr(&umem->fq, idx++) = - i * opt_xsk_frame_size; - xsk_ring_prod__submit(&umem->fq, XSK_RING_PROD__DEFAULT_NUM_DESCS * 2); -} - -static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem, - bool rx, bool tx) -{ - struct xsk_socket_config cfg; - struct xsk_socket_info *xsk; - struct xsk_ring_cons *rxr; - struct xsk_ring_prod *txr; - int ret; - - xsk = calloc(1, sizeof(*xsk)); - if (!xsk) - exit_with_error(errno); - - xsk->umem = umem; - cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS; - cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; - if (opt_num_xsks > 1 || opt_reduced_cap) - cfg.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD; - else - cfg.libbpf_flags = 0; - cfg.xdp_flags = opt_xdp_flags; - cfg.bind_flags = opt_xdp_bind_flags; - - rxr = rx ? &xsk->rx : NULL; - txr = tx ? &xsk->tx : NULL; - ret = xsk_socket__create(&xsk->xsk, opt_if, opt_queue, umem->umem, - rxr, txr, &cfg); - if (ret) - exit_with_error(-ret); - - ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags); - if (ret) - exit_with_error(-ret); - - xsk->app_stats.rx_empty_polls = 0; - xsk->app_stats.fill_fail_polls = 0; - xsk->app_stats.copy_tx_sendtos = 0; - xsk->app_stats.tx_wakeup_sendtos = 0; - xsk->app_stats.opt_polls = 0; - xsk->app_stats.prev_rx_empty_polls = 0; - xsk->app_stats.prev_fill_fail_polls = 0; - xsk->app_stats.prev_copy_tx_sendtos = 0; - xsk->app_stats.prev_tx_wakeup_sendtos = 0; - xsk->app_stats.prev_opt_polls = 0; - - return xsk; -} - -static struct option long_options[] = { - {"rxdrop", no_argument, 0, 'r'}, - {"txonly", no_argument, 0, 't'}, - {"l2fwd", no_argument, 0, 'l'}, - {"interface", required_argument, 0, 'i'}, - {"queue", required_argument, 0, 'q'}, - {"poll", no_argument, 0, 'p'}, - {"xdp-skb", no_argument, 0, 'S'}, - {"xdp-native", no_argument, 0, 'N'}, - {"interval", required_argument, 0, 'n'}, - {"retries", required_argument, 0, 'O'}, - {"zero-copy", no_argument, 0, 'z'}, - {"copy", no_argument, 0, 'c'}, - {"frame-size", required_argument, 0, 'f'}, - {"no-need-wakeup", no_argument, 0, 'm'}, - {"unaligned", no_argument, 0, 'u'}, - {"shared-umem", no_argument, 0, 'M'}, - {"force", no_argument, 0, 'F'}, - {"duration", required_argument, 0, 'd'}, - {"clock", required_argument, 0, 'w'}, - {"batch-size", required_argument, 0, 'b'}, - {"tx-pkt-count", required_argument, 0, 'C'}, - {"tx-pkt-size", required_argument, 0, 's'}, - {"tx-pkt-pattern", required_argument, 0, 'P'}, - {"tx-vlan", no_argument, 0, 'V'}, - {"tx-vlan-id", required_argument, 0, 'J'}, - {"tx-vlan-pri", required_argument, 0, 'K'}, - {"tx-dmac", required_argument, 0, 'G'}, - {"tx-smac", required_argument, 0, 'H'}, - {"tx-cycle", required_argument, 0, 'T'}, - {"tstamp", no_argument, 0, 'y'}, - {"policy", required_argument, 0, 'W'}, - {"schpri", required_argument, 0, 'U'}, - {"extra-stats", no_argument, 0, 'x'}, - {"quiet", no_argument, 0, 'Q'}, - {"app-stats", no_argument, 0, 'a'}, - {"irq-string", no_argument, 0, 'I'}, - {"busy-poll", no_argument, 0, 'B'}, - {"reduce-cap", no_argument, 0, 'R'}, - {0, 0, 0, 0} -}; - -static void usage(const char *prog) -{ - const char *str = - " Usage: %s [OPTIONS]\n" - " Options:\n" - " -r, --rxdrop Discard all incoming packets (default)\n" - " -t, --txonly Only send packets\n" - " -l, --l2fwd MAC swap L2 forwarding\n" - " -i, --interface=n Run on interface n\n" - " -q, --queue=n Use queue n (default 0)\n" - " -p, --poll Use poll syscall\n" - " -S, --xdp-skb=n Use XDP skb-mod\n" - " -N, --xdp-native=n Enforce XDP native mode\n" - " -n, --interval=n Specify statistics update interval (default 1 sec).\n" - " -O, --retries=n Specify time-out retries (1s interval) attempt (default 3).\n" - " -z, --zero-copy Force zero-copy mode.\n" - " -c, --copy Force copy mode.\n" - " -m, --no-need-wakeup Turn off use of driver need wakeup flag.\n" - " -f, --frame-size=n Set the frame size (must be a power of two in aligned mode, default is %d).\n" - " -u, --unaligned Enable unaligned chunk placement\n" - " -M, --shared-umem Enable XDP_SHARED_UMEM (cannot be used with -R)\n" - " -F, --force Force loading the XDP prog\n" - " -d, --duration=n Duration in secs to run command.\n" - " Default: forever.\n" - " -w, --clock=CLOCK Clock NAME (default MONOTONIC).\n" - " -b, --batch-size=n Batch size for sending or receiving\n" - " packets. Default: %d\n" - " -C, --tx-pkt-count=n Number of packets to send.\n" - " Default: Continuous packets.\n" - " -s, --tx-pkt-size=n Transmit packet size.\n" - " (Default: %d bytes)\n" - " Min size: %d, Max size %d.\n" - " -P, --tx-pkt-pattern=nPacket fill pattern. Default: 0x%x\n" - " -V, --tx-vlan Send VLAN tagged packets (For -t|--txonly)\n" - " -J, --tx-vlan-id=n Tx VLAN ID [1-4095]. Default: %d (For -V|--tx-vlan)\n" - " -K, --tx-vlan-pri=n Tx VLAN Priority [0-7]. Default: %d (For -V|--tx-vlan)\n" - " -G, --tx-dmac=<MAC> Dest MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n" - " -H, --tx-smac=<MAC> Src MAC addr of TX frame in aa:bb:cc:dd:ee:ff format (For -V|--tx-vlan)\n" - " -T, --tx-cycle=n Tx cycle time in micro-seconds (For -t|--txonly).\n" - " -y, --tstamp Add time-stamp to packet (For -t|--txonly).\n" - " -W, --policy=POLICY Schedule policy. Default: SCHED_OTHER\n" - " -U, --schpri=n Schedule priority. Default: %d\n" - " -x, --extra-stats Display extra statistics.\n" - " -Q, --quiet Do not display any stats.\n" - " -a, --app-stats Display application (syscall) statistics.\n" - " -I, --irq-string Display driver interrupt statistics for interface associated with irq-string.\n" - " -B, --busy-poll Busy poll.\n" - " -R, --reduce-cap Use reduced capabilities (cannot be used with -M)\n" - "\n"; - fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE, - opt_batch_size, MIN_PKT_SIZE, MIN_PKT_SIZE, - XSK_UMEM__DEFAULT_FRAME_SIZE, opt_pkt_fill_pattern, - VLAN_VID__DEFAULT, VLAN_PRI__DEFAULT, - SCHED_PRI__DEFAULT); - - exit(EXIT_FAILURE); -} - -static void parse_command_line(int argc, char **argv) -{ - int option_index, c; - - opterr = 0; - - for (;;) { - c = getopt_long(argc, argv, - "Frtli:q:pSNn:w:O:czf:muMd:b:C:s:P:VJ:K:G:H:T:yW:U:xQaI:BR", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'r': - opt_bench = BENCH_RXDROP; - break; - case 't': - opt_bench = BENCH_TXONLY; - break; - case 'l': - opt_bench = BENCH_L2FWD; - break; - case 'i': - opt_if = optarg; - break; - case 'q': - opt_queue = atoi(optarg); - break; - case 'p': - opt_poll = 1; - break; - case 'S': - opt_xdp_flags |= XDP_FLAGS_SKB_MODE; - opt_xdp_bind_flags |= XDP_COPY; - break; - case 'N': - /* default, set below */ - break; - case 'n': - opt_interval = atoi(optarg); - break; - case 'w': - if (get_clockid(&opt_clock, optarg)) { - fprintf(stderr, - "ERROR: Invalid clock %s. Default to CLOCK_MONOTONIC.\n", - optarg); - opt_clock = CLOCK_MONOTONIC; - } - break; - case 'O': - opt_retries = atoi(optarg); - break; - case 'z': - opt_xdp_bind_flags |= XDP_ZEROCOPY; - break; - case 'c': - opt_xdp_bind_flags |= XDP_COPY; - break; - case 'u': - opt_umem_flags |= XDP_UMEM_UNALIGNED_CHUNK_FLAG; - opt_unaligned_chunks = 1; - opt_mmap_flags = MAP_HUGETLB; - break; - case 'F': - opt_xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; - break; - case 'f': - opt_xsk_frame_size = atoi(optarg); - break; - case 'm': - opt_need_wakeup = false; - opt_xdp_bind_flags &= ~XDP_USE_NEED_WAKEUP; - break; - case 'M': - opt_num_xsks = MAX_SOCKS; - break; - case 'd': - opt_duration = atoi(optarg); - opt_duration *= 1000000000; - break; - case 'b': - opt_batch_size = atoi(optarg); - break; - case 'C': - opt_pkt_count = atoi(optarg); - break; - case 's': - opt_pkt_size = atoi(optarg); - if (opt_pkt_size > (XSK_UMEM__DEFAULT_FRAME_SIZE) || - opt_pkt_size < MIN_PKT_SIZE) { - fprintf(stderr, - "ERROR: Invalid frame size %d\n", - opt_pkt_size); - usage(basename(argv[0])); - } - break; - case 'P': - opt_pkt_fill_pattern = strtol(optarg, NULL, 16); - break; - case 'V': - opt_vlan_tag = true; - break; - case 'J': - opt_pkt_vlan_id = atoi(optarg); - break; - case 'K': - opt_pkt_vlan_pri = atoi(optarg); - break; - case 'G': - if (!ether_aton_r(optarg, - (struct ether_addr *)&opt_txdmac)) { - fprintf(stderr, "Invalid dmac address:%s\n", - optarg); - usage(basename(argv[0])); - } - break; - case 'H': - if (!ether_aton_r(optarg, - (struct ether_addr *)&opt_txsmac)) { - fprintf(stderr, "Invalid smac address:%s\n", - optarg); - usage(basename(argv[0])); - } - break; - case 'T': - opt_tx_cycle_ns = atoi(optarg); - opt_tx_cycle_ns *= NSEC_PER_USEC; - break; - case 'y': - opt_tstamp = 1; - break; - case 'W': - if (get_schpolicy(&opt_schpolicy, optarg)) { - fprintf(stderr, - "ERROR: Invalid policy %s. Default to SCHED_OTHER.\n", - optarg); - opt_schpolicy = SCHED_OTHER; - } - break; - case 'U': - opt_schprio = atoi(optarg); - break; - case 'x': - opt_extra_stats = 1; - break; - case 'Q': - opt_quiet = 1; - break; - case 'a': - opt_app_stats = 1; - break; - case 'I': - opt_irq_str = optarg; - if (get_interrupt_number()) - irqs_at_init = get_irqs(); - if (irqs_at_init < 0) { - fprintf(stderr, "ERROR: Failed to get irqs for %s\n", opt_irq_str); - usage(basename(argv[0])); - } - break; - case 'B': - opt_busy_poll = 1; - break; - case 'R': - opt_reduced_cap = true; - break; - default: - usage(basename(argv[0])); - } - } - - if (!(opt_xdp_flags & XDP_FLAGS_SKB_MODE)) - opt_xdp_flags |= XDP_FLAGS_DRV_MODE; - - opt_ifindex = if_nametoindex(opt_if); - if (!opt_ifindex) { - fprintf(stderr, "ERROR: interface \"%s\" does not exist\n", - opt_if); - usage(basename(argv[0])); - } - - if ((opt_xsk_frame_size & (opt_xsk_frame_size - 1)) && - !opt_unaligned_chunks) { - fprintf(stderr, "--frame-size=%d is not a power of two\n", - opt_xsk_frame_size); - usage(basename(argv[0])); - } - - if (opt_reduced_cap && opt_num_xsks > 1) { - fprintf(stderr, "ERROR: -M and -R cannot be used together\n"); - usage(basename(argv[0])); - } -} - -static void kick_tx(struct xsk_socket_info *xsk) -{ - int ret; - - ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); - if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN || - errno == EBUSY || errno == ENETDOWN) - return; - exit_with_error(errno); -} - -static inline void complete_tx_l2fwd(struct xsk_socket_info *xsk) -{ - struct xsk_umem_info *umem = xsk->umem; - u32 idx_cq = 0, idx_fq = 0; - unsigned int rcvd; - size_t ndescs; - - if (!xsk->outstanding_tx) - return; - - /* In copy mode, Tx is driven by a syscall so we need to use e.g. sendto() to - * really send the packets. In zero-copy mode we do not have to do this, since Tx - * is driven by the NAPI loop. So as an optimization, we do not have to call - * sendto() all the time in zero-copy mode for l2fwd. - */ - if (opt_xdp_bind_flags & XDP_COPY) { - xsk->app_stats.copy_tx_sendtos++; - kick_tx(xsk); - } - - ndescs = (xsk->outstanding_tx > opt_batch_size) ? opt_batch_size : - xsk->outstanding_tx; - - /* re-add completed Tx buffers */ - rcvd = xsk_ring_cons__peek(&umem->cq, ndescs, &idx_cq); - if (rcvd > 0) { - unsigned int i; - int ret; - - ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq); - while (ret != rcvd) { - if (ret < 0) - exit_with_error(-ret); - if (opt_busy_poll || xsk_ring_prod__needs_wakeup(&umem->fq)) { - xsk->app_stats.fill_fail_polls++; - recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, - NULL); - } - ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq); - } - - for (i = 0; i < rcvd; i++) - *xsk_ring_prod__fill_addr(&umem->fq, idx_fq++) = - *xsk_ring_cons__comp_addr(&umem->cq, idx_cq++); - - xsk_ring_prod__submit(&xsk->umem->fq, rcvd); - xsk_ring_cons__release(&xsk->umem->cq, rcvd); - xsk->outstanding_tx -= rcvd; - } -} - -static inline void complete_tx_only(struct xsk_socket_info *xsk, - int batch_size) -{ - unsigned int rcvd; - u32 idx; - - if (!xsk->outstanding_tx) - return; - - if (!opt_need_wakeup || xsk_ring_prod__needs_wakeup(&xsk->tx)) { - xsk->app_stats.tx_wakeup_sendtos++; - kick_tx(xsk); - } - - rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx); - if (rcvd > 0) { - xsk_ring_cons__release(&xsk->umem->cq, rcvd); - xsk->outstanding_tx -= rcvd; - } -} - -static void rx_drop(struct xsk_socket_info *xsk) -{ - unsigned int rcvd, i; - u32 idx_rx = 0, idx_fq = 0; - int ret; - - rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx); - if (!rcvd) { - if (opt_busy_poll || xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) { - xsk->app_stats.rx_empty_polls++; - recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL); - } - return; - } - - ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq); - while (ret != rcvd) { - if (ret < 0) - exit_with_error(-ret); - if (opt_busy_poll || xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) { - xsk->app_stats.fill_fail_polls++; - recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL); - } - ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq); - } - - for (i = 0; i < rcvd; i++) { - u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr; - u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len; - u64 orig = xsk_umem__extract_addr(addr); - - addr = xsk_umem__add_offset_to_addr(addr); - char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); - - hex_dump(pkt, len, addr); - *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = orig; - } - - xsk_ring_prod__submit(&xsk->umem->fq, rcvd); - xsk_ring_cons__release(&xsk->rx, rcvd); - xsk->ring_stats.rx_npkts += rcvd; -} - -static void rx_drop_all(void) -{ - struct pollfd fds[MAX_SOCKS] = {}; - int i, ret; - - for (i = 0; i < num_socks; i++) { - fds[i].fd = xsk_socket__fd(xsks[i]->xsk); - fds[i].events = POLLIN; - } - - for (;;) { - if (opt_poll) { - for (i = 0; i < num_socks; i++) - xsks[i]->app_stats.opt_polls++; - ret = poll(fds, num_socks, opt_timeout); - if (ret <= 0) - continue; - } - - for (i = 0; i < num_socks; i++) - rx_drop(xsks[i]); - - if (benchmark_done) - break; - } -} - -static int tx_only(struct xsk_socket_info *xsk, u32 *frame_nb, - int batch_size, unsigned long tx_ns) -{ - u32 idx, tv_sec, tv_usec; - unsigned int i; - - while (xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx) < - batch_size) { - complete_tx_only(xsk, batch_size); - if (benchmark_done) - return 0; - } - - if (opt_tstamp) { - tv_sec = (u32)(tx_ns / NSEC_PER_SEC); - tv_usec = (u32)((tx_ns % NSEC_PER_SEC) / 1000); - } - - for (i = 0; i < batch_size; i++) { - struct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, - idx + i); - tx_desc->addr = (*frame_nb + i) * opt_xsk_frame_size; - tx_desc->len = PKT_SIZE; - - if (opt_tstamp) { - struct pktgen_hdr *pktgen_hdr; - u64 addr = tx_desc->addr; - char *pkt; - - pkt = xsk_umem__get_data(xsk->umem->buffer, addr); - pktgen_hdr = (struct pktgen_hdr *)(pkt + PKTGEN_HDR_OFFSET); - - pktgen_hdr->seq_num = htonl(sequence++); - pktgen_hdr->tv_sec = htonl(tv_sec); - pktgen_hdr->tv_usec = htonl(tv_usec); - - hex_dump(pkt, PKT_SIZE, addr); - } - } - - xsk_ring_prod__submit(&xsk->tx, batch_size); - xsk->ring_stats.tx_npkts += batch_size; - xsk->outstanding_tx += batch_size; - *frame_nb += batch_size; - *frame_nb %= NUM_FRAMES; - complete_tx_only(xsk, batch_size); - - return batch_size; -} - -static inline int get_batch_size(int pkt_cnt) -{ - if (!opt_pkt_count) - return opt_batch_size; - - if (pkt_cnt + opt_batch_size <= opt_pkt_count) - return opt_batch_size; - - return opt_pkt_count - pkt_cnt; -} - -static void complete_tx_only_all(void) -{ - bool pending; - int i; - - do { - pending = false; - for (i = 0; i < num_socks; i++) { - if (xsks[i]->outstanding_tx) { - complete_tx_only(xsks[i], opt_batch_size); - pending = !!xsks[i]->outstanding_tx; - } - } - sleep(1); - } while (pending && opt_retries-- > 0); -} - -static void tx_only_all(void) -{ - struct pollfd fds[MAX_SOCKS] = {}; - u32 frame_nb[MAX_SOCKS] = {}; - unsigned long next_tx_ns = 0; - int pkt_cnt = 0; - int i, ret; - - if (opt_poll && opt_tx_cycle_ns) { - fprintf(stderr, - "Error: --poll and --tx-cycles are both set\n"); - return; - } - - for (i = 0; i < num_socks; i++) { - fds[0].fd = xsk_socket__fd(xsks[i]->xsk); - fds[0].events = POLLOUT; - } - - if (opt_tx_cycle_ns) { - /* Align Tx time to micro-second boundary */ - next_tx_ns = (get_nsecs() / NSEC_PER_USEC + 1) * - NSEC_PER_USEC; - next_tx_ns += opt_tx_cycle_ns; - - /* Initialize periodic Tx scheduling variance */ - tx_cycle_diff_min = 1000000000; - tx_cycle_diff_max = 0; - tx_cycle_diff_ave = 0.0; - } - - while ((opt_pkt_count && pkt_cnt < opt_pkt_count) || !opt_pkt_count) { - int batch_size = get_batch_size(pkt_cnt); - unsigned long tx_ns = 0; - struct timespec next; - int tx_cnt = 0; - long diff; - int err; - - if (opt_poll) { - for (i = 0; i < num_socks; i++) - xsks[i]->app_stats.opt_polls++; - ret = poll(fds, num_socks, opt_timeout); - if (ret <= 0) - continue; - - if (!(fds[0].revents & POLLOUT)) - continue; - } - - if (opt_tx_cycle_ns) { - next.tv_sec = next_tx_ns / NSEC_PER_SEC; - next.tv_nsec = next_tx_ns % NSEC_PER_SEC; - err = clock_nanosleep(opt_clock, TIMER_ABSTIME, &next, NULL); - if (err) { - if (err != EINTR) - fprintf(stderr, - "clock_nanosleep failed. Err:%d errno:%d\n", - err, errno); - break; - } - - /* Measure periodic Tx scheduling variance */ - tx_ns = get_nsecs(); - diff = tx_ns - next_tx_ns; - if (diff < tx_cycle_diff_min) - tx_cycle_diff_min = diff; - - if (diff > tx_cycle_diff_max) - tx_cycle_diff_max = diff; - - tx_cycle_diff_ave += (double)diff; - tx_cycle_cnt++; - } else if (opt_tstamp) { - tx_ns = get_nsecs(); - } - - for (i = 0; i < num_socks; i++) - tx_cnt += tx_only(xsks[i], &frame_nb[i], batch_size, tx_ns); - - pkt_cnt += tx_cnt; - - if (benchmark_done) - break; - - if (opt_tx_cycle_ns) - next_tx_ns += opt_tx_cycle_ns; - } - - if (opt_pkt_count) - complete_tx_only_all(); -} - -static void l2fwd(struct xsk_socket_info *xsk) -{ - unsigned int rcvd, i; - u32 idx_rx = 0, idx_tx = 0; - int ret; - - complete_tx_l2fwd(xsk); - - rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx); - if (!rcvd) { - if (opt_busy_poll || xsk_ring_prod__needs_wakeup(&xsk->umem->fq)) { - xsk->app_stats.rx_empty_polls++; - recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL); - } - return; - } - xsk->ring_stats.rx_npkts += rcvd; - - ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx); - while (ret != rcvd) { - if (ret < 0) - exit_with_error(-ret); - complete_tx_l2fwd(xsk); - if (opt_busy_poll || xsk_ring_prod__needs_wakeup(&xsk->tx)) { - xsk->app_stats.tx_wakeup_sendtos++; - kick_tx(xsk); - } - ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx); - } - - for (i = 0; i < rcvd; i++) { - u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr; - u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len; - u64 orig = addr; - - addr = xsk_umem__add_offset_to_addr(addr); - char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); - - swap_mac_addresses(pkt); - - hex_dump(pkt, len, addr); - xsk_ring_prod__tx_desc(&xsk->tx, idx_tx)->addr = orig; - xsk_ring_prod__tx_desc(&xsk->tx, idx_tx++)->len = len; - } - - xsk_ring_prod__submit(&xsk->tx, rcvd); - xsk_ring_cons__release(&xsk->rx, rcvd); - - xsk->ring_stats.tx_npkts += rcvd; - xsk->outstanding_tx += rcvd; -} - -static void l2fwd_all(void) -{ - struct pollfd fds[MAX_SOCKS] = {}; - int i, ret; - - for (;;) { - if (opt_poll) { - for (i = 0; i < num_socks; i++) { - fds[i].fd = xsk_socket__fd(xsks[i]->xsk); - fds[i].events = POLLOUT | POLLIN; - xsks[i]->app_stats.opt_polls++; - } - ret = poll(fds, num_socks, opt_timeout); - if (ret <= 0) - continue; - } - - for (i = 0; i < num_socks; i++) - l2fwd(xsks[i]); - - if (benchmark_done) - break; - } -} - -static void load_xdp_program(char **argv, struct bpf_object **obj) -{ - struct bpf_prog_load_attr prog_load_attr = { - .prog_type = BPF_PROG_TYPE_XDP, - }; - char xdp_filename[256]; - int prog_fd; - - snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv[0]); - prog_load_attr.file = xdp_filename; - - if (bpf_prog_load_xattr(&prog_load_attr, obj, &prog_fd)) - exit(EXIT_FAILURE); - if (prog_fd < 0) { - fprintf(stderr, "ERROR: no program found: %s\n", - strerror(prog_fd)); - exit(EXIT_FAILURE); - } - - if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd, opt_xdp_flags) < 0) { - fprintf(stderr, "ERROR: link set xdp fd failed\n"); - exit(EXIT_FAILURE); - } -} - -static void enter_xsks_into_map(struct bpf_object *obj) -{ - struct bpf_map *map; - int i, xsks_map; - - map = bpf_object__find_map_by_name(obj, "xsks_map"); - xsks_map = bpf_map__fd(map); - if (xsks_map < 0) { - fprintf(stderr, "ERROR: no xsks map found: %s\n", - strerror(xsks_map)); - exit(EXIT_FAILURE); - } - - for (i = 0; i < num_socks; i++) { - int fd = xsk_socket__fd(xsks[i]->xsk); - int key, ret; - - key = i; - ret = bpf_map_update_elem(xsks_map, &key, &fd, 0); - if (ret) { - fprintf(stderr, "ERROR: bpf_map_update_elem %d\n", i); - exit(EXIT_FAILURE); - } - } -} - -static void apply_setsockopt(struct xsk_socket_info *xsk) -{ - int sock_opt; - - if (!opt_busy_poll) - return; - - sock_opt = 1; - if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_PREFER_BUSY_POLL, - (void *)&sock_opt, sizeof(sock_opt)) < 0) - exit_with_error(errno); - - sock_opt = 20; - if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_BUSY_POLL, - (void *)&sock_opt, sizeof(sock_opt)) < 0) - exit_with_error(errno); - - sock_opt = opt_batch_size; - if (setsockopt(xsk_socket__fd(xsk->xsk), SOL_SOCKET, SO_BUSY_POLL_BUDGET, - (void *)&sock_opt, sizeof(sock_opt)) < 0) - exit_with_error(errno); -} - -static int recv_xsks_map_fd_from_ctrl_node(int sock, int *_fd) -{ - char cms[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsg; - struct msghdr msg; - struct iovec iov; - int value; - int len; - - iov.iov_base = &value; - iov.iov_len = sizeof(int); - - msg.msg_name = 0; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = (caddr_t)cms; - msg.msg_controllen = sizeof(cms); - - len = recvmsg(sock, &msg, 0); - - if (len < 0) { - fprintf(stderr, "Recvmsg failed length incorrect.\n"); - return -EINVAL; - } - - if (len == 0) { - fprintf(stderr, "Recvmsg failed no data\n"); - return -EINVAL; - } - - cmsg = CMSG_FIRSTHDR(&msg); - *_fd = *(int *)CMSG_DATA(cmsg); - - return 0; -} - -static int -recv_xsks_map_fd(int *xsks_map_fd) -{ - struct sockaddr_un server; - int err; - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - fprintf(stderr, "Error opening socket stream: %s", strerror(errno)); - return errno; - } - - server.sun_family = AF_UNIX; - strcpy(server.sun_path, SOCKET_NAME); - - if (connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) { - close(sock); - fprintf(stderr, "Error connecting stream socket: %s", strerror(errno)); - return errno; - } - - err = recv_xsks_map_fd_from_ctrl_node(sock, xsks_map_fd); - if (err) { - fprintf(stderr, "Error %d receiving fd\n", err); - return err; - } - return 0; -} - -int main(int argc, char **argv) -{ - struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; - struct __user_cap_data_struct data[2] = { { 0 } }; - struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; - bool rx = false, tx = false; - struct sched_param schparam; - struct xsk_umem_info *umem; - struct bpf_object *obj; - int xsks_map_fd = 0; - pthread_t pt; - int i, ret; - void *bufs; - - parse_command_line(argc, argv); - - if (opt_reduced_cap) { - if (capget(&hdr, data) < 0) - fprintf(stderr, "Error getting capabilities\n"); - - data->effective &= CAP_TO_MASK(CAP_NET_RAW); - data->permitted &= CAP_TO_MASK(CAP_NET_RAW); - - if (capset(&hdr, data) < 0) - fprintf(stderr, "Setting capabilities failed\n"); - - if (capget(&hdr, data) < 0) { - fprintf(stderr, "Error getting capabilities\n"); - } else { - fprintf(stderr, "Capabilities EFF %x Caps INH %x Caps Per %x\n", - data[0].effective, data[0].inheritable, data[0].permitted); - fprintf(stderr, "Capabilities EFF %x Caps INH %x Caps Per %x\n", - data[1].effective, data[1].inheritable, data[1].permitted); - } - } else { - if (setrlimit(RLIMIT_MEMLOCK, &r)) { - fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - - if (opt_num_xsks > 1) - load_xdp_program(argv, &obj); - } - - /* Reserve memory for the umem. Use hugepages if unaligned chunk mode */ - bufs = mmap(NULL, NUM_FRAMES * opt_xsk_frame_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | opt_mmap_flags, -1, 0); - if (bufs == MAP_FAILED) { - printf("ERROR: mmap failed\n"); - exit(EXIT_FAILURE); - } - - /* Create sockets... */ - umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size); - if (opt_bench == BENCH_RXDROP || opt_bench == BENCH_L2FWD) { - rx = true; - xsk_populate_fill_ring(umem); - } - if (opt_bench == BENCH_L2FWD || opt_bench == BENCH_TXONLY) - tx = true; - for (i = 0; i < opt_num_xsks; i++) - xsks[num_socks++] = xsk_configure_socket(umem, rx, tx); - - for (i = 0; i < opt_num_xsks; i++) - apply_setsockopt(xsks[i]); - - if (opt_bench == BENCH_TXONLY) { - if (opt_tstamp && opt_pkt_size < PKTGEN_SIZE_MIN) - opt_pkt_size = PKTGEN_SIZE_MIN; - - gen_eth_hdr_data(); - - for (i = 0; i < NUM_FRAMES; i++) - gen_eth_frame(umem, i * opt_xsk_frame_size); - } - - if (opt_num_xsks > 1 && opt_bench != BENCH_TXONLY) - enter_xsks_into_map(obj); - - if (opt_reduced_cap) { - ret = recv_xsks_map_fd(&xsks_map_fd); - if (ret) { - fprintf(stderr, "Error %d receiving xsks_map_fd\n", ret); - exit_with_error(ret); - } - if (xsks[0]->xsk) { - ret = xsk_socket__update_xskmap(xsks[0]->xsk, xsks_map_fd); - if (ret) { - fprintf(stderr, "Update of BPF map failed(%d)\n", ret); - exit_with_error(ret); - } - } - } - - signal(SIGINT, int_exit); - signal(SIGTERM, int_exit); - signal(SIGABRT, int_exit); - - setlocale(LC_ALL, ""); - - if (!opt_quiet) { - ret = pthread_create(&pt, NULL, poller, NULL); - if (ret) - exit_with_error(ret); - } - - prev_time = get_nsecs(); - start_time = prev_time; - - /* Configure sched priority for better wake-up accuracy */ - memset(&schparam, 0, sizeof(schparam)); - schparam.sched_priority = opt_schprio; - ret = sched_setscheduler(0, opt_schpolicy, &schparam); - if (ret) { - fprintf(stderr, "Error(%d) in setting priority(%d): %s\n", - errno, opt_schprio, strerror(errno)); - goto out; - } - - if (opt_bench == BENCH_RXDROP) - rx_drop_all(); - else if (opt_bench == BENCH_TXONLY) - tx_only_all(); - else - l2fwd_all(); - -out: - benchmark_done = true; - - if (!opt_quiet) - pthread_join(pt, NULL); - - xdpsock_cleanup(); - - munmap(bufs, NUM_FRAMES * opt_xsk_frame_size); - - return 0; -} diff --git a/samples/bpf/xsk_fwd.c b/samples/bpf/xsk_fwd.c deleted file mode 100644 index 52e7c4ffd228..000000000000 --- a/samples/bpf/xsk_fwd.c +++ /dev/null @@ -1,1088 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2020 Intel Corporation. */ - -#define _GNU_SOURCE -#include <poll.h> -#include <pthread.h> -#include <signal.h> -#include <sched.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> -#include <getopt.h> -#include <netinet/ether.h> -#include <net/if.h> - -#include <linux/bpf.h> -#include <linux/if_link.h> -#include <linux/if_xdp.h> - -#include <bpf/libbpf.h> -#include <bpf/xsk.h> -#include <bpf/bpf.h> - -/* libbpf APIs for AF_XDP are deprecated starting from v0.7 */ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -typedef __u64 u64; -typedef __u32 u32; -typedef __u16 u16; -typedef __u8 u8; - -/* This program illustrates the packet forwarding between multiple AF_XDP - * sockets in multi-threaded environment. All threads are sharing a common - * buffer pool, with each socket having its own private buffer cache. - * - * Example 1: Single thread handling two sockets. The packets received by socket - * A (interface IFA, queue QA) are forwarded to socket B (interface IFB, queue - * QB), while the packets received by socket B are forwarded to socket A. The - * thread is running on CPU core X: - * - * ./xsk_fwd -i IFA -q QA -i IFB -q QB -c X - * - * Example 2: Two threads, each handling two sockets. The thread running on CPU - * core X forwards all the packets received by socket A to socket B, and all the - * packets received by socket B to socket A. The thread running on CPU core Y is - * performing the same packet forwarding between sockets C and D: - * - * ./xsk_fwd -i IFA -q QA -i IFB -q QB -i IFC -q QC -i IFD -q QD - * -c CX -c CY - */ - -/* - * Buffer pool and buffer cache - * - * For packet forwarding, the packet buffers are typically allocated from the - * pool for packet reception and freed back to the pool for further reuse once - * the packet transmission is completed. - * - * The buffer pool is shared between multiple threads. In order to minimize the - * access latency to the shared buffer pool, each thread creates one (or - * several) buffer caches, which, unlike the buffer pool, are private to the - * thread that creates them and therefore cannot be shared with other threads. - * The access to the shared pool is only needed either (A) when the cache gets - * empty due to repeated buffer allocations and it needs to be replenished from - * the pool, or (B) when the cache gets full due to repeated buffer free and it - * needs to be flushed back to the pull. - * - * In a packet forwarding system, a packet received on any input port can - * potentially be transmitted on any output port, depending on the forwarding - * configuration. For AF_XDP sockets, for this to work with zero-copy of the - * packet buffers when, it is required that the buffer pool memory fits into the - * UMEM area shared by all the sockets. - */ - -struct bpool_params { - u32 n_buffers; - u32 buffer_size; - int mmap_flags; - - u32 n_users_max; - u32 n_buffers_per_slab; -}; - -/* This buffer pool implementation organizes the buffers into equally sized - * slabs of *n_buffers_per_slab*. Initially, there are *n_slabs* slabs in the - * pool that are completely filled with buffer pointers (full slabs). - * - * Each buffer cache has a slab for buffer allocation and a slab for buffer - * free, with both of these slabs initially empty. When the cache's allocation - * slab goes empty, it is swapped with one of the available full slabs from the - * pool, if any is available. When the cache's free slab goes full, it is - * swapped for one of the empty slabs from the pool, which is guaranteed to - * succeed. - * - * Partially filled slabs never get traded between the cache and the pool - * (except when the cache itself is destroyed), which enables fast operation - * through pointer swapping. - */ -struct bpool { - struct bpool_params params; - pthread_mutex_t lock; - void *addr; - - u64 **slabs; - u64 **slabs_reserved; - u64 *buffers; - u64 *buffers_reserved; - - u64 n_slabs; - u64 n_slabs_reserved; - u64 n_buffers; - - u64 n_slabs_available; - u64 n_slabs_reserved_available; - - struct xsk_umem_config umem_cfg; - struct xsk_ring_prod umem_fq; - struct xsk_ring_cons umem_cq; - struct xsk_umem *umem; -}; - -static struct bpool * -bpool_init(struct bpool_params *params, - struct xsk_umem_config *umem_cfg) -{ - struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; - u64 n_slabs, n_slabs_reserved, n_buffers, n_buffers_reserved; - u64 slabs_size, slabs_reserved_size; - u64 buffers_size, buffers_reserved_size; - u64 total_size, i; - struct bpool *bp; - u8 *p; - int status; - - /* mmap prep. */ - if (setrlimit(RLIMIT_MEMLOCK, &r)) - return NULL; - - /* bpool internals dimensioning. */ - n_slabs = (params->n_buffers + params->n_buffers_per_slab - 1) / - params->n_buffers_per_slab; - n_slabs_reserved = params->n_users_max * 2; - n_buffers = n_slabs * params->n_buffers_per_slab; - n_buffers_reserved = n_slabs_reserved * params->n_buffers_per_slab; - - slabs_size = n_slabs * sizeof(u64 *); - slabs_reserved_size = n_slabs_reserved * sizeof(u64 *); - buffers_size = n_buffers * sizeof(u64); - buffers_reserved_size = n_buffers_reserved * sizeof(u64); - - total_size = sizeof(struct bpool) + - slabs_size + slabs_reserved_size + - buffers_size + buffers_reserved_size; - - /* bpool memory allocation. */ - p = calloc(total_size, sizeof(u8)); - if (!p) - return NULL; - - /* bpool memory initialization. */ - bp = (struct bpool *)p; - memcpy(&bp->params, params, sizeof(*params)); - bp->params.n_buffers = n_buffers; - - bp->slabs = (u64 **)&p[sizeof(struct bpool)]; - bp->slabs_reserved = (u64 **)&p[sizeof(struct bpool) + - slabs_size]; - bp->buffers = (u64 *)&p[sizeof(struct bpool) + - slabs_size + slabs_reserved_size]; - bp->buffers_reserved = (u64 *)&p[sizeof(struct bpool) + - slabs_size + slabs_reserved_size + buffers_size]; - - bp->n_slabs = n_slabs; - bp->n_slabs_reserved = n_slabs_reserved; - bp->n_buffers = n_buffers; - - for (i = 0; i < n_slabs; i++) - bp->slabs[i] = &bp->buffers[i * params->n_buffers_per_slab]; - bp->n_slabs_available = n_slabs; - - for (i = 0; i < n_slabs_reserved; i++) - bp->slabs_reserved[i] = &bp->buffers_reserved[i * - params->n_buffers_per_slab]; - bp->n_slabs_reserved_available = n_slabs_reserved; - - for (i = 0; i < n_buffers; i++) - bp->buffers[i] = i * params->buffer_size; - - /* lock. */ - status = pthread_mutex_init(&bp->lock, NULL); - if (status) { - free(p); - return NULL; - } - - /* mmap. */ - bp->addr = mmap(NULL, - n_buffers * params->buffer_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | params->mmap_flags, - -1, - 0); - if (bp->addr == MAP_FAILED) { - pthread_mutex_destroy(&bp->lock); - free(p); - return NULL; - } - - /* umem. */ - status = xsk_umem__create(&bp->umem, - bp->addr, - bp->params.n_buffers * bp->params.buffer_size, - &bp->umem_fq, - &bp->umem_cq, - umem_cfg); - if (status) { - munmap(bp->addr, bp->params.n_buffers * bp->params.buffer_size); - pthread_mutex_destroy(&bp->lock); - free(p); - return NULL; - } - memcpy(&bp->umem_cfg, umem_cfg, sizeof(*umem_cfg)); - - return bp; -} - -static void -bpool_free(struct bpool *bp) -{ - if (!bp) - return; - - xsk_umem__delete(bp->umem); - munmap(bp->addr, bp->params.n_buffers * bp->params.buffer_size); - pthread_mutex_destroy(&bp->lock); - free(bp); -} - -struct bcache { - struct bpool *bp; - - u64 *slab_cons; - u64 *slab_prod; - - u64 n_buffers_cons; - u64 n_buffers_prod; -}; - -static u32 -bcache_slab_size(struct bcache *bc) -{ - struct bpool *bp = bc->bp; - - return bp->params.n_buffers_per_slab; -} - -static struct bcache * -bcache_init(struct bpool *bp) -{ - struct bcache *bc; - - bc = calloc(1, sizeof(struct bcache)); - if (!bc) - return NULL; - - bc->bp = bp; - bc->n_buffers_cons = 0; - bc->n_buffers_prod = 0; - - pthread_mutex_lock(&bp->lock); - if (bp->n_slabs_reserved_available == 0) { - pthread_mutex_unlock(&bp->lock); - free(bc); - return NULL; - } - - bc->slab_cons = bp->slabs_reserved[bp->n_slabs_reserved_available - 1]; - bc->slab_prod = bp->slabs_reserved[bp->n_slabs_reserved_available - 2]; - bp->n_slabs_reserved_available -= 2; - pthread_mutex_unlock(&bp->lock); - - return bc; -} - -static void -bcache_free(struct bcache *bc) -{ - struct bpool *bp; - - if (!bc) - return; - - /* In order to keep this example simple, the case of freeing any - * existing buffers from the cache back to the pool is ignored. - */ - - bp = bc->bp; - pthread_mutex_lock(&bp->lock); - bp->slabs_reserved[bp->n_slabs_reserved_available] = bc->slab_prod; - bp->slabs_reserved[bp->n_slabs_reserved_available + 1] = bc->slab_cons; - bp->n_slabs_reserved_available += 2; - pthread_mutex_unlock(&bp->lock); - - free(bc); -} - -/* To work correctly, the implementation requires that the *n_buffers* input - * argument is never greater than the buffer pool's *n_buffers_per_slab*. This - * is typically the case, with one exception taking place when large number of - * buffers are allocated at init time (e.g. for the UMEM fill queue setup). - */ -static inline u32 -bcache_cons_check(struct bcache *bc, u32 n_buffers) -{ - struct bpool *bp = bc->bp; - u64 n_buffers_per_slab = bp->params.n_buffers_per_slab; - u64 n_buffers_cons = bc->n_buffers_cons; - u64 n_slabs_available; - u64 *slab_full; - - /* - * Consumer slab is not empty: Use what's available locally. Do not - * look for more buffers from the pool when the ask can only be - * partially satisfied. - */ - if (n_buffers_cons) - return (n_buffers_cons < n_buffers) ? - n_buffers_cons : - n_buffers; - - /* - * Consumer slab is empty: look to trade the current consumer slab - * (full) for a full slab from the pool, if any is available. - */ - pthread_mutex_lock(&bp->lock); - n_slabs_available = bp->n_slabs_available; - if (!n_slabs_available) { - pthread_mutex_unlock(&bp->lock); - return 0; - } - - n_slabs_available--; - slab_full = bp->slabs[n_slabs_available]; - bp->slabs[n_slabs_available] = bc->slab_cons; - bp->n_slabs_available = n_slabs_available; - pthread_mutex_unlock(&bp->lock); - - bc->slab_cons = slab_full; - bc->n_buffers_cons = n_buffers_per_slab; - return n_buffers; -} - -static inline u64 -bcache_cons(struct bcache *bc) -{ - u64 n_buffers_cons = bc->n_buffers_cons - 1; - u64 buffer; - - buffer = bc->slab_cons[n_buffers_cons]; - bc->n_buffers_cons = n_buffers_cons; - return buffer; -} - -static inline void -bcache_prod(struct bcache *bc, u64 buffer) -{ - struct bpool *bp = bc->bp; - u64 n_buffers_per_slab = bp->params.n_buffers_per_slab; - u64 n_buffers_prod = bc->n_buffers_prod; - u64 n_slabs_available; - u64 *slab_empty; - - /* - * Producer slab is not yet full: store the current buffer to it. - */ - if (n_buffers_prod < n_buffers_per_slab) { - bc->slab_prod[n_buffers_prod] = buffer; - bc->n_buffers_prod = n_buffers_prod + 1; - return; - } - - /* - * Producer slab is full: trade the cache's current producer slab - * (full) for an empty slab from the pool, then store the current - * buffer to the new producer slab. As one full slab exists in the - * cache, it is guaranteed that there is at least one empty slab - * available in the pool. - */ - pthread_mutex_lock(&bp->lock); - n_slabs_available = bp->n_slabs_available; - slab_empty = bp->slabs[n_slabs_available]; - bp->slabs[n_slabs_available] = bc->slab_prod; - bp->n_slabs_available = n_slabs_available + 1; - pthread_mutex_unlock(&bp->lock); - - slab_empty[0] = buffer; - bc->slab_prod = slab_empty; - bc->n_buffers_prod = 1; -} - -/* - * Port - * - * Each of the forwarding ports sits on top of an AF_XDP socket. In order for - * packet forwarding to happen with no packet buffer copy, all the sockets need - * to share the same UMEM area, which is used as the buffer pool memory. - */ -#ifndef MAX_BURST_RX -#define MAX_BURST_RX 64 -#endif - -#ifndef MAX_BURST_TX -#define MAX_BURST_TX 64 -#endif - -struct burst_rx { - u64 addr[MAX_BURST_RX]; - u32 len[MAX_BURST_RX]; -}; - -struct burst_tx { - u64 addr[MAX_BURST_TX]; - u32 len[MAX_BURST_TX]; - u32 n_pkts; -}; - -struct port_params { - struct xsk_socket_config xsk_cfg; - struct bpool *bp; - const char *iface; - u32 iface_queue; -}; - -struct port { - struct port_params params; - - struct bcache *bc; - - struct xsk_ring_cons rxq; - struct xsk_ring_prod txq; - struct xsk_ring_prod umem_fq; - struct xsk_ring_cons umem_cq; - struct xsk_socket *xsk; - int umem_fq_initialized; - - u64 n_pkts_rx; - u64 n_pkts_tx; -}; - -static void -port_free(struct port *p) -{ - if (!p) - return; - - /* To keep this example simple, the code to free the buffers from the - * socket's receive and transmit queues, as well as from the UMEM fill - * and completion queues, is not included. - */ - - if (p->xsk) - xsk_socket__delete(p->xsk); - - bcache_free(p->bc); - - free(p); -} - -static struct port * -port_init(struct port_params *params) -{ - struct port *p; - u32 umem_fq_size, pos = 0; - int status, i; - - /* Memory allocation and initialization. */ - p = calloc(sizeof(struct port), 1); - if (!p) - return NULL; - - memcpy(&p->params, params, sizeof(p->params)); - umem_fq_size = params->bp->umem_cfg.fill_size; - - /* bcache. */ - p->bc = bcache_init(params->bp); - if (!p->bc || - (bcache_slab_size(p->bc) < umem_fq_size) || - (bcache_cons_check(p->bc, umem_fq_size) < umem_fq_size)) { - port_free(p); - return NULL; - } - - /* xsk socket. */ - status = xsk_socket__create_shared(&p->xsk, - params->iface, - params->iface_queue, - params->bp->umem, - &p->rxq, - &p->txq, - &p->umem_fq, - &p->umem_cq, - ¶ms->xsk_cfg); - if (status) { - port_free(p); - return NULL; - } - - /* umem fq. */ - xsk_ring_prod__reserve(&p->umem_fq, umem_fq_size, &pos); - - for (i = 0; i < umem_fq_size; i++) - *xsk_ring_prod__fill_addr(&p->umem_fq, pos + i) = - bcache_cons(p->bc); - - xsk_ring_prod__submit(&p->umem_fq, umem_fq_size); - p->umem_fq_initialized = 1; - - return p; -} - -static inline u32 -port_rx_burst(struct port *p, struct burst_rx *b) -{ - u32 n_pkts, pos, i; - - /* Free buffers for FQ replenish. */ - n_pkts = ARRAY_SIZE(b->addr); - - n_pkts = bcache_cons_check(p->bc, n_pkts); - if (!n_pkts) - return 0; - - /* RXQ. */ - n_pkts = xsk_ring_cons__peek(&p->rxq, n_pkts, &pos); - if (!n_pkts) { - if (xsk_ring_prod__needs_wakeup(&p->umem_fq)) { - struct pollfd pollfd = { - .fd = xsk_socket__fd(p->xsk), - .events = POLLIN, - }; - - poll(&pollfd, 1, 0); - } - return 0; - } - - for (i = 0; i < n_pkts; i++) { - b->addr[i] = xsk_ring_cons__rx_desc(&p->rxq, pos + i)->addr; - b->len[i] = xsk_ring_cons__rx_desc(&p->rxq, pos + i)->len; - } - - xsk_ring_cons__release(&p->rxq, n_pkts); - p->n_pkts_rx += n_pkts; - - /* UMEM FQ. */ - for ( ; ; ) { - int status; - - status = xsk_ring_prod__reserve(&p->umem_fq, n_pkts, &pos); - if (status == n_pkts) - break; - - if (xsk_ring_prod__needs_wakeup(&p->umem_fq)) { - struct pollfd pollfd = { - .fd = xsk_socket__fd(p->xsk), - .events = POLLIN, - }; - - poll(&pollfd, 1, 0); - } - } - - for (i = 0; i < n_pkts; i++) - *xsk_ring_prod__fill_addr(&p->umem_fq, pos + i) = - bcache_cons(p->bc); - - xsk_ring_prod__submit(&p->umem_fq, n_pkts); - - return n_pkts; -} - -static inline void -port_tx_burst(struct port *p, struct burst_tx *b) -{ - u32 n_pkts, pos, i; - int status; - - /* UMEM CQ. */ - n_pkts = p->params.bp->umem_cfg.comp_size; - - n_pkts = xsk_ring_cons__peek(&p->umem_cq, n_pkts, &pos); - - for (i = 0; i < n_pkts; i++) { - u64 addr = *xsk_ring_cons__comp_addr(&p->umem_cq, pos + i); - - bcache_prod(p->bc, addr); - } - - xsk_ring_cons__release(&p->umem_cq, n_pkts); - - /* TXQ. */ - n_pkts = b->n_pkts; - - for ( ; ; ) { - status = xsk_ring_prod__reserve(&p->txq, n_pkts, &pos); - if (status == n_pkts) - break; - - if (xsk_ring_prod__needs_wakeup(&p->txq)) - sendto(xsk_socket__fd(p->xsk), NULL, 0, MSG_DONTWAIT, - NULL, 0); - } - - for (i = 0; i < n_pkts; i++) { - xsk_ring_prod__tx_desc(&p->txq, pos + i)->addr = b->addr[i]; - xsk_ring_prod__tx_desc(&p->txq, pos + i)->len = b->len[i]; - } - - xsk_ring_prod__submit(&p->txq, n_pkts); - if (xsk_ring_prod__needs_wakeup(&p->txq)) - sendto(xsk_socket__fd(p->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); - p->n_pkts_tx += n_pkts; -} - -/* - * Thread - * - * Packet forwarding threads. - */ -#ifndef MAX_PORTS_PER_THREAD -#define MAX_PORTS_PER_THREAD 16 -#endif - -struct thread_data { - struct port *ports_rx[MAX_PORTS_PER_THREAD]; - struct port *ports_tx[MAX_PORTS_PER_THREAD]; - u32 n_ports_rx; - struct burst_rx burst_rx; - struct burst_tx burst_tx[MAX_PORTS_PER_THREAD]; - u32 cpu_core_id; - int quit; -}; - -static void swap_mac_addresses(void *data) -{ - struct ether_header *eth = (struct ether_header *)data; - struct ether_addr *src_addr = (struct ether_addr *)ð->ether_shost; - struct ether_addr *dst_addr = (struct ether_addr *)ð->ether_dhost; - struct ether_addr tmp; - - tmp = *src_addr; - *src_addr = *dst_addr; - *dst_addr = tmp; -} - -static void * -thread_func(void *arg) -{ - struct thread_data *t = arg; - cpu_set_t cpu_cores; - u32 i; - - CPU_ZERO(&cpu_cores); - CPU_SET(t->cpu_core_id, &cpu_cores); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_cores); - - for (i = 0; !t->quit; i = (i + 1) & (t->n_ports_rx - 1)) { - struct port *port_rx = t->ports_rx[i]; - struct port *port_tx = t->ports_tx[i]; - struct burst_rx *brx = &t->burst_rx; - struct burst_tx *btx = &t->burst_tx[i]; - u32 n_pkts, j; - - /* RX. */ - n_pkts = port_rx_burst(port_rx, brx); - if (!n_pkts) - continue; - - /* Process & TX. */ - for (j = 0; j < n_pkts; j++) { - u64 addr = xsk_umem__add_offset_to_addr(brx->addr[j]); - u8 *pkt = xsk_umem__get_data(port_rx->params.bp->addr, - addr); - - swap_mac_addresses(pkt); - - btx->addr[btx->n_pkts] = brx->addr[j]; - btx->len[btx->n_pkts] = brx->len[j]; - btx->n_pkts++; - - if (btx->n_pkts == MAX_BURST_TX) { - port_tx_burst(port_tx, btx); - btx->n_pkts = 0; - } - } - } - - return NULL; -} - -/* - * Process - */ -static const struct bpool_params bpool_params_default = { - .n_buffers = 64 * 1024, - .buffer_size = XSK_UMEM__DEFAULT_FRAME_SIZE, - .mmap_flags = 0, - - .n_users_max = 16, - .n_buffers_per_slab = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, -}; - -static const struct xsk_umem_config umem_cfg_default = { - .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS * 2, - .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, - .frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE, - .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, - .flags = 0, -}; - -static const struct port_params port_params_default = { - .xsk_cfg = { - .rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, - .tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, - .libbpf_flags = 0, - .xdp_flags = XDP_FLAGS_DRV_MODE, - .bind_flags = XDP_USE_NEED_WAKEUP | XDP_ZEROCOPY, - }, - - .bp = NULL, - .iface = NULL, - .iface_queue = 0, -}; - -#ifndef MAX_PORTS -#define MAX_PORTS 64 -#endif - -#ifndef MAX_THREADS -#define MAX_THREADS 64 -#endif - -static struct bpool_params bpool_params; -static struct xsk_umem_config umem_cfg; -static struct bpool *bp; - -static struct port_params port_params[MAX_PORTS]; -static struct port *ports[MAX_PORTS]; -static u64 n_pkts_rx[MAX_PORTS]; -static u64 n_pkts_tx[MAX_PORTS]; -static int n_ports; - -static pthread_t threads[MAX_THREADS]; -static struct thread_data thread_data[MAX_THREADS]; -static int n_threads; - -static void -print_usage(char *prog_name) -{ - const char *usage = - "Usage:\n" - "\t%s [ -b SIZE ] -c CORE -i INTERFACE [ -q QUEUE ]\n" - "\n" - "-c CORE CPU core to run a packet forwarding thread\n" - " on. May be invoked multiple times.\n" - "\n" - "-b SIZE Number of buffers in the buffer pool shared\n" - " by all the forwarding threads. Default: %u.\n" - "\n" - "-i INTERFACE Network interface. Each (INTERFACE, QUEUE)\n" - " pair specifies one forwarding port. May be\n" - " invoked multiple times.\n" - "\n" - "-q QUEUE Network interface queue for RX and TX. Each\n" - " (INTERFACE, QUEUE) pair specified one\n" - " forwarding port. Default: %u. May be invoked\n" - " multiple times.\n" - "\n"; - printf(usage, - prog_name, - bpool_params_default.n_buffers, - port_params_default.iface_queue); -} - -static int -parse_args(int argc, char **argv) -{ - struct option lgopts[] = { - { NULL, 0, 0, 0 } - }; - int opt, option_index; - - /* Parse the input arguments. */ - for ( ; ;) { - opt = getopt_long(argc, argv, "c:i:q:", lgopts, &option_index); - if (opt == EOF) - break; - - switch (opt) { - case 'b': - bpool_params.n_buffers = atoi(optarg); - break; - - case 'c': - if (n_threads == MAX_THREADS) { - printf("Max number of threads (%d) reached.\n", - MAX_THREADS); - return -1; - } - - thread_data[n_threads].cpu_core_id = atoi(optarg); - n_threads++; - break; - - case 'i': - if (n_ports == MAX_PORTS) { - printf("Max number of ports (%d) reached.\n", - MAX_PORTS); - return -1; - } - - port_params[n_ports].iface = optarg; - port_params[n_ports].iface_queue = 0; - n_ports++; - break; - - case 'q': - if (n_ports == 0) { - printf("No port specified for queue.\n"); - return -1; - } - port_params[n_ports - 1].iface_queue = atoi(optarg); - break; - - default: - printf("Illegal argument.\n"); - return -1; - } - } - - optind = 1; /* reset getopt lib */ - - /* Check the input arguments. */ - if (!n_ports) { - printf("No ports specified.\n"); - return -1; - } - - if (!n_threads) { - printf("No threads specified.\n"); - return -1; - } - - if (n_ports % n_threads) { - printf("Ports cannot be evenly distributed to threads.\n"); - return -1; - } - - return 0; -} - -static void -print_port(u32 port_id) -{ - struct port *port = ports[port_id]; - - printf("Port %u: interface = %s, queue = %u\n", - port_id, port->params.iface, port->params.iface_queue); -} - -static void -print_thread(u32 thread_id) -{ - struct thread_data *t = &thread_data[thread_id]; - u32 i; - - printf("Thread %u (CPU core %u): ", - thread_id, t->cpu_core_id); - - for (i = 0; i < t->n_ports_rx; i++) { - struct port *port_rx = t->ports_rx[i]; - struct port *port_tx = t->ports_tx[i]; - - printf("(%s, %u) -> (%s, %u), ", - port_rx->params.iface, - port_rx->params.iface_queue, - port_tx->params.iface, - port_tx->params.iface_queue); - } - - printf("\n"); -} - -static void -print_port_stats_separator(void) -{ - printf("+-%4s-+-%12s-+-%13s-+-%12s-+-%13s-+\n", - "----", - "------------", - "-------------", - "------------", - "-------------"); -} - -static void -print_port_stats_header(void) -{ - print_port_stats_separator(); - printf("| %4s | %12s | %13s | %12s | %13s |\n", - "Port", - "RX packets", - "RX rate (pps)", - "TX packets", - "TX_rate (pps)"); - print_port_stats_separator(); -} - -static void -print_port_stats_trailer(void) -{ - print_port_stats_separator(); - printf("\n"); -} - -static void -print_port_stats(int port_id, u64 ns_diff) -{ - struct port *p = ports[port_id]; - double rx_pps, tx_pps; - - rx_pps = (p->n_pkts_rx - n_pkts_rx[port_id]) * 1000000000. / ns_diff; - tx_pps = (p->n_pkts_tx - n_pkts_tx[port_id]) * 1000000000. / ns_diff; - - printf("| %4d | %12llu | %13.0f | %12llu | %13.0f |\n", - port_id, - p->n_pkts_rx, - rx_pps, - p->n_pkts_tx, - tx_pps); - - n_pkts_rx[port_id] = p->n_pkts_rx; - n_pkts_tx[port_id] = p->n_pkts_tx; -} - -static void -print_port_stats_all(u64 ns_diff) -{ - int i; - - print_port_stats_header(); - for (i = 0; i < n_ports; i++) - print_port_stats(i, ns_diff); - print_port_stats_trailer(); -} - -static int quit; - -static void -signal_handler(int sig) -{ - quit = 1; -} - -static void remove_xdp_program(void) -{ - int i; - - for (i = 0 ; i < n_ports; i++) - bpf_set_link_xdp_fd(if_nametoindex(port_params[i].iface), -1, - port_params[i].xsk_cfg.xdp_flags); -} - -int main(int argc, char **argv) -{ - struct timespec time; - u64 ns0; - int i; - - /* Parse args. */ - memcpy(&bpool_params, &bpool_params_default, - sizeof(struct bpool_params)); - memcpy(&umem_cfg, &umem_cfg_default, - sizeof(struct xsk_umem_config)); - for (i = 0; i < MAX_PORTS; i++) - memcpy(&port_params[i], &port_params_default, - sizeof(struct port_params)); - - if (parse_args(argc, argv)) { - print_usage(argv[0]); - return -1; - } - - /* Buffer pool initialization. */ - bp = bpool_init(&bpool_params, &umem_cfg); - if (!bp) { - printf("Buffer pool initialization failed.\n"); - return -1; - } - printf("Buffer pool created successfully.\n"); - - /* Ports initialization. */ - for (i = 0; i < MAX_PORTS; i++) - port_params[i].bp = bp; - - for (i = 0; i < n_ports; i++) { - ports[i] = port_init(&port_params[i]); - if (!ports[i]) { - printf("Port %d initialization failed.\n", i); - return -1; - } - print_port(i); - } - printf("All ports created successfully.\n"); - - /* Threads. */ - for (i = 0; i < n_threads; i++) { - struct thread_data *t = &thread_data[i]; - u32 n_ports_per_thread = n_ports / n_threads, j; - - for (j = 0; j < n_ports_per_thread; j++) { - t->ports_rx[j] = ports[i * n_ports_per_thread + j]; - t->ports_tx[j] = ports[i * n_ports_per_thread + - (j + 1) % n_ports_per_thread]; - } - - t->n_ports_rx = n_ports_per_thread; - - print_thread(i); - } - - for (i = 0; i < n_threads; i++) { - int status; - - status = pthread_create(&threads[i], - NULL, - thread_func, - &thread_data[i]); - if (status) { - printf("Thread %d creation failed.\n", i); - return -1; - } - } - printf("All threads created successfully.\n"); - - /* Print statistics. */ - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - signal(SIGABRT, signal_handler); - - clock_gettime(CLOCK_MONOTONIC, &time); - ns0 = time.tv_sec * 1000000000UL + time.tv_nsec; - for ( ; !quit; ) { - u64 ns1, ns_diff; - - sleep(1); - clock_gettime(CLOCK_MONOTONIC, &time); - ns1 = time.tv_sec * 1000000000UL + time.tv_nsec; - ns_diff = ns1 - ns0; - ns0 = ns1; - - print_port_stats_all(ns_diff); - } - - /* Threads completion. */ - printf("Quit.\n"); - for (i = 0; i < n_threads; i++) - thread_data[i].quit = 1; - - for (i = 0; i < n_threads; i++) - pthread_join(threads[i], NULL); - - for (i = 0; i < n_ports; i++) - port_free(ports[i]); - - bpool_free(bp); - - remove_xdp_program(); - - return 0; -} diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..b3fce4af2347 --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(srctree)/drivers/hwtracing/coresight diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..25485c80b5e3 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2020 Linaro Limited. All rights reserved. + * Author: Mike Leach <mike.leach@linaro.org> + */ + +#include "coresight-config.h" +#include "coresight-syscfg.h" + +/* create an alternate autofdo configuration */ + +/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2 + +static const char *afdo2_ref_names[] = { + "strobing", +}; + +/* + * set of presets leaves strobing window constant while varying period to allow + * experimentation with mark / space ratios for various workloads + */ +static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = { + { 1000, 100 }, + { 1000, 1000 }, + { 1000, 5000 }, + { 1000, 10000 }, +}; + +struct cscfg_config_desc afdo2 = { + .name = "autofdo2", + .description = "Setup ETMs with strobing for autofdo\n" + "Supplied presets allow experimentation with mark-space ratio for various loads\n", + .nr_feat_refs = ARRAY_SIZE(afdo2_ref_names), + .feat_ref_names = afdo2_ref_names, + .nr_presets = AFDO2_NR_PRESETS, + .nr_total_params = AFDO2_NR_PARAM_SUM, + .presets = &afdo2_presets[0][0], +}; + +static struct cscfg_feature_desc *sample_feats[] = { + NULL +}; + +static struct cscfg_config_desc *sample_cfgs[] = { + &afdo2, + NULL +}; + +static struct cscfg_load_owner_info mod_owner = { + .type = CSCFG_OWNER_MODULE, + .owner_handle = THIS_MODULE, +}; + +/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{ + return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner); +} + +static void __exit cscfg_sample_exit(void) +{ + cscfg_unload_config_sets(&mod_owner); +} + +module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach <mike.leach@linaro.org>"); +MODULE_DESCRIPTION("CoreSight Syscfg Example"); diff --git a/samples/fprobe/Makefile b/samples/fprobe/Makefile new file mode 100644 index 000000000000..ecccbfa6e99b --- /dev/null +++ b/samples/fprobe/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_FPROBE) += fprobe_example.o diff --git a/samples/fprobe/fprobe_example.c b/samples/fprobe/fprobe_example.c new file mode 100644 index 000000000000..e22da8573116 --- /dev/null +++ b/samples/fprobe/fprobe_example.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Here's a sample kernel module showing the use of fprobe to dump a + * stack trace and selected registers when kernel_clone() is called. + * + * For more information on theory of operation of kprobes, see + * Documentation/trace/kprobes.rst + * + * You will see the trace data in /var/log/messages and on the console + * whenever kernel_clone() is invoked to create a new process. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fprobe.h> +#include <linux/sched/debug.h> +#include <linux/slab.h> + +#define BACKTRACE_DEPTH 16 +#define MAX_SYMBOL_LEN 4096 +static struct fprobe sample_probe; +static unsigned long nhit; + +static char symbol[MAX_SYMBOL_LEN] = "kernel_clone"; +module_param_string(symbol, symbol, sizeof(symbol), 0644); +MODULE_PARM_DESC(symbol, "Probed symbol(s), given by comma separated symbols or a wildcard pattern."); + +static char nosymbol[MAX_SYMBOL_LEN] = ""; +module_param_string(nosymbol, nosymbol, sizeof(nosymbol), 0644); +MODULE_PARM_DESC(nosymbol, "Not-probed symbols, given by a wildcard pattern."); + +static bool stackdump = true; +module_param(stackdump, bool, 0644); +MODULE_PARM_DESC(stackdump, "Enable stackdump."); + +static bool use_trace = false; +module_param(use_trace, bool, 0644); +MODULE_PARM_DESC(use_trace, "Use trace_printk instead of printk. This is only for debugging."); + +static void show_backtrace(void) +{ + unsigned long stacks[BACKTRACE_DEPTH]; + unsigned int len; + + len = stack_trace_save(stacks, BACKTRACE_DEPTH, 2); + stack_trace_print(stacks, len, 24); +} + +static void sample_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + if (use_trace) + /* + * This is just an example, no kernel code should call + * trace_printk() except when actively debugging. + */ + trace_printk("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); + else + pr_info("Enter <%pS> ip = 0x%p\n", (void *)ip, (void *)ip); + nhit++; + if (stackdump) + show_backtrace(); +} + +static void sample_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs) +{ + unsigned long rip = instruction_pointer(regs); + + if (use_trace) + /* + * This is just an example, no kernel code should call + * trace_printk() except when actively debugging. + */ + trace_printk("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", + (void *)ip, (void *)ip, (void *)rip, (void *)rip); + else + pr_info("Return from <%pS> ip = 0x%p to rip = 0x%p (%pS)\n", + (void *)ip, (void *)ip, (void *)rip, (void *)rip); + nhit++; + if (stackdump) + show_backtrace(); +} + +static int __init fprobe_init(void) +{ + char *p, *symbuf = NULL; + const char **syms; + int ret, count, i; + + sample_probe.entry_handler = sample_entry_handler; + sample_probe.exit_handler = sample_exit_handler; + + if (strchr(symbol, '*')) { + /* filter based fprobe */ + ret = register_fprobe(&sample_probe, symbol, + nosymbol[0] == '\0' ? NULL : nosymbol); + goto out; + } else if (!strchr(symbol, ',')) { + symbuf = symbol; + ret = register_fprobe_syms(&sample_probe, (const char **)&symbuf, 1); + goto out; + } + + /* Comma separated symbols */ + symbuf = kstrdup(symbol, GFP_KERNEL); + if (!symbuf) + return -ENOMEM; + p = symbuf; + count = 1; + while ((p = strchr(++p, ',')) != NULL) + count++; + + pr_info("%d symbols found\n", count); + + syms = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!syms) { + kfree(symbuf); + return -ENOMEM; + } + + p = symbuf; + for (i = 0; i < count; i++) + syms[i] = strsep(&p, ","); + + ret = register_fprobe_syms(&sample_probe, syms, count); + kfree(syms); + kfree(symbuf); +out: + if (ret < 0) + pr_err("register_fprobe failed, returned %d\n", ret); + else + pr_info("Planted fprobe at %s\n", symbol); + + return ret; +} + +static void __exit fprobe_exit(void) +{ + unregister_fprobe(&sample_probe); + + pr_info("fprobe at %s unregistered. %ld times hit, %ld times missed\n", + symbol, nhit, sample_probe.nmissed); +} + +module_init(fprobe_init) +module_exit(fprobe_exit) +MODULE_LICENSE("GPL"); diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c index 2877cb053a82..39146fa83e20 100644 --- a/samples/ftrace/ftrace-direct-modify.c +++ b/samples/ftrace/ftrace-direct-modify.c @@ -24,25 +24,30 @@ static unsigned long my_ip = (unsigned long)schedule; #ifdef CONFIG_X86_64 +#include <asm/ibt.h> + asm ( " .pushsection .text, \"ax\", @progbits\n" " .type my_tramp1, @function\n" " .globl my_tramp1\n" " my_tramp1:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " call my_direct_func1\n" " leave\n" " .size my_tramp1, .-my_tramp1\n" -" ret\n" + ASM_RET + " .type my_tramp2, @function\n" " .globl my_tramp2\n" " my_tramp2:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " call my_direct_func2\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp2, .-my_tramp2\n" " .popsection\n" ); diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c index 6f43a39decd0..65aa94d96f4e 100644 --- a/samples/ftrace/ftrace-direct-multi-modify.c +++ b/samples/ftrace/ftrace-direct-multi-modify.c @@ -22,11 +22,14 @@ extern void my_tramp2(void *); #ifdef CONFIG_X86_64 +#include <asm/ibt.h> + asm ( " .pushsection .text, \"ax\", @progbits\n" " .type my_tramp1, @function\n" " .globl my_tramp1\n" " my_tramp1:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " pushq %rdi\n" @@ -34,12 +37,13 @@ asm ( " call my_direct_func1\n" " popq %rdi\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp1, .-my_tramp1\n" + " .type my_tramp2, @function\n" -"\n" " .globl my_tramp2\n" " my_tramp2:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " pushq %rdi\n" @@ -47,7 +51,7 @@ asm ( " call my_direct_func2\n" " popq %rdi\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp2, .-my_tramp2\n" " .popsection\n" ); diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c index 2fafc9afcbf0..41ded7c615c7 100644 --- a/samples/ftrace/ftrace-direct-multi.c +++ b/samples/ftrace/ftrace-direct-multi.c @@ -17,11 +17,14 @@ extern void my_tramp(void *); #ifdef CONFIG_X86_64 +#include <asm/ibt.h> + asm ( " .pushsection .text, \"ax\", @progbits\n" " .type my_tramp, @function\n" " .globl my_tramp\n" " my_tramp:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " pushq %rdi\n" @@ -29,7 +32,7 @@ asm ( " call my_direct_func\n" " popq %rdi\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp, .-my_tramp\n" " .popsection\n" ); diff --git a/samples/ftrace/ftrace-direct-too.c b/samples/ftrace/ftrace-direct-too.c index b97e5ed46233..6690468c5cc2 100644 --- a/samples/ftrace/ftrace-direct-too.c +++ b/samples/ftrace/ftrace-direct-too.c @@ -19,11 +19,14 @@ extern void my_tramp(void *); #ifdef CONFIG_X86_64 +#include <asm/ibt.h> + asm ( " .pushsection .text, \"ax\", @progbits\n" " .type my_tramp, @function\n" " .globl my_tramp\n" " my_tramp:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " pushq %rdi\n" @@ -34,7 +37,7 @@ asm ( " popq %rsi\n" " popq %rdi\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp, .-my_tramp\n" " .popsection\n" ); diff --git a/samples/ftrace/ftrace-direct.c b/samples/ftrace/ftrace-direct.c index c918b13edb49..e8f1e440b9b8 100644 --- a/samples/ftrace/ftrace-direct.c +++ b/samples/ftrace/ftrace-direct.c @@ -16,18 +16,21 @@ extern void my_tramp(void *); #ifdef CONFIG_X86_64 +#include <asm/ibt.h> + asm ( " .pushsection .text, \"ax\", @progbits\n" " .type my_tramp, @function\n" " .globl my_tramp\n" " my_tramp:" + ASM_ENDBR " pushq %rbp\n" " movq %rsp, %rbp\n" " pushq %rdi\n" " call my_direct_func\n" " popq %rdi\n" " leave\n" -" ret\n" + ASM_RET " .size my_tramp, .-my_tramp\n" " .popsection\n" ); diff --git a/samples/kobject/kobject-example.c b/samples/kobject/kobject-example.c index 9e383fdbaa00..96678ed73216 100644 --- a/samples/kobject/kobject-example.c +++ b/samples/kobject/kobject-example.c @@ -28,7 +28,7 @@ static int bar; static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", foo); + return sysfs_emit(buf, "%d\n", foo); } static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -60,7 +60,7 @@ static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr, var = baz; else var = bar; - return sprintf(buf, "%d\n", var); + return sysfs_emit(buf, "%d\n", var); } static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr, diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index c8010f126808..52f1acabd479 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -112,7 +112,7 @@ static void foo_release(struct kobject *kobj) static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", foo_obj->foo); + return sysfs_emit(buf, "%d\n", foo_obj->foo); } static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr, @@ -144,7 +144,7 @@ static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr, var = foo_obj->baz; else var = foo_obj->bar; - return sprintf(buf, "%d\n", var); + return sysfs_emit(buf, "%d\n", var); } static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr, diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.c index f991a66b5b02..fd346f58ddba 100644 --- a/samples/kprobes/kprobe_example.c +++ b/samples/kprobes/kprobe_example.c @@ -16,9 +16,8 @@ #include <linux/module.h> #include <linux/kprobes.h> -#define MAX_SYMBOL_LEN 64 -static char symbol[MAX_SYMBOL_LEN] = "kernel_clone"; -module_param_string(symbol, symbol, sizeof(symbol), 0644); +static char symbol[KSYM_NAME_LEN] = "kernel_clone"; +module_param_string(symbol, symbol, KSYM_NAME_LEN, 0644); /* For each probe you need to allocate a kprobe structure */ static struct kprobe kp = { diff --git a/samples/kprobes/kretprobe_example.c b/samples/kprobes/kretprobe_example.c index 228321ecb161..cbf16542d84e 100644 --- a/samples/kprobes/kretprobe_example.c +++ b/samples/kprobes/kretprobe_example.c @@ -23,11 +23,10 @@ #include <linux/module.h> #include <linux/kprobes.h> #include <linux/ktime.h> -#include <linux/limits.h> #include <linux/sched.h> -static char func_name[NAME_MAX] = "kernel_clone"; -module_param_string(func, func_name, NAME_MAX, S_IRUGO); +static char func_name[KSYM_NAME_LEN] = "kernel_clone"; +module_param_string(func, func_name, KSYM_NAME_LEN, 0644); MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the" " function's execution time"); diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c index 7a15910d2171..f29bb3c72230 100644 --- a/samples/landlock/sandboxer.c +++ b/samples/landlock/sandboxer.c @@ -22,9 +22,9 @@ #include <unistd.h> #ifndef landlock_create_ruleset -static inline int landlock_create_ruleset( - const struct landlock_ruleset_attr *const attr, - const size_t size, const __u32 flags) +static inline int +landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, + const size_t size, const __u32 flags) { return syscall(__NR_landlock_create_ruleset, attr, size, flags); } @@ -32,17 +32,18 @@ static inline int landlock_create_ruleset( #ifndef landlock_add_rule static inline int landlock_add_rule(const int ruleset_fd, - const enum landlock_rule_type rule_type, - const void *const rule_attr, const __u32 flags) + const enum landlock_rule_type rule_type, + const void *const rule_attr, + const __u32 flags) { - return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, - rule_attr, flags); + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, + flags); } #endif #ifndef landlock_restrict_self static inline int landlock_restrict_self(const int ruleset_fd, - const __u32 flags) + const __u32 flags) { return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); } @@ -70,14 +71,17 @@ static int parse_path(char *env_path, const char ***const path_list) return num_paths; } +/* clang-format off */ + #define ACCESS_FILE ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_WRITE_FILE | \ LANDLOCK_ACCESS_FS_READ_FILE) -static int populate_ruleset( - const char *const env_var, const int ruleset_fd, - const __u64 allowed_access) +/* clang-format on */ + +static int populate_ruleset(const char *const env_var, const int ruleset_fd, + const __u64 allowed_access) { int num_paths, i, ret = 1; char *env_path_name; @@ -107,12 +111,10 @@ static int populate_ruleset( for (i = 0; i < num_paths; i++) { struct stat statbuf; - path_beneath.parent_fd = open(path_list[i], O_PATH | - O_CLOEXEC); + path_beneath.parent_fd = open(path_list[i], O_PATH | O_CLOEXEC); if (path_beneath.parent_fd < 0) { fprintf(stderr, "Failed to open \"%s\": %s\n", - path_list[i], - strerror(errno)); + path_list[i], strerror(errno)); goto out_free_name; } if (fstat(path_beneath.parent_fd, &statbuf)) { @@ -123,9 +125,10 @@ static int populate_ruleset( if (!S_ISDIR(statbuf.st_mode)) path_beneath.allowed_access &= ACCESS_FILE; if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, - &path_beneath, 0)) { - fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", - path_list[i], strerror(errno)); + &path_beneath, 0)) { + fprintf(stderr, + "Failed to update the ruleset with \"%s\": %s\n", + path_list[i], strerror(errno)); close(path_beneath.parent_fd); goto out_free_name; } @@ -134,10 +137,13 @@ static int populate_ruleset( ret = 0; out_free_name: + free(path_list); free(env_path_name); return ret; } +/* clang-format off */ + #define ACCESS_FS_ROUGHLY_READ ( \ LANDLOCK_ACCESS_FS_EXECUTE | \ LANDLOCK_ACCESS_FS_READ_FILE | \ @@ -153,64 +159,110 @@ out_free_name: LANDLOCK_ACCESS_FS_MAKE_SOCK | \ LANDLOCK_ACCESS_FS_MAKE_FIFO | \ LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ - LANDLOCK_ACCESS_FS_MAKE_SYM) + LANDLOCK_ACCESS_FS_MAKE_SYM | \ + LANDLOCK_ACCESS_FS_REFER) + +/* clang-format on */ + +#define LANDLOCK_ABI_LAST 2 int main(const int argc, char *const argv[], char *const *const envp) { const char *cmd_path; char *const *cmd_argv; - int ruleset_fd; + int ruleset_fd, abi; + __u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ, + access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE; struct landlock_ruleset_attr ruleset_attr = { - .handled_access_fs = ACCESS_FS_ROUGHLY_READ | - ACCESS_FS_ROUGHLY_WRITE, + .handled_access_fs = access_fs_rw, }; if (argc < 2) { - fprintf(stderr, "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); - fprintf(stderr, "Launch a command in a restricted environment.\n\n"); + fprintf(stderr, + "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); + fprintf(stderr, + "Launch a command in a restricted environment.\n\n"); fprintf(stderr, "Environment variables containing paths, " "each separated by a colon:\n"); - fprintf(stderr, "* %s: list of paths allowed to be used in a read-only way.\n", - ENV_FS_RO_NAME); - fprintf(stderr, "* %s: list of paths allowed to be used in a read-write way.\n", - ENV_FS_RW_NAME); - fprintf(stderr, "\nexample:\n" - "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " - "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " - "%s bash -i\n", - ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); + fprintf(stderr, + "* %s: list of paths allowed to be used in a read-only way.\n", + ENV_FS_RO_NAME); + fprintf(stderr, + "* %s: list of paths allowed to be used in a read-write way.\n", + ENV_FS_RW_NAME); + fprintf(stderr, + "\nexample:\n" + "%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " + "%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " + "%s bash -i\n\n", + ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); + fprintf(stderr, + "This sandboxer can use Landlock features " + "up to ABI version %d.\n", + LANDLOCK_ABI_LAST); return 1; } - ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); - if (ruleset_fd < 0) { + abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION); + if (abi < 0) { const int err = errno; - perror("Failed to create a ruleset"); + perror("Failed to check Landlock compatibility"); switch (err) { case ENOSYS: - fprintf(stderr, "Hint: Landlock is not supported by the current kernel. " - "To support it, build the kernel with " - "CONFIG_SECURITY_LANDLOCK=y and prepend " - "\"landlock,\" to the content of CONFIG_LSM.\n"); + fprintf(stderr, + "Hint: Landlock is not supported by the current kernel. " + "To support it, build the kernel with " + "CONFIG_SECURITY_LANDLOCK=y and prepend " + "\"landlock,\" to the content of CONFIG_LSM.\n"); break; case EOPNOTSUPP: - fprintf(stderr, "Hint: Landlock is currently disabled. " - "It can be enabled in the kernel configuration by " - "prepending \"landlock,\" to the content of CONFIG_LSM, " - "or at boot time by setting the same content to the " - "\"lsm\" kernel parameter.\n"); + fprintf(stderr, + "Hint: Landlock is currently disabled. " + "It can be enabled in the kernel configuration by " + "prepending \"landlock,\" to the content of CONFIG_LSM, " + "or at boot time by setting the same content to the " + "\"lsm\" kernel parameter.\n"); break; } return 1; } - if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, - ACCESS_FS_ROUGHLY_READ)) { + + /* Best-effort security. */ + switch (abi) { + case 1: + /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */ + ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER; + + fprintf(stderr, + "Hint: You should update the running kernel " + "to leverage Landlock features " + "provided by ABI version %d (instead of %d).\n", + LANDLOCK_ABI_LAST, abi); + __attribute__((fallthrough)); + case LANDLOCK_ABI_LAST: + break; + default: + fprintf(stderr, + "Hint: You should update this sandboxer " + "to leverage Landlock features " + "provided by ABI version %d (instead of %d).\n", + abi, LANDLOCK_ABI_LAST); + } + access_fs_ro &= ruleset_attr.handled_access_fs; + access_fs_rw &= ruleset_attr.handled_access_fs; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + if (ruleset_fd < 0) { + perror("Failed to create a ruleset"); + return 1; + } + if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) { goto err_close_ruleset; } - if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, - ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE)) { + if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) { goto err_close_ruleset; } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { @@ -227,7 +279,7 @@ int main(const int argc, char *const argv[], char *const *const envp) cmd_argv = argv + 1; execvpe(cmd_path, cmd_argv, envp); fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path, - strerror(errno)); + strerror(errno)); fprintf(stderr, "Hint: access to the binary, the interpreter or " "shared libraries may be denied.\n"); return 1; diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c index 918ce17b43fd..6701641bf12d 100644 --- a/samples/livepatch/livepatch-shadow-fix1.c +++ b/samples/livepatch/livepatch-shadow-fix1.c @@ -109,9 +109,9 @@ static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data) void *d = obj; int **shadow_leak = shadow_data; - kfree(*shadow_leak); pr_info("%s: dummy @ %p, prevented leak @ %p\n", __func__, d, *shadow_leak); + kfree(*shadow_leak); } static void livepatch_fix1_dummy_free(struct dummy *d) diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c index 29fe5cd42047..361046a4f10c 100644 --- a/samples/livepatch/livepatch-shadow-fix2.c +++ b/samples/livepatch/livepatch-shadow-fix2.c @@ -61,9 +61,9 @@ static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data) void *d = obj; int **shadow_leak = shadow_data; - kfree(*shadow_leak); pr_info("%s: dummy @ %p, prevented leak @ %p\n", __func__, d, *shadow_leak); + kfree(*shadow_leak); } static void livepatch_fix2_dummy_free(struct dummy *d) diff --git a/samples/qmi/qmi_sample_client.c b/samples/qmi/qmi_sample_client.c index 78fcedbd25e2..c045e3d24326 100644 --- a/samples/qmi/qmi_sample_client.c +++ b/samples/qmi/qmi_sample_client.c @@ -42,7 +42,7 @@ struct test_name_type_v01 { char name[TEST_MAX_NAME_SIZE_V01]; }; -static struct qmi_elem_info test_name_type_v01_ei[] = { +static const struct qmi_elem_info test_name_type_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -71,7 +71,7 @@ struct test_ping_req_msg_v01 { struct test_name_type_v01 client_name; }; -static struct qmi_elem_info test_ping_req_msg_v01_ei[] = { +static const struct qmi_elem_info test_ping_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 4, @@ -113,7 +113,7 @@ struct test_ping_resp_msg_v01 { struct test_name_type_v01 service_name; }; -static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { +static const struct qmi_elem_info test_ping_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -172,7 +172,7 @@ struct test_data_req_msg_v01 { struct test_name_type_v01 client_name; }; -static struct qmi_elem_info test_data_req_msg_v01_ei[] = { +static const struct qmi_elem_info test_data_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -224,7 +224,7 @@ struct test_data_resp_msg_v01 { struct test_name_type_v01 service_name; }; -static struct qmi_elem_info test_data_resp_msg_v01_ei[] = { +static const struct qmi_elem_info test_data_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig new file mode 100644 index 000000000000..841e0906e943 --- /dev/null +++ b/samples/rust/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0 + +menuconfig SAMPLES_RUST + bool "Rust samples" + depends on RUST + help + You can build sample Rust kernel code here. + + If unsure, say N. + +if SAMPLES_RUST + +config SAMPLE_RUST_MINIMAL + tristate "Minimal" + help + This option builds the Rust minimal module sample. + + To compile this as a module, choose M here: + the module will be called rust_minimal. + + If unsure, say N. + +config SAMPLE_RUST_HOSTPROGS + bool "Host programs" + help + This option builds the Rust host program samples. + + If unsure, say N. + +endif # SAMPLES_RUST diff --git a/samples/rust/Makefile b/samples/rust/Makefile new file mode 100644 index 000000000000..1daba5f8658a --- /dev/null +++ b/samples/rust/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o + +subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/hostprogs/.gitignore b/samples/rust/hostprogs/.gitignore new file mode 100644 index 000000000000..a6c173da5048 --- /dev/null +++ b/samples/rust/hostprogs/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +single diff --git a/samples/rust/hostprogs/Makefile b/samples/rust/hostprogs/Makefile new file mode 100644 index 000000000000..8ddcbd7416db --- /dev/null +++ b/samples/rust/hostprogs/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +hostprogs-always-y := single + +single-rust := y diff --git a/samples/rust/hostprogs/a.rs b/samples/rust/hostprogs/a.rs new file mode 100644 index 000000000000..f7a4a3d0f4e0 --- /dev/null +++ b/samples/rust/hostprogs/a.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust single host program sample: module `a`. + +pub(crate) fn f(x: i32) { + println!("The number is {}.", x); +} diff --git a/samples/rust/hostprogs/b.rs b/samples/rust/hostprogs/b.rs new file mode 100644 index 000000000000..c1675890648f --- /dev/null +++ b/samples/rust/hostprogs/b.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust single host program sample: module `b`. + +pub(crate) const CONSTANT: i32 = 42; diff --git a/samples/rust/hostprogs/single.rs b/samples/rust/hostprogs/single.rs new file mode 100644 index 000000000000..8c48a119339a --- /dev/null +++ b/samples/rust/hostprogs/single.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust single host program sample. + +mod a; +mod b; + +fn main() { + println!("Hello world!"); + + a::f(b::CONSTANT); +} diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs new file mode 100644 index 000000000000..54ad17685742 --- /dev/null +++ b/samples/rust/rust_minimal.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust minimal sample. + +use kernel::prelude::*; + +module! { + type: RustMinimal, + name: b"rust_minimal", + author: b"Rust for Linux Contributors", + description: b"Rust minimal sample", + license: b"GPL", +} + +struct RustMinimal { + numbers: Vec<i32>, +} + +impl kernel::Module for RustMinimal { + fn init(_module: &'static ThisModule) -> Result<Self> { + pr_info!("Rust minimal sample (init)\n"); + pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); + + let mut numbers = Vec::new(); + numbers.try_push(72)?; + numbers.try_push(108)?; + numbers.try_push(200)?; + + Ok(RustMinimal { numbers }) + } +} + +impl Drop for RustMinimal { + fn drop(&mut self) { + pr_info!("My numbers are {:?}\n", self.numbers); + pr_info!("Rust minimal sample (exit)\n"); + } +} diff --git a/samples/seccomp/dropper.c b/samples/seccomp/dropper.c index cc0648eb389e..4bca4b70f665 100644 --- a/samples/seccomp/dropper.c +++ b/samples/seccomp/dropper.c @@ -25,7 +25,7 @@ #include <sys/prctl.h> #include <unistd.h> -static int install_filter(int nr, int arch, int error) +static int install_filter(int arch, int nr, int error) { struct sock_filter filter[] = { BPF_STMT(BPF_LD+BPF_W+BPF_ABS, @@ -42,6 +42,10 @@ static int install_filter(int nr, int arch, int error) .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .filter = filter, }; + if (error == -1) { + struct sock_filter kill = BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL); + filter[4] = kill; + } if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("prctl(NO_NEW_PRIVS)"); return 1; @@ -57,9 +61,10 @@ int main(int argc, char **argv) { if (argc < 5) { fprintf(stderr, "Usage:\n" - "dropper <syscall_nr> <arch> <errno> <prog> [<args>]\n" + "dropper <arch> <syscall_nr> <errno> <prog> [<args>]\n" "Hint: AUDIT_ARCH_I386: 0x%X\n" " AUDIT_ARCH_X86_64: 0x%X\n" + " errno == -1 means SECCOMP_RET_KILL\n" "\n", AUDIT_ARCH_I386, AUDIT_ARCH_X86_64); return 1; } diff --git a/samples/trace_events/Makefile b/samples/trace_events/Makefile index b78344e7bbed..b3808bb4cf8b 100644 --- a/samples/trace_events/Makefile +++ b/samples/trace_events/Makefile @@ -11,5 +11,7 @@ # Here trace-events-sample.c does the CREATE_TRACE_POINTS. # CFLAGS_trace-events-sample.o := -I$(src) +CFLAGS_trace_custom_sched.o := -I$(src) obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace-events-sample.o +obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_custom_sched.o diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c index 1a72b7d95cdc..608c4ae3b08a 100644 --- a/samples/trace_events/trace-events-sample.c +++ b/samples/trace_events/trace-events-sample.c @@ -19,8 +19,10 @@ static const char *random_strings[] = { "One ring to rule them all" }; -static void simple_thread_func(int cnt) +static void do_simple_thread_func(int cnt, const char *fmt, ...) { + unsigned long bitmask[1] = {0xdeadbeefUL}; + va_list va; int array[6]; int len = cnt % 5; int i; @@ -32,9 +34,13 @@ static void simple_thread_func(int cnt) array[i] = i + 1; array[i] = 0; + va_start(va, fmt); + /* Silly tracepoints */ trace_foo_bar("hello", cnt, array, random_strings[len], - current->cpus_ptr); + current->cpus_ptr, fmt, &va); + + va_end(va); trace_foo_with_template_simple("HELLO", cnt); @@ -43,6 +49,13 @@ static void simple_thread_func(int cnt) trace_foo_with_template_cond("prints other times", cnt); trace_foo_with_template_print("I have to be different", cnt); + + trace_foo_rel_loc("Hello __rel_loc", cnt, bitmask); +} + +static void simple_thread_func(int cnt) +{ + do_simple_thread_func(cnt, "iter=%d", cnt); } static int simple_thread(void *arg) diff --git a/samples/trace_events/trace-events-sample.h b/samples/trace_events/trace-events-sample.h index e61471ab7d14..1a92226202fc 100644 --- a/samples/trace_events/trace-events-sample.h +++ b/samples/trace_events/trace-events-sample.h @@ -141,6 +141,27 @@ * In most cases, the __assign_str() macro will take the same * parameters as the __string() macro had to declare the string. * + * __vstring: This is similar to __string() but instead of taking a + * dynamic length, it takes a variable list va_list 'va' variable. + * Some event callers already have a message from parameters saved + * in a va_list. Passing in the format and the va_list variable + * will save just enough on the ring buffer for that string. + * Note, the va variable used is a pointer to a va_list, not + * to the va_list directly. + * + * (va_list *va) + * + * __vstring(foo, fmt, va) is similar to: vsnprintf(foo, fmt, va) + * + * To assign the string, use the helper macro __assign_vstr(). + * + * __assign_vstr(foo, fmt, va); + * + * In most cases, the __assign_vstr() macro will take the same + * parameters as the __vstring() macro had to declare the string. + * Use __get_str() to retrieve the __vstring() just like it would for + * __string(). + * * __string_len: This is a helper to a __dynamic_array, but it understands * that the array has characters in it, and with the combined * use of __assign_str_len(), it will allocate 'len' + 1 bytes @@ -155,7 +176,7 @@ * * To assign this string, use the helper macro __assign_str_len(). * - * __assign_str(foo, bar, len); + * __assign_str_len(foo, bar, len); * * Then len + 1 is allocated to the ring buffer, and a nul terminating * byte is added. This is similar to: @@ -256,9 +277,10 @@ TRACE_DEFINE_ENUM(TRACE_SAMPLE_ZOO); TRACE_EVENT(foo_bar, TP_PROTO(const char *foo, int bar, const int *lst, - const char *string, const struct cpumask *mask), + const char *string, const struct cpumask *mask, + const char *fmt, va_list *va), - TP_ARGS(foo, bar, lst, string, mask), + TP_ARGS(foo, bar, lst, string, mask, fmt, va), TP_STRUCT__entry( __array( char, foo, 10 ) @@ -266,6 +288,7 @@ TRACE_EVENT(foo_bar, __dynamic_array(int, list, __length_of(lst)) __string( str, string ) __bitmask( cpus, num_possible_cpus() ) + __vstring( vstr, fmt, va ) ), TP_fast_assign( @@ -274,10 +297,11 @@ TRACE_EVENT(foo_bar, memcpy(__get_dynamic_array(list), lst, __length_of(lst) * sizeof(int)); __assign_str(str, string); + __assign_vstr(vstr, fmt, va); __assign_bitmask(cpus, cpumask_bits(mask), num_possible_cpus()); ), - TP_printk("foo %s %d %s %s %s %s (%s)", __entry->foo, __entry->bar, + TP_printk("foo %s %d %s %s %s %s (%s) %s", __entry->foo, __entry->bar, /* * Notice here the use of some helper functions. This includes: @@ -321,7 +345,7 @@ TRACE_EVENT(foo_bar, __print_array(__get_dynamic_array(list), __get_dynamic_array_len(list) / sizeof(int), sizeof(int)), - __get_str(str), __get_bitmask(cpus)) + __get_str(str), __get_bitmask(cpus), __get_str(vstr)) ); /* @@ -506,6 +530,39 @@ DEFINE_EVENT_PRINT(foo_template, foo_with_template_print, TP_ARGS(foo, bar), TP_printk("bar %s %d", __get_str(foo), __entry->bar)); +/* + * There are yet another __rel_loc dynamic data attribute. If you + * use __rel_dynamic_array() and __rel_string() etc. macros, you + * can use this attribute. There is no difference from the viewpoint + * of functionality with/without 'rel' but the encoding is a bit + * different. This is expected to be used with user-space event, + * there is no reason that the kernel event use this, but only for + * testing. + */ + +TRACE_EVENT(foo_rel_loc, + + TP_PROTO(const char *foo, int bar, unsigned long *mask), + + TP_ARGS(foo, bar, mask), + + TP_STRUCT__entry( + __rel_string( foo, foo ) + __field( int, bar ) + __rel_bitmask( bitmask, + BITS_PER_BYTE * sizeof(unsigned long) ) + ), + + TP_fast_assign( + __assign_rel_str(foo, foo); + __entry->bar = bar; + __assign_rel_bitmask(bitmask, mask, + BITS_PER_BYTE * sizeof(unsigned long)); + ), + + TP_printk("foo_rel_loc %s, %d, %s", __get_rel_str(foo), __entry->bar, + __get_rel_bitmask(bitmask)) +); #endif /***** NOTICE! The #if protection ends here. *****/ diff --git a/samples/trace_events/trace_custom_sched.c b/samples/trace_events/trace_custom_sched.c new file mode 100644 index 000000000000..b99d9ab7db85 --- /dev/null +++ b/samples/trace_events/trace_custom_sched.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * event tracer + * + * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org> + */ + +#define pr_fmt(fmt) fmt + +#include <linux/trace_events.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/sched.h> + +/* + * Must include the event header that the custom event will attach to, + * from the C file, and not in the custom header file. + */ +#include <trace/events/sched.h> + +/* Declare CREATE_CUSTOM_TRACE_EVENTS before including custom header */ +#define CREATE_CUSTOM_TRACE_EVENTS + +#include "trace_custom_sched.h" + +/* + * As the trace events are not exported to modules, the use of + * for_each_kernel_tracepoint() is needed to find the trace event + * to attach to. The fct() function below, is a callback that + * will be called for every event. + * + * Helper functions are created by the TRACE_CUSTOM_EVENT() macro + * update the event. Those are of the form: + * + * trace_custom_event_<event>_update() + * + * Where <event> is the event to attach. + */ +static void fct(struct tracepoint *tp, void *priv) +{ + trace_custom_event_sched_switch_update(tp); + trace_custom_event_sched_waking_update(tp); +} + +static int __init trace_sched_init(void) +{ + for_each_kernel_tracepoint(fct, NULL); + return 0; +} + +static void __exit trace_sched_exit(void) +{ +} + +module_init(trace_sched_init); +module_exit(trace_sched_exit); + +MODULE_AUTHOR("Steven Rostedt"); +MODULE_DESCRIPTION("Custom scheduling events"); +MODULE_LICENSE("GPL"); diff --git a/samples/trace_events/trace_custom_sched.h b/samples/trace_events/trace_custom_sched.h new file mode 100644 index 000000000000..951388334a3f --- /dev/null +++ b/samples/trace_events/trace_custom_sched.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Like the headers that use TRACE_EVENT(), the TRACE_CUSTOM_EVENT() + * needs a header that allows for multiple inclusions. + * + * Test for a unique name (here we have _TRACE_CUSTOM_SCHED_H), + * also allowing to continue if TRACE_CUSTOM_MULTI_READ is defined. + */ +#if !defined(_TRACE_CUSTOM_SCHED_H) || defined(TRACE_CUSTOM_MULTI_READ) +#define _TRACE_CUSTOM_SCHED_H + +/* Include linux/trace_events.h for initial defines of TRACE_CUSTOM_EVENT() */ +#include <linux/trace_events.h> + +/* + * TRACE_CUSTOM_EVENT() is just like TRACE_EVENT(). The first parameter + * is the event name of an existing event where the TRACE_EVENT has been included + * in the C file before including this file. + */ +TRACE_CUSTOM_EVENT(sched_switch, + + /* + * The TP_PROTO() and TP_ARGS must match the trace event + * that the custom event is using. + */ + TP_PROTO(bool preempt, + struct task_struct *prev, + struct task_struct *next, + unsigned int prev_state), + + TP_ARGS(preempt, prev, next, prev_state), + + /* + * The next fields are where the customization happens. + * The TP_STRUCT__entry() defines what will be recorded + * in the ring buffer when the custom event triggers. + * + * The rest is just like the TRACE_EVENT() macro except that + * it uses the custom entry. + */ + TP_STRUCT__entry( + __field( unsigned short, prev_prio ) + __field( unsigned short, next_prio ) + __field( pid_t, next_pid ) + ), + + TP_fast_assign( + __entry->prev_prio = prev->prio; + __entry->next_pid = next->pid; + __entry->next_prio = next->prio; + ), + + TP_printk("prev_prio=%d next_pid=%d next_prio=%d", + __entry->prev_prio, __entry->next_pid, __entry->next_prio) +) + + +TRACE_CUSTOM_EVENT(sched_waking, + + TP_PROTO(struct task_struct *p), + + TP_ARGS(p), + + TP_STRUCT__entry( + __field( pid_t, pid ) + __field( unsigned short, prio ) + ), + + TP_fast_assign( + __entry->pid = p->pid; + __entry->prio = p->prio; + ), + + TP_printk("pid=%d prio=%d", __entry->pid, __entry->prio) +) +#endif +/* + * Just like the headers that create TRACE_EVENTs, the below must + * be outside the protection of the above #if block. + */ + +/* + * It is required that the Makefile includes: + * CFLAGS_<c_file>.o := -I$(src) + */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . + +/* + * It is requred that the TRACE_INCLUDE_FILE be the same + * as this file without the ".h". + */ +#define TRACE_INCLUDE_FILE trace_custom_sched +#include <trace/define_custom_trace.h> diff --git a/samples/user_events/Makefile b/samples/user_events/Makefile new file mode 100644 index 000000000000..7252b589db57 --- /dev/null +++ b/samples/user_events/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +CFLAGS += -Wl,-no-as-needed -Wall -I../../usr/include + +example: example.o +example.o: example.c diff --git a/samples/user_events/example.c b/samples/user_events/example.c new file mode 100644 index 000000000000..d06dc24156ec --- /dev/null +++ b/samples/user_events/example.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, Microsoft Corporation. + * + * Authors: + * Beau Belgrave <beaub@linux.microsoft.com> + */ + +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <asm/bitsperlong.h> +#include <endian.h> +#include <linux/user_events.h> + +#if __BITS_PER_LONG == 64 +#define endian_swap(x) htole64(x) +#else +#define endian_swap(x) htole32(x) +#endif + +/* Assumes debugfs is mounted */ +const char *data_file = "/sys/kernel/debug/tracing/user_events_data"; +const char *status_file = "/sys/kernel/debug/tracing/user_events_status"; + +static int event_status(long **status) +{ + int fd = open(status_file, O_RDONLY); + + *status = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ, + MAP_SHARED, fd, 0); + + close(fd); + + if (*status == MAP_FAILED) + return -1; + + return 0; +} + +static int event_reg(int fd, const char *command, long *index, long *mask, + int *write) +{ + struct user_reg reg = {0}; + + reg.size = sizeof(reg); + reg.name_args = (__u64)command; + + if (ioctl(fd, DIAG_IOCSREG, ®) == -1) + return -1; + + *index = reg.status_bit / __BITS_PER_LONG; + *mask = endian_swap(1L << (reg.status_bit % __BITS_PER_LONG)); + *write = reg.write_index; + + return 0; +} + +int main(int argc, char **argv) +{ + int data_fd, write; + long index, mask; + long *status_page; + struct iovec io[2]; + __u32 count = 0; + + if (event_status(&status_page) == -1) + return errno; + + data_fd = open(data_file, O_RDWR); + + if (event_reg(data_fd, "test u32 count", &index, &mask, &write) == -1) + return errno; + + /* Setup iovec */ + io[0].iov_base = &write; + io[0].iov_len = sizeof(write); + io[1].iov_base = &count; + io[1].iov_len = sizeof(count); + +ask: + printf("Press enter to check status...\n"); + getchar(); + + /* Check if anyone is listening */ + if (status_page[index] & mask) { + /* Yep, trace out our data */ + writev(data_fd, (const struct iovec *)io, 2); + + /* Increase the count */ + count++; + + printf("Something was attached, wrote data\n"); + } + + goto ask; + + return 0; +} diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index 3fa6582b4a68..a61f94db18d9 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * This is a V4L2 PCI Skeleton Driver. It gives an initial skeleton source * for use with other PCI drivers. @@ -6,19 +7,6 @@ * input 0 and an HDMI connector as input 1. * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include <linux/types.h> @@ -766,7 +754,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ret = pci_enable_device(pdev); if (ret) return ret; - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { dev_err(&pdev->dev, "no suitable DMA available.\n"); goto disable_pci; diff --git a/samples/vfio-mdev/mbochs.c b/samples/vfio-mdev/mbochs.c index e90c8552cc31..117a8d799f71 100644 --- a/samples/vfio-mdev/mbochs.c +++ b/samples/vfio-mdev/mbochs.c @@ -21,7 +21,6 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -100,35 +99,44 @@ MODULE_PARM_DESC(mem, "megabytes available to " MBOCHS_NAME " devices"); #define MBOCHS_TYPE_2 "medium" #define MBOCHS_TYPE_3 "large" -static const struct mbochs_type { - const char *name; +static struct mbochs_type { + struct mdev_type type; u32 mbytes; u32 max_x; u32 max_y; } mbochs_types[] = { { - .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_1, + .type.sysfs_name = MBOCHS_TYPE_1, + .type.pretty_name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_1, .mbytes = 4, .max_x = 800, .max_y = 600, }, { - .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_2, + .type.sysfs_name = MBOCHS_TYPE_2, + .type.pretty_name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_2, .mbytes = 16, .max_x = 1920, .max_y = 1440, }, { - .name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_3, + .type.sysfs_name = MBOCHS_TYPE_3, + .type.pretty_name = MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_3, .mbytes = 64, .max_x = 0, .max_y = 0, }, }; +static struct mdev_type *mbochs_mdev_types[] = { + &mbochs_types[0].type, + &mbochs_types[1].type, + &mbochs_types[2].type, +}; static dev_t mbochs_devt; static struct class *mbochs_class; static struct cdev mbochs_cdev; static struct device mbochs_dev; +static struct mdev_parent mbochs_parent; static atomic_t mbochs_avail_mbytes; static const struct vfio_device_ops mbochs_dev_ops; @@ -505,13 +513,14 @@ static int mbochs_reset(struct mdev_state *mdev_state) return 0; } -static int mbochs_probe(struct mdev_device *mdev) +static int mbochs_init_dev(struct vfio_device *vdev) { + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + struct mdev_device *mdev = to_mdev_device(vdev->dev); + struct mbochs_type *type = + container_of(mdev->type, struct mbochs_type, type); int avail_mbytes = atomic_read(&mbochs_avail_mbytes); - const struct mbochs_type *type = - &mbochs_types[mdev_get_type_group_id(mdev)]; - struct device *dev = mdev_dev(mdev); - struct mdev_state *mdev_state; int ret = -ENOMEM; do { @@ -520,14 +529,9 @@ static int mbochs_probe(struct mdev_device *mdev) } while (!atomic_try_cmpxchg(&mbochs_avail_mbytes, &avail_mbytes, avail_mbytes - type->mbytes)); - mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); - if (mdev_state == NULL) - goto err_avail; - vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mbochs_dev_ops); - mdev_state->vconfig = kzalloc(MBOCHS_CONFIG_SPACE_SIZE, GFP_KERNEL); - if (mdev_state->vconfig == NULL) - goto err_mem; + if (!mdev_state->vconfig) + goto err_avail; mdev_state->memsize = type->mbytes * 1024 * 1024; mdev_state->pagecount = mdev_state->memsize >> PAGE_SHIFT; @@ -535,10 +539,7 @@ static int mbochs_probe(struct mdev_device *mdev) sizeof(struct page *), GFP_KERNEL); if (!mdev_state->pages) - goto err_mem; - - dev_info(dev, "%s: %s, %d MB, %ld pages\n", __func__, - type->name, type->mbytes, mdev_state->pagecount); + goto err_vconfig; mutex_init(&mdev_state->ops_lock); mdev_state->mdev = mdev; @@ -553,19 +554,47 @@ static int mbochs_probe(struct mdev_device *mdev) mbochs_create_config_space(mdev_state); mbochs_reset(mdev_state); + dev_info(vdev->dev, "%s: %s, %d MB, %ld pages\n", __func__, + type->type.pretty_name, type->mbytes, mdev_state->pagecount); + return 0; + +err_vconfig: + kfree(mdev_state->vconfig); +err_avail: + atomic_add(type->mbytes, &mbochs_avail_mbytes); + return ret; +} + +static int mbochs_probe(struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + int ret = -ENOMEM; + + mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev, + &mbochs_dev_ops); + if (IS_ERR(mdev_state)) + return PTR_ERR(mdev_state); + ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev); if (ret) - goto err_mem; + goto err_put_vdev; dev_set_drvdata(&mdev->dev, mdev_state); return 0; -err_mem: - vfio_uninit_group_dev(&mdev_state->vdev); + +err_put_vdev: + vfio_put_device(&mdev_state->vdev); + return ret; +} + +static void mbochs_release_dev(struct vfio_device *vdev) +{ + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + + atomic_add(mdev_state->type->mbytes, &mbochs_avail_mbytes); kfree(mdev_state->pages); kfree(mdev_state->vconfig); - kfree(mdev_state); -err_avail: - atomic_add(type->mbytes, &mbochs_avail_mbytes); - return ret; + vfio_free_device(vdev); } static void mbochs_remove(struct mdev_device *mdev) @@ -573,11 +602,7 @@ static void mbochs_remove(struct mdev_device *mdev) struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev); vfio_unregister_group_dev(&mdev_state->vdev); - vfio_uninit_group_dev(&mdev_state->vdev); - atomic_add(mdev_state->type->mbytes, &mbochs_avail_mbytes); - kfree(mdev_state->pages); - kfree(mdev_state->vconfig); - kfree(mdev_state); + vfio_put_device(&mdev_state->vdev); } static ssize_t mbochs_read(struct vfio_device *vdev, char __user *buf, @@ -1325,78 +1350,27 @@ static const struct attribute_group *mdev_dev_groups[] = { NULL, }; -static ssize_t name_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) +static ssize_t mbochs_show_description(struct mdev_type *mtype, char *buf) { - const struct mbochs_type *type = - &mbochs_types[mtype_get_type_group_id(mtype)]; - - return sprintf(buf, "%s\n", type->name); -} -static MDEV_TYPE_ATTR_RO(name); - -static ssize_t description_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - const struct mbochs_type *type = - &mbochs_types[mtype_get_type_group_id(mtype)]; + struct mbochs_type *type = + container_of(mtype, struct mbochs_type, type); return sprintf(buf, "virtual display, %d MB video memory\n", type ? type->mbytes : 0); } -static MDEV_TYPE_ATTR_RO(description); -static ssize_t available_instances_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, - char *buf) +static unsigned int mbochs_get_available(struct mdev_type *mtype) { - const struct mbochs_type *type = - &mbochs_types[mtype_get_type_group_id(mtype)]; - int count = atomic_read(&mbochs_avail_mbytes) / type->mbytes; + struct mbochs_type *type = + container_of(mtype, struct mbochs_type, type); - return sprintf(buf, "%d\n", count); -} -static MDEV_TYPE_ATTR_RO(available_instances); - -static ssize_t device_api_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); + return atomic_read(&mbochs_avail_mbytes) / type->mbytes; } -static MDEV_TYPE_ATTR_RO(device_api); - -static struct attribute *mdev_types_attrs[] = { - &mdev_type_attr_name.attr, - &mdev_type_attr_description.attr, - &mdev_type_attr_device_api.attr, - &mdev_type_attr_available_instances.attr, - NULL, -}; - -static struct attribute_group mdev_type_group1 = { - .name = MBOCHS_TYPE_1, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group mdev_type_group2 = { - .name = MBOCHS_TYPE_2, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group mdev_type_group3 = { - .name = MBOCHS_TYPE_3, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group *mdev_type_groups[] = { - &mdev_type_group1, - &mdev_type_group2, - &mdev_type_group3, - NULL, -}; static const struct vfio_device_ops mbochs_dev_ops = { .close_device = mbochs_close_device, + .init = mbochs_init_dev, + .release = mbochs_release_dev, .read = mbochs_read, .write = mbochs_write, .ioctl = mbochs_ioctl, @@ -1404,6 +1378,7 @@ static const struct vfio_device_ops mbochs_dev_ops = { }; static struct mdev_driver mbochs_driver = { + .device_api = VFIO_DEVICE_API_PCI_STRING, .driver = { .name = "mbochs", .owner = THIS_MODULE, @@ -1412,12 +1387,8 @@ static struct mdev_driver mbochs_driver = { }, .probe = mbochs_probe, .remove = mbochs_remove, -}; - -static const struct mdev_parent_ops mdev_fops = { - .owner = THIS_MODULE, - .device_driver = &mbochs_driver, - .supported_type_groups = mdev_type_groups, + .get_available = mbochs_get_available, + .show_description = mbochs_show_description, }; static const struct file_operations vd_fops = { @@ -1462,7 +1433,9 @@ static int __init mbochs_dev_init(void) if (ret) goto err_class; - ret = mdev_register_device(&mbochs_dev, &mdev_fops); + ret = mdev_register_parent(&mbochs_parent, &mbochs_dev, &mbochs_driver, + mbochs_mdev_types, + ARRAY_SIZE(mbochs_mdev_types)); if (ret) goto err_device; @@ -1483,7 +1456,7 @@ err_cdev: static void __exit mbochs_dev_exit(void) { mbochs_dev.bus = NULL; - mdev_unregister_device(&mbochs_dev); + mdev_unregister_parent(&mbochs_parent); device_unregister(&mbochs_dev); mdev_unregister_driver(&mbochs_driver); diff --git a/samples/vfio-mdev/mdpy.c b/samples/vfio-mdev/mdpy.c index fe5d43e797b6..946e8cfde6fd 100644 --- a/samples/vfio-mdev/mdpy.c +++ b/samples/vfio-mdev/mdpy.c @@ -17,7 +17,6 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -43,36 +42,34 @@ MODULE_LICENSE("GPL v2"); -static int max_devices = 4; -module_param_named(count, max_devices, int, 0444); -MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); - - #define MDPY_TYPE_1 "vga" #define MDPY_TYPE_2 "xga" #define MDPY_TYPE_3 "hd" -static const struct mdpy_type { - const char *name; +static struct mdpy_type { + struct mdev_type type; u32 format; u32 bytepp; u32 width; u32 height; } mdpy_types[] = { { - .name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, + .type.sysfs_name = MDPY_TYPE_1, + .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 640, .height = 480, }, { - .name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, + .type.sysfs_name = MDPY_TYPE_2, + .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1024, .height = 768, }, { - .name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, + .type.sysfs_name = MDPY_TYPE_3, + .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1920, @@ -80,11 +77,17 @@ static const struct mdpy_type { }, }; +static struct mdev_type *mdpy_mdev_types[] = { + &mdpy_types[0].type, + &mdpy_types[1].type, + &mdpy_types[2].type, +}; + static dev_t mdpy_devt; static struct class *mdpy_class; static struct cdev mdpy_cdev; static struct device mdpy_dev; -static u32 mdpy_count; +static struct mdev_parent mdpy_parent; static const struct vfio_device_ops mdpy_dev_ops; /* State of each mdev device */ @@ -216,61 +219,71 @@ static int mdpy_reset(struct mdev_state *mdev_state) return 0; } -static int mdpy_probe(struct mdev_device *mdev) +static int mdpy_init_dev(struct vfio_device *vdev) { + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + struct mdev_device *mdev = to_mdev_device(vdev->dev); const struct mdpy_type *type = - &mdpy_types[mdev_get_type_group_id(mdev)]; - struct device *dev = mdev_dev(mdev); - struct mdev_state *mdev_state; + container_of(mdev->type, struct mdpy_type, type); u32 fbsize; - int ret; - - if (mdpy_count >= max_devices) - return -ENOMEM; - - mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); - if (mdev_state == NULL) - return -ENOMEM; - vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mdpy_dev_ops); + int ret = -ENOMEM; mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); - if (mdev_state->vconfig == NULL) { - ret = -ENOMEM; - goto err_state; - } + if (!mdev_state->vconfig) + return ret; fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); mdev_state->memblk = vmalloc_user(fbsize); - if (!mdev_state->memblk) { - ret = -ENOMEM; - goto err_vconfig; - } - dev_info(dev, "%s: %s (%dx%d)\n", __func__, type->name, type->width, - type->height); + if (!mdev_state->memblk) + goto out_vconfig; mutex_init(&mdev_state->ops_lock); mdev_state->mdev = mdev; - mdev_state->type = type; + mdev_state->type = type; mdev_state->memsize = fbsize; mdpy_create_config_space(mdev_state); mdpy_reset(mdev_state); - mdpy_count++; + dev_info(vdev->dev, "%s: %s (%dx%d)\n", __func__, type->type.pretty_name, + type->width, type->height); + return 0; + +out_vconfig: + kfree(mdev_state->vconfig); + return ret; +} + +static int mdpy_probe(struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + int ret; + + mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev, + &mdpy_dev_ops); + if (IS_ERR(mdev_state)) + return PTR_ERR(mdev_state); ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev); if (ret) - goto err_mem; + goto err_put_vdev; dev_set_drvdata(&mdev->dev, mdev_state); return 0; -err_mem: + +err_put_vdev: + vfio_put_device(&mdev_state->vdev); + return ret; +} + +static void mdpy_release_dev(struct vfio_device *vdev) +{ + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + vfree(mdev_state->memblk); -err_vconfig: kfree(mdev_state->vconfig); -err_state: - vfio_uninit_group_dev(&mdev_state->vdev); - kfree(mdev_state); - return ret; + vfio_free_device(vdev); } static void mdpy_remove(struct mdev_device *mdev) @@ -280,12 +293,7 @@ static void mdpy_remove(struct mdev_device *mdev) dev_info(&mdev->dev, "%s\n", __func__); vfio_unregister_group_dev(&mdev_state->vdev); - vfree(mdev_state->memblk); - kfree(mdev_state->vconfig); - vfio_uninit_group_dev(&mdev_state->vdev); - kfree(mdev_state); - - mdpy_count--; + vfio_put_device(&mdev_state->vdev); } static ssize_t mdpy_read(struct vfio_device *vdev, char __user *buf, @@ -641,73 +649,17 @@ static const struct attribute_group *mdev_dev_groups[] = { NULL, }; -static ssize_t name_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) +static ssize_t mdpy_show_description(struct mdev_type *mtype, char *buf) { - const struct mdpy_type *type = - &mdpy_types[mtype_get_type_group_id(mtype)]; - - return sprintf(buf, "%s\n", type->name); -} -static MDEV_TYPE_ATTR_RO(name); - -static ssize_t description_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - const struct mdpy_type *type = - &mdpy_types[mtype_get_type_group_id(mtype)]; + struct mdpy_type *type = container_of(mtype, struct mdpy_type, type); return sprintf(buf, "virtual display, %dx%d framebuffer\n", type->width, type->height); } -static MDEV_TYPE_ATTR_RO(description); - -static ssize_t available_instances_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", max_devices - mdpy_count); -} -static MDEV_TYPE_ATTR_RO(available_instances); - -static ssize_t device_api_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); -} -static MDEV_TYPE_ATTR_RO(device_api); - -static struct attribute *mdev_types_attrs[] = { - &mdev_type_attr_name.attr, - &mdev_type_attr_description.attr, - &mdev_type_attr_device_api.attr, - &mdev_type_attr_available_instances.attr, - NULL, -}; - -static struct attribute_group mdev_type_group1 = { - .name = MDPY_TYPE_1, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group mdev_type_group2 = { - .name = MDPY_TYPE_2, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group mdev_type_group3 = { - .name = MDPY_TYPE_3, - .attrs = mdev_types_attrs, -}; - -static struct attribute_group *mdev_type_groups[] = { - &mdev_type_group1, - &mdev_type_group2, - &mdev_type_group3, - NULL, -}; static const struct vfio_device_ops mdpy_dev_ops = { + .init = mdpy_init_dev, + .release = mdpy_release_dev, .read = mdpy_read, .write = mdpy_write, .ioctl = mdpy_ioctl, @@ -715,6 +667,8 @@ static const struct vfio_device_ops mdpy_dev_ops = { }; static struct mdev_driver mdpy_driver = { + .device_api = VFIO_DEVICE_API_PCI_STRING, + .max_instances = 4, .driver = { .name = "mdpy", .owner = THIS_MODULE, @@ -723,12 +677,7 @@ static struct mdev_driver mdpy_driver = { }, .probe = mdpy_probe, .remove = mdpy_remove, -}; - -static const struct mdev_parent_ops mdev_fops = { - .owner = THIS_MODULE, - .device_driver = &mdpy_driver, - .supported_type_groups = mdev_type_groups, + .show_description = mdpy_show_description, }; static const struct file_operations vd_fops = { @@ -771,7 +720,9 @@ static int __init mdpy_dev_init(void) if (ret) goto err_class; - ret = mdev_register_device(&mdpy_dev, &mdev_fops); + ret = mdev_register_parent(&mdpy_parent, &mdpy_dev, &mdpy_driver, + mdpy_mdev_types, + ARRAY_SIZE(mdpy_mdev_types)); if (ret) goto err_device; @@ -792,7 +743,7 @@ err_cdev: static void __exit mdpy_dev_exit(void) { mdpy_dev.bus = NULL; - mdev_unregister_device(&mdpy_dev); + mdev_unregister_parent(&mdpy_parent); device_unregister(&mdpy_dev); mdev_unregister_driver(&mdpy_driver); @@ -802,5 +753,8 @@ static void __exit mdpy_dev_exit(void) mdpy_class = NULL; } +module_param_named(count, mdpy_driver.max_instances, int, 0444); +MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); + module_init(mdpy_dev_init) module_exit(mdpy_dev_exit) diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c index a0e1a469bd47..e72085fc1376 100644 --- a/samples/vfio-mdev/mtty.c +++ b/samples/vfio-mdev/mtty.c @@ -12,7 +12,6 @@ #include <linux/init.h> #include <linux/module.h> -#include <linux/device.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/poll.h> @@ -20,7 +19,6 @@ #include <linux/cdev.h> #include <linux/sched.h> #include <linux/wait.h> -#include <linux/uuid.h> #include <linux/vfio.h> #include <linux/iommu.h> #include <linux/sysfs.h> @@ -74,6 +72,7 @@ static struct mtty_dev { struct cdev vd_cdev; struct idr vd_idr; struct device dev; + struct mdev_parent parent; } mtty_dev; struct mdev_region_info { @@ -144,6 +143,21 @@ struct mdev_state { int nr_ports; }; +static struct mtty_type { + struct mdev_type type; + int nr_ports; +} mtty_types[2] = { + { .nr_ports = 1, .type.sysfs_name = "1", + .type.pretty_name = "Single port serial" }, + { .nr_ports = 2, .type.sysfs_name = "2", + .type.pretty_name = "Dual port serial" }, +}; + +static struct mdev_type *mtty_mdev_types[] = { + &mtty_types[0].type, + &mtty_types[1].type, +}; + static atomic_t mdev_avail_ports = ATOMIC_INIT(MAX_MTTYS); static const struct file_operations vd_fops = { @@ -703,71 +717,82 @@ accessfailed: return ret; } -static int mtty_probe(struct mdev_device *mdev) +static int mtty_init_dev(struct vfio_device *vdev) { - struct mdev_state *mdev_state; - int nr_ports = mdev_get_type_group_id(mdev) + 1; + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + struct mdev_device *mdev = to_mdev_device(vdev->dev); + struct mtty_type *type = + container_of(mdev->type, struct mtty_type, type); int avail_ports = atomic_read(&mdev_avail_ports); int ret; do { - if (avail_ports < nr_ports) + if (avail_ports < type->nr_ports) return -ENOSPC; } while (!atomic_try_cmpxchg(&mdev_avail_ports, - &avail_ports, avail_ports - nr_ports)); - - mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); - if (mdev_state == NULL) { - ret = -ENOMEM; - goto err_nr_ports; - } + &avail_ports, + avail_ports - type->nr_ports)); - vfio_init_group_dev(&mdev_state->vdev, &mdev->dev, &mtty_dev_ops); - - mdev_state->nr_ports = nr_ports; + mdev_state->nr_ports = type->nr_ports; mdev_state->irq_index = -1; mdev_state->s[0].max_fifo_size = MAX_FIFO_SIZE; mdev_state->s[1].max_fifo_size = MAX_FIFO_SIZE; mutex_init(&mdev_state->rxtx_lock); - mdev_state->vconfig = kzalloc(MTTY_CONFIG_SPACE_SIZE, GFP_KERNEL); - if (mdev_state->vconfig == NULL) { + mdev_state->vconfig = kzalloc(MTTY_CONFIG_SPACE_SIZE, GFP_KERNEL); + if (!mdev_state->vconfig) { ret = -ENOMEM; - goto err_state; + goto err_nr_ports; } mutex_init(&mdev_state->ops_lock); mdev_state->mdev = mdev; - mtty_create_config_space(mdev_state); + return 0; + +err_nr_ports: + atomic_add(type->nr_ports, &mdev_avail_ports); + return ret; +} + +static int mtty_probe(struct mdev_device *mdev) +{ + struct mdev_state *mdev_state; + int ret; + + mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev, + &mtty_dev_ops); + if (IS_ERR(mdev_state)) + return PTR_ERR(mdev_state); ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev); if (ret) - goto err_vconfig; + goto err_put_vdev; dev_set_drvdata(&mdev->dev, mdev_state); return 0; -err_vconfig: - kfree(mdev_state->vconfig); -err_state: - vfio_uninit_group_dev(&mdev_state->vdev); - kfree(mdev_state); -err_nr_ports: - atomic_add(nr_ports, &mdev_avail_ports); +err_put_vdev: + vfio_put_device(&mdev_state->vdev); return ret; } +static void mtty_release_dev(struct vfio_device *vdev) +{ + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + + atomic_add(mdev_state->nr_ports, &mdev_avail_ports); + kfree(mdev_state->vconfig); + vfio_free_device(vdev); +} + static void mtty_remove(struct mdev_device *mdev) { struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev); - int nr_ports = mdev_state->nr_ports; vfio_unregister_group_dev(&mdev_state->vdev); - - kfree(mdev_state->vconfig); - vfio_uninit_group_dev(&mdev_state->vdev); - kfree(mdev_state); - atomic_add(nr_ports, &mdev_avail_ports); + vfio_put_device(&mdev_state->vdev); } static int mtty_reset(struct mdev_state *mdev_state) @@ -1208,37 +1233,10 @@ static long mtty_ioctl(struct vfio_device *vdev, unsigned int cmd, } static ssize_t -sample_mtty_dev_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "This is phy device\n"); -} - -static DEVICE_ATTR_RO(sample_mtty_dev); - -static struct attribute *mtty_dev_attrs[] = { - &dev_attr_sample_mtty_dev.attr, - NULL, -}; - -static const struct attribute_group mtty_dev_group = { - .name = "mtty_dev", - .attrs = mtty_dev_attrs, -}; - -static const struct attribute_group *mtty_dev_groups[] = { - &mtty_dev_group, - NULL, -}; - -static ssize_t sample_mdev_dev_show(struct device *dev, struct device_attribute *attr, char *buf) { - if (mdev_from_dev(dev)) - return sprintf(buf, "This is MDEV %s\n", dev_name(dev)); - - return sprintf(buf, "\n"); + return sprintf(buf, "This is MDEV %s\n", dev_name(dev)); } static DEVICE_ATTR_RO(sample_mdev_dev); @@ -1258,68 +1256,24 @@ static const struct attribute_group *mdev_dev_groups[] = { NULL, }; -static ssize_t name_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - static const char *name_str[2] = { "Single port serial", - "Dual port serial" }; - - return sysfs_emit(buf, "%s\n", - name_str[mtype_get_type_group_id(mtype)]); -} - -static MDEV_TYPE_ATTR_RO(name); - -static ssize_t available_instances_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, - char *buf) +static unsigned int mtty_get_available(struct mdev_type *mtype) { - unsigned int ports = mtype_get_type_group_id(mtype) + 1; - - return sprintf(buf, "%d\n", atomic_read(&mdev_avail_ports) / ports); -} - -static MDEV_TYPE_ATTR_RO(available_instances); + struct mtty_type *type = container_of(mtype, struct mtty_type, type); -static ssize_t device_api_show(struct mdev_type *mtype, - struct mdev_type_attribute *attr, char *buf) -{ - return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); + return atomic_read(&mdev_avail_ports) / type->nr_ports; } -static MDEV_TYPE_ATTR_RO(device_api); - -static struct attribute *mdev_types_attrs[] = { - &mdev_type_attr_name.attr, - &mdev_type_attr_device_api.attr, - &mdev_type_attr_available_instances.attr, - NULL, -}; - -static struct attribute_group mdev_type_group1 = { - .name = "1", - .attrs = mdev_types_attrs, -}; - -static struct attribute_group mdev_type_group2 = { - .name = "2", - .attrs = mdev_types_attrs, -}; - -static struct attribute_group *mdev_type_groups[] = { - &mdev_type_group1, - &mdev_type_group2, - NULL, -}; - static const struct vfio_device_ops mtty_dev_ops = { .name = "vfio-mtty", + .init = mtty_init_dev, + .release = mtty_release_dev, .read = mtty_read, .write = mtty_write, .ioctl = mtty_ioctl, }; static struct mdev_driver mtty_driver = { + .device_api = VFIO_DEVICE_API_PCI_STRING, .driver = { .name = "mtty", .owner = THIS_MODULE, @@ -1328,13 +1282,7 @@ static struct mdev_driver mtty_driver = { }, .probe = mtty_probe, .remove = mtty_remove, -}; - -static const struct mdev_parent_ops mdev_fops = { - .owner = THIS_MODULE, - .device_driver = &mtty_driver, - .dev_attr_groups = mtty_dev_groups, - .supported_type_groups = mdev_type_groups, + .get_available = mtty_get_available, }; static void mtty_device_release(struct device *dev) @@ -1385,7 +1333,9 @@ static int __init mtty_dev_init(void) if (ret) goto err_class; - ret = mdev_register_device(&mtty_dev.dev, &mdev_fops); + ret = mdev_register_parent(&mtty_dev.parent, &mtty_dev.dev, + &mtty_driver, mtty_mdev_types, + ARRAY_SIZE(mtty_mdev_types)); if (ret) goto err_device; return 0; @@ -1405,7 +1355,7 @@ err_cdev: static void __exit mtty_dev_exit(void) { mtty_dev.dev.bus = NULL; - mdev_unregister_device(&mtty_dev.dev); + mdev_unregister_parent(&mtty_dev.parent); device_unregister(&mtty_dev.dev); idr_destroy(&mtty_dev.vd_idr); |