diff options
Diffstat (limited to 'tools')
636 files changed, 24230 insertions, 15598 deletions
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 1f6d904c6481..798e60b5454b 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -434,6 +434,7 @@ #define X86_FEATURE_SEV_ES (19*32+ 3) /* AMD Secure Encrypted Virtualization - Encrypted State */ #define X86_FEATURE_V_TSC_AUX (19*32+ 9) /* "" Virtual TSC_AUX */ #define X86_FEATURE_SME_COHERENT (19*32+10) /* "" AMD hardware-enforced cache coherency */ +#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* AMD SEV-ES full debug state swap support */ /* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */ #define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* "" No Nested Data Breakpoints */ diff --git a/tools/arch/x86/include/asm/irq_vectors.h b/tools/arch/x86/include/asm/irq_vectors.h index 43dcb9284208..3a19904c2db6 100644 --- a/tools/arch/x86/include/asm/irq_vectors.h +++ b/tools/arch/x86/include/asm/irq_vectors.h @@ -35,13 +35,6 @@ */ #define FIRST_EXTERNAL_VECTOR 0x20 -/* - * Reserve the lowest usable vector (and hence lowest priority) 0x20 for - * triggering cleanup after irq migration. 0x21-0x2f will still be used - * for device interrupts. - */ -#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR - #define IA32_SYSCALL_VECTOR 0x80 /* diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index a00a53e15ab7..1d111350197f 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -57,6 +57,7 @@ #define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */ #define PRED_CMD_IBPB BIT(0) /* Indirect Branch Prediction Barrier */ +#define PRED_CMD_SBPB BIT(7) /* Selective Branch Prediction Barrier */ #define MSR_PPIN_CTL 0x0000004e #define MSR_PPIN 0x0000004f @@ -155,6 +156,15 @@ * Not susceptible to Post-Barrier * Return Stack Buffer Predictions. */ +#define ARCH_CAP_GDS_CTRL BIT(25) /* + * CPU is vulnerable to Gather + * Data Sampling (GDS) and + * has controls for mitigation. + */ +#define ARCH_CAP_GDS_NO BIT(26) /* + * CPU is not vulnerable to Gather + * Data Sampling (GDS). + */ #define ARCH_CAP_XAPIC_DISABLE BIT(21) /* * IA32_XAPIC_DISABLE_STATUS MSR @@ -178,6 +188,8 @@ #define RNGDS_MITG_DIS BIT(0) /* SRBDS support */ #define RTM_ALLOW BIT(1) /* TSX development mode */ #define FB_CLEAR_DIS BIT(3) /* CPU Fill buffer clear disable */ +#define GDS_MITG_DIS BIT(4) /* Disable GDS mitigation */ +#define GDS_MITG_LOCKED BIT(5) /* GDS mitigation locked */ #define MSR_IA32_SYSENTER_CS 0x00000174 #define MSR_IA32_SYSENTER_ESP 0x00000175 diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c index 2883660d6b67..04c47745b3ea 100644 --- a/tools/bpf/bpftool/gen.c +++ b/tools/bpf/bpftool/gen.c @@ -1209,7 +1209,7 @@ static int do_skeleton(int argc, char **argv) codegen("\ \n\ \n\ - s->data = (void *)%2$s__elf_bytes(&s->data_sz); \n\ + s->data = %2$s__elf_bytes(&s->data_sz); \n\ \n\ obj->skeleton = s; \n\ return 0; \n\ diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c index 0b214f6ab5c8..2e5c231e08ac 100644 --- a/tools/bpf/bpftool/link.c +++ b/tools/bpf/bpftool/link.c @@ -83,7 +83,7 @@ const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] = { #define perf_event_name(array, id) ({ \ const char *event_str = NULL; \ \ - if ((id) >= 0 && (id) < ARRAY_SIZE(array)) \ + if ((id) < ARRAY_SIZE(array)) \ event_str = array[id]; \ event_str; \ }) diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index 89430338a3d9..fac42486a8cf 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build @@ -117,6 +117,16 @@ $(OUTPUT)%.s: %.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_s_c) +# bison and flex files are generated in the OUTPUT directory +# so it needs a separate rule to depend on them properly +$(OUTPUT)%-bison.o: $(OUTPUT)%-bison.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,$(host)cc_o_c) + +$(OUTPUT)%-flex.o: $(OUTPUT)%-flex.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,$(host)cc_o_c) + # Gather build data: # obj-y - list of build objects # subdir-y - list of directories to nest diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index f0c5de018a95..dad79ede4e0a 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -340,7 +340,7 @@ $(OUTPUT)test-jvmti-cmlr.bin: $(BUILD) $(OUTPUT)test-llvm.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ -L$(shell $(LLVM_CONFIG) --libdir) \ $(shell $(LLVM_CONFIG) --libs Core BPF) \ @@ -348,17 +348,15 @@ $(OUTPUT)test-llvm.bin: > $(@:.bin=.make.output) 2>&1 $(OUTPUT)test-llvm-version.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ > $(@:.bin=.make.output) 2>&1 $(OUTPUT)test-clang.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ -L$(shell $(LLVM_CONFIG) --libdir) \ - -Wl,--start-group -lclangBasic -lclangDriver \ - -lclangFrontend -lclangEdit -lclangLex \ - -lclangAST -Wl,--end-group \ + -Wl,--start-group -lclang-cpp -Wl,--end-group \ $(shell $(LLVM_CONFIG) --libs Core option) \ $(shell $(LLVM_CONFIG) --system-libs) \ > $(@:.bin=.make.output) 2>&1 diff --git a/tools/build/feature/test-clang.cpp b/tools/build/feature/test-clang.cpp deleted file mode 100644 index 7d87075cd1c5..000000000000 --- a/tools/build/feature/test-clang.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "clang/Basic/Version.h" -#if CLANG_VERSION_MAJOR < 8 -#include "clang/Basic/VirtualFileSystem.h" -#endif -#include "clang/Driver/Driver.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/Support/ManagedStatic.h" -#if CLANG_VERSION_MAJOR >= 8 -#include "llvm/Support/VirtualFileSystem.h" -#endif -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::driver; - -int main() -{ - IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); - IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); - - DiagnosticsEngine Diags(DiagID, &*DiagOpts); - Driver TheDriver("test", "bpf-pc-linux", Diags); - - llvm::llvm_shutdown(); - return 0; -} diff --git a/tools/build/feature/test-cxx.cpp b/tools/build/feature/test-cxx.cpp deleted file mode 100644 index 396aaedd2418..000000000000 --- a/tools/build/feature/test-cxx.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <iostream> -#include <memory> - -static void print_str(std::string s) -{ - std::cout << s << std::endl; -} - -int main() -{ - std::string s("Hello World!"); - print_str(std::move(s)); - std::cout << "|" << s << "|" << std::endl; - return 0; -} diff --git a/tools/build/feature/test-llvm-version.cpp b/tools/build/feature/test-llvm-version.cpp deleted file mode 100644 index 8a091625446a..000000000000 --- a/tools/build/feature/test-llvm-version.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <cstdio> -#include "llvm/Config/llvm-config.h" - -#define NUM_VERSION (((LLVM_VERSION_MAJOR) << 16) + (LLVM_VERSION_MINOR << 8) + LLVM_VERSION_PATCH) -#define pass int main() {printf("%x\n", NUM_VERSION); return 0;} - -#if NUM_VERSION >= 0x030900 -pass -#else -# error This LLVM is not tested yet. -#endif diff --git a/tools/build/feature/test-llvm.cpp b/tools/build/feature/test-llvm.cpp deleted file mode 100644 index 88a3d1bdd9f6..000000000000 --- a/tools/build/feature/test-llvm.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/raw_ostream.h" -#define NUM_VERSION (((LLVM_VERSION_MAJOR) << 16) + (LLVM_VERSION_MINOR << 8) + LLVM_VERSION_PATCH) - -#if NUM_VERSION < 0x030900 -# error "LLVM version too low" -#endif -int main() -{ - llvm::errs() << "Hello World!\n"; - llvm::llvm_shutdown(); - return 0; -} diff --git a/tools/cgroup/iocost_monitor.py b/tools/cgroup/iocost_monitor.py index 0dbbc67400fc..933c750b319b 100644 --- a/tools/cgroup/iocost_monitor.py +++ b/tools/cgroup/iocost_monitor.py @@ -100,6 +100,7 @@ class IocStat: self.period_at = ioc.period_at.value_() / 1_000_000 self.vperiod_at = ioc.period_at_vtime.value_() / VTIME_PER_SEC self.vrate_pct = ioc.vtime_base_rate.value_() * 100 / VTIME_PER_USEC + self.ivrate_pct = ioc.vtime_rate.counter.value_() * 100 / VTIME_PER_USEC self.busy_level = ioc.busy_level.value_() self.autop_idx = ioc.autop_idx.value_() self.user_cost_model = ioc.user_cost_model.value_() @@ -119,7 +120,9 @@ class IocStat: 'period_at' : self.period_at, 'period_vtime_at' : self.vperiod_at, 'busy_level' : self.busy_level, - 'vrate_pct' : self.vrate_pct, } + 'vrate_pct' : self.vrate_pct, + 'ivrate_pct' : self.ivrate_pct, + } def table_preamble_str(self): state = ('RUN' if self.running else 'IDLE') if self.enabled else 'OFF' @@ -127,7 +130,7 @@ class IocStat: f'per={self.period_ms}ms ' \ f'cur_per={self.period_at:.3f}:v{self.vperiod_at:.3f} ' \ f'busy={self.busy_level:+3} ' \ - f'vrate={self.vrate_pct:6.2f}% ' \ + f'vrate={self.vrate_pct:6.2f}%:{self.ivrate_pct:6.2f}% ' \ f'params={self.autop_name}' if self.user_cost_model or self.user_qos_params: output += f'({"C" if self.user_cost_model else ""}{"Q" if self.user_qos_params else ""})' @@ -135,7 +138,7 @@ class IocStat: def table_header_str(self): return f'{"":25} active {"weight":>9} {"hweight%":>13} {"inflt%":>6} ' \ - f'{"debt":>7} {"delay":>7} {"usage%"}' + f'{"usage%":>6} {"wait":>7} {"debt":>7} {"delay":>7}' class IocgStat: def __init__(self, iocg): @@ -161,6 +164,8 @@ class IocgStat: self.usage = (100 * iocg.usage_delta_us.value_() / ioc.period_us.value_()) if self.active else 0 + self.wait_ms = (iocg.stat.wait_us.value_() - + iocg.last_stat.wait_us.value_()) / 1000 self.debt_ms = iocg.abs_vdebt.value_() / VTIME_PER_USEC / 1000 if blkg.use_delay.counter.value_() != 0: self.delay_ms = blkg.delay_nsec.counter.value_() / 1_000_000 @@ -177,9 +182,10 @@ class IocgStat: 'hweight_active_pct' : self.hwa_pct, 'hweight_inuse_pct' : self.hwi_pct, 'inflight_pct' : self.inflight_pct, + 'usage_pct' : self.usage, + 'wait_ms' : self.wait_ms, 'debt_ms' : self.debt_ms, 'delay_ms' : self.delay_ms, - 'usage_pct' : self.usage, 'address' : self.address } return out @@ -189,9 +195,10 @@ class IocgStat: f'{round(self.inuse):5}/{round(self.active):5} ' \ f'{self.hwi_pct:6.2f}/{self.hwa_pct:6.2f} ' \ f'{self.inflight_pct:6.2f} ' \ + f'{min(self.usage, 999):6.2f} ' \ + f'{self.wait_ms:7.2f} ' \ f'{self.debt_ms:7.2f} ' \ - f'{self.delay_ms:7.2f} '\ - f'{min(self.usage, 999):6.2f}' + f'{self.delay_ms:7.2f}' out = out.rstrip(':') return out @@ -221,7 +228,7 @@ ioc = None for i, ptr in radix_tree_for_each(blkcg_root.blkg_tree.address_of_()): blkg = drgn.Object(prog, 'struct blkcg_gq', address=ptr) try: - if devname == blkg.q.kobj.parent.name.string_().decode('utf-8'): + if devname == blkg.q.mq_kobj.parent.name.string_().decode('utf-8'): q_id = blkg.q.id.value_() if blkg.pd[plid]: root_iocg = container_of(blkg.pd[plid], 'struct ioc_gq', 'pd') diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index f8deae4e26a1..44bbf80f0cfd 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -51,9 +51,9 @@ enum autochan { * Has the side effect of filling the channels[i].location values used * in processing the buffer output. **/ -static int size_from_channelarray(struct iio_channel_info *channels, int num_channels) +static unsigned int size_from_channelarray(struct iio_channel_info *channels, int num_channels) { - int bytes = 0; + unsigned int bytes = 0; int i = 0; while (i < num_channels) { @@ -348,7 +348,7 @@ int main(int argc, char **argv) ssize_t read_size; int dev_num = -1, trig_num = -1; char *buffer_access = NULL; - int scan_size; + unsigned int scan_size; int noevents = 0; int notrigger = 0; char *dummy; @@ -674,7 +674,16 @@ int main(int argc, char **argv) } scan_size = size_from_channelarray(channels, num_channels); - data = malloc(scan_size * buf_len); + + size_t total_buf_len = scan_size * buf_len; + + if (scan_size > 0 && total_buf_len / scan_size != buf_len) { + ret = -EFAULT; + perror("Integer overflow happened when calculate scan_size * buf_len"); + goto error; + } + + data = malloc(total_buf_len); if (!data) { ret = -ENOMEM; goto error; diff --git a/tools/include/linux/btf_ids.h b/tools/include/linux/btf_ids.h index 71e54b1e3796..2f882d5cb30f 100644 --- a/tools/include/linux/btf_ids.h +++ b/tools/include/linux/btf_ids.h @@ -38,7 +38,7 @@ asm( \ ____BTF_ID(symbol) #define __ID(prefix) \ - __PASTE(prefix, __COUNTER__) + __PASTE(__PASTE(prefix, __COUNTER__), __LINE__) /* * The BTF_ID defines unique symbol for each ID pointing diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h index a03d9bba5151..f3c82ab5b14c 100644 --- a/tools/include/linux/mm.h +++ b/tools/include/linux/mm.h @@ -11,8 +11,6 @@ #define PHYS_ADDR_MAX (~(phys_addr_t)0) -#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) -#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) @@ -29,7 +27,7 @@ static inline void *phys_to_virt(unsigned long address) return __va(address); } -void reserve_bootmem_region(phys_addr_t start, phys_addr_t end); +void reserve_bootmem_region(phys_addr_t start, phys_addr_t end, int nid); static inline void totalram_pages_inc(void) { diff --git a/tools/include/linux/seq_file.h b/tools/include/linux/seq_file.h index 102fd9217f1f..f6bc226af0c1 100644 --- a/tools/include/linux/seq_file.h +++ b/tools/include/linux/seq_file.h @@ -1,4 +1,6 @@ #ifndef _TOOLS_INCLUDE_LINUX_SEQ_FILE_H #define _TOOLS_INCLUDE_LINUX_SEQ_FILE_H +struct seq_file; + #endif /* _TOOLS_INCLUDE_LINUX_SEQ_FILE_H */ diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h index fd6c1cb585db..abe087c53b4b 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -820,8 +820,11 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) #define __NR_cachestat 451 __SYSCALL(__NR_cachestat, sys_cachestat) +#define __NR_fchmodat2 452 +__SYSCALL(__NR_fchmodat2, sys_fchmodat2) + #undef __NR_syscalls -#define __NR_syscalls 452 +#define __NR_syscalls 453 /* * 32 bit systems traditionally used different diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h index a87bbbbca2d4..794c1d857677 100644 --- a/tools/include/uapi/drm/drm.h +++ b/tools/include/uapi/drm/drm.h @@ -673,8 +673,11 @@ struct drm_gem_open { * Bitfield of supported PRIME sharing capabilities. See &DRM_PRIME_CAP_IMPORT * and &DRM_PRIME_CAP_EXPORT. * - * PRIME buffers are exposed as dma-buf file descriptors. See - * Documentation/gpu/drm-mm.rst, section "PRIME Buffer Sharing". + * Starting from kernel version 6.6, both &DRM_PRIME_CAP_IMPORT and + * &DRM_PRIME_CAP_EXPORT are always advertised. + * + * PRIME buffers are exposed as dma-buf file descriptors. + * See :ref:`prime_buffer_sharing`. */ #define DRM_CAP_PRIME 0x5 /** @@ -682,6 +685,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports importing PRIME * buffers via the &DRM_IOCTL_PRIME_FD_TO_HANDLE ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_IMPORT 0x1 /** @@ -689,6 +694,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports exporting PRIME * buffers via the &DRM_IOCTL_PRIME_HANDLE_TO_FD ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_EXPORT 0x2 /** @@ -756,15 +763,14 @@ struct drm_gem_open { /** * DRM_CAP_SYNCOBJ * - * If set to 1, the driver supports sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * If set to 1, the driver supports sync objects. See :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ 0x13 /** * DRM_CAP_SYNCOBJ_TIMELINE * * If set to 1, the driver supports timeline operations on sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ_TIMELINE 0x14 @@ -909,6 +915,27 @@ struct drm_syncobj_timeline_wait { __u32 pad; }; +/** + * struct drm_syncobj_eventfd + * @handle: syncobj handle. + * @flags: Zero to wait for the point to be signalled, or + * &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE to wait for a fence to be + * available for the point. + * @point: syncobj timeline point (set to zero for binary syncobjs). + * @fd: Existing eventfd to sent events to. + * @pad: Must be zero. + * + * Register an eventfd to be signalled by a syncobj. The eventfd counter will + * be incremented by one. + */ +struct drm_syncobj_eventfd { + __u32 handle; + __u32 flags; + __u64 point; + __s32 fd; + __u32 pad; +}; + struct drm_syncobj_array { __u64 handles; @@ -1169,6 +1196,8 @@ extern "C" { */ #define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd) + /* * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x9f. @@ -1180,25 +1209,50 @@ extern "C" { #define DRM_COMMAND_BASE 0x40 #define DRM_COMMAND_END 0xA0 -/* - * Header for events written back to userspace on the drm fd. The - * type defines the type of event, the length specifies the total - * length of the event (including the header), and user_data is - * typically a 64 bit value passed with the ioctl that triggered the - * event. A read on the drm fd will always only return complete - * events, that is, if for example the read buffer is 100 bytes, and - * there are two 64 byte events pending, only one will be returned. +/** + * struct drm_event - Header for DRM events + * @type: event type. + * @length: total number of payload bytes (including header). * - * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and - * up are chipset specific. + * This struct is a header for events written back to user-space on the DRM FD. + * A read on the DRM FD will always only return complete events: e.g. if the + * read buffer is 100 bytes large and there are two 64 byte events pending, + * only one will be returned. + * + * Event types 0 - 0x7fffffff are generic DRM events, 0x80000000 and + * up are chipset specific. Generic DRM events include &DRM_EVENT_VBLANK, + * &DRM_EVENT_FLIP_COMPLETE and &DRM_EVENT_CRTC_SEQUENCE. */ struct drm_event { __u32 type; __u32 length; }; +/** + * DRM_EVENT_VBLANK - vertical blanking event + * + * This event is sent in response to &DRM_IOCTL_WAIT_VBLANK with the + * &_DRM_VBLANK_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_VBLANK 0x01 +/** + * DRM_EVENT_FLIP_COMPLETE - page-flip completion event + * + * This event is sent in response to an atomic commit or legacy page-flip with + * the &DRM_MODE_PAGE_FLIP_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_FLIP_COMPLETE 0x02 +/** + * DRM_EVENT_CRTC_SEQUENCE - CRTC sequence event + * + * This event is sent in response to &DRM_IOCTL_CRTC_QUEUE_SEQUENCE. + * + * The event payload is a struct drm_event_crtc_sequence. + */ #define DRM_EVENT_CRTC_SEQUENCE 0x03 struct drm_event_vblank { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 8790b3962e4b..5f13db15a3c7 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -932,7 +932,14 @@ enum bpf_map_type { */ BPF_MAP_TYPE_CGROUP_STORAGE = BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, - BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED, + /* BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE is available to bpf programs + * attaching to a cgroup. The new mechanism (BPF_MAP_TYPE_CGRP_STORAGE + + * local percpu kptr) supports all BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE + * functionality and more. So mark * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE + * deprecated. + */ + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_STACK, BPF_MAP_TYPE_SK_STORAGE, @@ -1962,7 +1969,9 @@ union bpf_attr { * performed again, if the helper is used in combination with * direct packet access. * Return - * 0 on success, or a negative error in case of failure. + * 0 on success, or a negative error in case of failure. Positive + * error indicates a potential drop or congestion in the target + * device. The particular positive error codes are not defined. * * u64 bpf_get_current_pid_tgid(void) * Description diff --git a/tools/include/uapi/linux/netdev.h b/tools/include/uapi/linux/netdev.h index c1634b95c223..2943a151d4f1 100644 --- a/tools/include/uapi/linux/netdev.h +++ b/tools/include/uapi/linux/netdev.h @@ -38,11 +38,27 @@ enum netdev_xdp_act { NETDEV_XDP_ACT_MASK = 127, }; +/** + * enum netdev_xdp_rx_metadata + * @NETDEV_XDP_RX_METADATA_TIMESTAMP: Device is capable of exposing receive HW + * timestamp via bpf_xdp_metadata_rx_timestamp(). + * @NETDEV_XDP_RX_METADATA_HASH: Device is capable of exposing receive packet + * hash via bpf_xdp_metadata_rx_hash(). + */ +enum netdev_xdp_rx_metadata { + NETDEV_XDP_RX_METADATA_TIMESTAMP = 1, + NETDEV_XDP_RX_METADATA_HASH = 2, + + /* private: */ + NETDEV_XDP_RX_METADATA_MASK = 3, +}; + enum { NETDEV_A_DEV_IFINDEX = 1, NETDEV_A_DEV_PAD, NETDEV_A_DEV_XDP_FEATURES, NETDEV_A_DEV_XDP_ZC_MAX_SEGS, + NETDEV_A_DEV_XDP_RX_METADATA_FEATURES, __NETDEV_A_DEV_MAX, NETDEV_A_DEV_MAX = (__NETDEV_A_DEV_MAX - 1) diff --git a/tools/include/uapi/linux/seccomp.h b/tools/include/uapi/linux/seccomp.h new file mode 100644 index 000000000000..dbfc9b37fcae --- /dev/null +++ b/tools/include/uapi/linux/seccomp.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_SECCOMP_H +#define _UAPI_LINUX_SECCOMP_H + +#include <linux/compiler.h> +#include <linux/types.h> + + +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */ +#define SECCOMP_MODE_DISABLED 0 /* seccomp is not in use. */ +#define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */ +#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ + +/* Valid operations for seccomp syscall. */ +#define SECCOMP_SET_MODE_STRICT 0 +#define SECCOMP_SET_MODE_FILTER 1 +#define SECCOMP_GET_ACTION_AVAIL 2 +#define SECCOMP_GET_NOTIF_SIZES 3 + +/* Valid flags for SECCOMP_SET_MODE_FILTER */ +#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0) +#define SECCOMP_FILTER_FLAG_LOG (1UL << 1) +#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) +#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3) +#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) +/* Received notifications wait in killable state (only respond to fatal signals) */ +#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5) + +/* + * All BPF programs must return a 32-bit value. + * The bottom 16-bits are for optional return data. + * The upper 16-bits are ordered from least permissive values to most, + * as a signed value (so 0x8000000 is negative). + * + * The ordering ensures that a min_t() over composed return values always + * selects the least permissive choice. + */ +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ +#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ +#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */ +#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ +#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ +#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ + +/* Masks for the return value sections. */ +#define SECCOMP_RET_ACTION_FULL 0xffff0000U +#define SECCOMP_RET_ACTION 0x7fff0000U +#define SECCOMP_RET_DATA 0x0000ffffU + +/** + * struct seccomp_data - the format the BPF program executes over. + * @nr: the system call number + * @arch: indicates system call convention as an AUDIT_ARCH_* value + * as defined in <linux/audit.h>. + * @instruction_pointer: at the time of the system call. + * @args: up to 6 system call arguments always stored as 64-bit values + * regardless of the architecture. + */ +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; + +struct seccomp_notif_sizes { + __u16 seccomp_notif; + __u16 seccomp_notif_resp; + __u16 seccomp_data; +}; + +struct seccomp_notif { + __u64 id; + __u32 pid; + __u32 flags; + struct seccomp_data data; +}; + +/* + * Valid flags for struct seccomp_notif_resp + * + * Note, the SECCOMP_USER_NOTIF_FLAG_CONTINUE flag must be used with caution! + * If set by the process supervising the syscalls of another process the + * syscall will continue. This is problematic because of an inherent TOCTOU. + * An attacker can exploit the time while the supervised process is waiting on + * a response from the supervising process to rewrite syscall arguments which + * are passed as pointers of the intercepted syscall. + * It should be absolutely clear that this means that the seccomp notifier + * _cannot_ be used to implement a security policy! It should only ever be used + * in scenarios where a more privileged process supervises the syscalls of a + * lesser privileged process to get around kernel-enforced security + * restrictions when the privileged process deems this safe. In other words, + * in order to continue a syscall the supervising process should be sure that + * another security mechanism or the kernel itself will sufficiently block + * syscalls if arguments are rewritten to something unsafe. + * + * Similar precautions should be applied when stacking SECCOMP_RET_USER_NOTIF + * or SECCOMP_RET_TRACE. For SECCOMP_RET_USER_NOTIF filters acting on the + * same syscall, the most recently added filter takes precedence. This means + * that the new SECCOMP_RET_USER_NOTIF filter can override any + * SECCOMP_IOCTL_NOTIF_SEND from earlier filters, essentially allowing all + * such filtered syscalls to be executed by sending the response + * SECCOMP_USER_NOTIF_FLAG_CONTINUE. Note that SECCOMP_RET_TRACE can equally + * be overriden by SECCOMP_USER_NOTIF_FLAG_CONTINUE. + */ +#define SECCOMP_USER_NOTIF_FLAG_CONTINUE (1UL << 0) + +struct seccomp_notif_resp { + __u64 id; + __s64 val; + __s32 error; + __u32 flags; +}; + +#define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0) + +/* valid flags for seccomp_notif_addfd */ +#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0) /* Specify remote fd */ +#define SECCOMP_ADDFD_FLAG_SEND (1UL << 1) /* Addfd and return it, atomically */ + +/** + * struct seccomp_notif_addfd + * @id: The ID of the seccomp notification + * @flags: SECCOMP_ADDFD_FLAG_* + * @srcfd: The local fd number + * @newfd: Optional remote FD number if SETFD option is set, otherwise 0. + * @newfd_flags: The O_* flags the remote FD should have applied + */ +struct seccomp_notif_addfd { + __u64 id; + __u32 flags; + __u32 srcfd; + __u32 newfd; + __u32 newfd_flags; +}; + +#define SECCOMP_IOC_MAGIC '!' +#define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr) +#define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type) +#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type) +#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type) + +/* Flags for seccomp notification fd ioctl. */ +#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif) +#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \ + struct seccomp_notif_resp) +#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOW(2, __u64) +/* On success, the return value is the remote process's added fd number */ +#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, \ + struct seccomp_notif_addfd) + +#define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64) + +#endif /* _UAPI_LINUX_SECCOMP_H */ diff --git a/tools/io_uring/Makefile b/tools/io_uring/Makefile deleted file mode 100644 index 00f146c54c53..000000000000 --- a/tools/io_uring/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for io_uring test tools -CFLAGS += -Wall -Wextra -g -D_GNU_SOURCE -LDLIBS += -lpthread - -all: io_uring-cp io_uring-bench -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -io_uring-bench: syscall.o io_uring-bench.o - $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) - -io_uring-cp: setup.o syscall.o queue.o - -clean: - $(RM) io_uring-cp io_uring-bench *.o - -.PHONY: all clean diff --git a/tools/io_uring/README b/tools/io_uring/README deleted file mode 100644 index 67fd70115cff..000000000000 --- a/tools/io_uring/README +++ /dev/null @@ -1,29 +0,0 @@ -This directory includes a few programs that demonstrate how to use io_uring -in an application. The examples are: - -io_uring-cp - A very basic io_uring implementation of cp(1). It takes two - arguments, copies the first argument to the second. This example - is part of liburing, and hence uses the simplified liburing API - for setting up an io_uring instance, submitting IO, completing IO, - etc. The support functions in queue.c and setup.c are straight - out of liburing. - -io_uring-bench - Benchmark program that does random reads on a number of files. This - app demonstrates the various features of io_uring, like fixed files, - fixed buffers, and polled IO. There are options in the program to - control which features to use. Arguments is the file (or files) that - io_uring-bench should operate on. This uses the raw io_uring - interface. - -liburing can be cloned with git here: - - git://git.kernel.dk/liburing - -and contains a number of unit tests as well for testing io_uring. It also -comes with man pages for the three system calls. - -Fio includes an io_uring engine, you can clone fio here: - - git://git.kernel.dk/fio diff --git a/tools/io_uring/barrier.h b/tools/io_uring/barrier.h deleted file mode 100644 index ef00f6722ba9..000000000000 --- a/tools/io_uring/barrier.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef LIBURING_BARRIER_H -#define LIBURING_BARRIER_H - -#if defined(__x86_64) || defined(__i386__) -#define read_barrier() __asm__ __volatile__("":::"memory") -#define write_barrier() __asm__ __volatile__("":::"memory") -#else -/* - * Add arch appropriate definitions. Be safe and use full barriers for - * archs we don't have support for. - */ -#define read_barrier() __sync_synchronize() -#define write_barrier() __sync_synchronize() -#endif - -#endif diff --git a/tools/io_uring/io_uring-bench.c b/tools/io_uring/io_uring-bench.c deleted file mode 100644 index 7703f0118385..000000000000 --- a/tools/io_uring/io_uring-bench.c +++ /dev/null @@ -1,592 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Simple benchmark program that uses the various features of io_uring - * to provide fast random access to a device/file. It has various - * options that are control how we use io_uring, see the OPTIONS section - * below. This uses the raw io_uring interface. - * - * Copyright (C) 2018-2019 Jens Axboe - */ -#include <stdio.h> -#include <errno.h> -#include <assert.h> -#include <stdlib.h> -#include <stddef.h> -#include <signal.h> -#include <inttypes.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/syscall.h> -#include <sys/resource.h> -#include <sys/mman.h> -#include <sys/uio.h> -#include <linux/fs.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <pthread.h> -#include <sched.h> - -#include "liburing.h" -#include "barrier.h" - -#define min(a, b) ((a < b) ? (a) : (b)) - -struct io_sq_ring { - unsigned *head; - unsigned *tail; - unsigned *ring_mask; - unsigned *ring_entries; - unsigned *flags; - unsigned *array; -}; - -struct io_cq_ring { - unsigned *head; - unsigned *tail; - unsigned *ring_mask; - unsigned *ring_entries; - struct io_uring_cqe *cqes; -}; - -#define DEPTH 128 - -#define BATCH_SUBMIT 32 -#define BATCH_COMPLETE 32 - -#define BS 4096 - -#define MAX_FDS 16 - -static unsigned sq_ring_mask, cq_ring_mask; - -struct file { - unsigned long max_blocks; - unsigned pending_ios; - int real_fd; - int fixed_fd; -}; - -struct submitter { - pthread_t thread; - int ring_fd; - struct drand48_data rand; - struct io_sq_ring sq_ring; - struct io_uring_sqe *sqes; - struct iovec iovecs[DEPTH]; - struct io_cq_ring cq_ring; - int inflight; - unsigned long reaps; - unsigned long done; - unsigned long calls; - volatile int finish; - - __s32 *fds; - - struct file files[MAX_FDS]; - unsigned nr_files; - unsigned cur_file; -}; - -static struct submitter submitters[1]; -static volatile int finish; - -/* - * OPTIONS: Set these to test the various features of io_uring. - */ -static int polled = 1; /* use IO polling */ -static int fixedbufs = 1; /* use fixed user buffers */ -static int register_files = 1; /* use fixed files */ -static int buffered = 0; /* use buffered IO, not O_DIRECT */ -static int sq_thread_poll = 0; /* use kernel submission/poller thread */ -static int sq_thread_cpu = -1; /* pin above thread to this CPU */ -static int do_nop = 0; /* no-op SQ ring commands */ - -static int io_uring_register_buffers(struct submitter *s) -{ - if (do_nop) - return 0; - - return io_uring_register(s->ring_fd, IORING_REGISTER_BUFFERS, s->iovecs, - DEPTH); -} - -static int io_uring_register_files(struct submitter *s) -{ - unsigned i; - - if (do_nop) - return 0; - - s->fds = calloc(s->nr_files, sizeof(__s32)); - for (i = 0; i < s->nr_files; i++) { - s->fds[i] = s->files[i].real_fd; - s->files[i].fixed_fd = i; - } - - return io_uring_register(s->ring_fd, IORING_REGISTER_FILES, s->fds, - s->nr_files); -} - -static int lk_gettid(void) -{ - return syscall(__NR_gettid); -} - -static unsigned file_depth(struct submitter *s) -{ - return (DEPTH + s->nr_files - 1) / s->nr_files; -} - -static void init_io(struct submitter *s, unsigned index) -{ - struct io_uring_sqe *sqe = &s->sqes[index]; - unsigned long offset; - struct file *f; - long r; - - if (do_nop) { - sqe->opcode = IORING_OP_NOP; - return; - } - - if (s->nr_files == 1) { - f = &s->files[0]; - } else { - f = &s->files[s->cur_file]; - if (f->pending_ios >= file_depth(s)) { - s->cur_file++; - if (s->cur_file == s->nr_files) - s->cur_file = 0; - f = &s->files[s->cur_file]; - } - } - f->pending_ios++; - - lrand48_r(&s->rand, &r); - offset = (r % (f->max_blocks - 1)) * BS; - - if (register_files) { - sqe->flags = IOSQE_FIXED_FILE; - sqe->fd = f->fixed_fd; - } else { - sqe->flags = 0; - sqe->fd = f->real_fd; - } - if (fixedbufs) { - sqe->opcode = IORING_OP_READ_FIXED; - sqe->addr = (unsigned long) s->iovecs[index].iov_base; - sqe->len = BS; - sqe->buf_index = index; - } else { - sqe->opcode = IORING_OP_READV; - sqe->addr = (unsigned long) &s->iovecs[index]; - sqe->len = 1; - sqe->buf_index = 0; - } - sqe->ioprio = 0; - sqe->off = offset; - sqe->user_data = (unsigned long) f; -} - -static int prep_more_ios(struct submitter *s, unsigned max_ios) -{ - struct io_sq_ring *ring = &s->sq_ring; - unsigned index, tail, next_tail, prepped = 0; - - next_tail = tail = *ring->tail; - do { - next_tail++; - read_barrier(); - if (next_tail == *ring->head) - break; - - index = tail & sq_ring_mask; - init_io(s, index); - ring->array[index] = index; - prepped++; - tail = next_tail; - } while (prepped < max_ios); - - if (*ring->tail != tail) { - /* order tail store with writes to sqes above */ - write_barrier(); - *ring->tail = tail; - write_barrier(); - } - return prepped; -} - -static int get_file_size(struct file *f) -{ - struct stat st; - - if (fstat(f->real_fd, &st) < 0) - return -1; - if (S_ISBLK(st.st_mode)) { - unsigned long long bytes; - - if (ioctl(f->real_fd, BLKGETSIZE64, &bytes) != 0) - return -1; - - f->max_blocks = bytes / BS; - return 0; - } else if (S_ISREG(st.st_mode)) { - f->max_blocks = st.st_size / BS; - return 0; - } - - return -1; -} - -static int reap_events(struct submitter *s) -{ - struct io_cq_ring *ring = &s->cq_ring; - struct io_uring_cqe *cqe; - unsigned head, reaped = 0; - - head = *ring->head; - do { - struct file *f; - - read_barrier(); - if (head == *ring->tail) - break; - cqe = &ring->cqes[head & cq_ring_mask]; - if (!do_nop) { - f = (struct file *) (uintptr_t) cqe->user_data; - f->pending_ios--; - if (cqe->res != BS) { - printf("io: unexpected ret=%d\n", cqe->res); - if (polled && cqe->res == -EOPNOTSUPP) - printf("Your filesystem doesn't support poll\n"); - return -1; - } - } - reaped++; - head++; - } while (1); - - s->inflight -= reaped; - *ring->head = head; - write_barrier(); - return reaped; -} - -static void *submitter_fn(void *data) -{ - struct submitter *s = data; - struct io_sq_ring *ring = &s->sq_ring; - int ret, prepped; - - printf("submitter=%d\n", lk_gettid()); - - srand48_r(pthread_self(), &s->rand); - - prepped = 0; - do { - int to_wait, to_submit, this_reap, to_prep; - - if (!prepped && s->inflight < DEPTH) { - to_prep = min(DEPTH - s->inflight, BATCH_SUBMIT); - prepped = prep_more_ios(s, to_prep); - } - s->inflight += prepped; -submit_more: - to_submit = prepped; -submit: - if (to_submit && (s->inflight + to_submit <= DEPTH)) - to_wait = 0; - else - to_wait = min(s->inflight + to_submit, BATCH_COMPLETE); - - /* - * Only need to call io_uring_enter if we're not using SQ thread - * poll, or if IORING_SQ_NEED_WAKEUP is set. - */ - if (!sq_thread_poll || (*ring->flags & IORING_SQ_NEED_WAKEUP)) { - unsigned flags = 0; - - if (to_wait) - flags = IORING_ENTER_GETEVENTS; - if ((*ring->flags & IORING_SQ_NEED_WAKEUP)) - flags |= IORING_ENTER_SQ_WAKEUP; - ret = io_uring_enter(s->ring_fd, to_submit, to_wait, - flags, NULL); - s->calls++; - } - - /* - * For non SQ thread poll, we already got the events we needed - * through the io_uring_enter() above. For SQ thread poll, we - * need to loop here until we find enough events. - */ - this_reap = 0; - do { - int r; - r = reap_events(s); - if (r == -1) { - s->finish = 1; - break; - } else if (r > 0) - this_reap += r; - } while (sq_thread_poll && this_reap < to_wait); - s->reaps += this_reap; - - if (ret >= 0) { - if (!ret) { - to_submit = 0; - if (s->inflight) - goto submit; - continue; - } else if (ret < to_submit) { - int diff = to_submit - ret; - - s->done += ret; - prepped -= diff; - goto submit_more; - } - s->done += ret; - prepped = 0; - continue; - } else if (ret < 0) { - if (errno == EAGAIN) { - if (s->finish) - break; - if (this_reap) - goto submit; - to_submit = 0; - goto submit; - } - printf("io_submit: %s\n", strerror(errno)); - break; - } - } while (!s->finish); - - finish = 1; - return NULL; -} - -static void sig_int(int sig) -{ - printf("Exiting on signal %d\n", sig); - submitters[0].finish = 1; - finish = 1; -} - -static void arm_sig_int(void) -{ - struct sigaction act; - - memset(&act, 0, sizeof(act)); - act.sa_handler = sig_int; - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); -} - -static int setup_ring(struct submitter *s) -{ - struct io_sq_ring *sring = &s->sq_ring; - struct io_cq_ring *cring = &s->cq_ring; - struct io_uring_params p; - int ret, fd; - void *ptr; - - memset(&p, 0, sizeof(p)); - - if (polled && !do_nop) - p.flags |= IORING_SETUP_IOPOLL; - if (sq_thread_poll) { - p.flags |= IORING_SETUP_SQPOLL; - if (sq_thread_cpu != -1) { - p.flags |= IORING_SETUP_SQ_AFF; - p.sq_thread_cpu = sq_thread_cpu; - } - } - - fd = io_uring_setup(DEPTH, &p); - if (fd < 0) { - perror("io_uring_setup"); - return 1; - } - s->ring_fd = fd; - - if (fixedbufs) { - ret = io_uring_register_buffers(s); - if (ret < 0) { - perror("io_uring_register_buffers"); - return 1; - } - } - - if (register_files) { - ret = io_uring_register_files(s); - if (ret < 0) { - perror("io_uring_register_files"); - return 1; - } - } - - ptr = mmap(0, p.sq_off.array + p.sq_entries * sizeof(__u32), - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, - IORING_OFF_SQ_RING); - printf("sq_ring ptr = 0x%p\n", ptr); - sring->head = ptr + p.sq_off.head; - sring->tail = ptr + p.sq_off.tail; - sring->ring_mask = ptr + p.sq_off.ring_mask; - sring->ring_entries = ptr + p.sq_off.ring_entries; - sring->flags = ptr + p.sq_off.flags; - sring->array = ptr + p.sq_off.array; - sq_ring_mask = *sring->ring_mask; - - s->sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe), - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, - IORING_OFF_SQES); - printf("sqes ptr = 0x%p\n", s->sqes); - - ptr = mmap(0, p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe), - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, - IORING_OFF_CQ_RING); - printf("cq_ring ptr = 0x%p\n", ptr); - cring->head = ptr + p.cq_off.head; - cring->tail = ptr + p.cq_off.tail; - cring->ring_mask = ptr + p.cq_off.ring_mask; - cring->ring_entries = ptr + p.cq_off.ring_entries; - cring->cqes = ptr + p.cq_off.cqes; - cq_ring_mask = *cring->ring_mask; - return 0; -} - -static void file_depths(char *buf) -{ - struct submitter *s = &submitters[0]; - unsigned i; - char *p; - - buf[0] = '\0'; - p = buf; - for (i = 0; i < s->nr_files; i++) { - struct file *f = &s->files[i]; - - if (i + 1 == s->nr_files) - p += sprintf(p, "%d", f->pending_ios); - else - p += sprintf(p, "%d, ", f->pending_ios); - } -} - -int main(int argc, char *argv[]) -{ - struct submitter *s = &submitters[0]; - unsigned long done, calls, reap; - int err, i, flags, fd; - char *fdepths; - void *ret; - - if (!do_nop && argc < 2) { - printf("%s: filename\n", argv[0]); - return 1; - } - - flags = O_RDONLY | O_NOATIME; - if (!buffered) - flags |= O_DIRECT; - - i = 1; - while (!do_nop && i < argc) { - struct file *f; - - if (s->nr_files == MAX_FDS) { - printf("Max number of files (%d) reached\n", MAX_FDS); - break; - } - fd = open(argv[i], flags); - if (fd < 0) { - perror("open"); - return 1; - } - - f = &s->files[s->nr_files]; - f->real_fd = fd; - if (get_file_size(f)) { - printf("failed getting size of device/file\n"); - return 1; - } - if (f->max_blocks <= 1) { - printf("Zero file/device size?\n"); - return 1; - } - f->max_blocks--; - - printf("Added file %s\n", argv[i]); - s->nr_files++; - i++; - } - - if (fixedbufs) { - struct rlimit rlim; - - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; - if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) { - perror("setrlimit"); - return 1; - } - } - - arm_sig_int(); - - for (i = 0; i < DEPTH; i++) { - void *buf; - - if (posix_memalign(&buf, BS, BS)) { - printf("failed alloc\n"); - return 1; - } - s->iovecs[i].iov_base = buf; - s->iovecs[i].iov_len = BS; - } - - err = setup_ring(s); - if (err) { - printf("ring setup failed: %s, %d\n", strerror(errno), err); - return 1; - } - printf("polled=%d, fixedbufs=%d, buffered=%d", polled, fixedbufs, buffered); - printf(" QD=%d, sq_ring=%d, cq_ring=%d\n", DEPTH, *s->sq_ring.ring_entries, *s->cq_ring.ring_entries); - - pthread_create(&s->thread, NULL, submitter_fn, s); - - fdepths = malloc(8 * s->nr_files); - reap = calls = done = 0; - do { - unsigned long this_done = 0; - unsigned long this_reap = 0; - unsigned long this_call = 0; - unsigned long rpc = 0, ipc = 0; - - sleep(1); - this_done += s->done; - this_call += s->calls; - this_reap += s->reaps; - if (this_call - calls) { - rpc = (this_done - done) / (this_call - calls); - ipc = (this_reap - reap) / (this_call - calls); - } else - rpc = ipc = -1; - file_depths(fdepths); - printf("IOPS=%lu, IOS/call=%ld/%ld, inflight=%u (%s)\n", - this_done - done, rpc, ipc, s->inflight, - fdepths); - done = this_done; - calls = this_call; - reap = this_reap; - } while (!finish); - - pthread_join(s->thread, &ret); - close(s->ring_fd); - free(fdepths); - return 0; -} diff --git a/tools/io_uring/io_uring-cp.c b/tools/io_uring/io_uring-cp.c deleted file mode 100644 index d9bd6f5f8f46..000000000000 --- a/tools/io_uring/io_uring-cp.c +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Simple test program that demonstrates a file copy through io_uring. This - * uses the API exposed by liburing. - * - * Copyright (C) 2018-2019 Jens Axboe - */ -#include <stdio.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <assert.h> -#include <errno.h> -#include <inttypes.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include "liburing.h" - -#define QD 64 -#define BS (32*1024) - -static int infd, outfd; - -struct io_data { - int read; - off_t first_offset, offset; - size_t first_len; - struct iovec iov; -}; - -static int setup_context(unsigned entries, struct io_uring *ring) -{ - int ret; - - ret = io_uring_queue_init(entries, ring, 0); - if (ret < 0) { - fprintf(stderr, "queue_init: %s\n", strerror(-ret)); - return -1; - } - - return 0; -} - -static int get_file_size(int fd, off_t *size) -{ - struct stat st; - - if (fstat(fd, &st) < 0) - return -1; - if (S_ISREG(st.st_mode)) { - *size = st.st_size; - return 0; - } else if (S_ISBLK(st.st_mode)) { - unsigned long long bytes; - - if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) - return -1; - - *size = bytes; - return 0; - } - - return -1; -} - -static void queue_prepped(struct io_uring *ring, struct io_data *data) -{ - struct io_uring_sqe *sqe; - - sqe = io_uring_get_sqe(ring); - assert(sqe); - - if (data->read) - io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset); - else - io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset); - - io_uring_sqe_set_data(sqe, data); -} - -static int queue_read(struct io_uring *ring, off_t size, off_t offset) -{ - struct io_uring_sqe *sqe; - struct io_data *data; - - data = malloc(size + sizeof(*data)); - if (!data) - return 1; - - sqe = io_uring_get_sqe(ring); - if (!sqe) { - free(data); - return 1; - } - - data->read = 1; - data->offset = data->first_offset = offset; - - data->iov.iov_base = data + 1; - data->iov.iov_len = size; - data->first_len = size; - - io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); - io_uring_sqe_set_data(sqe, data); - return 0; -} - -static void queue_write(struct io_uring *ring, struct io_data *data) -{ - data->read = 0; - data->offset = data->first_offset; - - data->iov.iov_base = data + 1; - data->iov.iov_len = data->first_len; - - queue_prepped(ring, data); - io_uring_submit(ring); -} - -static int copy_file(struct io_uring *ring, off_t insize) -{ - unsigned long reads, writes; - struct io_uring_cqe *cqe; - off_t write_left, offset; - int ret; - - write_left = insize; - writes = reads = offset = 0; - - while (insize || write_left) { - int had_reads, got_comp; - - /* - * Queue up as many reads as we can - */ - had_reads = reads; - while (insize) { - off_t this_size = insize; - - if (reads + writes >= QD) - break; - if (this_size > BS) - this_size = BS; - else if (!this_size) - break; - - if (queue_read(ring, this_size, offset)) - break; - - insize -= this_size; - offset += this_size; - reads++; - } - - if (had_reads != reads) { - ret = io_uring_submit(ring); - if (ret < 0) { - fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); - break; - } - } - - /* - * Queue is full at this point. Find at least one completion. - */ - got_comp = 0; - while (write_left) { - struct io_data *data; - - if (!got_comp) { - ret = io_uring_wait_cqe(ring, &cqe); - got_comp = 1; - } else { - ret = io_uring_peek_cqe(ring, &cqe); - if (ret == -EAGAIN) { - cqe = NULL; - ret = 0; - } - } - if (ret < 0) { - fprintf(stderr, "io_uring_peek_cqe: %s\n", - strerror(-ret)); - return 1; - } - if (!cqe) - break; - - data = io_uring_cqe_get_data(cqe); - if (cqe->res < 0) { - if (cqe->res == -EAGAIN) { - queue_prepped(ring, data); - io_uring_cqe_seen(ring, cqe); - continue; - } - fprintf(stderr, "cqe failed: %s\n", - strerror(-cqe->res)); - return 1; - } else if (cqe->res != data->iov.iov_len) { - /* Short read/write, adjust and requeue */ - data->iov.iov_base += cqe->res; - data->iov.iov_len -= cqe->res; - data->offset += cqe->res; - queue_prepped(ring, data); - io_uring_cqe_seen(ring, cqe); - continue; - } - - /* - * All done. if write, nothing else to do. if read, - * queue up corresponding write. - */ - if (data->read) { - queue_write(ring, data); - write_left -= data->first_len; - reads--; - writes++; - } else { - free(data); - writes--; - } - io_uring_cqe_seen(ring, cqe); - } - } - - /* wait out pending writes */ - while (writes) { - struct io_data *data; - - ret = io_uring_wait_cqe(ring, &cqe); - if (ret) { - fprintf(stderr, "wait_cqe=%d\n", ret); - return 1; - } - if (cqe->res < 0) { - fprintf(stderr, "write res=%d\n", cqe->res); - return 1; - } - data = io_uring_cqe_get_data(cqe); - free(data); - writes--; - io_uring_cqe_seen(ring, cqe); - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - struct io_uring ring; - off_t insize; - int ret; - - if (argc < 3) { - printf("%s: infile outfile\n", argv[0]); - return 1; - } - - infd = open(argv[1], O_RDONLY); - if (infd < 0) { - perror("open infile"); - return 1; - } - outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (outfd < 0) { - perror("open outfile"); - return 1; - } - - if (setup_context(QD, &ring)) - return 1; - if (get_file_size(infd, &insize)) - return 1; - - ret = copy_file(&ring, insize); - - close(infd); - close(outfd); - io_uring_queue_exit(&ring); - return ret; -} diff --git a/tools/io_uring/liburing.h b/tools/io_uring/liburing.h deleted file mode 100644 index 28a837b6069d..000000000000 --- a/tools/io_uring/liburing.h +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef LIB_URING_H -#define LIB_URING_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <sys/uio.h> -#include <signal.h> -#include <string.h> -#include "../../include/uapi/linux/io_uring.h" -#include <inttypes.h> -#include <linux/swab.h> -#include "barrier.h" - -/* - * Library interface to io_uring - */ -struct io_uring_sq { - unsigned *khead; - unsigned *ktail; - unsigned *kring_mask; - unsigned *kring_entries; - unsigned *kflags; - unsigned *kdropped; - unsigned *array; - struct io_uring_sqe *sqes; - - unsigned sqe_head; - unsigned sqe_tail; - - size_t ring_sz; -}; - -struct io_uring_cq { - unsigned *khead; - unsigned *ktail; - unsigned *kring_mask; - unsigned *kring_entries; - unsigned *koverflow; - struct io_uring_cqe *cqes; - - size_t ring_sz; -}; - -struct io_uring { - struct io_uring_sq sq; - struct io_uring_cq cq; - int ring_fd; -}; - -/* - * System calls - */ -extern int io_uring_setup(unsigned entries, struct io_uring_params *p); -extern int io_uring_enter(int fd, unsigned to_submit, - unsigned min_complete, unsigned flags, sigset_t *sig); -extern int io_uring_register(int fd, unsigned int opcode, void *arg, - unsigned int nr_args); - -/* - * Library interface - */ -extern int io_uring_queue_init(unsigned entries, struct io_uring *ring, - unsigned flags); -extern int io_uring_queue_mmap(int fd, struct io_uring_params *p, - struct io_uring *ring); -extern void io_uring_queue_exit(struct io_uring *ring); -extern int io_uring_peek_cqe(struct io_uring *ring, - struct io_uring_cqe **cqe_ptr); -extern int io_uring_wait_cqe(struct io_uring *ring, - struct io_uring_cqe **cqe_ptr); -extern int io_uring_submit(struct io_uring *ring); -extern struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring); - -/* - * Must be called after io_uring_{peek,wait}_cqe() after the cqe has - * been processed by the application. - */ -static inline void io_uring_cqe_seen(struct io_uring *ring, - struct io_uring_cqe *cqe) -{ - if (cqe) { - struct io_uring_cq *cq = &ring->cq; - - (*cq->khead)++; - /* - * Ensure that the kernel sees our new head, the kernel has - * the matching read barrier. - */ - write_barrier(); - } -} - -/* - * Command prep helpers - */ -static inline void io_uring_sqe_set_data(struct io_uring_sqe *sqe, void *data) -{ - sqe->user_data = (unsigned long) data; -} - -static inline void *io_uring_cqe_get_data(struct io_uring_cqe *cqe) -{ - return (void *) (uintptr_t) cqe->user_data; -} - -static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd, - const void *addr, unsigned len, - off_t offset) -{ - memset(sqe, 0, sizeof(*sqe)); - sqe->opcode = op; - sqe->fd = fd; - sqe->off = offset; - sqe->addr = (unsigned long) addr; - sqe->len = len; -} - -static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd, - const struct iovec *iovecs, - unsigned nr_vecs, off_t offset) -{ - io_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset); -} - -static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd, - void *buf, unsigned nbytes, - off_t offset) -{ - io_uring_prep_rw(IORING_OP_READ_FIXED, sqe, fd, buf, nbytes, offset); -} - -static inline void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd, - const struct iovec *iovecs, - unsigned nr_vecs, off_t offset) -{ - io_uring_prep_rw(IORING_OP_WRITEV, sqe, fd, iovecs, nr_vecs, offset); -} - -static inline void io_uring_prep_write_fixed(struct io_uring_sqe *sqe, int fd, - const void *buf, unsigned nbytes, - off_t offset) -{ - io_uring_prep_rw(IORING_OP_WRITE_FIXED, sqe, fd, buf, nbytes, offset); -} - -static inline void io_uring_prep_poll_add(struct io_uring_sqe *sqe, int fd, - unsigned poll_mask) -{ - memset(sqe, 0, sizeof(*sqe)); - sqe->opcode = IORING_OP_POLL_ADD; - sqe->fd = fd; -#if __BYTE_ORDER == __BIG_ENDIAN - poll_mask = __swahw32(poll_mask); -#endif - sqe->poll_events = poll_mask; -} - -static inline void io_uring_prep_poll_remove(struct io_uring_sqe *sqe, - void *user_data) -{ - memset(sqe, 0, sizeof(*sqe)); - sqe->opcode = IORING_OP_POLL_REMOVE; - sqe->addr = (unsigned long) user_data; -} - -static inline void io_uring_prep_fsync(struct io_uring_sqe *sqe, int fd, - unsigned fsync_flags) -{ - memset(sqe, 0, sizeof(*sqe)); - sqe->opcode = IORING_OP_FSYNC; - sqe->fd = fd; - sqe->fsync_flags = fsync_flags; -} - -static inline void io_uring_prep_nop(struct io_uring_sqe *sqe) -{ - memset(sqe, 0, sizeof(*sqe)); - sqe->opcode = IORING_OP_NOP; -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/io_uring/queue.c b/tools/io_uring/queue.c deleted file mode 100644 index 321819c132c7..000000000000 --- a/tools/io_uring/queue.c +++ /dev/null @@ -1,156 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> - -#include "liburing.h" -#include "barrier.h" - -static int __io_uring_get_cqe(struct io_uring *ring, - struct io_uring_cqe **cqe_ptr, int wait) -{ - struct io_uring_cq *cq = &ring->cq; - const unsigned mask = *cq->kring_mask; - unsigned head; - int ret; - - *cqe_ptr = NULL; - head = *cq->khead; - do { - /* - * It's necessary to use a read_barrier() before reading - * the CQ tail, since the kernel updates it locklessly. The - * kernel has the matching store barrier for the update. The - * kernel also ensures that previous stores to CQEs are ordered - * with the tail update. - */ - read_barrier(); - if (head != *cq->ktail) { - *cqe_ptr = &cq->cqes[head & mask]; - break; - } - if (!wait) - break; - ret = io_uring_enter(ring->ring_fd, 0, 1, - IORING_ENTER_GETEVENTS, NULL); - if (ret < 0) - return -errno; - } while (1); - - return 0; -} - -/* - * Return an IO completion, if one is readily available. Returns 0 with - * cqe_ptr filled in on success, -errno on failure. - */ -int io_uring_peek_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr) -{ - return __io_uring_get_cqe(ring, cqe_ptr, 0); -} - -/* - * Return an IO completion, waiting for it if necessary. Returns 0 with - * cqe_ptr filled in on success, -errno on failure. - */ -int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr) -{ - return __io_uring_get_cqe(ring, cqe_ptr, 1); -} - -/* - * Submit sqes acquired from io_uring_get_sqe() to the kernel. - * - * Returns number of sqes submitted - */ -int io_uring_submit(struct io_uring *ring) -{ - struct io_uring_sq *sq = &ring->sq; - const unsigned mask = *sq->kring_mask; - unsigned ktail, ktail_next, submitted, to_submit; - int ret; - - /* - * If we have pending IO in the kring, submit it first. We need a - * read barrier here to match the kernels store barrier when updating - * the SQ head. - */ - read_barrier(); - if (*sq->khead != *sq->ktail) { - submitted = *sq->kring_entries; - goto submit; - } - - if (sq->sqe_head == sq->sqe_tail) - return 0; - - /* - * Fill in sqes that we have queued up, adding them to the kernel ring - */ - submitted = 0; - ktail = ktail_next = *sq->ktail; - to_submit = sq->sqe_tail - sq->sqe_head; - while (to_submit--) { - ktail_next++; - read_barrier(); - - sq->array[ktail & mask] = sq->sqe_head & mask; - ktail = ktail_next; - - sq->sqe_head++; - submitted++; - } - - if (!submitted) - return 0; - - if (*sq->ktail != ktail) { - /* - * First write barrier ensures that the SQE stores are updated - * with the tail update. This is needed so that the kernel - * will never see a tail update without the preceeding sQE - * stores being done. - */ - write_barrier(); - *sq->ktail = ktail; - /* - * The kernel has the matching read barrier for reading the - * SQ tail. - */ - write_barrier(); - } - -submit: - ret = io_uring_enter(ring->ring_fd, submitted, 0, - IORING_ENTER_GETEVENTS, NULL); - if (ret < 0) - return -errno; - - return ret; -} - -/* - * Return an sqe to fill. Application must later call io_uring_submit() - * when it's ready to tell the kernel about it. The caller may call this - * function multiple times before calling io_uring_submit(). - * - * Returns a vacant sqe, or NULL if we're full. - */ -struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring) -{ - struct io_uring_sq *sq = &ring->sq; - unsigned next = sq->sqe_tail + 1; - struct io_uring_sqe *sqe; - - /* - * All sqes are used - */ - if (next - sq->sqe_head > *sq->kring_entries) - return NULL; - - sqe = &sq->sqes[sq->sqe_tail & *sq->kring_mask]; - sq->sqe_tail = next; - return sqe; -} diff --git a/tools/io_uring/setup.c b/tools/io_uring/setup.c deleted file mode 100644 index 0b50fcd78520..000000000000 --- a/tools/io_uring/setup.c +++ /dev/null @@ -1,107 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> - -#include "liburing.h" - -static int io_uring_mmap(int fd, struct io_uring_params *p, - struct io_uring_sq *sq, struct io_uring_cq *cq) -{ - size_t size; - void *ptr; - int ret; - - sq->ring_sz = p->sq_off.array + p->sq_entries * sizeof(unsigned); - ptr = mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQ_RING); - if (ptr == MAP_FAILED) - return -errno; - sq->khead = ptr + p->sq_off.head; - sq->ktail = ptr + p->sq_off.tail; - sq->kring_mask = ptr + p->sq_off.ring_mask; - sq->kring_entries = ptr + p->sq_off.ring_entries; - sq->kflags = ptr + p->sq_off.flags; - sq->kdropped = ptr + p->sq_off.dropped; - sq->array = ptr + p->sq_off.array; - - size = p->sq_entries * sizeof(struct io_uring_sqe); - sq->sqes = mmap(0, size, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE, fd, - IORING_OFF_SQES); - if (sq->sqes == MAP_FAILED) { - ret = -errno; -err: - munmap(sq->khead, sq->ring_sz); - return ret; - } - - cq->ring_sz = p->cq_off.cqes + p->cq_entries * sizeof(struct io_uring_cqe); - ptr = mmap(0, cq->ring_sz, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_CQ_RING); - if (ptr == MAP_FAILED) { - ret = -errno; - munmap(sq->sqes, p->sq_entries * sizeof(struct io_uring_sqe)); - goto err; - } - cq->khead = ptr + p->cq_off.head; - cq->ktail = ptr + p->cq_off.tail; - cq->kring_mask = ptr + p->cq_off.ring_mask; - cq->kring_entries = ptr + p->cq_off.ring_entries; - cq->koverflow = ptr + p->cq_off.overflow; - cq->cqes = ptr + p->cq_off.cqes; - return 0; -} - -/* - * For users that want to specify sq_thread_cpu or sq_thread_idle, this - * interface is a convenient helper for mmap()ing the rings. - * Returns -1 on error, or zero on success. On success, 'ring' - * contains the necessary information to read/write to the rings. - */ -int io_uring_queue_mmap(int fd, struct io_uring_params *p, struct io_uring *ring) -{ - int ret; - - memset(ring, 0, sizeof(*ring)); - ret = io_uring_mmap(fd, p, &ring->sq, &ring->cq); - if (!ret) - ring->ring_fd = fd; - return ret; -} - -/* - * Returns -1 on error, or zero on success. On success, 'ring' - * contains the necessary information to read/write to the rings. - */ -int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags) -{ - struct io_uring_params p; - int fd, ret; - - memset(&p, 0, sizeof(p)); - p.flags = flags; - - fd = io_uring_setup(entries, &p); - if (fd < 0) - return fd; - - ret = io_uring_queue_mmap(fd, &p, ring); - if (ret) - close(fd); - - return ret; -} - -void io_uring_queue_exit(struct io_uring *ring) -{ - struct io_uring_sq *sq = &ring->sq; - struct io_uring_cq *cq = &ring->cq; - - munmap(sq->sqes, *sq->kring_entries * sizeof(struct io_uring_sqe)); - munmap(sq->khead, sq->ring_sz); - munmap(cq->khead, cq->ring_sz); - close(ring->ring_fd); -} diff --git a/tools/io_uring/syscall.c b/tools/io_uring/syscall.c deleted file mode 100644 index b22e0aa54e9d..000000000000 --- a/tools/io_uring/syscall.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Will go away once libc support is there - */ -#include <unistd.h> -#include <sys/syscall.h> -#include <sys/uio.h> -#include <signal.h> -#include "liburing.h" - -#ifdef __alpha__ -/* - * alpha is the only exception, all other architectures - * have common numbers for new system calls. - */ -# ifndef __NR_io_uring_setup -# define __NR_io_uring_setup 535 -# endif -# ifndef __NR_io_uring_enter -# define __NR_io_uring_enter 536 -# endif -# ifndef __NR_io_uring_register -# define __NR_io_uring_register 537 -# endif -#else /* !__alpha__ */ -# ifndef __NR_io_uring_setup -# define __NR_io_uring_setup 425 -# endif -# ifndef __NR_io_uring_enter -# define __NR_io_uring_enter 426 -# endif -# ifndef __NR_io_uring_register -# define __NR_io_uring_register 427 -# endif -#endif - -int io_uring_register(int fd, unsigned int opcode, void *arg, - unsigned int nr_args) -{ - return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args); -} - -int io_uring_setup(unsigned int entries, struct io_uring_params *p) -{ - return syscall(__NR_io_uring_setup, entries, p); -} - -int io_uring_enter(int fd, unsigned int to_submit, unsigned int min_complete, - unsigned int flags, sigset_t *sig) -{ - return syscall(__NR_io_uring_enter, fd, to_submit, min_complete, - flags, sig, _NSIG / 8); -} diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h index bbab9ad9dc5a..77ceea575dc7 100644 --- a/tools/lib/bpf/bpf_helpers.h +++ b/tools/lib/bpf/bpf_helpers.h @@ -181,6 +181,7 @@ enum libbpf_tristate { #define __ksym __attribute__((section(".ksyms"))) #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) #define __kptr __attribute__((btf_type_tag("kptr"))) +#define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr"))) #define bpf_ksym_exists(sym) ({ \ _Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \ diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 8484b563b53d..ee95fd379d4d 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -448,6 +448,165 @@ static int btf_parse_type_sec(struct btf *btf) return 0; } +static int btf_validate_str(const struct btf *btf, __u32 str_off, const char *what, __u32 type_id) +{ + const char *s; + + s = btf__str_by_offset(btf, str_off); + if (!s) { + pr_warn("btf: type [%u]: invalid %s (string offset %u)\n", type_id, what, str_off); + return -EINVAL; + } + + return 0; +} + +static int btf_validate_id(const struct btf *btf, __u32 id, __u32 ctx_id) +{ + const struct btf_type *t; + + t = btf__type_by_id(btf, id); + if (!t) { + pr_warn("btf: type [%u]: invalid referenced type ID %u\n", ctx_id, id); + return -EINVAL; + } + + return 0; +} + +static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __u32 id) +{ + __u32 kind = btf_kind(t); + int err, i, n; + + err = btf_validate_str(btf, t->name_off, "type name", id); + if (err) + return err; + + switch (kind) { + case BTF_KIND_UNKN: + case BTF_KIND_INT: + case BTF_KIND_FWD: + case BTF_KIND_FLOAT: + break; + case BTF_KIND_PTR: + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + case BTF_KIND_VAR: + case BTF_KIND_DECL_TAG: + case BTF_KIND_TYPE_TAG: + err = btf_validate_id(btf, t->type, id); + if (err) + return err; + break; + case BTF_KIND_ARRAY: { + const struct btf_array *a = btf_array(t); + + err = btf_validate_id(btf, a->type, id); + err = err ?: btf_validate_id(btf, a->index_type, id); + if (err) + return err; + break; + } + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: { + const struct btf_member *m = btf_members(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, m++) { + err = btf_validate_str(btf, m->name_off, "field name", id); + err = err ?: btf_validate_id(btf, m->type, id); + if (err) + return err; + } + break; + } + case BTF_KIND_ENUM: { + const struct btf_enum *m = btf_enum(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, m++) { + err = btf_validate_str(btf, m->name_off, "enum name", id); + if (err) + return err; + } + break; + } + case BTF_KIND_ENUM64: { + const struct btf_enum64 *m = btf_enum64(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, m++) { + err = btf_validate_str(btf, m->name_off, "enum name", id); + if (err) + return err; + } + break; + } + case BTF_KIND_FUNC: { + const struct btf_type *ft; + + err = btf_validate_id(btf, t->type, id); + if (err) + return err; + ft = btf__type_by_id(btf, t->type); + if (btf_kind(ft) != BTF_KIND_FUNC_PROTO) { + pr_warn("btf: type [%u]: referenced type [%u] is not FUNC_PROTO\n", id, t->type); + return -EINVAL; + } + break; + } + case BTF_KIND_FUNC_PROTO: { + const struct btf_param *m = btf_params(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, m++) { + err = btf_validate_str(btf, m->name_off, "param name", id); + err = err ?: btf_validate_id(btf, m->type, id); + if (err) + return err; + } + break; + } + case BTF_KIND_DATASEC: { + const struct btf_var_secinfo *m = btf_var_secinfos(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, m++) { + err = btf_validate_id(btf, m->type, id); + if (err) + return err; + } + break; + } + default: + pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind); + return -EINVAL; + } + return 0; +} + +/* Validate basic sanity of BTF. It's intentionally less thorough than + * kernel's validation and validates only properties of BTF that libbpf relies + * on to be correct (e.g., valid type IDs, valid string offsets, etc) + */ +static int btf_sanity_check(const struct btf *btf) +{ + const struct btf_type *t; + __u32 i, n = btf__type_cnt(btf); + int err; + + for (i = 1; i < n; i++) { + t = btf_type_by_id(btf, i); + err = btf_validate_type(btf, t, i); + if (err) + return err; + } + return 0; +} + __u32 btf__type_cnt(const struct btf *btf) { return btf->start_id + btf->nr_types; @@ -902,6 +1061,7 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) err = btf_parse_str_sec(btf); err = err ?: btf_parse_type_sec(btf); + err = err ?: btf_sanity_check(btf); if (err) goto done; diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 96ff1aa4bf6a..3a6108e3238b 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -436,9 +436,11 @@ struct bpf_program { int fd; bool autoload; bool autoattach; + bool sym_global; bool mark_btf_static; enum bpf_prog_type type; enum bpf_attach_type expected_attach_type; + int exception_cb_idx; int prog_ifindex; __u32 attach_btf_obj_fd; @@ -765,6 +767,7 @@ bpf_object__init_prog(struct bpf_object *obj, struct bpf_program *prog, prog->type = BPF_PROG_TYPE_UNSPEC; prog->fd = -1; + prog->exception_cb_idx = -1; /* libbpf's convention for SEC("?abc...") is that it's just like * SEC("abc...") but the corresponding bpf_program starts out with @@ -871,14 +874,16 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data, if (err) return err; + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) + prog->sym_global = true; + /* if function is a global/weak symbol, but has restricted * (STV_HIDDEN or STV_INTERNAL) visibility, mark its BTF FUNC * as static to enable more permissive BPF verification mode * with more outside context available to BPF verifier */ - if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL - && (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN - || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL)) + if (prog->sym_global && (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN + || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL)) prog->mark_btf_static = true; nr_progs++; @@ -3142,6 +3147,86 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj) } } + if (!kernel_supports(obj, FEAT_BTF_DECL_TAG)) + goto skip_exception_cb; + for (i = 0; i < obj->nr_programs; i++) { + struct bpf_program *prog = &obj->programs[i]; + int j, k, n; + + if (prog_is_subprog(obj, prog)) + continue; + n = btf__type_cnt(obj->btf); + for (j = 1; j < n; j++) { + const char *str = "exception_callback:", *name; + size_t len = strlen(str); + struct btf_type *t; + + t = btf_type_by_id(obj->btf, j); + if (!btf_is_decl_tag(t) || btf_decl_tag(t)->component_idx != -1) + continue; + + name = btf__str_by_offset(obj->btf, t->name_off); + if (strncmp(name, str, len)) + continue; + + t = btf_type_by_id(obj->btf, t->type); + if (!btf_is_func(t) || btf_func_linkage(t) != BTF_FUNC_GLOBAL) { + pr_warn("prog '%s': exception_callback:<value> decl tag not applied to the main program\n", + prog->name); + return -EINVAL; + } + if (strcmp(prog->name, btf__str_by_offset(obj->btf, t->name_off))) + continue; + /* Multiple callbacks are specified for the same prog, + * the verifier will eventually return an error for this + * case, hence simply skip appending a subprog. + */ + if (prog->exception_cb_idx >= 0) { + prog->exception_cb_idx = -1; + break; + } + + name += len; + if (str_is_empty(name)) { + pr_warn("prog '%s': exception_callback:<value> decl tag contains empty value\n", + prog->name); + return -EINVAL; + } + + for (k = 0; k < obj->nr_programs; k++) { + struct bpf_program *subprog = &obj->programs[k]; + + if (!prog_is_subprog(obj, subprog)) + continue; + if (strcmp(name, subprog->name)) + continue; + /* Enforce non-hidden, as from verifier point of + * view it expects global functions, whereas the + * mark_btf_static fixes up linkage as static. + */ + if (!subprog->sym_global || subprog->mark_btf_static) { + pr_warn("prog '%s': exception callback %s must be a global non-hidden function\n", + prog->name, subprog->name); + return -EINVAL; + } + /* Let's see if we already saw a static exception callback with the same name */ + if (prog->exception_cb_idx >= 0) { + pr_warn("prog '%s': multiple subprogs with same name as exception callback '%s'\n", + prog->name, subprog->name); + return -EINVAL; + } + prog->exception_cb_idx = k; + break; + } + + if (prog->exception_cb_idx >= 0) + continue; + pr_warn("prog '%s': cannot find exception callback '%s'\n", prog->name, name); + return -ENOENT; + } + } +skip_exception_cb: + sanitize = btf_needs_sanitization(obj); if (sanitize) { const void *raw_data; @@ -6235,13 +6320,45 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra } static int +bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main_prog, + struct bpf_program *subprog) +{ + struct bpf_insn *insns; + size_t new_cnt; + int err; + + subprog->sub_insn_off = main_prog->insns_cnt; + + new_cnt = main_prog->insns_cnt + subprog->insns_cnt; + insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns)); + if (!insns) { + pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name); + return -ENOMEM; + } + main_prog->insns = insns; + main_prog->insns_cnt = new_cnt; + + memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns, + subprog->insns_cnt * sizeof(*insns)); + + pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n", + main_prog->name, subprog->insns_cnt, subprog->name); + + /* The subprog insns are now appended. Append its relos too. */ + err = append_subprog_relos(main_prog, subprog); + if (err) + return err; + return 0; +} + +static int bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog, struct bpf_program *prog) { - size_t sub_insn_idx, insn_idx, new_cnt; + size_t sub_insn_idx, insn_idx; struct bpf_program *subprog; - struct bpf_insn *insns, *insn; struct reloc_desc *relo; + struct bpf_insn *insn; int err; err = reloc_prog_func_and_line_info(obj, main_prog, prog); @@ -6316,25 +6433,7 @@ bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog, * and relocate. */ if (subprog->sub_insn_off == 0) { - subprog->sub_insn_off = main_prog->insns_cnt; - - new_cnt = main_prog->insns_cnt + subprog->insns_cnt; - insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns)); - if (!insns) { - pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name); - return -ENOMEM; - } - main_prog->insns = insns; - main_prog->insns_cnt = new_cnt; - - memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns, - subprog->insns_cnt * sizeof(*insns)); - - pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n", - main_prog->name, subprog->insns_cnt, subprog->name); - - /* The subprog insns are now appended. Append its relos too. */ - err = append_subprog_relos(main_prog, subprog); + err = bpf_object__append_subprog_code(obj, main_prog, subprog); if (err) return err; err = bpf_object__reloc_code(obj, main_prog, subprog); @@ -6568,6 +6667,25 @@ bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path) prog->name, err); return err; } + + /* Now, also append exception callback if it has not been done already. */ + if (prog->exception_cb_idx >= 0) { + struct bpf_program *subprog = &obj->programs[prog->exception_cb_idx]; + + /* Calling exception callback directly is disallowed, which the + * verifier will reject later. In case it was processed already, + * we can skip this step, otherwise for all other valid cases we + * have to append exception callback now. + */ + if (subprog->sub_insn_off == 0) { + err = bpf_object__append_subprog_code(obj, prog, subprog); + if (err) + return err; + err = bpf_object__reloc_code(obj, prog, subprog); + if (err) + return err; + } + } } /* Process data relos for main programs */ for (i = 0; i < obj->nr_programs; i++) { diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index ba2dcf64f4e6..ae64090184d3 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -148,8 +148,18 @@ struct perf_record_switch { struct perf_record_header_attr { struct perf_event_header header; struct perf_event_attr attr; - __u64 id[]; -}; + /* + * Array of u64 id follows here but we cannot use a flexible array + * because size of attr in the data can be different then current + * version. Please use perf_record_header_attr_id() below. + * + * __u64 id[]; // do not use this + */ +}; + +/* Returns the pointer to id array based on the actual attr size. */ +#define perf_record_header_attr_id(evt) \ + ((void *)&(evt)->attr.attr + (evt)->attr.attr.size) enum { PERF_CPU_MAP__CPUS = 0, diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c index 0d1634cedf44..2184814b37dd 100644 --- a/tools/lib/perf/mmap.c +++ b/tools/lib/perf/mmap.c @@ -392,6 +392,72 @@ static u64 read_perf_counter(unsigned int counter) static u64 read_timestamp(void) { return read_sysreg(cntvct_el0); } +/* __riscv_xlen contains the witdh of the native base integer, here 64-bit */ +#elif defined(__riscv) && __riscv_xlen == 64 + +/* TODO: implement rv32 support */ + +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, %1" \ + : "=r" (__v) \ + : "i" (csr) : ); \ + __v; \ +}) + +static unsigned long csr_read_num(int csr_num) +{ +#define switchcase_csr_read(__csr_num, __val) {\ + case __csr_num: \ + __val = csr_read(__csr_num); \ + break; } +#define switchcase_csr_read_2(__csr_num, __val) {\ + switchcase_csr_read(__csr_num + 0, __val) \ + switchcase_csr_read(__csr_num + 1, __val)} +#define switchcase_csr_read_4(__csr_num, __val) {\ + switchcase_csr_read_2(__csr_num + 0, __val) \ + switchcase_csr_read_2(__csr_num + 2, __val)} +#define switchcase_csr_read_8(__csr_num, __val) {\ + switchcase_csr_read_4(__csr_num + 0, __val) \ + switchcase_csr_read_4(__csr_num + 4, __val)} +#define switchcase_csr_read_16(__csr_num, __val) {\ + switchcase_csr_read_8(__csr_num + 0, __val) \ + switchcase_csr_read_8(__csr_num + 8, __val)} +#define switchcase_csr_read_32(__csr_num, __val) {\ + switchcase_csr_read_16(__csr_num + 0, __val) \ + switchcase_csr_read_16(__csr_num + 16, __val)} + + unsigned long ret = 0; + + switch (csr_num) { + switchcase_csr_read_32(CSR_CYCLE, ret) + default: + break; + } + + return ret; +#undef switchcase_csr_read_32 +#undef switchcase_csr_read_16 +#undef switchcase_csr_read_8 +#undef switchcase_csr_read_4 +#undef switchcase_csr_read_2 +#undef switchcase_csr_read +} + +static u64 read_perf_counter(unsigned int counter) +{ + return csr_read_num(CSR_CYCLE + counter); +} + +static u64 read_timestamp(void) +{ + return csr_read_num(CSR_TIME); +} + #else static u64 read_perf_counter(unsigned int counter __maybe_unused) { return 0; } static u64 read_timestamp(void) { return 0; } diff --git a/tools/mm/Makefile b/tools/mm/Makefile index 6c1da51f4177..1c5606cc3334 100644 --- a/tools/mm/Makefile +++ b/tools/mm/Makefile @@ -8,8 +8,8 @@ TARGETS=page-types slabinfo page_owner_sort LIB_DIR = ../lib/api LIBS = $(LIB_DIR)/libapi.a -CFLAGS += -Wall -Wextra -I../lib/ -LDFLAGS += $(LIBS) +CFLAGS += -Wall -Wextra -I../lib/ -pthread +LDFLAGS += $(LIBS) -pthread all: $(TARGETS) diff --git a/tools/net/ynl/Makefile b/tools/net/ynl/Makefile index 8156f03e23ac..d664b36deb5b 100644 --- a/tools/net/ynl/Makefile +++ b/tools/net/ynl/Makefile @@ -3,7 +3,6 @@ SUBDIRS = lib generated samples all: $(SUBDIRS) - ./ynl-regen.sh -f -p $(PWD)/../../../ $(SUBDIRS): @if [ -f "$@/Makefile" ] ; then \ diff --git a/tools/net/ynl/generated/Makefile b/tools/net/ynl/generated/Makefile index f8817d2e56e4..0f359ee3c46a 100644 --- a/tools/net/ynl/generated/Makefile +++ b/tools/net/ynl/generated/Makefile @@ -19,7 +19,7 @@ SRCS=$(patsubst %,%-user.c,${GENS}) HDRS=$(patsubst %,%-user.h,${GENS}) OBJS=$(patsubst %,%-user.o,${GENS}) -all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) regen +all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) protos.a: $(OBJS) @echo -e "\tAR $@" diff --git a/tools/net/ynl/generated/handshake-user.h b/tools/net/ynl/generated/handshake-user.h index 47646bb91cea..2b34acc608de 100644 --- a/tools/net/ynl/generated/handshake-user.h +++ b/tools/net/ynl/generated/handshake-user.h @@ -28,8 +28,8 @@ struct handshake_x509 { __u32 privkey:1; } _present; - __u32 cert; - __u32 privkey; + __s32 cert; + __s32 privkey; }; /* ============== HANDSHAKE_CMD_ACCEPT ============== */ @@ -65,7 +65,7 @@ struct handshake_accept_rsp { __u32 peername_len; } _present; - __u32 sockfd; + __s32 sockfd; enum handshake_msg_type message_type; __u32 timeout; enum handshake_auth auth_mode; @@ -104,7 +104,7 @@ struct handshake_done_req { } _present; __u32 status; - __u32 sockfd; + __s32 sockfd; unsigned int n_remote_auth; __u32 *remote_auth; }; @@ -122,7 +122,7 @@ handshake_done_req_set_status(struct handshake_done_req *req, __u32 status) req->status = status; } static inline void -handshake_done_req_set_sockfd(struct handshake_done_req *req, __u32 sockfd) +handshake_done_req_set_sockfd(struct handshake_done_req *req, __s32 sockfd) { req->_present.sockfd = 1; req->sockfd = sockfd; diff --git a/tools/net/ynl/generated/netdev-user.c b/tools/net/ynl/generated/netdev-user.c index 68b408ca0f7f..b5ffe8cd1144 100644 --- a/tools/net/ynl/generated/netdev-user.c +++ b/tools/net/ynl/generated/netdev-user.c @@ -45,12 +45,26 @@ const char *netdev_xdp_act_str(enum netdev_xdp_act value) return netdev_xdp_act_strmap[value]; } +static const char * const netdev_xdp_rx_metadata_strmap[] = { + [0] = "timestamp", + [1] = "hash", +}; + +const char *netdev_xdp_rx_metadata_str(enum netdev_xdp_rx_metadata value) +{ + value = ffs(value) - 1; + if (value < 0 || value >= (int)MNL_ARRAY_SIZE(netdev_xdp_rx_metadata_strmap)) + return NULL; + return netdev_xdp_rx_metadata_strmap[value]; +} + /* Policies */ struct ynl_policy_attr netdev_dev_policy[NETDEV_A_DEV_MAX + 1] = { [NETDEV_A_DEV_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, }, [NETDEV_A_DEV_PAD] = { .name = "pad", .type = YNL_PT_IGNORE, }, [NETDEV_A_DEV_XDP_FEATURES] = { .name = "xdp-features", .type = YNL_PT_U64, }, [NETDEV_A_DEV_XDP_ZC_MAX_SEGS] = { .name = "xdp-zc-max-segs", .type = YNL_PT_U32, }, + [NETDEV_A_DEV_XDP_RX_METADATA_FEATURES] = { .name = "xdp-rx-metadata-features", .type = YNL_PT_U64, }, }; struct ynl_policy_nest netdev_dev_nest = { @@ -97,6 +111,11 @@ int netdev_dev_get_rsp_parse(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; dst->_present.xdp_zc_max_segs = 1; dst->xdp_zc_max_segs = mnl_attr_get_u32(attr); + } else if (type == NETDEV_A_DEV_XDP_RX_METADATA_FEATURES) { + if (ynl_attr_validate(yarg, attr)) + return MNL_CB_ERROR; + dst->_present.xdp_rx_metadata_features = 1; + dst->xdp_rx_metadata_features = mnl_attr_get_u64(attr); } } diff --git a/tools/net/ynl/generated/netdev-user.h b/tools/net/ynl/generated/netdev-user.h index 0952d3261f4d..b4351ff34595 100644 --- a/tools/net/ynl/generated/netdev-user.h +++ b/tools/net/ynl/generated/netdev-user.h @@ -18,6 +18,7 @@ extern const struct ynl_family ynl_netdev_family; /* Enums */ const char *netdev_op_str(int op); const char *netdev_xdp_act_str(enum netdev_xdp_act value); +const char *netdev_xdp_rx_metadata_str(enum netdev_xdp_rx_metadata value); /* Common nested types */ /* ============== NETDEV_CMD_DEV_GET ============== */ @@ -48,11 +49,13 @@ struct netdev_dev_get_rsp { __u32 ifindex:1; __u32 xdp_features:1; __u32 xdp_zc_max_segs:1; + __u32 xdp_rx_metadata_features:1; } _present; __u32 ifindex; __u64 xdp_features; __u32 xdp_zc_max_segs; + __u64 xdp_rx_metadata_features; }; void netdev_dev_get_rsp_free(struct netdev_dev_get_rsp *rsp); diff --git a/tools/net/ynl/samples/Makefile b/tools/net/ynl/samples/Makefile index f2db8bb78309..3dbb106e87d9 100644 --- a/tools/net/ynl/samples/Makefile +++ b/tools/net/ynl/samples/Makefile @@ -19,6 +19,9 @@ include $(wildcard *.d) all: $(BINS) $(BINS): ../lib/ynl.a ../generated/protos.a + @echo -e '\tCC sample $@' + @$(COMPILE.c) $(CFLAGS_$@) $@.c -o $@.o + @$(LINK.c) $@.o -o $@ $(LDLIBS) clean: rm -f *.o *.d *~ diff --git a/tools/net/ynl/samples/netdev.c b/tools/net/ynl/samples/netdev.c index 06433400dddd..b828225daad0 100644 --- a/tools/net/ynl/samples/netdev.c +++ b/tools/net/ynl/samples/netdev.c @@ -32,12 +32,18 @@ static void netdev_print_device(struct netdev_dev_get_rsp *d, unsigned int op) if (!d->_present.xdp_features) return; - printf("%llx:", d->xdp_features); + printf("xdp-features (%llx):", d->xdp_features); for (int i = 0; d->xdp_features > 1U << i; i++) { if (d->xdp_features & (1U << i)) printf(" %s", netdev_xdp_act_str(1 << i)); } + printf(" xdp-rx-metadata-features (%llx):", d->xdp_rx_metadata_features); + for (int i = 0; d->xdp_rx_metadata_features > 1U << i; i++) { + if (d->xdp_rx_metadata_features & (1U << i)) + printf(" %s", netdev_xdp_rx_metadata_str(1 << i)); + } + printf(" xdp-zc-max-segs=%u", d->xdp_zc_max_segs); name = netdev_op_str(op); diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index 897af958cee8..168fe612b029 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -805,6 +805,10 @@ class Family(SpecFamily): self.uapi_header = self.yaml['uapi-header'] else: self.uapi_header = f"linux/{self.name}.h" + if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'): + self.uapi_header_name = self.uapi_header[6:-2] + else: + self.uapi_header_name = self.name def resolve(self): self.resolve_up(super()) @@ -2124,7 +2128,7 @@ def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'): def render_uapi(family, cw): - hdr_prot = f"_UAPI_LINUX_{family.name.upper()}_H" + hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H" cw.p('#ifndef ' + hdr_prot) cw.p('#define ' + hdr_prot) cw.nl() diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 1384090530db..e308d1ba664e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4333,7 +4333,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn continue; } - if (insn_func(dest) && insn_func(dest) == insn_func(insn)) { + if (insn_func(dest) && insn_func(insn) && + insn_func(dest)->pfunc == insn_func(insn)->pfunc) { /* * Anything from->to self is either _THIS_IP_ or * IRET-to-self. diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index f04f0eaded98..ca5789625cd2 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -67,6 +67,9 @@ SUBSYSTEM 'internals':: Benchmark internal perf functionality. +'uprobe':: + Benchmark overhead of uprobe + BPF. + 'all':: All benchmark subsystems. diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 1478068ad5dd..0b4e79dbd3f6 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -125,9 +125,6 @@ Given a $HOME/.perfconfig like this: group = true skip-empty = true - [llvm] - dump-obj = true - clang-opt = -g You can hide source code of annotate feature setting the config to false with @@ -657,36 +654,6 @@ ftrace.*:: -F option is not specified. Possible values are 'function' and 'function_graph'. -llvm.*:: - llvm.clang-path:: - Path to clang. If omit, search it from $PATH. - - llvm.clang-bpf-cmd-template:: - Cmdline template. Below lines show its default value. Environment - variable is used to pass options. - "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ - "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ - "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ - "-Wno-unused-value -Wno-pointer-sign " \ - "-working-directory $WORKING_DIR " \ - "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" - - llvm.clang-opt:: - Options passed to clang. - - llvm.kbuild-dir:: - kbuild directory. If not set, use /lib/modules/`uname -r`/build. - If set to "" deliberately, skip kernel header auto-detector. - - llvm.kbuild-opts:: - Options passed to 'make' when detecting kernel header options. - - llvm.dump-obj:: - Enable perf dump BPF object files compiled by LLVM. - - llvm.opts:: - Options passed to llc. - samples.*:: samples.context:: diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt index fb22e3b31dc5..8887cc20a809 100644 --- a/tools/perf/Documentation/perf-dlfilter.txt +++ b/tools/perf/Documentation/perf-dlfilter.txt @@ -64,6 +64,12 @@ internal filtering. If implemented, 'filter_description' should return a one-line description of the filter, and optionally a longer description. +Do not assume the 'sample' argument is valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + +Do not assume data referenced by pointers in struct perf_dlfilter_sample +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_sample structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -150,7 +156,8 @@ struct perf_dlfilter_fns { const char *(*srcline)(void *ctx, __u32 *line_number); struct perf_event_attr *(*attr)(void *ctx); __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len); - void *(*reserved[120])(void *); + void (*al_cleanup)(void *ctx, struct perf_dlfilter_al *al); + void *(*reserved[119])(void *); }; ---- @@ -161,7 +168,8 @@ struct perf_dlfilter_fns { 'args' returns arguments from --dlarg options. 'resolve_address' provides information about 'address'. al->size must be set -before calling. Returns 0 on success, -1 otherwise. +before calling. Returns 0 on success, -1 otherwise. Call al_cleanup() (if present, +see below) when 'al' data is no longer needed. 'insn' returns instruction bytes and length. @@ -171,6 +179,12 @@ before calling. Returns 0 on success, -1 otherwise. 'object_code' reads object code and returns the number of bytes read. +'al_cleanup' must be called (if present, so check perf_dlfilter_fns.al_cleanup != NULL) +after resolve_address() to free any associated resources. + +Do not assume pointers obtained via perf_dlfilter_fns are valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_al structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -197,9 +211,13 @@ struct perf_dlfilter_al { /* Below members are only populated by resolve_ip() */ __u8 filtered; /* true if this sample event will be filtered out */ const char *comm; + void *priv; /* Private data. Do not change */ }; ---- +Do not assume data referenced by pointers in struct perf_dlfilter_al +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + perf_dlfilter_sample flags ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index df4595563801..d780b93fcf87 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -96,8 +96,9 @@ OPTIONS for 'perf ftrace trace' --func-opts:: List of options allowed to set: - call-graph - Display kernel stack trace for function tracer. - irq-info - Display irq context info for function tracer. + + - call-graph - Display kernel stack trace for function tracer. + - irq-info - Display irq context info for function tracer. -G:: --graph-funcs=:: @@ -118,11 +119,12 @@ OPTIONS for 'perf ftrace trace' --graph-opts:: List of options allowed to set: - nosleep-time - Measure on-CPU time only for function_graph tracer. - noirqs - Ignore functions that happen inside interrupt. - verbose - Show process names, PIDs, timestamps, etc. - thresh=<n> - Setup trace duration threshold in microseconds. - depth=<n> - Set max depth for function graph tracer to follow. + + - nosleep-time - Measure on-CPU time only for function_graph tracer. + - noirqs - Ignore functions that happen inside interrupt. + - verbose - Show process names, PIDs, timestamps, etc. + - thresh=<n> - Setup trace duration threshold in microseconds. + - depth=<n> - Set max depth for function graph tracer to follow. OPTIONS for 'perf ftrace latency' diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 680396c56bd1..d5217be012d7 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -99,20 +99,6 @@ OPTIONS If you want to profile write accesses in [0x1000~1008), just set 'mem:0x1000/8:w'. - - a BPF source file (ending in .c) or a precompiled object file (ending - in .o) selects one or more BPF events. - The BPF program can attach to various perf events based on the ELF section - names. - - When processing a '.c' file, perf searches an installed LLVM to compile it - into an object file first. Optional clang options can be passed via the - '--clang-opt' command line option, e.g.: - - perf record --clang-opt "-DLINUX_VERSION_CODE=0x50000" \ - -e tests/bpf-script-example.c - - Note: '--clang-opt' must be placed before '--event/-e'. - - a group of events surrounded by a pair of brace ("{event1,event2,...}"). Each event is separated by commas and the group should be quoted to prevent the shell interpretation. You also need to use --group on @@ -523,9 +509,10 @@ CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI. Select AUX area tracing Snapshot Mode. This option is valid only with an AUX area tracing event. Optionally, certain snapshot capturing parameters can be specified in a string that follows this option: - 'e': take one last snapshot on exit; guarantees that there is at least one + + - 'e': take one last snapshot on exit; guarantees that there is at least one snapshot in the output file; - <size>: if the PMU supports this, specify the desired snapshot size. + - <size>: if the PMU supports this, specify the desired snapshot size. In Snapshot Mode trace data is captured only when signal SIGUSR2 is received and on exit if the above 'e' option is given. @@ -547,14 +534,6 @@ PERF_RECORD_SWITCH_CPU_WIDE. In some cases (e.g. Intel PT, CoreSight or Arm SPE) switch events will be enabled automatically, which can be suppressed by by the option --no-switch-events. ---clang-path=PATH:: -Path to clang binary to use for compiling BPF scriptlets. -(enabled when BPF support is on) - ---clang-opt=OPTIONS:: -Options passed to clang when compiling BPF scriptlets. -(enabled when BPF support is on) - --vmlinux=PATH:: Specify vmlinux path which has debuginfo. (enabled when BPF prologue is on) @@ -572,8 +551,9 @@ providing implementation for Posix AIO API. --affinity=mode:: Set affinity mask of trace reading thread according to the policy defined by 'mode' value: - node - thread affinity mask is set to NUMA node cpu mask of the processed mmap buffer - cpu - thread affinity mask is set to cpu of the processed mmap buffer + + - node - thread affinity mask is set to NUMA node cpu mask of the processed mmap buffer + - cpu - thread affinity mask is set to cpu of the processed mmap buffer --mmap-flush=number:: @@ -625,16 +605,17 @@ Record timestamp boundary (time of first/last samples). --switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one based on 'mode' value: - "signal" - when receiving a SIGUSR2 (default value) or - <size> - when reaching the size threshold, size is expected to - be a number with appended unit character - B/K/M/G - <time> - when reaching the time threshold, size is expected to - be a number with appended unit character - s/m/h/d - Note: the precision of the size threshold hugely depends - on your configuration - the number and size of your ring - buffers (-m). It is generally more precise for higher sizes - (like >5M), for lower values expect different sizes. + - "signal" - when receiving a SIGUSR2 (default value) or + - <size> - when reaching the size threshold, size is expected to + be a number with appended unit character - B/K/M/G + - <time> - when reaching the time threshold, size is expected to + be a number with appended unit character - s/m/h/d + + Note: the precision of the size threshold hugely depends + on your configuration - the number and size of your ring + buffers (-m). It is generally more precise for higher sizes + (like >5M), for lower values expect different sizes. A possible use case is to, given an external event, slice the perf.data file that gets then processed, possibly via a perf script, to decide if that @@ -680,11 +661,12 @@ choice in this option. For example, --synth=no would have MMAP events for kernel and modules. Available types are: - 'task' - synthesize FORK and COMM events for each task - 'mmap' - synthesize MMAP events for each process (implies 'task') - 'cgroup' - synthesize CGROUP events for each cgroup - 'all' - synthesize all events (default) - 'no' - do not synthesize any of the above events + + - 'task' - synthesize FORK and COMM events for each task + - 'mmap' - synthesize MMAP events for each process (implies 'task') + - 'cgroup' - synthesize CGROUP events for each cgroup + - 'all' - synthesize all events (default) + - 'no' - do not synthesize any of the above events --tail-synthesize:: Instead of collecting non-sample events (for example, fork, comm, mmap) at @@ -736,18 +718,19 @@ ctl-fifo / ack-fifo are opened and used as ctl-fd / ack-fd as follows. Listen on ctl-fd descriptor for command to control measurement. Available commands: - 'enable' : enable events - 'disable' : disable events - 'enable name' : enable event 'name' - 'disable name' : disable event 'name' - 'snapshot' : AUX area tracing snapshot). - 'stop' : stop perf record - 'ping' : ping - - 'evlist [-v|-g|-F] : display all events - -F Show just the sample frequency used for each event. - -v Show all fields. - -g Show event group information. + + - 'enable' : enable events + - 'disable' : disable events + - 'enable name' : enable event 'name' + - 'disable name' : disable event 'name' + - 'snapshot' : AUX area tracing snapshot). + - 'stop' : stop perf record + - 'ping' : ping + - 'evlist [-v|-g|-F] : display all events + + -F Show just the sample frequency used for each event. + -v Show all fields. + -g Show event group information. Measurements can be started with events disabled using --delay=-1 option. Optionally send control command completion ('ack\n') to ack-fd descriptor to synchronize with the @@ -808,10 +791,10 @@ the second monitors CPUs 1 and 5-7 with the affinity mask 5-7. <spec> value can also be a string meaning predefined parallel threads layout: - cpu - create new data streaming thread for every monitored cpu - core - create new thread to monitor CPUs grouped by a core - package - create new thread to monitor CPUs grouped by a package - numa - create new threed to monitor CPUs grouped by a NUMA domain + - cpu - create new data streaming thread for every monitored cpu + - core - create new thread to monitor CPUs grouped by a core + - package - create new thread to monitor CPUs grouped by a package + - numa - create new threed to monitor CPUs grouped by a NUMA domain Predefined layouts can be used on systems with large number of CPUs in order not to spawn multiple per-cpu streaming threads but still avoid LOST diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index 635ba043fd7d..010a4edcd384 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -43,7 +43,7 @@ struct perf_file_section { Flags section: -For each of the optional features a perf_file_section it placed after the data +For each of the optional features a perf_file_section is placed after the data section if the feature bit is set in the perf_header flags bitset. The respective perf_file_section points to the data of the additional header and defines its size. diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index c5db0de49868..d66b52407e19 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -246,6 +246,9 @@ ifeq ($(CC_NO_CLANG), 0) else CORE_CFLAGS += -O6 endif +else + CORE_CFLAGS += -g + CXXFLAGS += -g endif ifdef PARSER_DEBUG @@ -256,6 +259,11 @@ ifdef PARSER_DEBUG $(call detected_var,PARSER_DEBUG_FLEX) endif +ifdef LTO + CORE_CFLAGS += -flto + CXXFLAGS += -flto +endif + # Try different combinations to accommodate systems that only have # python[2][3]-config in weird combinations in the following order of # priority from lowest to highest: @@ -319,18 +327,14 @@ FEATURE_CHECK_LDFLAGS-disassembler-four-args = -lbfd -lopcodes -ldl FEATURE_CHECK_LDFLAGS-disassembler-init-styled = -lbfd -lopcodes -ldl CORE_CFLAGS += -fno-omit-frame-pointer -CORE_CFLAGS += -ggdb3 -CORE_CFLAGS += -funwind-tables CORE_CFLAGS += -Wall CORE_CFLAGS += -Wextra CORE_CFLAGS += -std=gnu11 -CXXFLAGS += -std=gnu++14 -fno-exceptions -fno-rtti +CXXFLAGS += -std=gnu++17 -fno-exceptions -fno-rtti CXXFLAGS += -Wall +CXXFLAGS += -Wextra CXXFLAGS += -fno-omit-frame-pointer -CXXFLAGS += -ggdb3 -CXXFLAGS += -funwind-tables -CXXFLAGS += -Wno-strict-aliasing HOSTCFLAGS += -Wall HOSTCFLAGS += -Wextra @@ -585,18 +589,6 @@ ifndef NO_LIBELF LIBBPF_STATIC := 1 endif endif - - ifndef NO_DWARF - ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET - CFLAGS += -DHAVE_BPF_PROLOGUE - $(call detected,CONFIG_BPF_PROLOGUE) - else - msg := $(warning BPF prologue is not supported by architecture $(SRCARCH), missing regs_query_register_offset()); - endif - else - msg := $(warning DWARF support is off, BPF prologue is disabled); - endif - endif # NO_LIBBPF endif # NO_LIBELF @@ -1123,37 +1115,6 @@ ifndef NO_JVMTI endif endif -USE_CXX = 0 -USE_CLANGLLVM = 0 -ifdef LIBCLANGLLVM - $(call feature_check,cxx) - ifneq ($(feature-cxx), 1) - msg := $(warning No g++ found, disable clang and llvm support. Please install g++) - else - $(call feature_check,llvm) - $(call feature_check,llvm-version) - ifneq ($(feature-llvm), 1) - msg := $(warning No suitable libLLVM found, disabling builtin clang and LLVM support. Please install llvm-dev(el) (>= 3.9.0)) - else - $(call feature_check,clang) - ifneq ($(feature-clang), 1) - msg := $(warning No suitable libclang found, disabling builtin clang and LLVM support. Please install libclang-dev(el) (>= 3.9.0)) - else - CFLAGS += -DHAVE_LIBCLANGLLVM_SUPPORT - CXXFLAGS += -DHAVE_LIBCLANGLLVM_SUPPORT -I$(shell $(LLVM_CONFIG) --includedir) - $(call detected,CONFIG_CXX) - $(call detected,CONFIG_CLANGLLVM) - USE_CXX = 1 - USE_LLVM = 1 - USE_CLANG = 1 - ifneq ($(feature-llvm-version),1) - msg := $(warning This version of LLVM is not tested. May cause build errors) - endif - endif - endif - endif -endif - ifndef NO_LIBPFM4 $(call feature_check,libpfm4) ifeq ($(feature-libpfm4), 1) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 097316ef38e6..37af6df7b978 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -99,10 +99,6 @@ include ../scripts/utilities.mak # Define NO_JVMTI_CMLR (debug only) if you do not want to process CMLR # data for java source lines. # -# Define LIBCLANGLLVM if you DO want builtin clang and llvm support. -# When selected, pass LLVM_CONFIG=/path/to/llvm-config to `make' if -# llvm-config is not in $PATH. -# # Define CORESIGHT if you DO WANT support for CoreSight trace decoding. # # Define NO_AIO if you do not want support of Posix AIO based trace @@ -381,7 +377,7 @@ ifndef NO_JVMTI PROGRAMS += $(OUTPUT)$(LIBJVMTI) endif -DLFILTERS := dlfilter-test-api-v0.so dlfilter-show-cycles.so +DLFILTERS := dlfilter-test-api-v0.so dlfilter-test-api-v2.so dlfilter-show-cycles.so DLFILTERS := $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS)) # what 'all' will build and 'install' will install, in perfexecdir @@ -425,22 +421,6 @@ endif EXTLIBS := $(call filter-out,$(EXCLUDE_EXTLIBS),$(EXTLIBS)) LIBS = -Wl,--whole-archive $(PERFLIBS) $(EXTRA_PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group -ifeq ($(USE_CLANG), 1) - CLANGLIBS_LIST = AST Basic CodeGen Driver Frontend Lex Tooling Edit Sema Analysis Parse Serialization - CLANGLIBS_NOEXT_LIST = $(foreach l,$(CLANGLIBS_LIST),$(shell $(LLVM_CONFIG) --libdir)/libclang$(l)) - LIBCLANG = $(foreach l,$(CLANGLIBS_NOEXT_LIST),$(wildcard $(l).a $(l).so)) - LIBS += -Wl,--start-group $(LIBCLANG) -Wl,--end-group -endif - -ifeq ($(USE_LLVM), 1) - LIBLLVM = $(shell $(LLVM_CONFIG) --libs all) $(shell $(LLVM_CONFIG) --system-libs) - LIBS += -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM) -endif - -ifeq ($(USE_CXX), 1) - LIBS += -lstdc++ -endif - export INSTALL SHELL_PATH ### Build rules @@ -978,11 +958,6 @@ ifndef NO_JVMTI endif $(call QUIET_INSTALL, libexec) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' -ifndef NO_LIBBPF - $(call QUIET_INSTALL, bpf-examples) \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf'; \ - $(INSTALL) examples/bpf/*.c -m 644 -t '$(DESTDIR_SQ)$(perf_examples_instdir_SQ)/bpf' -endif $(call QUIET_INSTALL, perf-archive) \ $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(call QUIET_INSTALL, perf-iostat) \ @@ -1057,6 +1032,8 @@ SKELETONS += $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.skel.h SKELETONS += $(SKEL_OUT)/bperf_cgroup.skel.h $(SKEL_OUT)/func_latency.skel.h SKELETONS += $(SKEL_OUT)/off_cpu.skel.h $(SKEL_OUT)/lock_contention.skel.h SKELETONS += $(SKEL_OUT)/kwork_trace.skel.h $(SKEL_OUT)/sample_filter.skel.h +SKELETONS += $(SKEL_OUT)/bench_uprobe.skel.h +SKELETONS += $(SKEL_OUT)/augmented_raw_syscalls.skel.h $(SKEL_TMP_OUT) $(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT): $(Q)$(MKDIR) -p $@ @@ -1079,10 +1056,15 @@ ifneq ($(CROSS_COMPILE),) CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%)) endif +CLANG_OPTIONS = -Wall CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH)) BPF_INCLUDE := -I$(SKEL_TMP_OUT)/.. -I$(LIBBPF_INCLUDE) $(CLANG_SYS_INCLUDES) TOOLS_UAPI_INCLUDE := -I$(srctree)/tools/include/uapi +ifneq ($(WERROR),0) + CLANG_OPTIONS += -Werror +endif + $(BPFTOOL): | $(SKEL_TMP_OUT) $(Q)CFLAGS= $(MAKE) -C ../bpf/bpftool \ OUTPUT=$(SKEL_TMP_OUT)/ bootstrap @@ -1124,7 +1106,7 @@ else endif $(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) $(SKEL_OUT)/vmlinux.h | $(SKEL_TMP_OUT) - $(QUIET_CLANG)$(CLANG) -g -O2 --target=bpf -Wall -Werror $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \ + $(QUIET_CLANG)$(CLANG) -g -O2 --target=bpf $(CLANG_OPTIONS) $(BPF_INCLUDE) $(TOOLS_UAPI_INCLUDE) \ -c $(filter util/bpf_skel/%.bpf.c,$^) -o $@ $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o | $(BPFTOOL) diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h index 99a06550e25d..75ce1c370114 100644 --- a/tools/perf/arch/arm/include/perf_regs.h +++ b/tools/perf/arch/arm/include/perf_regs.h @@ -12,7 +12,4 @@ void perf_regs_load(u64 *regs); #define PERF_REGS_MAX PERF_REG_ARM_MAX #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 -#define PERF_REG_IP PERF_REG_ARM_PC -#define PERF_REG_SP PERF_REG_ARM_SP - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 7c51fa182b51..b8d6a953fd74 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -79,9 +79,9 @@ static int cs_etm_validate_context_id(struct auxtrace_record *itr, int err; u32 val; u64 contextid = evsel->core.attr.config & - (perf_pmu__format_bits(&cs_etm_pmu->format, "contextid") | - perf_pmu__format_bits(&cs_etm_pmu->format, "contextid1") | - perf_pmu__format_bits(&cs_etm_pmu->format, "contextid2")); + (perf_pmu__format_bits(cs_etm_pmu, "contextid") | + perf_pmu__format_bits(cs_etm_pmu, "contextid1") | + perf_pmu__format_bits(cs_etm_pmu, "contextid2")); if (!contextid) return 0; @@ -106,7 +106,7 @@ static int cs_etm_validate_context_id(struct auxtrace_record *itr, } if (contextid & - perf_pmu__format_bits(&cs_etm_pmu->format, "contextid1")) { + perf_pmu__format_bits(cs_etm_pmu, "contextid1")) { /* * TRCIDR2.CIDSIZE, bit [9-5], indicates whether contextID * tracing is supported: @@ -122,7 +122,7 @@ static int cs_etm_validate_context_id(struct auxtrace_record *itr, } if (contextid & - perf_pmu__format_bits(&cs_etm_pmu->format, "contextid2")) { + perf_pmu__format_bits(cs_etm_pmu, "contextid2")) { /* * TRCIDR2.VMIDOPT[30:29] != 0 and * TRCIDR2.VMIDSIZE[14:10] == 0b00100 (32bit virtual contextid) @@ -151,7 +151,7 @@ static int cs_etm_validate_timestamp(struct auxtrace_record *itr, u32 val; if (!(evsel->core.attr.config & - perf_pmu__format_bits(&cs_etm_pmu->format, "timestamp"))) + perf_pmu__format_bits(cs_etm_pmu, "timestamp"))) return 0; if (!cs_etm_is_etmv4(itr, cpu)) { diff --git a/tools/perf/arch/arm/util/perf_regs.c b/tools/perf/arch/arm/util/perf_regs.c index 2833e101a7c6..2c56e8b56ddf 100644 --- a/tools/perf/arch/arm/util/perf_regs.c +++ b/tools/perf/arch/arm/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c index 1834a0cd9ce3..4e02cef461e3 100644 --- a/tools/perf/arch/arm/util/unwind-libdw.c +++ b/tools/perf/arch/arm/util/unwind-libdw.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../../util/unwind-libdw.h" #include "../../../util/perf_regs.h" #include "../../../util/sample.h" diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h index 452b3d904521..474d7cf5afbd 100644 --- a/tools/perf/arch/arm64/include/arch-tests.h +++ b/tools/perf/arch/arm64/include/arch-tests.h @@ -2,6 +2,9 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H +struct test_suite; + +int test__cpuid_match(struct test_suite *test, int subtest); extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index 35a3cc775b39..58639ee9f7ea 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -14,7 +14,4 @@ void perf_regs_load(u64 *regs); #define PERF_REGS_MAX PERF_REG_ARM64_MAX #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64 -#define PERF_REG_IP PERF_REG_ARM64_PC -#define PERF_REG_SP PERF_REG_ARM64_SP - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build index a61c06bdb757..e337c09e7f56 100644 --- a/tools/perf/arch/arm64/tests/Build +++ b/tools/perf/arch/arm64/tests/Build @@ -2,3 +2,4 @@ perf-y += regs_load.o perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-y += arch-tests.o +perf-y += cpuid-match.o diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c index ad16b4f8f63e..74932e72c727 100644 --- a/tools/perf/arch/arm64/tests/arch-tests.c +++ b/tools/perf/arch/arm64/tests/arch-tests.c @@ -3,9 +3,13 @@ #include "tests/tests.h" #include "arch-tests.h" + +DEFINE_SUITE("arm64 CPUID matching", cpuid_match); + struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT &suite__dwarf_unwind, #endif + &suite__cpuid_match, NULL, }; diff --git a/tools/perf/arch/arm64/tests/cpuid-match.c b/tools/perf/arch/arm64/tests/cpuid-match.c new file mode 100644 index 000000000000..e8e3947cca18 --- /dev/null +++ b/tools/perf/arch/arm64/tests/cpuid-match.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/compiler.h> + +#include "arch-tests.h" +#include "tests/tests.h" +#include "util/header.h" + +int test__cpuid_match(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + /* midr with no leading zeros matches */ + if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410fd0c0")) + return -1; + /* Upper case matches */ + if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410FD0C0")) + return -1; + /* r0p0 = r0p0 matches */ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd480")) + return -1; + /* r0p1 > r0p0 matches */ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd481")) + return -1; + /* r1p0 > r0p0 matches*/ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000411fd480")) + return -1; + /* r0p0 < r0p1 doesn't match */ + if (!strcmp_cpuid_str("0x00000000410fd481", "0x00000000410fd480")) + return -1; + /* r0p0 < r1p0 doesn't match */ + if (!strcmp_cpuid_str("0x00000000411fd480", "0x00000000410fd480")) + return -1; + /* Different CPU doesn't match */ + if (!strcmp_cpuid_str("0x00000000410fd4c0", "0x00000000430f0af0")) + return -1; + + return 0; +} diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index 3b1676ff03f9..9cc3d6dcb849 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -230,7 +230,7 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, * inform that the resulting output's SPE samples contain physical addresses * where applicable. */ - bit = perf_pmu__format_bits(&arm_spe_pmu->format, "pa_enable"); + bit = perf_pmu__format_bits(arm_spe_pmu, "pa_enable"); if (arm_spe_evsel->core.attr.config & bit) evsel__set_sample_bit(arm_spe_evsel, PHYS_ADDR); diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index 80b9f6287fe2..a2eef9ec5491 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -1,3 +1,6 @@ +#include <linux/kernel.h> +#include <linux/bits.h> +#include <linux/bitfield.h> #include <stdio.h> #include <stdlib.h> #include <perf/cpumap.h> @@ -10,15 +13,14 @@ #define MIDR "/regs/identification/midr_el1" #define MIDR_SIZE 19 -#define MIDR_REVISION_MASK 0xf -#define MIDR_VARIANT_SHIFT 20 -#define MIDR_VARIANT_MASK (0xf << MIDR_VARIANT_SHIFT) +#define MIDR_REVISION_MASK GENMASK(3, 0) +#define MIDR_VARIANT_MASK GENMASK(23, 20) static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) { const char *sysfs = sysfs__mountpoint(); - u64 midr = 0; int cpu; + int ret = EINVAL; if (!sysfs || sz < MIDR_SIZE) return EINVAL; @@ -44,22 +46,13 @@ static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) } fclose(file); - /* Ignore/clear Variant[23:20] and - * Revision[3:0] of MIDR - */ - midr = strtoul(buf, NULL, 16); - midr &= (~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK)); - scnprintf(buf, MIDR_SIZE, "0x%016lx", midr); /* got midr break loop */ + ret = 0; break; } perf_cpu_map__put(cpus); - - if (!midr) - return EINVAL; - - return 0; + return ret; } int get_cpuid(char *buf, size_t sz) @@ -99,3 +92,47 @@ char *get_cpuid_str(struct perf_pmu *pmu) return buf; } + +/* + * Return 0 if idstr is a higher or equal to version of the same part as + * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any + * version of idstr will match as long as it's the same CPU type. + * + * Return 1 if the CPU type is different or the version of idstr is lower. + */ +int strcmp_cpuid_str(const char *mapcpuid, const char *idstr) +{ + u64 map_id = strtoull(mapcpuid, NULL, 16); + char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id); + char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id); + u64 id = strtoull(idstr, NULL, 16); + char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id); + char id_revision = FIELD_GET(MIDR_REVISION_MASK, id); + u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK); + + /* Compare without version first */ + if ((map_id & id_fields) != (id & id_fields)) + return 1; + + /* + * ID matches, now compare version. + * + * Arm revisions (like r0p0) are compared here like two digit semver + * values eg. 1.3 < 2.0 < 2.1 < 2.2. + * + * r = high value = 'Variant' field in MIDR + * p = low value = 'Revision' field in MIDR + * + */ + if (id_variant > map_id_variant) + return 0; + + if (id_variant == map_id_variant && id_revision >= map_id_revision) + return 0; + + /* + * variant is less than mapfile variant or variants are the same but + * the revision doesn't match. Return no match. + */ + return 1; +} diff --git a/tools/perf/arch/arm64/util/machine.c b/tools/perf/arch/arm64/util/machine.c index 235a0a1e1ec7..ba1144366e85 100644 --- a/tools/perf/arch/arm64/util/machine.c +++ b/tools/perf/arch/arm64/util/machine.c @@ -6,6 +6,7 @@ #include "debug.h" #include "symbol.h" #include "callchain.h" +#include "perf_regs.h" #include "record.h" #include "util/perf_regs.h" diff --git a/tools/perf/arch/arm64/util/mem-events.c b/tools/perf/arch/arm64/util/mem-events.c index df817d1f9f3e..3bcc5c7035c2 100644 --- a/tools/perf/arch/arm64/util/mem-events.c +++ b/tools/perf/arch/arm64/util/mem-events.c @@ -20,7 +20,7 @@ struct perf_mem_event *perf_mem_events__ptr(int i) return &perf_mem_events[i]; } -char *perf_mem_events__name(int i, char *pmu_name __maybe_unused) +const char *perf_mem_events__name(int i, const char *pmu_name __maybe_unused) { struct perf_mem_event *e = perf_mem_events__ptr(i); diff --git a/tools/perf/arch/arm64/util/perf_regs.c b/tools/perf/arch/arm64/util/perf_regs.c index 006692c9b040..1b79d8eab22f 100644 --- a/tools/perf/arch/arm64/util/perf_regs.c +++ b/tools/perf/arch/arm64/util/perf_regs.c @@ -6,6 +6,7 @@ #include <linux/kernel.h> #include <linux/zalloc.h> +#include "perf_regs.h" #include "../../../perf-sys.h" #include "../../../util/debug.h" #include "../../../util/event.h" @@ -139,6 +140,11 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op) return SDT_ARG_VALID; } +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + uint64_t arch__user_reg_mask(void) { struct perf_event_attr attr = { diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index 512a8f13c4de..615084eb88d8 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -2,28 +2,12 @@ #include <internal/cpumap.h> #include "../../../util/cpumap.h" +#include "../../../util/header.h" #include "../../../util/pmu.h" #include "../../../util/pmus.h" #include <api/fs/fs.h> #include <math.h> -static struct perf_pmu *pmu__find_core_pmu(void) -{ - struct perf_pmu *pmu = NULL; - - while ((pmu = perf_pmus__scan_core(pmu))) { - /* - * The cpumap should cover all CPUs. Otherwise, some CPUs may - * not support some events or have different event IDs. - */ - if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu) - return NULL; - - return pmu; - } - return NULL; -} - const struct pmu_metrics_table *pmu_metrics_table__find(void) { struct perf_pmu *pmu = pmu__find_core_pmu(); diff --git a/tools/perf/arch/arm64/util/unwind-libdw.c b/tools/perf/arch/arm64/util/unwind-libdw.c index 09385081bb03..e056d50ab42e 100644 --- a/tools/perf/arch/arm64/util/unwind-libdw.c +++ b/tools/perf/arch/arm64/util/unwind-libdw.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../../util/unwind-libdw.h" #include "../../../util/perf_regs.h" #include "../../../util/sample.h" diff --git a/tools/perf/arch/csky/include/perf_regs.h b/tools/perf/arch/csky/include/perf_regs.h index 1afcc0e916c2..076c7746c8a2 100644 --- a/tools/perf/arch/csky/include/perf_regs.h +++ b/tools/perf/arch/csky/include/perf_regs.h @@ -12,7 +12,4 @@ #define PERF_REGS_MAX PERF_REG_CSKY_MAX #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 -#define PERF_REG_IP PERF_REG_CSKY_PC -#define PERF_REG_SP PERF_REG_CSKY_SP - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/csky/util/perf_regs.c b/tools/perf/arch/csky/util/perf_regs.c index 2864e2e3776d..c0877c264d49 100644 --- a/tools/perf/arch/csky/util/perf_regs.c +++ b/tools/perf/arch/csky/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/csky/util/unwind-libdw.c b/tools/perf/arch/csky/util/unwind-libdw.c index 4bb4a06776e4..79df4374ab18 100644 --- a/tools/perf/arch/csky/util/unwind-libdw.c +++ b/tools/perf/arch/csky/util/unwind-libdw.c @@ -2,6 +2,7 @@ // Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../util/unwind-libdw.h" #include "../../util/perf_regs.h" #include "../../util/event.h" diff --git a/tools/perf/arch/loongarch/include/perf_regs.h b/tools/perf/arch/loongarch/include/perf_regs.h index 7833c7dbd38d..45c799fa5330 100644 --- a/tools/perf/arch/loongarch/include/perf_regs.h +++ b/tools/perf/arch/loongarch/include/perf_regs.h @@ -7,8 +7,6 @@ #include <asm/perf_regs.h> #define PERF_REGS_MAX PERF_REG_LOONGARCH_MAX -#define PERF_REG_IP PERF_REG_LOONGARCH_PC -#define PERF_REG_SP PERF_REG_LOONGARCH_R3 #define PERF_REGS_MASK ((1ULL << PERF_REG_LOONGARCH_MAX) - 1) diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c index 2833e101a7c6..2c56e8b56ddf 100644 --- a/tools/perf/arch/loongarch/util/perf_regs.c +++ b/tools/perf/arch/loongarch/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/loongarch/util/unwind-libdw.c b/tools/perf/arch/loongarch/util/unwind-libdw.c index a9415385230a..7b3b9a4b21f8 100644 --- a/tools/perf/arch/loongarch/util/unwind-libdw.c +++ b/tools/perf/arch/loongarch/util/unwind-libdw.c @@ -2,6 +2,7 @@ /* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */ #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../util/unwind-libdw.h" #include "../../util/perf_regs.h" #include "../../util/sample.h" diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl index cfda2511badf..cb5e757f6621 100644 --- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl +++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl @@ -366,3 +366,4 @@ 449 n64 futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 n64 cachestat sys_cachestat +452 n64 fchmodat2 sys_fchmodat2 diff --git a/tools/perf/arch/mips/include/perf_regs.h b/tools/perf/arch/mips/include/perf_regs.h index b8cd8bbb37ba..7082e91e0ed1 100644 --- a/tools/perf/arch/mips/include/perf_regs.h +++ b/tools/perf/arch/mips/include/perf_regs.h @@ -7,8 +7,6 @@ #include <asm/perf_regs.h> #define PERF_REGS_MAX PERF_REG_MIPS_MAX -#define PERF_REG_IP PERF_REG_MIPS_PC -#define PERF_REG_SP PERF_REG_MIPS_R29 #define PERF_REGS_MASK ((1ULL << PERF_REG_MIPS_MAX) - 1) diff --git a/tools/perf/arch/mips/util/perf_regs.c b/tools/perf/arch/mips/util/perf_regs.c index 2864e2e3776d..c0877c264d49 100644 --- a/tools/perf/arch/mips/util/perf_regs.c +++ b/tools/perf/arch/mips/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index 8c0b08b7a80e..20e50586e8a2 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -538,3 +538,4 @@ 449 common futex_waitv sys_futex_waitv 450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 diff --git a/tools/perf/arch/powerpc/include/perf_regs.h b/tools/perf/arch/powerpc/include/perf_regs.h index 9bb17c3f370b..1c66f6ba6773 100644 --- a/tools/perf/arch/powerpc/include/perf_regs.h +++ b/tools/perf/arch/powerpc/include/perf_regs.h @@ -16,7 +16,4 @@ void perf_regs_load(u64 *regs); #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 #endif -#define PERF_REG_IP PERF_REG_POWERPC_NIP -#define PERF_REG_SP PERF_REG_POWERPC_R1 - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/powerpc/util/mem-events.c b/tools/perf/arch/powerpc/util/mem-events.c index 4120fafe0be4..78b986e5268d 100644 --- a/tools/perf/arch/powerpc/util/mem-events.c +++ b/tools/perf/arch/powerpc/util/mem-events.c @@ -3,10 +3,10 @@ #include "mem-events.h" /* PowerPC does not support 'ldlat' parameter. */ -char *perf_mem_events__name(int i, char *pmu_name __maybe_unused) +const char *perf_mem_events__name(int i, const char *pmu_name __maybe_unused) { if (i == PERF_MEM_EVENTS__LOAD) - return (char *) "cpu/mem-loads/"; + return "cpu/mem-loads/"; - return (char *) "cpu/mem-stores/"; + return "cpu/mem-stores/"; } diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c index 8d07a78e742a..b38aa056eea0 100644 --- a/tools/perf/arch/powerpc/util/perf_regs.c +++ b/tools/perf/arch/powerpc/util/perf_regs.c @@ -4,6 +4,7 @@ #include <regex.h> #include <linux/zalloc.h> +#include "perf_regs.h" #include "../../../util/perf_regs.h" #include "../../../util/debug.h" #include "../../../util/event.h" @@ -226,3 +227,8 @@ uint64_t arch__intr_reg_mask(void) } return mask; } + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/powerpc/util/unwind-libdw.c b/tools/perf/arch/powerpc/util/unwind-libdw.c index e616642c754c..e9a5a8bb67d9 100644 --- a/tools/perf/arch/powerpc/util/unwind-libdw.c +++ b/tools/perf/arch/powerpc/util/unwind-libdw.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> #include <linux/kernel.h> +#include "perf_regs.h" #include "../../../util/unwind-libdw.h" #include "../../../util/perf_regs.h" #include "../../../util/sample.h" diff --git a/tools/perf/arch/riscv/include/perf_regs.h b/tools/perf/arch/riscv/include/perf_regs.h index 6944bf0de53e..d482edb413e5 100644 --- a/tools/perf/arch/riscv/include/perf_regs.h +++ b/tools/perf/arch/riscv/include/perf_regs.h @@ -16,7 +16,4 @@ #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 #endif -#define PERF_REG_IP PERF_REG_RISCV_PC -#define PERF_REG_SP PERF_REG_RISCV_SP - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/riscv/util/perf_regs.c b/tools/perf/arch/riscv/util/perf_regs.c index 2864e2e3776d..c0877c264d49 100644 --- a/tools/perf/arch/riscv/util/perf_regs.c +++ b/tools/perf/arch/riscv/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/arch/riscv/util/unwind-libdw.c index 54a198714eb8..5c98010d8b59 100644 --- a/tools/perf/arch/riscv/util/unwind-libdw.c +++ b/tools/perf/arch/riscv/util/unwind-libdw.c @@ -2,6 +2,7 @@ /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */ #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../util/unwind-libdw.h" #include "../../util/perf_regs.h" #include "../../util/sample.h" diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index a6935af2235c..0122cc156952 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -454,3 +454,4 @@ 449 common futex_waitv sys_futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 sys_fchmodat2 diff --git a/tools/perf/arch/s390/include/perf_regs.h b/tools/perf/arch/s390/include/perf_regs.h index 52fcc0891da6..130dfad2b96a 100644 --- a/tools/perf/arch/s390/include/perf_regs.h +++ b/tools/perf/arch/s390/include/perf_regs.h @@ -11,7 +11,4 @@ void perf_regs_load(u64 *regs); #define PERF_REGS_MAX PERF_REG_S390_MAX #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64 -#define PERF_REG_IP PERF_REG_S390_PC -#define PERF_REG_SP PERF_REG_S390_R15 - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/s390/util/perf_regs.c b/tools/perf/arch/s390/util/perf_regs.c index 2864e2e3776d..c0877c264d49 100644 --- a/tools/perf/arch/s390/util/perf_regs.c +++ b/tools/perf/arch/s390/util/perf_regs.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 +#include "perf_regs.h" #include "../../util/perf_regs.h" const struct sample_reg sample_reg_masks[] = { SMPL_REG_END }; + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/s390/util/unwind-libdw.c b/tools/perf/arch/s390/util/unwind-libdw.c index 7d92452d5287..f50fb6dbb35c 100644 --- a/tools/perf/arch/s390/util/unwind-libdw.c +++ b/tools/perf/arch/s390/util/unwind-libdw.c @@ -5,6 +5,7 @@ #include "../../util/event.h" #include "../../util/sample.h" #include "dwarf-regs-table.h" +#include "perf_regs.h" bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index 227538b0ce80..1d6eee30eceb 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -373,6 +373,8 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 64 map_shadow_stack sys_map_shadow_stack # # Due to a historical design error, certain syscalls are numbered differently diff --git a/tools/perf/arch/x86/entry/syscalls/syscalltbl.sh b/tools/perf/arch/x86/entry/syscalls/syscalltbl.sh index fa526a993845..59d7914ed6bb 100755 --- a/tools/perf/arch/x86/entry/syscalls/syscalltbl.sh +++ b/tools/perf/arch/x86/entry/syscalls/syscalltbl.sh @@ -24,7 +24,7 @@ sorted_table=$(mktemp /tmp/syscalltbl.XXXXXX) grep '^[0-9]' "$in" | sort -n > $sorted_table max_nr=0 -while read nr abi name entry compat; do +while read nr _abi name entry _compat; do if [ $nr -ge 512 ] ; then # discard compat sycalls break fi diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index 16e23b722042..f209ce2c1dd9 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -20,7 +20,5 @@ void perf_regs_load(u64 *regs); #define PERF_REGS_MASK (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~REG_NOSUPPORT) #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64 #endif -#define PERF_REG_IP PERF_REG_X86_IP -#define PERF_REG_SP PERF_REG_X86_SP #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index cbd582182932..b1ce0c52d88d 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -75,11 +75,12 @@ int arch_evlist__add_default_attrs(struct evlist *evlist, int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs) { - if (topdown_sys_has_perf_metrics() && evsel__sys_has_perf_metrics(lhs)) { + if (topdown_sys_has_perf_metrics() && + (arch_evsel__must_be_in_group(lhs) || arch_evsel__must_be_in_group(rhs))) { /* Ensure the topdown slots comes first. */ - if (strcasestr(lhs->name, "slots")) + if (strcasestr(lhs->name, "slots") && !strcasestr(lhs->name, "uops_retired.slots")) return -1; - if (strcasestr(rhs->name, "slots")) + if (strcasestr(rhs->name, "slots") && !strcasestr(rhs->name, "uops_retired.slots")) return 1; /* Followed by topdown events. */ if (strcasestr(lhs->name, "topdown") && !strcasestr(rhs->name, "topdown")) diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 81d22657922a..090d0f371891 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -40,12 +40,11 @@ bool evsel__sys_has_perf_metrics(const struct evsel *evsel) bool arch_evsel__must_be_in_group(const struct evsel *evsel) { - if (!evsel__sys_has_perf_metrics(evsel)) + if (!evsel__sys_has_perf_metrics(evsel) || !evsel->name || + strcasestr(evsel->name, "uops_retired.slots")) return false; - return evsel->name && - (strcasestr(evsel->name, "slots") || - strcasestr(evsel->name, "topdown")); + return strcasestr(evsel->name, "topdown") || strcasestr(evsel->name, "slots"); } int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 74b70fd379df..31807791589e 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -60,8 +60,7 @@ struct intel_pt_recording { size_t priv_size; }; -static int intel_pt_parse_terms_with_default(const char *pmu_name, - struct list_head *formats, +static int intel_pt_parse_terms_with_default(struct perf_pmu *pmu, const char *str, u64 *config) { @@ -75,13 +74,12 @@ static int intel_pt_parse_terms_with_default(const char *pmu_name, INIT_LIST_HEAD(terms); - err = parse_events_terms(terms, str); + err = parse_events_terms(terms, str, /*input=*/ NULL); if (err) goto out_free; attr.config = *config; - err = perf_pmu__config_terms(pmu_name, formats, &attr, terms, true, - NULL); + err = perf_pmu__config_terms(pmu, &attr, terms, /*zero=*/true, /*err=*/NULL); if (err) goto out_free; @@ -91,12 +89,10 @@ out_free: return err; } -static int intel_pt_parse_terms(const char *pmu_name, struct list_head *formats, - const char *str, u64 *config) +static int intel_pt_parse_terms(struct perf_pmu *pmu, const char *str, u64 *config) { *config = 0; - return intel_pt_parse_terms_with_default(pmu_name, formats, str, - config); + return intel_pt_parse_terms_with_default(pmu, str, config); } static u64 intel_pt_masked_bits(u64 mask, u64 bits) @@ -126,7 +122,7 @@ static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str, *res = 0; - mask = perf_pmu__format_bits(&intel_pt_pmu->format, str); + mask = perf_pmu__format_bits(intel_pt_pmu, str); if (!mask) return -EINVAL; @@ -236,8 +232,7 @@ static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu) pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, buf, - &config); + intel_pt_parse_terms(intel_pt_pmu, buf, &config); close(dirfd); return config; @@ -348,16 +343,11 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, if (priv_size != ptr->priv_size) return -EINVAL; - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "tsc", &tsc_bit); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "noretcomp", &noretcomp_bit); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "mtc", &mtc_bit); - mtc_freq_bits = perf_pmu__format_bits(&intel_pt_pmu->format, - "mtc_period"); - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "cyc", &cyc_bit); + intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); + intel_pt_parse_terms(intel_pt_pmu, "noretcomp", &noretcomp_bit); + intel_pt_parse_terms(intel_pt_pmu, "mtc", &mtc_bit); + mtc_freq_bits = perf_pmu__format_bits(intel_pt_pmu, "mtc_period"); + intel_pt_parse_terms(intel_pt_pmu, "cyc", &cyc_bit); intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); @@ -511,7 +501,7 @@ static int intel_pt_val_config_term(struct perf_pmu *intel_pt_pmu, int dirfd, valid |= 1; - bits = perf_pmu__format_bits(&intel_pt_pmu->format, name); + bits = perf_pmu__format_bits(intel_pt_pmu, name); config &= bits; @@ -781,8 +771,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr, intel_pt_evsel->core.attr.aux_watermark = aux_watermark; } - intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, - "tsc", &tsc_bit); + intel_pt_parse_terms(intel_pt_pmu, "tsc", &tsc_bit); if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit)) have_timing_info = true; diff --git a/tools/perf/arch/x86/util/mem-events.c b/tools/perf/arch/x86/util/mem-events.c index a8a782bcb121..191b372f9a2d 100644 --- a/tools/perf/arch/x86/util/mem-events.c +++ b/tools/perf/arch/x86/util/mem-events.c @@ -52,7 +52,7 @@ bool is_mem_loads_aux_event(struct evsel *leader) return leader->core.attr.config == MEM_LOADS_AUX; } -char *perf_mem_events__name(int i, char *pmu_name) +const char *perf_mem_events__name(int i, const char *pmu_name) { struct perf_mem_event *e = perf_mem_events__ptr(i); @@ -65,7 +65,7 @@ char *perf_mem_events__name(int i, char *pmu_name) if (!pmu_name) { mem_loads_name__init = true; - pmu_name = (char *)"cpu"; + pmu_name = "cpu"; } if (perf_pmus__have_event(pmu_name, "mem-loads-aux")) { @@ -82,12 +82,12 @@ char *perf_mem_events__name(int i, char *pmu_name) if (i == PERF_MEM_EVENTS__STORE) { if (!pmu_name) - pmu_name = (char *)"cpu"; + pmu_name = "cpu"; scnprintf(mem_stores_name, sizeof(mem_stores_name), e->name, pmu_name); return mem_stores_name; } - return (char *)e->name; + return e->name; } diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c index 8ad4112ad10c..b813502a2727 100644 --- a/tools/perf/arch/x86/util/perf_regs.c +++ b/tools/perf/arch/x86/util/perf_regs.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/zalloc.h> +#include "perf_regs.h" #include "../../../perf-sys.h" #include "../../../util/perf_regs.h" #include "../../../util/debug.h" @@ -317,3 +318,8 @@ uint64_t arch__intr_reg_mask(void) return PERF_REGS_MASK; } + +uint64_t arch__user_reg_mask(void) +{ + return PERF_REGS_MASK; +} diff --git a/tools/perf/arch/x86/util/pmu.c b/tools/perf/arch/x86/util/pmu.c index 65d8cdff4d5f..f428cffb0378 100644 --- a/tools/perf/arch/x86/util/pmu.c +++ b/tools/perf/arch/x86/util/pmu.c @@ -126,7 +126,7 @@ close_dir: return ret; } -static char *__pmu_find_real_name(const char *name) +static const char *__pmu_find_real_name(const char *name) { struct pmu_alias *pmu_alias; @@ -135,10 +135,10 @@ static char *__pmu_find_real_name(const char *name) return pmu_alias->name; } - return (char *)name; + return name; } -char *pmu_find_real_name(const char *name) +const char *pmu_find_real_name(const char *name) { if (cached_list) return __pmu_find_real_name(name); @@ -149,7 +149,7 @@ char *pmu_find_real_name(const char *name) return __pmu_find_real_name(name); } -static char *__pmu_find_alias_name(const char *name) +static const char *__pmu_find_alias_name(const char *name) { struct pmu_alias *pmu_alias; @@ -160,7 +160,7 @@ static char *__pmu_find_alias_name(const char *name) return NULL; } -char *pmu_find_alias_name(const char *name) +const char *pmu_find_alias_name(const char *name) { if (cached_list) return __pmu_find_alias_name(name); diff --git a/tools/perf/arch/x86/util/unwind-libdw.c b/tools/perf/arch/x86/util/unwind-libdw.c index ef71e8bf80bf..edb77e20e083 100644 --- a/tools/perf/arch/x86/util/unwind-libdw.c +++ b/tools/perf/arch/x86/util/unwind-libdw.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../../util/unwind-libdw.h" #include "../../../util/perf_regs.h" #include "util/sample.h" diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build index 07bbc449329e..c2ab30907ae7 100644 --- a/tools/perf/bench/Build +++ b/tools/perf/bench/Build @@ -17,6 +17,7 @@ perf-y += inject-buildid.o perf-y += evlist-open-close.o perf-y += breakpoint.o perf-y += pmu-scan.o +perf-y += uprobe.o perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o perf-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index a0625c77bea3..faa18e6d2467 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -43,6 +43,9 @@ int bench_inject_build_id(int argc, const char **argv); int bench_evlist_open_close(int argc, const char **argv); int bench_breakpoint_thread(int argc, const char **argv); int bench_breakpoint_enable(int argc, const char **argv); +int bench_uprobe_baseline(int argc, const char **argv); +int bench_uprobe_empty(int argc, const char **argv); +int bench_uprobe_trace_printk(int argc, const char **argv); int bench_pmu_scan(int argc, const char **argv); #define BENCH_FORMAT_DEFAULT_STR "default" diff --git a/tools/perf/bench/breakpoint.c b/tools/perf/bench/breakpoint.c index 41385f89ffc7..dfd18f5db97d 100644 --- a/tools/perf/bench/breakpoint.c +++ b/tools/perf/bench/breakpoint.c @@ -47,6 +47,7 @@ struct breakpoint { static int breakpoint_setup(void *addr) { struct perf_event_attr attr = { .size = 0, }; + int fd; attr.type = PERF_TYPE_BREAKPOINT; attr.size = sizeof(attr); @@ -56,7 +57,12 @@ static int breakpoint_setup(void *addr) attr.bp_addr = (unsigned long)addr; attr.bp_type = HW_BREAKPOINT_RW; attr.bp_len = HW_BREAKPOINT_LEN_1; - return syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0); + fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0); + + if (fd < 0) + fd = -errno; + + return fd; } static void *passive_thread(void *arg) @@ -122,8 +128,14 @@ int bench_breakpoint_thread(int argc, const char **argv) for (i = 0; i < thread_params.nbreakpoints; i++) { breakpoints[i].fd = breakpoint_setup(&breakpoints[i].watched); - if (breakpoints[i].fd == -1) + + if (breakpoints[i].fd < 0) { + if (breakpoints[i].fd == -ENODEV) { + printf("Skipping perf bench breakpoint thread: No hardware support\n"); + return 0; + } exit((perror("perf_event_open"), EXIT_FAILURE)); + } } gettimeofday(&start, NULL); for (i = 0; i < thread_params.nparallel; i++) { @@ -196,8 +208,14 @@ int bench_breakpoint_enable(int argc, const char **argv) exit(EXIT_FAILURE); } fd = breakpoint_setup(&watched); - if (fd == -1) + + if (fd < 0) { + if (fd == -ENODEV) { + printf("Skipping perf bench breakpoint enable: No hardware support\n"); + return 0; + } exit((perror("perf_event_open"), EXIT_FAILURE)); + } nthreads = enable_params.npassive + enable_params.nactive; threads = calloc(nthreads, sizeof(threads[0])); if (!threads) diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c index c7d207f8e13c..9e4d36486f62 100644 --- a/tools/perf/bench/pmu-scan.c +++ b/tools/perf/bench/pmu-scan.c @@ -57,9 +57,7 @@ static int save_result(void) r->is_core = pmu->is_core; r->nr_caps = pmu->nr_caps; - r->nr_aliases = 0; - list_for_each(list, &pmu->aliases) - r->nr_aliases++; + r->nr_aliases = perf_pmu__num_events(pmu); r->nr_formats = 0; list_for_each(list, &pmu->format) @@ -98,9 +96,7 @@ static int check_result(bool core_only) return -1; } - nr = 0; - list_for_each(list, &pmu->aliases) - nr++; + nr = perf_pmu__num_events(pmu); if (nr != r->nr_aliases) { pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n", pmu->name, r->nr_aliases, nr); diff --git a/tools/perf/bench/sched-seccomp-notify.c b/tools/perf/bench/sched-seccomp-notify.c index b04ebcde4036..a01c40131493 100644 --- a/tools/perf/bench/sched-seccomp-notify.c +++ b/tools/perf/bench/sched-seccomp-notify.c @@ -9,7 +9,7 @@ #include <sys/syscall.h> #include <sys/ioctl.h> #include <linux/time64.h> -#include <linux/seccomp.h> +#include <uapi/linux/seccomp.h> #include <sys/prctl.h> #include <unistd.h> diff --git a/tools/perf/bench/uprobe.c b/tools/perf/bench/uprobe.c new file mode 100644 index 000000000000..914c0817fe8a --- /dev/null +++ b/tools/perf/bench/uprobe.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +/* + * uprobe.c + * + * uprobe benchmarks + * + * Copyright (C) 2023, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + */ +#include "../perf.h" +#include "../util/util.h" +#include <subcmd/parse-options.h> +#include "../builtin.h" +#include "bench.h" +#include <linux/compiler.h> +#include <linux/time64.h> + +#include <inttypes.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> + +#define LOOPS_DEFAULT 1000 +static int loops = LOOPS_DEFAULT; + +enum bench_uprobe { + BENCH_UPROBE__BASELINE, + BENCH_UPROBE__EMPTY, + BENCH_UPROBE__TRACE_PRINTK, +}; + +static const struct option options[] = { + OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), + OPT_END() +}; + +static const char * const bench_uprobe_usage[] = { + "perf bench uprobe <options>", + NULL +}; + +#ifdef HAVE_BPF_SKEL +#include "bpf_skel/bench_uprobe.skel.h" + +#define bench_uprobe__attach_uprobe(prog) \ + skel->links.prog = bpf_program__attach_uprobe_opts(/*prog=*/skel->progs.prog, \ + /*pid=*/-1, \ + /*binary_path=*/"/lib64/libc.so.6", \ + /*func_offset=*/0, \ + /*opts=*/&uprobe_opts); \ + if (!skel->links.prog) { \ + err = -errno; \ + fprintf(stderr, "Failed to attach bench uprobe \"%s\": %s\n", #prog, strerror(errno)); \ + goto cleanup; \ + } + +struct bench_uprobe_bpf *skel; + +static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench) +{ + DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); + int err; + + /* Load and verify BPF application */ + skel = bench_uprobe_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load uprobes bench BPF skeleton\n"); + return -1; + } + + err = bench_uprobe_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + uprobe_opts.func_name = "usleep"; + switch (bench) { + case BENCH_UPROBE__BASELINE: break; + case BENCH_UPROBE__EMPTY: bench_uprobe__attach_uprobe(empty); break; + case BENCH_UPROBE__TRACE_PRINTK: bench_uprobe__attach_uprobe(trace_printk); break; + default: + fprintf(stderr, "Invalid bench: %d\n", bench); + goto cleanup; + } + + return err; +cleanup: + bench_uprobe_bpf__destroy(skel); + return err; +} + +static void bench_uprobe__teardown_bpf_skel(void) +{ + if (skel) { + bench_uprobe_bpf__destroy(skel); + skel = NULL; + } +} +#else +static int bench_uprobe__setup_bpf_skel(enum bench_uprobe bench __maybe_unused) { return 0; } +static void bench_uprobe__teardown_bpf_skel(void) {}; +#endif + +static int bench_uprobe_format__default_fprintf(const char *name, const char *unit, u64 diff, FILE *fp) +{ + static u64 baseline, previous; + s64 diff_to_baseline = diff - baseline, + diff_to_previous = diff - previous; + int printed = fprintf(fp, "# Executed %'d %s calls\n", loops, name); + + printed += fprintf(fp, " %14s: %'" PRIu64 " %ss", "Total time", diff, unit); + + if (baseline) { + printed += fprintf(fp, " %s%'" PRId64 " to baseline", diff_to_baseline > 0 ? "+" : "", diff_to_baseline); + + if (previous != baseline) + fprintf(stdout, " %s%'" PRId64 " to previous", diff_to_previous > 0 ? "+" : "", diff_to_previous); + } + + printed += fprintf(fp, "\n\n %'.3f %ss/op", (double)diff / (double)loops, unit); + + if (baseline) { + printed += fprintf(fp, " %'.3f %ss/op to baseline", (double)diff_to_baseline / (double)loops, unit); + + if (previous != baseline) + printed += fprintf(fp, " %'.3f %ss/op to previous", (double)diff_to_previous / (double)loops, unit); + } else { + baseline = diff; + } + + fputc('\n', fp); + + previous = diff; + + return printed + 1; +} + +static int bench_uprobe(int argc, const char **argv, enum bench_uprobe bench) +{ + const char *name = "usleep(1000)", *unit = "usec"; + struct timespec start, end; + u64 diff; + int i; + + argc = parse_options(argc, argv, options, bench_uprobe_usage, 0); + + if (bench != BENCH_UPROBE__BASELINE && bench_uprobe__setup_bpf_skel(bench) < 0) + return 0; + + clock_gettime(CLOCK_REALTIME, &start); + + for (i = 0; i < loops; i++) { + usleep(USEC_PER_MSEC); + } + + clock_gettime(CLOCK_REALTIME, &end); + + diff = end.tv_sec * NSEC_PER_SEC + end.tv_nsec - (start.tv_sec * NSEC_PER_SEC + start.tv_nsec); + diff /= NSEC_PER_USEC; + + switch (bench_format) { + case BENCH_FORMAT_DEFAULT: + bench_uprobe_format__default_fprintf(name, unit, diff, stdout); + break; + + case BENCH_FORMAT_SIMPLE: + printf("%" PRIu64 "\n", diff); + break; + + default: + /* reaching here is something of a disaster */ + fprintf(stderr, "Unknown format:%d\n", bench_format); + exit(1); + } + + if (bench != BENCH_UPROBE__BASELINE) + bench_uprobe__teardown_bpf_skel(); + + return 0; +} + +int bench_uprobe_baseline(int argc, const char **argv) +{ + return bench_uprobe(argc, argv, BENCH_UPROBE__BASELINE); +} + +int bench_uprobe_empty(int argc, const char **argv) +{ + return bench_uprobe(argc, argv, BENCH_UPROBE__EMPTY); +} + +int bench_uprobe_trace_printk(int argc, const char **argv) +{ + return bench_uprobe(argc, argv, BENCH_UPROBE__TRACE_PRINTK); +} diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index 5033e8bab276..1a8898d5b560 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -105,6 +105,13 @@ static struct bench breakpoint_benchmarks[] = { { NULL, NULL, NULL }, }; +static struct bench uprobe_benchmarks[] = { + { "baseline", "Baseline libc usleep(1000) call", bench_uprobe_baseline, }, + { "empty", "Attach empty BPF prog to uprobe on usleep, system wide", bench_uprobe_empty, }, + { "trace_printk", "Attach trace_printk BPF prog to uprobe on usleep syswide", bench_uprobe_trace_printk, }, + { NULL, NULL, NULL }, +}; + struct collection { const char *name; const char *summary; @@ -124,6 +131,7 @@ static struct collection collections[] = { #endif { "internals", "Perf-internals benchmarks", internals_benchmarks }, { "breakpoint", "Breakpoint benchmarks", breakpoint_benchmarks }, + { "uprobe", "uprobe benchmarks", uprobe_benchmarks }, { "all", "All benchmarks", NULL }, { NULL, NULL, NULL } }; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index e8a1b16aa5f8..57d300d8e570 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -1915,8 +1915,8 @@ static int data_init(int argc, const char **argv) struct perf_data *data = &d->data; data->path = use_default ? defaults[i] : argv[i]; - data->mode = PERF_DATA_MODE_READ, - data->force = force, + data->mode = PERF_DATA_MODE_READ; + data->force = force; d->idx = i; } diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 7fec2cca759f..a343823c8ddf 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -145,9 +145,20 @@ static void default_print_event(void *ps, const char *pmu_name, const char *topi putchar('\n'); if (desc && print_state->desc) { + char *desc_with_unit = NULL; + int desc_len = -1; + + if (pmu_name && strcmp(pmu_name, "default_core")) { + desc_len = strlen(desc); + desc_len = asprintf(&desc_with_unit, + desc[desc_len - 1] != '.' + ? "%s. Unit: %s" : "%s Unit: %s", + desc, pmu_name); + } printf("%*s", 8, "["); - wordwrap(desc, 8, pager_get_columns(), 0); + wordwrap(desc_len > 0 ? desc_with_unit : desc, 8, pager_get_columns(), 0); printf("]\n"); + free(desc_with_unit); } long_desc = long_desc ?: desc; if (long_desc && print_state->long_desc) { @@ -423,6 +434,13 @@ static void json_print_metric(void *ps __maybe_unused, const char *group, strbuf_release(&buf); } +static bool default_skip_duplicate_pmus(void *ps) +{ + struct print_state *print_state = ps; + + return !print_state->long_desc; +} + int cmd_list(int argc, const char **argv) { int i, ret = 0; @@ -434,6 +452,7 @@ int cmd_list(int argc, const char **argv) .print_end = default_print_end, .print_event = default_print_event, .print_metric = default_print_metric, + .skip_duplicate_pmus = default_skip_duplicate_pmus, }; const char *cputype = NULL; const char *unit_name = NULL; @@ -502,7 +521,7 @@ int cmd_list(int argc, const char **argv) ret = -1; goto out; } - default_ps.pmu_glob = pmu->name; + default_ps.pmu_glob = strdup(pmu->name); } } print_cb.print_start(ps); diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index c15386cb1033..b141f2134274 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -2052,6 +2052,7 @@ static int __cmd_contention(int argc, const char **argv) if (IS_ERR(session)) { pr_err("Initializing perf session failed\n"); err = PTR_ERR(session); + session = NULL; goto out_delete; } @@ -2506,7 +2507,7 @@ int cmd_lock(int argc, const char **argv) OPT_CALLBACK('M', "map-nr-entries", &bpf_map_entries, "num", "Max number of BPF map entries", parse_map_entry), OPT_CALLBACK(0, "max-stack", &max_stack_depth, "num", - "Set the maximum stack depth when collecting lopck contention, " + "Set the maximum stack depth when collecting lock contention, " "Default: " __stringify(CONTENTION_STACK_DEPTH), parse_max_stack), OPT_INTEGER(0, "stack-skip", &stack_skip, "Set the number of stack depth to skip when finding a lock caller, " diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index aec18db7ff23..34bb31f08bb5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -37,8 +37,6 @@ #include "util/parse-branch-options.h" #include "util/parse-regs-options.h" #include "util/perf_api_probe.h" -#include "util/llvm-utils.h" -#include "util/bpf-loader.h" #include "util/trigger.h" #include "util/perf-hooks.h" #include "util/cpu-set-sched.h" @@ -2465,16 +2463,6 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) } } - err = bpf__apply_obj_config(); - if (err) { - char errbuf[BUFSIZ]; - - bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); - pr_err("ERROR: Apply config to BPF failed: %s\n", - errbuf); - goto out_free_threads; - } - /* * Normally perf_session__new would do this, but it doesn't have the * evlist. @@ -3486,10 +3474,6 @@ static struct option __record_options[] = { "collect kernel callchains"), OPT_BOOLEAN(0, "user-callchains", &record.opts.user_callchains, "collect user callchains"), - OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path", - "clang binary to use for compiling BPF scriptlets"), - OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options", - "options passed to clang when compiling BPF scriptlets"), OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), OPT_BOOLEAN(0, "buildid-all", &record.buildid_all, @@ -3967,27 +3951,6 @@ int cmd_record(int argc, const char **argv) setlocale(LC_ALL, ""); -#ifndef HAVE_LIBBPF_SUPPORT -# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c) - set_nobuild('\0', "clang-path", true); - set_nobuild('\0', "clang-opt", true); -# undef set_nobuild -#endif - -#ifndef HAVE_BPF_PROLOGUE -# if !defined (HAVE_DWARF_SUPPORT) -# define REASON "NO_DWARF=1" -# elif !defined (HAVE_LIBBPF_SUPPORT) -# define REASON "NO_LIBBPF=1" -# else -# define REASON "this architecture doesn't support BPF prologue" -# endif -# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, REASON, c) - set_nobuild('\0', "vmlinux", true); -# undef set_nobuild -# undef REASON -#endif - #ifndef HAVE_BPF_SKEL # define set_nobuild(s, l, m, c) set_option_nobuild(record_options, s, l, m, c) set_nobuild('\0', "off-cpu", "no BUILD_BPF_SKEL=1", true); @@ -4116,14 +4079,6 @@ int cmd_record(int argc, const char **argv) if (dry_run) goto out; - err = bpf__setup_stdout(rec->evlist); - if (err) { - bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); - pr_err("ERROR: Setup BPF stdout failed: %s\n", - errbuf); - goto out; - } - err = -ENOMEM; if (rec->no_buildid_cache || rec->no_buildid) { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 200b3e7ea8da..517bf25750c8 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2199,6 +2199,17 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(RETIRE_LAT)) fprintf(fp, "%16" PRIu16, sample->retire_lat); + if (PRINT_FIELD(CGROUP)) { + const char *cgrp_name; + struct cgroup *cgrp = cgroup__find(machine->env, + sample->cgroup); + if (cgrp != NULL) + cgrp_name = cgrp->name; + else + cgrp_name = "unknown"; + fprintf(fp, " %s", cgrp_name); + } + if (PRINT_FIELD(IP)) { struct callchain_cursor *cursor = NULL; @@ -2243,17 +2254,6 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(CODE_PAGE_SIZE)) fprintf(fp, " %s", get_page_size_name(sample->code_page_size, str)); - if (PRINT_FIELD(CGROUP)) { - const char *cgrp_name; - struct cgroup *cgrp = cgroup__find(machine->env, - sample->cgroup); - if (cgrp != NULL) - cgrp_name = cgrp->name; - else - cgrp_name = "unknown"; - fprintf(fp, " %s", cgrp_name); - } - perf_sample__fprintf_ipc(sample, attr, fp); fprintf(fp, "\n"); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 1baa2acb3ced..ea8c7eca5eee 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1805,6 +1805,7 @@ int cmd_top(int argc, const char **argv) top.session = perf_session__new(NULL, NULL); if (IS_ERR(top.session)) { status = PTR_ERR(top.session); + top.session = NULL; goto out_delete_evlist; } diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6e73d0e95715..e541d0e2777a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -18,6 +18,10 @@ #include <api/fs/tracing_path.h> #ifdef HAVE_LIBBPF_SUPPORT #include <bpf/bpf.h> +#include <bpf/libbpf.h> +#ifdef HAVE_BPF_SKEL +#include "bpf_skel/augmented_raw_syscalls.skel.h" +#endif #endif #include "util/bpf_map.h" #include "util/rlimit.h" @@ -53,7 +57,6 @@ #include "trace/beauty/beauty.h" #include "trace-event.h" #include "util/parse-events.h" -#include "util/bpf-loader.h" #include "util/tracepoint.h" #include "callchain.h" #include "print_binary.h" @@ -127,25 +130,19 @@ struct trace { struct syscalltbl *sctbl; struct { struct syscall *table; - struct { // per syscall BPF_MAP_TYPE_PROG_ARRAY - struct bpf_map *sys_enter, - *sys_exit; - } prog_array; struct { struct evsel *sys_enter, - *sys_exit, - *augmented; + *sys_exit, + *bpf_output; } events; - struct bpf_program *unaugmented_prog; } syscalls; - struct { - struct bpf_map *map; - } dump; +#ifdef HAVE_BPF_SKEL + struct augmented_raw_syscalls_bpf *skel; +#endif struct record_opts opts; struct evlist *evlist; struct machine *host; struct thread *current; - struct bpf_object *bpf_obj; struct cgroup *cgroup; u64 base_time; FILE *output; @@ -415,6 +412,7 @@ static int evsel__init_syscall_tp(struct evsel *evsel) if (evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") && evsel__init_tp_uint_field(evsel, &sc->id, "nr")) return -ENOENT; + return 0; } @@ -1296,6 +1294,22 @@ static struct thread_trace *thread_trace__new(void) return ttrace; } +static void thread_trace__free_files(struct thread_trace *ttrace); + +static void thread_trace__delete(void *pttrace) +{ + struct thread_trace *ttrace = pttrace; + + if (!ttrace) + return; + + intlist__delete(ttrace->syscall_stats); + ttrace->syscall_stats = NULL; + thread_trace__free_files(ttrace); + zfree(&ttrace->entry_str); + free(ttrace); +} + static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) { struct thread_trace *ttrace; @@ -1333,6 +1347,17 @@ void syscall_arg__set_ret_scnprintf(struct syscall_arg *arg, static const size_t trace__entry_str_size = 2048; +static void thread_trace__free_files(struct thread_trace *ttrace) +{ + for (int i = 0; i < ttrace->files.max; ++i) { + struct file *file = ttrace->files.table + i; + zfree(&file->pathname); + } + + zfree(&ttrace->files.table); + ttrace->files.max = -1; +} + static struct file *thread_trace__files_entry(struct thread_trace *ttrace, int fd) { if (fd < 0) @@ -1635,6 +1660,8 @@ static int trace__symbols_init(struct trace *trace, struct evlist *evlist) if (trace->host == NULL) return -ENOMEM; + thread__set_priv_destructor(thread_trace__delete); + err = trace_event__register_resolver(trace->host, trace__machine__resolve_kernel_addr); if (err < 0) goto out; @@ -2816,7 +2843,7 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, if (thread) trace__fprintf_comm_tid(trace, thread, trace->output); - if (evsel == trace->syscalls.events.augmented) { + if (evsel == trace->syscalls.events.bpf_output) { int id = perf_evsel__sc_tp_uint(evsel, id, sample); struct syscall *sc = trace__syscall_info(trace, evsel, id); @@ -3136,13 +3163,8 @@ static void evlist__free_syscall_tp_fields(struct evlist *evlist) struct evsel *evsel; evlist__for_each_entry(evlist, evsel) { - struct evsel_trace *et = evsel->priv; - - if (!et || !evsel->tp_format || strcmp(evsel->tp_format->system, "syscalls")) - continue; - - zfree(&et->fmt); - free(et); + evsel_trace__delete(evsel->priv); + evsel->priv = NULL; } } @@ -3254,35 +3276,16 @@ out_enomem: goto out; } -#ifdef HAVE_LIBBPF_SUPPORT -static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace, const char *name) -{ - if (trace->bpf_obj == NULL) - return NULL; - - return bpf_object__find_map_by_name(trace->bpf_obj, name); -} - -static void trace__set_bpf_map_filtered_pids(struct trace *trace) -{ - trace->filter_pids.map = trace__find_bpf_map_by_name(trace, "pids_filtered"); -} - -static void trace__set_bpf_map_syscalls(struct trace *trace) -{ - trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter"); - trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit"); -} - +#ifdef HAVE_BPF_SKEL static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name) { struct bpf_program *pos, *prog = NULL; const char *sec_name; - if (trace->bpf_obj == NULL) + if (trace->skel->obj == NULL) return NULL; - bpf_object__for_each_program(pos, trace->bpf_obj) { + bpf_object__for_each_program(pos, trace->skel->obj) { sec_name = bpf_program__section_name(pos); if (sec_name && !strcmp(sec_name, name)) { prog = pos; @@ -3300,12 +3303,12 @@ static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, str if (prog_name == NULL) { char default_prog_name[256]; - scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->name); + scnprintf(default_prog_name, sizeof(default_prog_name), "tp/syscalls/sys_%s_%s", type, sc->name); prog = trace__find_bpf_program_by_title(trace, default_prog_name); if (prog != NULL) goto out_found; if (sc->fmt && sc->fmt->alias) { - scnprintf(default_prog_name, sizeof(default_prog_name), "!syscalls:sys_%s_%s", type, sc->fmt->alias); + scnprintf(default_prog_name, sizeof(default_prog_name), "tp/syscalls/sys_%s_%s", type, sc->fmt->alias); prog = trace__find_bpf_program_by_title(trace, default_prog_name); if (prog != NULL) goto out_found; @@ -3323,7 +3326,7 @@ out_found: pr_debug("Couldn't find BPF prog \"%s\" to associate with syscalls:sys_%s_%s, not augmenting it\n", prog_name, type, sc->name); out_unaugmented: - return trace->syscalls.unaugmented_prog; + return trace->skel->progs.syscall_unaugmented; } static void trace__init_syscall_bpf_progs(struct trace *trace, int id) @@ -3340,13 +3343,13 @@ static void trace__init_syscall_bpf_progs(struct trace *trace, int id) static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int id) { struct syscall *sc = trace__syscall_info(trace, NULL, id); - return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->syscalls.unaugmented_prog); + return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); } static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id) { struct syscall *sc = trace__syscall_info(trace, NULL, id); - return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->syscalls.unaugmented_prog); + return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->skel->progs.syscall_unaugmented); } static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc) @@ -3371,7 +3374,7 @@ try_to_find_pair: bool is_candidate = false; if (pair == NULL || pair == sc || - pair->bpf_prog.sys_enter == trace->syscalls.unaugmented_prog) + pair->bpf_prog.sys_enter == trace->skel->progs.syscall_unaugmented) continue; for (field = sc->args, candidate_field = pair->args; @@ -3395,6 +3398,19 @@ try_to_find_pair: if (strcmp(field->type, candidate_field->type)) goto next_candidate; + /* + * This is limited in the BPF program but sys_write + * uses "const char *" for its "buf" arg so we need to + * use some heuristic that is kinda future proof... + */ + if (strcmp(field->type, "const char *") == 0 && + !(strstr(field->name, "name") || + strstr(field->name, "path") || + strstr(field->name, "file") || + strstr(field->name, "root") || + strstr(field->name, "description"))) + goto next_candidate; + is_candidate = true; } @@ -3424,7 +3440,7 @@ try_to_find_pair: */ if (pair_prog == NULL) { pair_prog = trace__find_syscall_bpf_prog(trace, pair, pair->fmt ? pair->fmt->bpf_prog_name.sys_enter : NULL, "enter"); - if (pair_prog == trace->syscalls.unaugmented_prog) + if (pair_prog == trace->skel->progs.syscall_unaugmented) goto next_candidate; } @@ -3439,8 +3455,8 @@ try_to_find_pair: static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace) { - int map_enter_fd = bpf_map__fd(trace->syscalls.prog_array.sys_enter), - map_exit_fd = bpf_map__fd(trace->syscalls.prog_array.sys_exit); + int map_enter_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_enter); + int map_exit_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_exit); int err = 0, key; for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) { @@ -3502,7 +3518,7 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace) * For now we're just reusing the sys_enter prog, and if it * already has an augmenter, we don't need to find one. */ - if (sc->bpf_prog.sys_enter != trace->syscalls.unaugmented_prog) + if (sc->bpf_prog.sys_enter != trace->skel->progs.syscall_unaugmented) continue; /* @@ -3525,74 +3541,9 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace) break; } - return err; } - -static void trace__delete_augmented_syscalls(struct trace *trace) -{ - struct evsel *evsel, *tmp; - - evlist__remove(trace->evlist, trace->syscalls.events.augmented); - evsel__delete(trace->syscalls.events.augmented); - trace->syscalls.events.augmented = NULL; - - evlist__for_each_entry_safe(trace->evlist, tmp, evsel) { - if (evsel->bpf_obj == trace->bpf_obj) { - evlist__remove(trace->evlist, evsel); - evsel__delete(evsel); - } - - } - - bpf_object__close(trace->bpf_obj); - trace->bpf_obj = NULL; -} -#else // HAVE_LIBBPF_SUPPORT -static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace __maybe_unused, - const char *name __maybe_unused) -{ - return NULL; -} - -static void trace__set_bpf_map_filtered_pids(struct trace *trace __maybe_unused) -{ -} - -static void trace__set_bpf_map_syscalls(struct trace *trace __maybe_unused) -{ -} - -static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace __maybe_unused, - const char *name __maybe_unused) -{ - return NULL; -} - -static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace __maybe_unused) -{ - return 0; -} - -static void trace__delete_augmented_syscalls(struct trace *trace __maybe_unused) -{ -} -#endif // HAVE_LIBBPF_SUPPORT - -static bool trace__only_augmented_syscalls_evsels(struct trace *trace) -{ - struct evsel *evsel; - - evlist__for_each_entry(trace->evlist, evsel) { - if (evsel == trace->syscalls.events.augmented || - evsel->bpf_obj == trace->bpf_obj) - continue; - - return false; - } - - return true; -} +#endif // HAVE_BPF_SKEL static int trace__set_ev_qualifier_filter(struct trace *trace) { @@ -3956,23 +3907,31 @@ static int trace__run(struct trace *trace, int argc, const char **argv) err = evlist__open(evlist); if (err < 0) goto out_error_open; +#ifdef HAVE_BPF_SKEL + if (trace->syscalls.events.bpf_output) { + struct perf_cpu cpu; - err = bpf__apply_obj_config(); - if (err) { - char errbuf[BUFSIZ]; - - bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); - pr_err("ERROR: Apply config to BPF failed: %s\n", - errbuf); - goto out_error_open; + /* + * Set up the __augmented_syscalls__ BPF map to hold for each + * CPU the bpf-output event's file descriptor. + */ + perf_cpu_map__for_each_cpu(cpu, i, trace->syscalls.events.bpf_output->core.cpus) { + bpf_map__update_elem(trace->skel->maps.__augmented_syscalls__, + &cpu.cpu, sizeof(int), + xyarray__entry(trace->syscalls.events.bpf_output->core.fd, + cpu.cpu, 0), + sizeof(__u32), BPF_ANY); + } } - +#endif err = trace__set_filter_pids(trace); if (err < 0) goto out_error_mem; - if (trace->syscalls.prog_array.sys_enter) +#ifdef HAVE_BPF_SKEL + if (trace->skel && trace->skel->progs.sys_enter) trace__init_syscalls_bpf_prog_array_maps(trace); +#endif if (trace->ev_qualifier_ids.nr > 0) { err = trace__set_ev_qualifier_filter(trace); @@ -4005,9 +3964,6 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (err < 0) goto out_error_apply_filters; - if (trace->dump.map) - bpf_map__fprintf(trace->dump.map, trace->output); - err = evlist__mmap(evlist, trace->opts.mmap_pages); if (err < 0) goto out_error_mmap; @@ -4704,6 +4660,18 @@ static void trace__exit(struct trace *trace) zfree(&trace->perfconfig_events); } +#ifdef HAVE_BPF_SKEL +static int bpf__setup_bpf_output(struct evlist *evlist) +{ + int err = parse_event(evlist, "bpf-output/no-inherit=1,name=__augmented_syscalls__/"); + + if (err) + pr_debug("ERROR: failed to create the \"__augmented_syscalls__\" bpf-output event\n"); + + return err; +} +#endif + int cmd_trace(int argc, const char **argv) { const char *trace_usage[] = { @@ -4735,7 +4703,6 @@ int cmd_trace(int argc, const char **argv) .max_stack = UINT_MAX, .max_events = ULONG_MAX, }; - const char *map_dump_str = NULL; const char *output_name = NULL; const struct option trace_options[] = { OPT_CALLBACK('e', "event", &trace, "event", @@ -4769,9 +4736,6 @@ int cmd_trace(int argc, const char **argv) OPT_CALLBACK(0, "duration", &trace, "float", "show only events with duration > N.M ms", trace__set_duration), -#ifdef HAVE_LIBBPF_SUPPORT - OPT_STRING(0, "map-dump", &map_dump_str, "BPF map", "BPF map to periodically dump"), -#endif OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_BOOLEAN('T', "time", &trace.full_time, @@ -4898,87 +4862,48 @@ int cmd_trace(int argc, const char **argv) "cgroup monitoring only available in system-wide mode"); } - evsel = bpf__setup_output_event(trace.evlist, "__augmented_syscalls__"); - if (IS_ERR(evsel)) { - bpf__strerror_setup_output_event(trace.evlist, PTR_ERR(evsel), bf, sizeof(bf)); - pr_err("ERROR: Setup trace syscalls enter failed: %s\n", bf); - goto out; - } - - if (evsel) { - trace.syscalls.events.augmented = evsel; +#ifdef HAVE_BPF_SKEL + if (!trace.trace_syscalls) + goto skip_augmentation; - evsel = evlist__find_tracepoint_by_name(trace.evlist, "raw_syscalls:sys_enter"); - if (evsel == NULL) { - pr_err("ERROR: raw_syscalls:sys_enter not found in the augmented BPF object\n"); - goto out; - } + trace.skel = augmented_raw_syscalls_bpf__open(); + if (!trace.skel) { + pr_debug("Failed to open augmented syscalls BPF skeleton"); + } else { + /* + * Disable attaching the BPF programs except for sys_enter and + * sys_exit that tail call into this as necessary. + */ + struct bpf_program *prog; - if (evsel->bpf_obj == NULL) { - pr_err("ERROR: raw_syscalls:sys_enter not associated to a BPF object\n"); - goto out; + bpf_object__for_each_program(prog, trace.skel->obj) { + if (prog != trace.skel->progs.sys_enter && prog != trace.skel->progs.sys_exit) + bpf_program__set_autoattach(prog, /*autoattach=*/false); } - trace.bpf_obj = evsel->bpf_obj; + err = augmented_raw_syscalls_bpf__load(trace.skel); - /* - * If we have _just_ the augmenter event but don't have a - * explicit --syscalls, then assume we want all strace-like - * syscalls: - */ - if (!trace.trace_syscalls && trace__only_augmented_syscalls_evsels(&trace)) - trace.trace_syscalls = true; - /* - * So, if we have a syscall augmenter, but trace_syscalls, aka - * strace-like syscall tracing is not set, then we need to trow - * away the augmenter, i.e. all the events that were created - * from that BPF object file. - * - * This is more to fix the current .perfconfig trace.add_events - * style of setting up the strace-like eBPF based syscall point - * payload augmenter. - * - * All this complexity will be avoided by adding an alternative - * to trace.add_events in the form of - * trace.bpf_augmented_syscalls, that will be only parsed if we - * need it. - * - * .perfconfig trace.add_events is still useful if we want, for - * instance, have msr_write.msr in some .perfconfig profile based - * 'perf trace --config determinism.profile' mode, where for some - * particular goal/workload type we want a set of events and - * output mode (with timings, etc) instead of having to add - * all via the command line. - * - * Also --config to specify an alternate .perfconfig file needs - * to be implemented. - */ - if (!trace.trace_syscalls) { - trace__delete_augmented_syscalls(&trace); + if (err < 0) { + libbpf_strerror(err, bf, sizeof(bf)); + pr_debug("Failed to load augmented syscalls BPF skeleton: %s\n", bf); } else { - trace__set_bpf_map_filtered_pids(&trace); - trace__set_bpf_map_syscalls(&trace); - trace.syscalls.unaugmented_prog = trace__find_bpf_program_by_title(&trace, "!raw_syscalls:unaugmented"); + augmented_raw_syscalls_bpf__attach(trace.skel); + trace__add_syscall_newtp(&trace); } } - err = bpf__setup_stdout(trace.evlist); + err = bpf__setup_bpf_output(trace.evlist); if (err) { - bpf__strerror_setup_stdout(trace.evlist, err, bf, sizeof(bf)); - pr_err("ERROR: Setup BPF stdout failed: %s\n", bf); + libbpf_strerror(err, bf, sizeof(bf)); + pr_err("ERROR: Setup BPF output event failed: %s\n", bf); goto out; } - + trace.syscalls.events.bpf_output = evlist__last(trace.evlist); + assert(!strcmp(evsel__name(trace.syscalls.events.bpf_output), "__augmented_syscalls__")); +skip_augmentation: +#endif err = -1; - if (map_dump_str) { - trace.dump.map = trace__find_bpf_map_by_name(&trace, map_dump_str); - if (trace.dump.map == NULL) { - pr_err("ERROR: BPF map \"%s\" not found\n", map_dump_str); - goto out; - } - } - if (trace.trace_pgfaults) { trace.opts.sample_address = true; trace.opts.sample_time = true; @@ -5029,7 +4954,7 @@ int cmd_trace(int argc, const char **argv) * buffers that are being copied from kernel to userspace, think 'read' * syscall. */ - if (trace.syscalls.events.augmented) { + if (trace.syscalls.events.bpf_output) { evlist__for_each_entry(trace.evlist, evsel) { bool raw_syscalls_sys_exit = strcmp(evsel__name(evsel), "raw_syscalls:sys_exit") == 0; @@ -5038,9 +4963,9 @@ int cmd_trace(int argc, const char **argv) goto init_augmented_syscall_tp; } - if (trace.syscalls.events.augmented->priv == NULL && + if (trace.syscalls.events.bpf_output->priv == NULL && strstr(evsel__name(evsel), "syscalls:sys_enter")) { - struct evsel *augmented = trace.syscalls.events.augmented; + struct evsel *augmented = trace.syscalls.events.bpf_output; if (evsel__init_augmented_syscall_tp(augmented, evsel) || evsel__init_augmented_syscall_tp_args(augmented)) goto out; @@ -5145,5 +5070,8 @@ out_close: fclose(trace.output); out: trace__exit(&trace); +#ifdef HAVE_BPF_SKEL + augmented_raw_syscalls_bpf__destroy(trace.skel); +#endif return err; } diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index a0f1d8adce60..e21caadda7c1 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -21,6 +21,7 @@ FILES=( "include/uapi/linux/perf_event.h" "include/uapi/linux/prctl.h" "include/uapi/linux/sched.h" + "include/uapi/linux/seccomp.h" "include/uapi/linux/stat.h" "include/uapi/linux/usbdevice_fs.h" "include/uapi/linux/vhost.h" @@ -123,7 +124,7 @@ check () { shift - check_2 "tools/$file" "$file" $* + check_2 "tools/$file" "$file" "$@" } beauty_check () { @@ -131,7 +132,7 @@ beauty_check () { shift - check_2 "tools/perf/trace/beauty/$file" "$file" $* + check_2 "tools/perf/trace/beauty/$file" "$file" "$@" } # Check if we have the kernel headers (tools/perf/../../include), else @@ -183,7 +184,7 @@ done check_2 tools/perf/util/hashmap.h tools/lib/bpf/hashmap.h check_2 tools/perf/util/hashmap.c tools/lib/bpf/hashmap.c -cd tools/perf +cd tools/perf || exit if [ ${#FAILURES[@]} -gt 0 ] then diff --git a/tools/perf/dlfilters/dlfilter-test-api-v0.c b/tools/perf/dlfilters/dlfilter-test-api-v0.c index b1f51efd67d6..72f263d49121 100644 --- a/tools/perf/dlfilters/dlfilter-test-api-v0.c +++ b/tools/perf/dlfilters/dlfilter-test-api-v0.c @@ -254,6 +254,30 @@ static int check_addr_al(void *ctx) return 0; } +static int check_address_al(void *ctx, const struct perf_dlfilter_sample *sample) +{ + struct perf_dlfilter_al address_al; + const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip(ctx); + if (!al) + return test_fail("resolve_ip() failed"); + + address_al.size = sizeof(address_al); + if (perf_dlfilter_fns.resolve_address(ctx, sample->ip, &address_al)) + return test_fail("resolve_address() failed"); + + CHECK(address_al.sym && al->sym); + CHECK(!strcmp(address_al.sym, al->sym)); + CHECK(address_al.addr == al->addr); + CHECK(address_al.sym_start == al->sym_start); + CHECK(address_al.sym_end == al->sym_end); + CHECK(address_al.dso && al->dso); + CHECK(!strcmp(address_al.dso, al->dso)); + + return 0; +} + static int check_attr(void *ctx) { struct perf_event_attr *attr = perf_dlfilter_fns.attr(ctx); @@ -290,7 +314,7 @@ static int do_checks(void *data, const struct perf_dlfilter_sample *sample, void if (early && !d->do_early) return 0; - if (check_al(ctx) || check_addr_al(ctx)) + if (check_al(ctx) || check_addr_al(ctx) || check_address_al(ctx, sample)) return -1; if (early) diff --git a/tools/perf/dlfilters/dlfilter-test-api-v2.c b/tools/perf/dlfilters/dlfilter-test-api-v2.c new file mode 100644 index 000000000000..38e593d92920 --- /dev/null +++ b/tools/perf/dlfilters/dlfilter-test-api-v2.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test v2 API for perf --dlfilter shared object + * Copyright (c) 2023, Intel Corporation. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +/* + * Copy v2 API instead of including current API + */ +#include <linux/perf_event.h> +#include <linux/types.h> + +/* + * The following macro can be used to determine if this header defines + * perf_dlfilter_sample machine_pid and vcpu. + */ +#define PERF_DLFILTER_HAS_MACHINE_PID + +/* Definitions for perf_dlfilter_sample flags */ +enum { + PERF_DLFILTER_FLAG_BRANCH = 1ULL << 0, + PERF_DLFILTER_FLAG_CALL = 1ULL << 1, + PERF_DLFILTER_FLAG_RETURN = 1ULL << 2, + PERF_DLFILTER_FLAG_CONDITIONAL = 1ULL << 3, + PERF_DLFILTER_FLAG_SYSCALLRET = 1ULL << 4, + PERF_DLFILTER_FLAG_ASYNC = 1ULL << 5, + PERF_DLFILTER_FLAG_INTERRUPT = 1ULL << 6, + PERF_DLFILTER_FLAG_TX_ABORT = 1ULL << 7, + PERF_DLFILTER_FLAG_TRACE_BEGIN = 1ULL << 8, + PERF_DLFILTER_FLAG_TRACE_END = 1ULL << 9, + PERF_DLFILTER_FLAG_IN_TX = 1ULL << 10, + PERF_DLFILTER_FLAG_VMENTRY = 1ULL << 11, + PERF_DLFILTER_FLAG_VMEXIT = 1ULL << 12, +}; + +/* + * perf sample event information (as per perf script and <linux/perf_event.h>) + */ +struct perf_dlfilter_sample { + __u32 size; /* Size of this structure (for compatibility checking) */ + __u16 ins_lat; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ + __u16 p_stage_cyc; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ + __u64 ip; + __s32 pid; + __s32 tid; + __u64 time; + __u64 addr; + __u64 id; + __u64 stream_id; + __u64 period; + __u64 weight; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ + __u64 transaction; /* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */ + __u64 insn_cnt; /* For instructions-per-cycle (IPC) */ + __u64 cyc_cnt; /* For instructions-per-cycle (IPC) */ + __s32 cpu; + __u32 flags; /* Refer PERF_DLFILTER_FLAG_* above */ + __u64 data_src; /* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */ + __u64 phys_addr; /* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */ + __u64 data_page_size; /* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */ + __u64 code_page_size; /* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */ + __u64 cgroup; /* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */ + __u8 cpumode; /* Refer CPUMODE_MASK etc in <linux/perf_event.h> */ + __u8 addr_correlates_sym; /* True => resolve_addr() can be called */ + __u16 misc; /* Refer perf_event_header in <linux/perf_event.h> */ + __u32 raw_size; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ + const void *raw_data; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ + __u64 brstack_nr; /* Number of brstack entries */ + const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */ + __u64 raw_callchain_nr; /* Number of raw_callchain entries */ + const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */ + const char *event; + __s32 machine_pid; + __s32 vcpu; +}; + +/* + * Address location (as per perf script) + */ +struct perf_dlfilter_al { + __u32 size; /* Size of this structure (for compatibility checking) */ + __u32 symoff; + const char *sym; + __u64 addr; /* Mapped address (from dso) */ + __u64 sym_start; + __u64 sym_end; + const char *dso; + __u8 sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */ + __u8 is_64_bit; /* Only valid if dso is not NULL */ + __u8 is_kernel_ip; /* True if in kernel space */ + __u32 buildid_size; + __u8 *buildid; + /* Below members are only populated by resolve_ip() */ + __u8 filtered; /* True if this sample event will be filtered out */ + const char *comm; + void *priv; /* Private data (v2 API) */ +}; + +struct perf_dlfilter_fns { + /* Return information about ip */ + const struct perf_dlfilter_al *(*resolve_ip)(void *ctx); + /* Return information about addr (if addr_correlates_sym) */ + const struct perf_dlfilter_al *(*resolve_addr)(void *ctx); + /* Return arguments from --dlarg option */ + char **(*args)(void *ctx, int *dlargc); + /* + * Return information about address (al->size must be set before + * calling). Returns 0 on success, -1 otherwise. Call al_cleanup() + * when 'al' data is no longer needed. + */ + __s32 (*resolve_address)(void *ctx, __u64 address, struct perf_dlfilter_al *al); + /* Return instruction bytes and length */ + const __u8 *(*insn)(void *ctx, __u32 *length); + /* Return source file name and line number */ + const char *(*srcline)(void *ctx, __u32 *line_number); + /* Return perf_event_attr, refer <linux/perf_event.h> */ + struct perf_event_attr *(*attr)(void *ctx); + /* Read object code, return numbers of bytes read */ + __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len); + /* + * If present (i.e. must check al_cleanup != NULL), call after + * resolve_address() to free any associated resources. (v2 API) + */ + void (*al_cleanup)(void *ctx, struct perf_dlfilter_al *al); + /* Reserved */ + void *(*reserved[119])(void *); +}; + +struct perf_dlfilter_fns perf_dlfilter_fns; + +static int verbose; + +#define pr_debug(fmt, ...) do { \ + if (verbose > 0) \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + } while (0) + +static int test_fail(const char *msg) +{ + pr_debug("%s\n", msg); + return -1; +} + +#define CHECK(x) do { \ + if (!(x)) \ + return test_fail("Check '" #x "' failed\n"); \ + } while (0) + +struct filter_data { + __u64 ip; + __u64 addr; + int do_early; + int early_filter_cnt; + int filter_cnt; +}; + +static struct filter_data *filt_dat; + +int start(void **data, void *ctx) +{ + int dlargc; + char **dlargv; + struct filter_data *d; + static bool called; + + verbose = 1; + + CHECK(!filt_dat && !called); + called = true; + + d = calloc(1, sizeof(*d)); + if (!d) + test_fail("Failed to allocate memory"); + filt_dat = d; + *data = d; + + dlargv = perf_dlfilter_fns.args(ctx, &dlargc); + + CHECK(dlargc == 6); + CHECK(!strcmp(dlargv[0], "first")); + verbose = strtol(dlargv[1], NULL, 0); + d->ip = strtoull(dlargv[2], NULL, 0); + d->addr = strtoull(dlargv[3], NULL, 0); + d->do_early = strtol(dlargv[4], NULL, 0); + CHECK(!strcmp(dlargv[5], "last")); + + pr_debug("%s API\n", __func__); + + return 0; +} + +#define CHECK_SAMPLE(x) do { \ + if (sample->x != expected.x) \ + return test_fail("'" #x "' not expected value\n"); \ + } while (0) + +static int check_sample(struct filter_data *d, const struct perf_dlfilter_sample *sample) +{ + struct perf_dlfilter_sample expected = { + .ip = d->ip, + .pid = 12345, + .tid = 12346, + .time = 1234567890, + .addr = d->addr, + .id = 99, + .stream_id = 101, + .period = 543212345, + .cpu = 31, + .cpumode = PERF_RECORD_MISC_USER, + .addr_correlates_sym = 1, + .misc = PERF_RECORD_MISC_USER, + }; + + CHECK(sample->size >= sizeof(struct perf_dlfilter_sample)); + + CHECK_SAMPLE(ip); + CHECK_SAMPLE(pid); + CHECK_SAMPLE(tid); + CHECK_SAMPLE(time); + CHECK_SAMPLE(addr); + CHECK_SAMPLE(id); + CHECK_SAMPLE(stream_id); + CHECK_SAMPLE(period); + CHECK_SAMPLE(cpu); + CHECK_SAMPLE(cpumode); + CHECK_SAMPLE(addr_correlates_sym); + CHECK_SAMPLE(misc); + + CHECK(!sample->raw_data); + CHECK_SAMPLE(brstack_nr); + CHECK(!sample->brstack); + CHECK_SAMPLE(raw_callchain_nr); + CHECK(!sample->raw_callchain); + +#define EVENT_NAME "branches:" + CHECK(!strncmp(sample->event, EVENT_NAME, strlen(EVENT_NAME))); + + return 0; +} + +static int check_al(void *ctx) +{ + const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip(ctx); + if (!al) + return test_fail("resolve_ip() failed"); + + CHECK(al->sym && !strcmp("foo", al->sym)); + CHECK(!al->symoff); + + return 0; +} + +static int check_addr_al(void *ctx) +{ + const struct perf_dlfilter_al *addr_al; + + addr_al = perf_dlfilter_fns.resolve_addr(ctx); + if (!addr_al) + return test_fail("resolve_addr() failed"); + + CHECK(addr_al->sym && !strcmp("bar", addr_al->sym)); + CHECK(!addr_al->symoff); + + return 0; +} + +static int check_address_al(void *ctx, const struct perf_dlfilter_sample *sample) +{ + struct perf_dlfilter_al address_al; + const struct perf_dlfilter_al *al; + + al = perf_dlfilter_fns.resolve_ip(ctx); + if (!al) + return test_fail("resolve_ip() failed"); + + address_al.size = sizeof(address_al); + if (perf_dlfilter_fns.resolve_address(ctx, sample->ip, &address_al)) + return test_fail("resolve_address() failed"); + + CHECK(address_al.sym && al->sym); + CHECK(!strcmp(address_al.sym, al->sym)); + CHECK(address_al.addr == al->addr); + CHECK(address_al.sym_start == al->sym_start); + CHECK(address_al.sym_end == al->sym_end); + CHECK(address_al.dso && al->dso); + CHECK(!strcmp(address_al.dso, al->dso)); + + /* al_cleanup() is v2 API so may not be present */ + if (perf_dlfilter_fns.al_cleanup) + perf_dlfilter_fns.al_cleanup(ctx, &address_al); + + return 0; +} + +static int check_attr(void *ctx) +{ + struct perf_event_attr *attr = perf_dlfilter_fns.attr(ctx); + + CHECK(attr); + CHECK(attr->type == PERF_TYPE_HARDWARE); + CHECK(attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS); + + return 0; +} + +static int do_checks(void *data, const struct perf_dlfilter_sample *sample, void *ctx, bool early) +{ + struct filter_data *d = data; + + CHECK(data && filt_dat == data); + + if (early) { + CHECK(!d->early_filter_cnt); + d->early_filter_cnt += 1; + } else { + CHECK(!d->filter_cnt); + CHECK(d->early_filter_cnt); + CHECK(d->do_early != 2); + d->filter_cnt += 1; + } + + if (check_sample(data, sample)) + return -1; + + if (check_attr(ctx)) + return -1; + + if (early && !d->do_early) + return 0; + + if (check_al(ctx) || check_addr_al(ctx) || check_address_al(ctx, sample)) + return -1; + + if (early) + return d->do_early == 2; + + return 1; +} + +int filter_event_early(void *data, const struct perf_dlfilter_sample *sample, void *ctx) +{ + pr_debug("%s API\n", __func__); + + return do_checks(data, sample, ctx, true); +} + +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx) +{ + pr_debug("%s API\n", __func__); + + return do_checks(data, sample, ctx, false); +} + +int stop(void *data, void *ctx) +{ + static bool called; + + pr_debug("%s API\n", __func__); + + CHECK(data && filt_dat == data && !called); + called = true; + + free(data); + filt_dat = NULL; + return 0; +} + +const char *filter_description(const char **long_description) +{ + *long_description = "Filter used by the 'dlfilter C API' perf test"; + return "dlfilter to test v2 C API"; +} diff --git a/tools/perf/examples/bpf/5sec.c b/tools/perf/examples/bpf/5sec.c deleted file mode 100644 index 3bd7fc17631f..000000000000 --- a/tools/perf/examples/bpf/5sec.c +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - Description: - - . Disable strace like syscall tracing (--no-syscalls), or try tracing - just some (-e *sleep). - - . Attach a filter function to a kernel function, returning when it should - be considered, i.e. appear on the output. - - . Run it system wide, so that any sleep of >= 5 seconds and < than 6 - seconds gets caught. - - . Ask for callgraphs using DWARF info, so that userspace can be unwound - - . While this is running, run something like "sleep 5s". - - . If we decide to add tv_nsec as well, then it becomes: - - int probe(hrtimer_nanosleep, rqtp->tv_sec rqtp->tv_nsec)(void *ctx, int err, long sec, long nsec) - - I.e. add where it comes from (rqtp->tv_nsec) and where it will be - accessible in the function body (nsec) - - # perf trace --no-syscalls -e tools/perf/examples/bpf/5sec.c/call-graph=dwarf/ - 0.000 perf_bpf_probe:func:(ffffffff9811b5f0) tv_sec=5 - hrtimer_nanosleep ([kernel.kallsyms]) - __x64_sys_nanosleep ([kernel.kallsyms]) - do_syscall_64 ([kernel.kallsyms]) - entry_SYSCALL_64 ([kernel.kallsyms]) - __GI___nanosleep (/usr/lib64/libc-2.26.so) - rpl_nanosleep (/usr/bin/sleep) - xnanosleep (/usr/bin/sleep) - main (/usr/bin/sleep) - __libc_start_main (/usr/lib64/libc-2.26.so) - _start (/usr/bin/sleep) - ^C# - - Copyright (C) 2018 Red Hat, Inc., Arnaldo Carvalho de Melo <acme@redhat.com> -*/ - -#include <linux/bpf.h> -#include <bpf/bpf_helpers.h> - -#define NSEC_PER_SEC 1000000000L - -SEC("hrtimer_nanosleep=hrtimer_nanosleep rqtp") -int hrtimer_nanosleep(void *ctx, int err, long long sec) -{ - return sec / NSEC_PER_SEC == 5ULL; -} - -char _license[] SEC("license") = "GPL"; diff --git a/tools/perf/examples/bpf/hello.c b/tools/perf/examples/bpf/hello.c deleted file mode 100644 index e9080b0df158..000000000000 --- a/tools/perf/examples/bpf/hello.c +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <linux/bpf.h> -#include <bpf/bpf_helpers.h> - -struct __bpf_stdout__ { - __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); - __type(key, int); - __type(value, __u32); - __uint(max_entries, __NR_CPUS__); -} __bpf_stdout__ SEC(".maps"); - -#define puts(from) \ - ({ const int __len = sizeof(from); \ - char __from[sizeof(from)] = from; \ - bpf_perf_event_output(args, &__bpf_stdout__, BPF_F_CURRENT_CPU, \ - &__from, __len & (sizeof(from) - 1)); }) - -struct syscall_enter_args; - -SEC("raw_syscalls:sys_enter") -int sys_enter(struct syscall_enter_args *args) -{ - puts("Hello, world\n"); - return 0; -} - -char _license[] SEC("license") = "GPL"; diff --git a/tools/perf/examples/bpf/sys_enter_openat.c b/tools/perf/examples/bpf/sys_enter_openat.c deleted file mode 100644 index c4481c390d23..000000000000 --- a/tools/perf/examples/bpf/sys_enter_openat.c +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hook into 'openat' syscall entry tracepoint - * - * Test it with: - * - * perf trace -e tools/perf/examples/bpf/sys_enter_openat.c cat /etc/passwd > /dev/null - * - * It'll catch some openat syscalls related to the dynamic linked and - * the last one should be the one for '/etc/passwd'. - * - * The syscall_enter_openat_args can be used to get the syscall fields - * and use them for filtering calls, i.e. use in expressions for - * the return value. - */ - -#include <bpf/bpf.h> - -struct syscall_enter_openat_args { - unsigned long long unused; - long syscall_nr; - long dfd; - char *filename_ptr; - long flags; - long mode; -}; - -int syscall_enter(openat)(struct syscall_enter_openat_args *args) -{ - return 1; -} - -license(GPL); diff --git a/tools/perf/include/perf/perf_dlfilter.h b/tools/perf/include/perf/perf_dlfilter.h index a26e2f129f83..16fc4568ac53 100644 --- a/tools/perf/include/perf/perf_dlfilter.h +++ b/tools/perf/include/perf/perf_dlfilter.h @@ -91,6 +91,7 @@ struct perf_dlfilter_al { /* Below members are only populated by resolve_ip() */ __u8 filtered; /* True if this sample event will be filtered out */ const char *comm; + void *priv; /* Private data. Do not change */ }; struct perf_dlfilter_fns { @@ -102,7 +103,8 @@ struct perf_dlfilter_fns { char **(*args)(void *ctx, int *dlargc); /* * Return information about address (al->size must be set before - * calling). Returns 0 on success, -1 otherwise. + * calling). Returns 0 on success, -1 otherwise. Call al_cleanup() + * when 'al' data is no longer needed. */ __s32 (*resolve_address)(void *ctx, __u64 address, struct perf_dlfilter_al *al); /* Return instruction bytes and length */ @@ -113,8 +115,13 @@ struct perf_dlfilter_fns { struct perf_event_attr *(*attr)(void *ctx); /* Read object code, return numbers of bytes read */ __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len); + /* + * If present (i.e. must check al_cleanup != NULL), call after + * resolve_address() to free any associated resources. + */ + void (*al_cleanup)(void *ctx, struct perf_dlfilter_al *al); /* Reserved */ - void *(*reserved[120])(void *); + void *(*reserved[119])(void *); }; /* diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 38cae4721583..d3fc8090413c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -18,7 +18,6 @@ #include <subcmd/run-command.h> #include "util/parse-events.h" #include <subcmd/parse-options.h> -#include "util/bpf-loader.h" #include "util/debug.h" #include "util/event.h" #include "util/util.h" // usage() @@ -324,7 +323,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) perf_config__exit(); exit_browser(status); perf_env__exit(&perf_env); - bpf__clear(); if (status) return status & 0xff; diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index 150765f2baee..1d18bb89402e 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -35,3 +35,9 @@ $(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_L $(call rule_mkdir) $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) pmu-events/arch $@ endif + +# pmu-events.c file is generated in the OUTPUT directory so it needs a +# separate rule to depend on it properly +$(OUTPUT)pmu-events/pmu-events.o: $(PMU_EVENTS_C) + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/cache.json b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/cache.json index fc0633054211..7a2b7b200f14 100644 --- a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/cache.json +++ b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/cache.json @@ -93,9 +93,6 @@ "ArchStdEvent": "L1D_CACHE_LMISS_RD" }, { - "ArchStdEvent": "L1D_CACHE_LMISS" - }, - { "ArchStdEvent": "L1I_CACHE_LMISS" }, { diff --git a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/core-imp-def.json b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/core-imp-def.json index 95c30243f2b2..88b23b85e33c 100644 --- a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/core-imp-def.json +++ b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/core-imp-def.json @@ -534,66 +534,6 @@ "BriefDescription": "L2D OTB allocate" }, { - "PublicDescription": "DTLB Translation cache hit on S1L2 walk cache entry", - "EventCode": "0xD801", - "EventName": "MMU_D_TRANS_CACHE_HIT_S1L2_WALK", - "BriefDescription": "DTLB Translation cache hit on S1L2 walk cache entry" - }, - { - "PublicDescription": "DTLB Translation cache hit on S1L1 walk cache entry", - "EventCode": "0xD802", - "EventName": "MMU_D_TRANS_CACHE_HIT_S1L1_WALK", - "BriefDescription": "DTLB Translation cache hit on S1L1 walk cache entry" - }, - { - "PublicDescription": "DTLB Translation cache hit on S1L0 walk cache entry", - "EventCode": "0xD803", - "EventName": "MMU_D_TRANS_CACHE_HIT_S1L0_WALK", - "BriefDescription": "DTLB Translation cache hit on S1L0 walk cache entry" - }, - { - "PublicDescription": "DTLB Translation cache hit on S2L2 walk cache entry", - "EventCode": "0xD804", - "EventName": "MMU_D_TRANS_CACHE_HIT_S2L2_WALK", - "BriefDescription": "DTLB Translation cache hit on S2L2 walk cache entry" - }, - { - "PublicDescription": "DTLB Translation cache hit on S2L1 walk cache entry", - "EventCode": "0xD805", - "EventName": "MMU_D_TRANS_CACHE_HIT_S2L1_WALK", - "BriefDescription": "DTLB Translation cache hit on S2L1 walk cache entry" - }, - { - "PublicDescription": "DTLB Translation cache hit on S2L0 walk cache entry", - "EventCode": "0xD806", - "EventName": "MMU_D_TRANS_CACHE_HIT_S2L0_WALK", - "BriefDescription": "DTLB Translation cache hit on S2L0 walk cache entry" - }, - { - "PublicDescription": "D-side S1 Page walk cache lookup", - "EventCode": "0xD807", - "EventName": "MMU_D_S1_WALK_CACHE_LOOKUP", - "BriefDescription": "D-side S1 Page walk cache lookup" - }, - { - "PublicDescription": "D-side S1 Page walk cache refill", - "EventCode": "0xD808", - "EventName": "MMU_D_S1_WALK_CACHE_REFILL", - "BriefDescription": "D-side S1 Page walk cache refill" - }, - { - "PublicDescription": "D-side S2 Page walk cache lookup", - "EventCode": "0xD809", - "EventName": "MMU_D_S2_WALK_CACHE_LOOKUP", - "BriefDescription": "D-side S2 Page walk cache lookup" - }, - { - "PublicDescription": "D-side S2 Page walk cache refill", - "EventCode": "0xD80A", - "EventName": "MMU_D_S2_WALK_CACHE_REFILL", - "BriefDescription": "D-side S2 Page walk cache refill" - }, - { "PublicDescription": "D-side Stage1 tablewalk fault", "EventCode": "0xD80B", "EventName": "MMU_D_S1_WALK_FAULT", @@ -618,66 +558,6 @@ "BriefDescription": "L2I OTB allocate" }, { - "PublicDescription": "ITLB Translation cache hit on S1L2 walk cache entry", - "EventCode": "0xD901", - "EventName": "MMU_I_TRANS_CACHE_HIT_S1L2_WALK", - "BriefDescription": "ITLB Translation cache hit on S1L2 walk cache entry" - }, - { - "PublicDescription": "ITLB Translation cache hit on S1L1 walk cache entry", - "EventCode": "0xD902", - "EventName": "MMU_I_TRANS_CACHE_HIT_S1L1_WALK", - "BriefDescription": "ITLB Translation cache hit on S1L1 walk cache entry" - }, - { - "PublicDescription": "ITLB Translation cache hit on S1L0 walk cache entry", - "EventCode": "0xD903", - "EventName": "MMU_I_TRANS_CACHE_HIT_S1L0_WALK", - "BriefDescription": "ITLB Translation cache hit on S1L0 walk cache entry" - }, - { - "PublicDescription": "ITLB Translation cache hit on S2L2 walk cache entry", - "EventCode": "0xD904", - "EventName": "MMU_I_TRANS_CACHE_HIT_S2L2_WALK", - "BriefDescription": "ITLB Translation cache hit on S2L2 walk cache entry" - }, - { - "PublicDescription": "ITLB Translation cache hit on S2L1 walk cache entry", - "EventCode": "0xD905", - "EventName": "MMU_I_TRANS_CACHE_HIT_S2L1_WALK", - "BriefDescription": "ITLB Translation cache hit on S2L1 walk cache entry" - }, - { - "PublicDescription": "ITLB Translation cache hit on S2L0 walk cache entry", - "EventCode": "0xD906", - "EventName": "MMU_I_TRANS_CACHE_HIT_S2L0_WALK", - "BriefDescription": "ITLB Translation cache hit on S2L0 walk cache entry" - }, - { - "PublicDescription": "I-side S1 Page walk cache lookup", - "EventCode": "0xD907", - "EventName": "MMU_I_S1_WALK_CACHE_LOOKUP", - "BriefDescription": "I-side S1 Page walk cache lookup" - }, - { - "PublicDescription": "I-side S1 Page walk cache refill", - "EventCode": "0xD908", - "EventName": "MMU_I_S1_WALK_CACHE_REFILL", - "BriefDescription": "I-side S1 Page walk cache refill" - }, - { - "PublicDescription": "I-side S2 Page walk cache lookup", - "EventCode": "0xD909", - "EventName": "MMU_I_S2_WALK_CACHE_LOOKUP", - "BriefDescription": "I-side S2 Page walk cache lookup" - }, - { - "PublicDescription": "I-side S2 Page walk cache refill", - "EventCode": "0xD90A", - "EventName": "MMU_I_S2_WALK_CACHE_REFILL", - "BriefDescription": "I-side S2 Page walk cache refill" - }, - { "PublicDescription": "I-side Stage1 tablewalk fault", "EventCode": "0xD90B", "EventName": "MMU_I_S1_WALK_FAULT", diff --git a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/metrics.json b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/metrics.json new file mode 100644 index 000000000000..1e7e8901a445 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/metrics.json @@ -0,0 +1,362 @@ +[ + { + "MetricExpr": "BR_MIS_PRED / BR_PRED", + "BriefDescription": "Branch predictor misprediction rate. May not count branches that are never resolved because they are in the misprediction shadow of an earlier branch", + "MetricGroup": "Branch Prediction", + "MetricName": "Misprediction" + }, + { + "MetricExpr": "BR_MIS_PRED_RETIRED / BR_RETIRED", + "BriefDescription": "Branch predictor misprediction rate", + "MetricGroup": "Branch Prediction", + "MetricName": "Misprediction (retired)" + }, + { + "MetricExpr": "BUS_ACCESS / ( BUS_CYCLES * 1)", + "BriefDescription": "Core-to-uncore bus utilization", + "MetricGroup": "Bus", + "MetricName": "Bus utilization" + }, + { + "MetricExpr": "L1D_CACHE_REFILL / L1D_CACHE", + "BriefDescription": "L1D cache miss rate", + "MetricGroup": "Cache", + "MetricName": "L1D cache miss" + }, + { + "MetricExpr": "L1D_CACHE_LMISS_RD / L1D_CACHE_RD", + "BriefDescription": "L1D cache read miss rate", + "MetricGroup": "Cache", + "MetricName": "L1D cache read miss" + }, + { + "MetricExpr": "L1I_CACHE_REFILL / L1I_CACHE", + "BriefDescription": "L1I cache miss rate", + "MetricGroup": "Cache", + "MetricName": "L1I cache miss" + }, + { + "MetricExpr": "L2D_CACHE_REFILL / L2D_CACHE", + "BriefDescription": "L2 cache miss rate", + "MetricGroup": "Cache", + "MetricName": "L2 cache miss" + }, + { + "MetricExpr": "L1I_CACHE_LMISS / L1I_CACHE", + "BriefDescription": "L1I cache read miss rate", + "MetricGroup": "Cache", + "MetricName": "L1I cache read miss" + }, + { + "MetricExpr": "L2D_CACHE_LMISS_RD / L2D_CACHE_RD", + "BriefDescription": "L2 cache read miss rate", + "MetricGroup": "Cache", + "MetricName": "L2 cache read miss" + }, + { + "MetricExpr": "(L1D_CACHE_LMISS_RD * 1000) / INST_RETIRED", + "BriefDescription": "Misses per thousand instructions (data)", + "MetricGroup": "Cache", + "MetricName": "MPKI data" + }, + { + "MetricExpr": "(L1I_CACHE_LMISS * 1000) / INST_RETIRED", + "BriefDescription": "Misses per thousand instructions (instruction)", + "MetricGroup": "Cache", + "MetricName": "MPKI instruction" + }, + { + "MetricExpr": "ASE_SPEC / OP_SPEC", + "BriefDescription": "Proportion of advanced SIMD data processing operations (excluding DP_SPEC/LD_SPEC) operations", + "MetricGroup": "Instruction", + "MetricName": "ASE mix" + }, + { + "MetricExpr": "CRYPTO_SPEC / OP_SPEC", + "BriefDescription": "Proportion of crypto data processing operations", + "MetricGroup": "Instruction", + "MetricName": "Crypto mix" + }, + { + "MetricExpr": "VFP_SPEC / (duration_time *1000000000)", + "BriefDescription": "Giga-floating point operations per second", + "MetricGroup": "Instruction", + "MetricName": "GFLOPS_ISSUED" + }, + { + "MetricExpr": "DP_SPEC / OP_SPEC", + "BriefDescription": "Proportion of integer data processing operations", + "MetricGroup": "Instruction", + "MetricName": "Integer mix" + }, + { + "MetricExpr": "INST_RETIRED / CPU_CYCLES", + "BriefDescription": "Instructions per cycle", + "MetricGroup": "Instruction", + "MetricName": "IPC" + }, + { + "MetricExpr": "LD_SPEC / OP_SPEC", + "BriefDescription": "Proportion of load operations", + "MetricGroup": "Instruction", + "MetricName": "Load mix" + }, + { + "MetricExpr": "LDST_SPEC/ OP_SPEC", + "BriefDescription": "Proportion of load & store operations", + "MetricGroup": "Instruction", + "MetricName": "Load-store mix" + }, + { + "MetricExpr": "INST_RETIRED / (duration_time * 1000000)", + "BriefDescription": "Millions of instructions per second", + "MetricGroup": "Instruction", + "MetricName": "MIPS_RETIRED" + }, + { + "MetricExpr": "INST_SPEC / (duration_time * 1000000)", + "BriefDescription": "Millions of instructions per second", + "MetricGroup": "Instruction", + "MetricName": "MIPS_UTILIZATION" + }, + { + "MetricExpr": "PC_WRITE_SPEC / OP_SPEC", + "BriefDescription": "Proportion of software change of PC operations", + "MetricGroup": "Instruction", + "MetricName": "PC write mix" + }, + { + "MetricExpr": "ST_SPEC / OP_SPEC", + "BriefDescription": "Proportion of store operations", + "MetricGroup": "Instruction", + "MetricName": "Store mix" + }, + { + "MetricExpr": "VFP_SPEC / OP_SPEC", + "BriefDescription": "Proportion of FP operations", + "MetricGroup": "Instruction", + "MetricName": "VFP mix" + }, + { + "MetricExpr": "1 - (OP_RETIRED/ (CPU_CYCLES * 4))", + "BriefDescription": "Proportion of slots lost", + "MetricGroup": "Speculation / TDA", + "MetricName": "CPU lost" + }, + { + "MetricExpr": "OP_RETIRED/ (CPU_CYCLES * 4)", + "BriefDescription": "Proportion of slots retiring", + "MetricGroup": "Speculation / TDA", + "MetricName": "CPU utilization" + }, + { + "MetricExpr": "OP_RETIRED - OP_SPEC", + "BriefDescription": "Operations lost due to misspeculation", + "MetricGroup": "Speculation / TDA", + "MetricName": "Operations lost" + }, + { + "MetricExpr": "1 - (OP_RETIRED / OP_SPEC)", + "BriefDescription": "Proportion of operations lost", + "MetricGroup": "Speculation / TDA", + "MetricName": "Operations lost (ratio)" + }, + { + "MetricExpr": "OP_RETIRED / OP_SPEC", + "BriefDescription": "Proportion of operations retired", + "MetricGroup": "Speculation / TDA", + "MetricName": "Operations retired" + }, + { + "MetricExpr": "STALL_BACKEND_CACHE / CPU_CYCLES", + "BriefDescription": "Proportion of cycles stalled and no operations issued to backend and cache miss", + "MetricGroup": "Stall", + "MetricName": "Stall backend cache cycles" + }, + { + "MetricExpr": "STALL_BACKEND_RESOURCE / CPU_CYCLES", + "BriefDescription": "Proportion of cycles stalled and no operations issued to backend and resource full", + "MetricGroup": "Stall", + "MetricName": "Stall backend resource cycles" + }, + { + "MetricExpr": "STALL_BACKEND_TLB / CPU_CYCLES", + "BriefDescription": "Proportion of cycles stalled and no operations issued to backend and TLB miss", + "MetricGroup": "Stall", + "MetricName": "Stall backend tlb cycles" + }, + { + "MetricExpr": "STALL_FRONTEND_CACHE / CPU_CYCLES", + "BriefDescription": "Proportion of cycles stalled and no ops delivered from frontend and cache miss", + "MetricGroup": "Stall", + "MetricName": "Stall frontend cache cycles" + }, + { + "MetricExpr": "STALL_FRONTEND_TLB / CPU_CYCLES", + "BriefDescription": "Proportion of cycles stalled and no ops delivered from frontend and TLB miss", + "MetricGroup": "Stall", + "MetricName": "Stall frontend tlb cycles" + }, + { + "MetricExpr": "DTLB_WALK / L1D_TLB", + "BriefDescription": "D-side walk per d-side translation request", + "MetricGroup": "TLB", + "MetricName": "DTLB walks" + }, + { + "MetricExpr": "ITLB_WALK / L1I_TLB", + "BriefDescription": "I-side walk per i-side translation request", + "MetricGroup": "TLB", + "MetricName": "ITLB walks" + }, + { + "MetricExpr": "STALL_SLOT_BACKEND / (CPU_CYCLES * 4)", + "BriefDescription": "Fraction of slots backend bound", + "MetricGroup": "TopDownL1", + "MetricName": "backend" + }, + { + "MetricExpr": "1 - (retiring + lost + backend)", + "BriefDescription": "Fraction of slots frontend bound", + "MetricGroup": "TopDownL1", + "MetricName": "frontend" + }, + { + "MetricExpr": "((OP_SPEC - OP_RETIRED) / (CPU_CYCLES * 4))", + "BriefDescription": "Fraction of slots lost due to misspeculation", + "MetricGroup": "TopDownL1", + "MetricName": "lost" + }, + { + "MetricExpr": "(OP_RETIRED / (CPU_CYCLES * 4))", + "BriefDescription": "Fraction of slots retiring, useful work", + "MetricGroup": "TopDownL1", + "MetricName": "retiring" + }, + { + "MetricExpr": "backend - backend_memory", + "BriefDescription": "Fraction of slots the CPU was stalled due to backend non-memory subsystem issues", + "MetricGroup": "TopDownL2", + "MetricName": "backend_core" + }, + { + "MetricExpr": "(STALL_BACKEND_TLB + STALL_BACKEND_CACHE + STALL_BACKEND_MEM) / CPU_CYCLES ", + "BriefDescription": "Fraction of slots the CPU was stalled due to backend memory subsystem issues (cache/tlb miss)", + "MetricGroup": "TopDownL2", + "MetricName": "backend_memory" + }, + { + "MetricExpr": " (BR_MIS_PRED_RETIRED / GPC_FLUSH) * lost", + "BriefDescription": "Fraction of slots lost due to branch misprediciton", + "MetricGroup": "TopDownL2", + "MetricName": "branch_mispredict" + }, + { + "MetricExpr": "frontend - frontend_latency", + "BriefDescription": "Fraction of slots the CPU did not dispatch at full bandwidth - able to dispatch partial slots only (1, 2, or 3 uops)", + "MetricGroup": "TopDownL2", + "MetricName": "frontend_bandwidth" + }, + { + "MetricExpr": "(STALL_FRONTEND - ((STALL_SLOT_FRONTEND - (frontend * CPU_CYCLES * 4)) / 4)) / CPU_CYCLES", + "BriefDescription": "Fraction of slots the CPU was stalled due to frontend latency issues (cache/tlb miss); nothing to dispatch", + "MetricGroup": "TopDownL2", + "MetricName": "frontend_latency" + }, + { + "MetricExpr": "lost - branch_mispredict", + "BriefDescription": "Fraction of slots lost due to other/non-branch misprediction misspeculation", + "MetricGroup": "TopDownL2", + "MetricName": "other_clears" + }, + { + "MetricExpr": "(IXU_NUM_UOPS_ISSUED + FSU_ISSUED) / (CPU_CYCLES * 6)", + "BriefDescription": "Fraction of execute slots utilized", + "MetricGroup": "TopDownL2", + "MetricName": "pipe_utilization" + }, + { + "MetricExpr": "STALL_BACKEND_MEM / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to data L2 cache miss", + "MetricGroup": "TopDownL3", + "MetricName": "d_cache_l2_miss" + }, + { + "MetricExpr": "STALL_BACKEND_CACHE / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to data cache miss", + "MetricGroup": "TopDownL3", + "MetricName": "d_cache_miss" + }, + { + "MetricExpr": "STALL_BACKEND_TLB / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to data TLB miss", + "MetricGroup": "TopDownL3", + "MetricName": "d_tlb_miss" + }, + { + "MetricExpr": "FSU_ISSUED / (CPU_CYCLES * 2)", + "BriefDescription": "Fraction of FSU execute slots utilized", + "MetricGroup": "TopDownL3", + "MetricName": "fsu_pipe_utilization" + }, + { + "MetricExpr": "STALL_FRONTEND_CACHE / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to instruction cache miss", + "MetricGroup": "TopDownL3", + "MetricName": "i_cache_miss" + }, + { + "MetricExpr": " STALL_FRONTEND_TLB / CPU_CYCLES ", + "BriefDescription": "Fraction of cycles the CPU was stalled due to instruction TLB miss", + "MetricGroup": "TopDownL3", + "MetricName": "i_tlb_miss" + }, + { + "MetricExpr": "IXU_NUM_UOPS_ISSUED / (CPU_CYCLES / 4)", + "BriefDescription": "Fraction of IXU execute slots utilized", + "MetricGroup": "TopDownL3", + "MetricName": "ixu_pipe_utilization" + }, + { + "MetricExpr": "IDR_STALL_FLUSH / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to flush recovery", + "MetricGroup": "TopDownL3", + "MetricName": "recovery" + }, + { + "MetricExpr": "STALL_BACKEND_RESOURCE / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled due to core resource shortage", + "MetricGroup": "TopDownL3", + "MetricName": "resource" + }, + { + "MetricExpr": "IDR_STALL_FSU_SCHED / CPU_CYCLES ", + "BriefDescription": "Fraction of cycles the CPU was stalled and FSU was full", + "MetricGroup": "TopDownL4", + "MetricName": "stall_fsu_sched" + }, + { + "MetricExpr": "IDR_STALL_IXU_SCHED / CPU_CYCLES ", + "BriefDescription": "Fraction of cycles the CPU was stalled and IXU was full", + "MetricGroup": "TopDownL4", + "MetricName": "stall_ixu_sched" + }, + { + "MetricExpr": "IDR_STALL_LOB_ID / CPU_CYCLES ", + "BriefDescription": "Fraction of cycles the CPU was stalled and LOB was full", + "MetricGroup": "TopDownL4", + "MetricName": "stall_lob_id" + }, + { + "MetricExpr": "IDR_STALL_ROB_ID / CPU_CYCLES", + "BriefDescription": "Fraction of cycles the CPU was stalled and ROB was full", + "MetricGroup": "TopDownL4", + "MetricName": "stall_rob_id" + }, + { + "MetricExpr": "IDR_STALL_SOB_ID / CPU_CYCLES ", + "BriefDescription": "Fraction of cycles the CPU was stalled and SOB was full", + "MetricGroup": "TopDownL4", + "MetricName": "stall_sob_id" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/pipeline.json b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/pipeline.json index f9fae15f7555..711028377f3e 100644 --- a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/pipeline.json +++ b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/pipeline.json @@ -1,18 +1,24 @@ [ { - "ArchStdEvent": "STALL_FRONTEND" + "ArchStdEvent": "STALL_FRONTEND", + "Errata": "Errata AC03_CPU_29", + "BriefDescription": "Impacted by errata, use metrics instead -" }, { "ArchStdEvent": "STALL_BACKEND" }, { - "ArchStdEvent": "STALL" + "ArchStdEvent": "STALL", + "Errata": "Errata AC03_CPU_29", + "BriefDescription": "Impacted by errata, use metrics instead -" }, { "ArchStdEvent": "STALL_SLOT_BACKEND" }, { - "ArchStdEvent": "STALL_SLOT_FRONTEND" + "ArchStdEvent": "STALL_SLOT_FRONTEND", + "Errata": "Errata AC03_CPU_29", + "BriefDescription": "Impacted by errata, use metrics instead -" }, { "ArchStdEvent": "STALL_SLOT" diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/branch.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/branch.json deleted file mode 100644 index 79f2016c53b0..000000000000 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/branch.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "ArchStdEvent": "BR_MIS_PRED" - }, - { - "ArchStdEvent": "BR_PRED" - } -] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/bus.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/bus.json index 579c1c993d17..2e11a8c4a484 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/bus.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/bus.json @@ -1,20 +1,18 @@ [ { - "ArchStdEvent": "CPU_CYCLES" + "ArchStdEvent": "BUS_ACCESS", + "PublicDescription": "Counts memory transactions issued by the CPU to the external bus, including snoop requests and snoop responses. Each beat of data is counted individually." }, { - "ArchStdEvent": "BUS_ACCESS" + "ArchStdEvent": "BUS_CYCLES", + "PublicDescription": "Counts bus cycles in the CPU. Bus cycles represent a clock cycle in which a transaction could be sent or received on the interface from the CPU to the external bus. Since that interface is driven at the same clock speed as the CPU, this event is a duplicate of CPU_CYCLES." }, { - "ArchStdEvent": "BUS_CYCLES" + "ArchStdEvent": "BUS_ACCESS_RD", + "PublicDescription": "Counts memory read transactions seen on the external bus. Each beat of data is counted individually." }, { - "ArchStdEvent": "BUS_ACCESS_RD" - }, - { - "ArchStdEvent": "BUS_ACCESS_WR" - }, - { - "ArchStdEvent": "CNT_CYCLES" + "ArchStdEvent": "BUS_ACCESS_WR", + "PublicDescription": "Counts memory write transactions seen on the external bus. Each beat of data is counted individually." } ] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/cache.json deleted file mode 100644 index 0141f749bff3..000000000000 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/cache.json +++ /dev/null @@ -1,155 +0,0 @@ -[ - { - "ArchStdEvent": "L1I_CACHE_REFILL" - }, - { - "ArchStdEvent": "L1I_TLB_REFILL" - }, - { - "ArchStdEvent": "L1D_CACHE_REFILL" - }, - { - "ArchStdEvent": "L1D_CACHE" - }, - { - "ArchStdEvent": "L1D_TLB_REFILL" - }, - { - "ArchStdEvent": "L1I_CACHE" - }, - { - "ArchStdEvent": "L1D_CACHE_WB" - }, - { - "ArchStdEvent": "L2D_CACHE" - }, - { - "ArchStdEvent": "L2D_CACHE_REFILL" - }, - { - "ArchStdEvent": "L2D_CACHE_WB" - }, - { - "ArchStdEvent": "L2D_CACHE_ALLOCATE" - }, - { - "ArchStdEvent": "L1D_TLB" - }, - { - "ArchStdEvent": "L1I_TLB" - }, - { - "ArchStdEvent": "L3D_CACHE_ALLOCATE" - }, - { - "ArchStdEvent": "L3D_CACHE_REFILL" - }, - { - "ArchStdEvent": "L3D_CACHE" - }, - { - "ArchStdEvent": "L2D_TLB_REFILL" - }, - { - "ArchStdEvent": "L2D_TLB" - }, - { - "ArchStdEvent": "DTLB_WALK" - }, - { - "ArchStdEvent": "ITLB_WALK" - }, - { - "ArchStdEvent": "LL_CACHE_RD" - }, - { - "ArchStdEvent": "LL_CACHE_MISS_RD" - }, - { - "ArchStdEvent": "L1D_CACHE_LMISS_RD" - }, - { - "ArchStdEvent": "L1D_CACHE_RD" - }, - { - "ArchStdEvent": "L1D_CACHE_WR" - }, - { - "ArchStdEvent": "L1D_CACHE_REFILL_RD" - }, - { - "ArchStdEvent": "L1D_CACHE_REFILL_WR" - }, - { - "ArchStdEvent": "L1D_CACHE_REFILL_INNER" - }, - { - "ArchStdEvent": "L1D_CACHE_REFILL_OUTER" - }, - { - "ArchStdEvent": "L1D_CACHE_WB_VICTIM" - }, - { - "ArchStdEvent": "L1D_CACHE_WB_CLEAN" - }, - { - "ArchStdEvent": "L1D_CACHE_INVAL" - }, - { - "ArchStdEvent": "L1D_TLB_REFILL_RD" - }, - { - "ArchStdEvent": "L1D_TLB_REFILL_WR" - }, - { - "ArchStdEvent": "L1D_TLB_RD" - }, - { - "ArchStdEvent": "L1D_TLB_WR" - }, - { - "ArchStdEvent": "L2D_CACHE_RD" - }, - { - "ArchStdEvent": "L2D_CACHE_WR" - }, - { - "ArchStdEvent": "L2D_CACHE_REFILL_RD" - }, - { - "ArchStdEvent": "L2D_CACHE_REFILL_WR" - }, - { - "ArchStdEvent": "L2D_CACHE_WB_VICTIM" - }, - { - "ArchStdEvent": "L2D_CACHE_WB_CLEAN" - }, - { - "ArchStdEvent": "L2D_CACHE_INVAL" - }, - { - "ArchStdEvent": "L2D_TLB_REFILL_RD" - }, - { - "ArchStdEvent": "L2D_TLB_REFILL_WR" - }, - { - "ArchStdEvent": "L2D_TLB_RD" - }, - { - "ArchStdEvent": "L2D_TLB_WR" - }, - { - "ArchStdEvent": "L3D_CACHE_RD" - }, - { - "ArchStdEvent": "L1I_CACHE_LMISS" - }, - { - "ArchStdEvent": "L2D_CACHE_LMISS_RD" - }, - { - "ArchStdEvent": "L3D_CACHE_LMISS_RD" - } -] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/exception.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/exception.json index 344a2d552ad5..4404b8e91690 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/exception.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/exception.json @@ -1,47 +1,62 @@ [ { - "ArchStdEvent": "EXC_TAKEN" + "ArchStdEvent": "EXC_TAKEN", + "PublicDescription": "Counts any taken architecturally visible exceptions such as IRQ, FIQ, SError, and other synchronous exceptions. Exceptions are counted whether or not they are taken locally." }, { - "ArchStdEvent": "MEMORY_ERROR" + "ArchStdEvent": "EXC_RETURN", + "PublicDescription": "Counts any architecturally executed exception return instructions. Eg: AArch64: ERET" }, { - "ArchStdEvent": "EXC_UNDEF" + "ArchStdEvent": "EXC_UNDEF", + "PublicDescription": "Counts the number of synchronous exceptions which are taken locally that are due to attempting to execute an instruction that is UNDEFINED. Attempting to execute instruction bit patterns that have not been allocated. Attempting to execute instructions when they are disabled. Attempting to execute instructions at an inappropriate Exception level. Attempting to execute an instruction when the value of PSTATE.IL is 1." }, { - "ArchStdEvent": "EXC_SVC" + "ArchStdEvent": "EXC_SVC", + "PublicDescription": "Counts SVC exceptions taken locally." }, { - "ArchStdEvent": "EXC_PABORT" + "ArchStdEvent": "EXC_PABORT", + "PublicDescription": "Counts synchronous exceptions that are taken locally and caused by Instruction Aborts." }, { - "ArchStdEvent": "EXC_DABORT" + "ArchStdEvent": "EXC_DABORT", + "PublicDescription": "Counts exceptions that are taken locally and are caused by data aborts or SErrors. Conditions that could cause those exceptions are attempting to read or write memory where the MMU generates a fault, attempting to read or write memory with a misaligned address, interrupts from the nSEI inputs and internally generated SErrors." }, { - "ArchStdEvent": "EXC_IRQ" + "ArchStdEvent": "EXC_IRQ", + "PublicDescription": "Counts IRQ exceptions including the virtual IRQs that are taken locally." }, { - "ArchStdEvent": "EXC_FIQ" + "ArchStdEvent": "EXC_FIQ", + "PublicDescription": "Counts FIQ exceptions including the virtual FIQs that are taken locally." }, { - "ArchStdEvent": "EXC_SMC" + "ArchStdEvent": "EXC_SMC", + "PublicDescription": "Counts SMC exceptions take to EL3." }, { - "ArchStdEvent": "EXC_HVC" + "ArchStdEvent": "EXC_HVC", + "PublicDescription": "Counts HVC exceptions taken to EL2." }, { - "ArchStdEvent": "EXC_TRAP_PABORT" + "ArchStdEvent": "EXC_TRAP_PABORT", + "PublicDescription": "Counts exceptions which are traps not taken locally and are caused by Instruction Aborts. For example, attempting to execute an instruction with a misaligned PC." }, { - "ArchStdEvent": "EXC_TRAP_DABORT" + "ArchStdEvent": "EXC_TRAP_DABORT", + "PublicDescription": "Counts exceptions which are traps not taken locally and are caused by Data Aborts or SError interrupts. Conditions that could cause those exceptions are:\n\n1. Attempting to read or write memory where the MMU generates a fault,\n2. Attempting to read or write memory with a misaligned address,\n3. Interrupts from the SEI input.\n4. internally generated SErrors." }, { - "ArchStdEvent": "EXC_TRAP_OTHER" + "ArchStdEvent": "EXC_TRAP_OTHER", + "PublicDescription": "Counts the number of synchronous trap exceptions which are not taken locally and are not SVC, SMC, HVC, data aborts, Instruction Aborts, or interrupts." }, { - "ArchStdEvent": "EXC_TRAP_IRQ" + "ArchStdEvent": "EXC_TRAP_IRQ", + "PublicDescription": "Counts IRQ exceptions including the virtual IRQs that are not taken locally." }, { - "ArchStdEvent": "EXC_TRAP_FIQ" + "ArchStdEvent": "EXC_TRAP_FIQ", + "PublicDescription": "Counts FIQs which are not taken locally but taken from EL0, EL1,\n or EL2 to EL3 (which would be the normal behavior for FIQs when not executing\n in EL3)." } ] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/fp_operation.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/fp_operation.json new file mode 100644 index 000000000000..cec3435ac766 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/fp_operation.json @@ -0,0 +1,22 @@ +[ + { + "ArchStdEvent": "FP_HP_SPEC", + "PublicDescription": "Counts speculatively executed half precision floating point operations." + }, + { + "ArchStdEvent": "FP_SP_SPEC", + "PublicDescription": "Counts speculatively executed single precision floating point operations." + }, + { + "ArchStdEvent": "FP_DP_SPEC", + "PublicDescription": "Counts speculatively executed double precision floating point operations." + }, + { + "ArchStdEvent": "FP_SCALE_OPS_SPEC", + "PublicDescription": "Counts speculatively executed scalable single precision floating point operations." + }, + { + "ArchStdEvent": "FP_FIXED_OPS_SPEC", + "PublicDescription": "Counts speculatively executed non-scalable single precision floating point operations." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/general.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/general.json new file mode 100644 index 000000000000..428810f855b8 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/general.json @@ -0,0 +1,10 @@ +[ + { + "ArchStdEvent": "CPU_CYCLES", + "PublicDescription": "Counts CPU clock cycles (not timer cycles). The clock measured by this event is defined as the physical clock driving the CPU logic." + }, + { + "ArchStdEvent": "CNT_CYCLES", + "PublicDescription": "Counts constant frequency cycles" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/instruction.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/instruction.json deleted file mode 100644 index e57cd55937c6..000000000000 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/instruction.json +++ /dev/null @@ -1,143 +0,0 @@ -[ - { - "ArchStdEvent": "SW_INCR" - }, - { - "ArchStdEvent": "INST_RETIRED" - }, - { - "ArchStdEvent": "EXC_RETURN" - }, - { - "ArchStdEvent": "CID_WRITE_RETIRED" - }, - { - "ArchStdEvent": "INST_SPEC" - }, - { - "ArchStdEvent": "TTBR_WRITE_RETIRED" - }, - { - "ArchStdEvent": "BR_RETIRED" - }, - { - "ArchStdEvent": "BR_MIS_PRED_RETIRED" - }, - { - "ArchStdEvent": "OP_RETIRED" - }, - { - "ArchStdEvent": "OP_SPEC" - }, - { - "ArchStdEvent": "LDREX_SPEC" - }, - { - "ArchStdEvent": "STREX_PASS_SPEC" - }, - { - "ArchStdEvent": "STREX_FAIL_SPEC" - }, - { - "ArchStdEvent": "STREX_SPEC" - }, - { - "ArchStdEvent": "LD_SPEC" - }, - { - "ArchStdEvent": "ST_SPEC" - }, - { - "ArchStdEvent": "DP_SPEC" - }, - { - "ArchStdEvent": "ASE_SPEC" - }, - { - "ArchStdEvent": "VFP_SPEC" - }, - { - "ArchStdEvent": "PC_WRITE_SPEC" - }, - { - "ArchStdEvent": "CRYPTO_SPEC" - }, - { - "ArchStdEvent": "BR_IMMED_SPEC" - }, - { - "ArchStdEvent": "BR_RETURN_SPEC" - }, - { - "ArchStdEvent": "BR_INDIRECT_SPEC" - }, - { - "ArchStdEvent": "ISB_SPEC" - }, - { - "ArchStdEvent": "DSB_SPEC" - }, - { - "ArchStdEvent": "DMB_SPEC" - }, - { - "ArchStdEvent": "RC_LD_SPEC" - }, - { - "ArchStdEvent": "RC_ST_SPEC" - }, - { - "ArchStdEvent": "ASE_INST_SPEC" - }, - { - "ArchStdEvent": "SVE_INST_SPEC" - }, - { - "ArchStdEvent": "FP_HP_SPEC" - }, - { - "ArchStdEvent": "FP_SP_SPEC" - }, - { - "ArchStdEvent": "FP_DP_SPEC" - }, - { - "ArchStdEvent": "SVE_PRED_SPEC" - }, - { - "ArchStdEvent": "SVE_PRED_EMPTY_SPEC" - }, - { - "ArchStdEvent": "SVE_PRED_FULL_SPEC" - }, - { - "ArchStdEvent": "SVE_PRED_PARTIAL_SPEC" - }, - { - "ArchStdEvent": "SVE_PRED_NOT_FULL_SPEC" - }, - { - "ArchStdEvent": "SVE_LDFF_SPEC" - }, - { - "ArchStdEvent": "SVE_LDFF_FAULT_SPEC" - }, - { - "ArchStdEvent": "FP_SCALE_OPS_SPEC" - }, - { - "ArchStdEvent": "FP_FIXED_OPS_SPEC" - }, - { - "ArchStdEvent": "ASE_SVE_INT8_SPEC" - }, - { - "ArchStdEvent": "ASE_SVE_INT16_SPEC" - }, - { - "ArchStdEvent": "ASE_SVE_INT32_SPEC" - }, - { - "ArchStdEvent": "ASE_SVE_INT64_SPEC" - } -] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1d_cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1d_cache.json new file mode 100644 index 000000000000..da7c129f2569 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1d_cache.json @@ -0,0 +1,54 @@ +[ + { + "ArchStdEvent": "L1D_CACHE_REFILL", + "PublicDescription": "Counts level 1 data cache refills caused by speculatively executed load or store operations that missed in the level 1 data cache. This event only counts one event per cache line. This event does not count cache line allocations from preload instructions or from hardware cache prefetching." + }, + { + "ArchStdEvent": "L1D_CACHE", + "PublicDescription": "Counts level 1 data cache accesses from any load/store operations. Atomic operations that resolve in the CPUs caches (near atomic operations) count as both a write access and read access. Each access to a cache line is counted including the multiple accesses caused by single instructions such as LDM or STM. Each access to other level 1 data or unified memory structures, for example refill buffers, write buffers, and write-back buffers, are also counted." + }, + { + "ArchStdEvent": "L1D_CACHE_WB", + "PublicDescription": "Counts write-backs of dirty data from the L1 data cache to the L2 cache. This occurs when either a dirty cache line is evicted from L1 data cache and allocated in the L2 cache or dirty data is written to the L2 and possibly to the next level of cache. This event counts both victim cache line evictions and cache write-backs from snoops or cache maintenance operations. The following cache operations are not counted:\n\n1. Invalidations which do not result in data being transferred out of the L1 (such as evictions of clean data),\n2. Full line writes which write to L2 without writing L1, such as write streaming mode." + }, + { + "ArchStdEvent": "L1D_CACHE_LMISS_RD", + "PublicDescription": "Counts cache line refills into the level 1 data cache from any memory read operations, that incurred additional latency." + }, + { + "ArchStdEvent": "L1D_CACHE_RD", + "PublicDescription": "Counts level 1 data cache accesses from any load operation. Atomic load operations that resolve in the CPUs caches count as both a write access and read access." + }, + { + "ArchStdEvent": "L1D_CACHE_WR", + "PublicDescription": "Counts level 1 data cache accesses generated by store operations. This event also counts accesses caused by a DC ZVA (data cache zero, specified by virtual address) instruction. Near atomic operations that resolve in the CPUs caches count as a write access and read access." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_RD", + "PublicDescription": "Counts level 1 data cache refills caused by speculatively executed load instructions where the memory read operation misses in the level 1 data cache. This event only counts one event per cache line." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_WR", + "PublicDescription": "Counts level 1 data cache refills caused by speculatively executed store instructions where the memory write operation misses in the level 1 data cache. This event only counts one event per cache line." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_INNER", + "PublicDescription": "Counts level 1 data cache refills where the cache line data came from caches inside the immediate cluster of the core." + }, + { + "ArchStdEvent": "L1D_CACHE_REFILL_OUTER", + "PublicDescription": "Counts level 1 data cache refills for which the cache line data came from outside the immediate cluster of the core, like an SLC in the system interconnect or DRAM." + }, + { + "ArchStdEvent": "L1D_CACHE_WB_VICTIM", + "PublicDescription": "Counts dirty cache line evictions from the level 1 data cache caused by a new cache line allocation. This event does not count evictions caused by cache maintenance operations." + }, + { + "ArchStdEvent": "L1D_CACHE_WB_CLEAN", + "PublicDescription": "Counts write-backs from the level 1 data cache that are a result of a coherency operation made by another CPU. Event count includes cache maintenance operations." + }, + { + "ArchStdEvent": "L1D_CACHE_INVAL", + "PublicDescription": "Counts each explicit invalidation of a cache line in the level 1 data cache caused by:\n\n- Cache Maintenance Operations (CMO) that operate by a virtual address.\n- Broadcast cache coherency operations from another CPU in the system.\n\nThis event does not count for the following conditions:\n\n1. A cache refill invalidates a cache line.\n2. A CMO which is executed on that CPU and invalidates a cache line specified by set/way.\n\nNote that CMOs that operate by set/way cannot be broadcast from one CPU to another." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1i_cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1i_cache.json new file mode 100644 index 000000000000..633f1030359d --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l1i_cache.json @@ -0,0 +1,14 @@ +[ + { + "ArchStdEvent": "L1I_CACHE_REFILL", + "PublicDescription": "Counts cache line refills in the level 1 instruction cache caused by a missed instruction fetch. Instruction fetches may include accessing multiple instructions, but the single cache line allocation is counted once." + }, + { + "ArchStdEvent": "L1I_CACHE", + "PublicDescription": "Counts instruction fetches which access the level 1 instruction cache. Instruction cache accesses caused by cache maintenance operations are not counted." + }, + { + "ArchStdEvent": "L1I_CACHE_LMISS", + "PublicDescription": "Counts cache line refills into the level 1 instruction cache, that incurred additional latency." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l2_cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l2_cache.json new file mode 100644 index 000000000000..0e31d0daf88b --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l2_cache.json @@ -0,0 +1,50 @@ +[ + { + "ArchStdEvent": "L2D_CACHE", + "PublicDescription": "Counts level 2 cache accesses. level 2 cache is a unified cache for data and instruction accesses. Accesses are for misses in the first level caches or translation resolutions due to accesses. This event also counts write back of dirty data from level 1 data cache to the L2 cache." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL", + "PublicDescription": "Counts cache line refills into the level 2 cache. level 2 cache is a unified cache for data and instruction accesses. Accesses are for misses in the level 1 caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L2D_CACHE_WB", + "PublicDescription": "Counts write-backs of data from the L2 cache to outside the CPU. This includes snoops to the L2 (from other CPUs) which return data even if the snoops cause an invalidation. L2 cache line invalidations which do not write data outside the CPU and snoops which return data from an L1 cache are not counted. Data would not be written outside the cache when invalidating a clean cache line." + }, + { + "ArchStdEvent": "L2D_CACHE_ALLOCATE", + "PublicDescription": "TBD" + }, + { + "ArchStdEvent": "L2D_CACHE_RD", + "PublicDescription": "Counts level 2 cache accesses due to memory read operations. level 2 cache is a unified cache for data and instruction accesses, accesses are for misses in the level 1 caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L2D_CACHE_WR", + "PublicDescription": "Counts level 2 cache accesses due to memory write operations. level 2 cache is a unified cache for data and instruction accesses, accesses are for misses in the level 1 caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_RD", + "PublicDescription": "Counts refills for memory accesses due to memory read operation counted by L2D_CACHE_RD. level 2 cache is a unified cache for data and instruction accesses, accesses are for misses in the level 1 caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L2D_CACHE_REFILL_WR", + "PublicDescription": "Counts refills for memory accesses due to memory write operation counted by L2D_CACHE_WR. level 2 cache is a unified cache for data and instruction accesses, accesses are for misses in the level 1 caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L2D_CACHE_WB_VICTIM", + "PublicDescription": "Counts evictions from the level 2 cache because of a line being allocated into the L2 cache." + }, + { + "ArchStdEvent": "L2D_CACHE_WB_CLEAN", + "PublicDescription": "Counts write-backs from the level 2 cache that are a result of either:\n\n1. Cache maintenance operations,\n\n2. Snoop responses or,\n\n3. Direct cache transfers to another CPU due to a forwarding snoop request." + }, + { + "ArchStdEvent": "L2D_CACHE_INVAL", + "PublicDescription": "Counts each explicit invalidation of a cache line in the level 2 cache by cache maintenance operations that operate by a virtual address, or by external coherency operations. This event does not count if either:\n\n1. A cache refill invalidates a cache line or,\n2. A Cache Maintenance Operation (CMO), which invalidates a cache line specified by set/way, is executed on that CPU.\n\nCMOs that operate by set/way cannot be broadcast from one CPU to another." + }, + { + "ArchStdEvent": "L2D_CACHE_LMISS_RD", + "PublicDescription": "Counts cache line refills into the level 2 unified cache from any memory read operations that incurred additional latency." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l3_cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l3_cache.json new file mode 100644 index 000000000000..45bfba532df7 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/l3_cache.json @@ -0,0 +1,22 @@ +[ + { + "ArchStdEvent": "L3D_CACHE_ALLOCATE", + "PublicDescription": "Counts level 3 cache line allocates that do not fetch data from outside the level 3 data or unified cache. For example, allocates due to streaming stores." + }, + { + "ArchStdEvent": "L3D_CACHE_REFILL", + "PublicDescription": "Counts level 3 accesses that receive data from outside the L3 cache." + }, + { + "ArchStdEvent": "L3D_CACHE", + "PublicDescription": "Counts level 3 cache accesses. level 3 cache is a unified cache for data and instruction accesses. Accesses are for misses in the lower level caches or translation resolutions due to accesses." + }, + { + "ArchStdEvent": "L3D_CACHE_RD", + "PublicDescription": "TBD" + }, + { + "ArchStdEvent": "L3D_CACHE_LMISS_RD", + "PublicDescription": "Counts any cache line refill into the level 3 cache from memory read operations that incurred additional latency." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/ll_cache.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/ll_cache.json new file mode 100644 index 000000000000..bb712d57d58a --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/ll_cache.json @@ -0,0 +1,10 @@ +[ + { + "ArchStdEvent": "LL_CACHE_RD", + "PublicDescription": "Counts read transactions that were returned from outside the core cluster. This event counts when the system register CPUECTLR.EXTLLC bit is set. This event counts read transactions returned from outside the core if those transactions are either hit in the system level cache or missed in the SLC and are returned from any other external sources." + }, + { + "ArchStdEvent": "LL_CACHE_MISS_RD", + "PublicDescription": "Counts read transactions that were returned from outside the core cluster but missed in the system level cache. This event counts when the system register CPUECTLR.EXTLLC bit is set. This event counts read transactions returned from outside the core if those transactions are missed in the System level Cache. The data source of the transaction is indicated by a field in the CHI transaction returning to the CPU. This event does not count reads caused by cache maintenance operations." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/memory.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/memory.json index 7b2b21ac150f..106a97f8b2e7 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/memory.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/memory.json @@ -1,41 +1,46 @@ [ { - "ArchStdEvent": "MEM_ACCESS" + "ArchStdEvent": "MEM_ACCESS", + "PublicDescription": "Counts memory accesses issued by the CPU load store unit, where those accesses are issued due to load or store operations. This event counts memory accesses no matter whether the data is received from any level of cache hierarchy or external memory. If memory accesses are broken up into smaller transactions than what were specified in the load or store instructions, then the event counts those smaller memory transactions." }, { - "ArchStdEvent": "REMOTE_ACCESS" + "ArchStdEvent": "MEMORY_ERROR", + "PublicDescription": "Counts any detected correctable or uncorrectable physical memory errors (ECC or parity) in protected CPUs RAMs. On the core, this event counts errors in the caches (including data and tag rams). Any detected memory error (from either a speculative and abandoned access, or an architecturally executed access) is counted. Note that errors are only detected when the actual protected memory is accessed by an operation." }, { - "ArchStdEvent": "MEM_ACCESS_RD" + "ArchStdEvent": "REMOTE_ACCESS", + "PublicDescription": "Counts accesses to another chip, which is implemented as a different CMN mesh in the system. If the CHI bus response back to the core indicates that the data source is from another chip (mesh), then the counter is updated. If no data is returned, even if the system snoops another chip/mesh, then the counter is not updated." }, { - "ArchStdEvent": "MEM_ACCESS_WR" + "ArchStdEvent": "MEM_ACCESS_RD", + "PublicDescription": "Counts memory accesses issued by the CPU due to load operations. The event counts any memory load access, no matter whether the data is received from any level of cache hierarchy or external memory. The event also counts atomic load operations. If memory accesses are broken up by the load/store unit into smaller transactions that are issued by the bus interface, then the event counts those smaller transactions." }, { - "ArchStdEvent": "UNALIGNED_LD_SPEC" + "ArchStdEvent": "MEM_ACCESS_WR", + "PublicDescription": "Counts memory accesses issued by the CPU due to store operations. The event counts any memory store access, no matter whether the data is located in any level of cache or external memory. The event also counts atomic load and store operations. If memory accesses are broken up by the load/store unit into smaller transactions that are issued by the bus interface, then the event counts those smaller transactions." }, { - "ArchStdEvent": "UNALIGNED_ST_SPEC" + "ArchStdEvent": "LDST_ALIGN_LAT", + "PublicDescription": "Counts the number of memory read and write accesses in a cycle that incurred additional latency, due to the alignment of the address and the size of data being accessed, which results in store crossing a single cache line." }, { - "ArchStdEvent": "UNALIGNED_LDST_SPEC" + "ArchStdEvent": "LD_ALIGN_LAT", + "PublicDescription": "Counts the number of memory read accesses in a cycle that incurred additional latency, due to the alignment of the address and size of data being accessed, which results in load crossing a single cache line." }, { - "ArchStdEvent": "LDST_ALIGN_LAT" + "ArchStdEvent": "ST_ALIGN_LAT", + "PublicDescription": "Counts the number of memory write access in a cycle that incurred additional latency, due to the alignment of the address and size of data being accessed incurred additional latency." }, { - "ArchStdEvent": "LD_ALIGN_LAT" + "ArchStdEvent": "MEM_ACCESS_CHECKED", + "PublicDescription": "Counts the number of memory read and write accesses in a cycle that are tag checked by the Memory Tagging Extension (MTE)." }, { - "ArchStdEvent": "ST_ALIGN_LAT" + "ArchStdEvent": "MEM_ACCESS_CHECKED_RD", + "PublicDescription": "Counts the number of memory read accesses in a cycle that are tag checked by the Memory Tagging Extension (MTE)." }, { - "ArchStdEvent": "MEM_ACCESS_CHECKED" - }, - { - "ArchStdEvent": "MEM_ACCESS_CHECKED_RD" - }, - { - "ArchStdEvent": "MEM_ACCESS_CHECKED_WR" + "ArchStdEvent": "MEM_ACCESS_CHECKED_WR", + "PublicDescription": "Counts the number of memory write accesses in a cycle that is tag checked by the Memory Tagging Extension (MTE)." } ] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json index 8ad15b726dca..5f449270b448 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json @@ -1,272 +1,303 @@ [ { - "ArchStdEvent": "FRONTEND_BOUND", - "MetricExpr": "((stall_slot_frontend) if (#slots - 5) else (stall_slot_frontend - cpu_cycles)) / (#slots * cpu_cycles)" + "ArchStdEvent": "backend_bound", + "MetricExpr": "(100 * ((STALL_SLOT_BACKEND / (CPU_CYCLES * #slots)) - ((BR_MIS_PRED * 3) / CPU_CYCLES)))" }, { - "ArchStdEvent": "BAD_SPECULATION", - "MetricExpr": "(1 - op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))" + "MetricName": "backend_stalled_cycles", + "MetricExpr": "((STALL_BACKEND / CPU_CYCLES) * 100)", + "BriefDescription": "This metric is the percentage of cycles that were stalled due to resource constraints in the backend unit of the processor.", + "MetricGroup": "Cycle_Accounting", + "ScaleUnit": "1percent of cycles" }, { - "ArchStdEvent": "RETIRING", - "MetricExpr": "(op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))" + "ArchStdEvent": "bad_speculation", + "MetricExpr": "(100 * (((1 - (OP_RETIRED / OP_SPEC)) * (1 - (((STALL_SLOT) if (strcmp_cpuid_str(0x410fd493) | strcmp_cpuid_str(0x410fd490) ^ 1) else (STALL_SLOT - CPU_CYCLES)) / (CPU_CYCLES * #slots)))) + ((BR_MIS_PRED * 4) / CPU_CYCLES)))" }, { - "ArchStdEvent": "BACKEND_BOUND" + "MetricName": "branch_misprediction_ratio", + "MetricExpr": "(BR_MIS_PRED_RETIRED / BR_RETIRED)", + "BriefDescription": "This metric measures the ratio of branches mispredicted to the total number of branches architecturally executed. This gives an indication of the effectiveness of the branch prediction unit.", + "MetricGroup": "Miss_Ratio;Branch_Effectiveness", + "ScaleUnit": "1per branch" }, { - "MetricExpr": "L1D_TLB_REFILL / L1D_TLB", - "BriefDescription": "The rate of L1D TLB refill to the overall L1D TLB lookups", - "MetricGroup": "TLB", - "MetricName": "l1d_tlb_miss_rate", - "ScaleUnit": "100%" + "MetricName": "branch_mpki", + "MetricExpr": "((BR_MIS_PRED_RETIRED / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of branch mispredictions per thousand instructions executed.", + "MetricGroup": "MPKI;Branch_Effectiveness", + "ScaleUnit": "1MPKI" }, { - "MetricExpr": "L1I_TLB_REFILL / L1I_TLB", - "BriefDescription": "The rate of L1I TLB refill to the overall L1I TLB lookups", - "MetricGroup": "TLB", - "MetricName": "l1i_tlb_miss_rate", - "ScaleUnit": "100%" + "MetricName": "branch_percentage", + "MetricExpr": "(((BR_IMMED_SPEC + BR_INDIRECT_SPEC) / INST_SPEC) * 100)", + "BriefDescription": "This metric measures branch operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "L2D_TLB_REFILL / L2D_TLB", - "BriefDescription": "The rate of L2D TLB refill to the overall L2D TLB lookups", - "MetricGroup": "TLB", - "MetricName": "l2_tlb_miss_rate", - "ScaleUnit": "100%" + "MetricName": "crypto_percentage", + "MetricExpr": "((CRYPTO_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures crypto operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "DTLB_WALK / INST_RETIRED * 1000", - "BriefDescription": "The rate of TLB Walks per kilo instructions for data accesses", - "MetricGroup": "TLB", "MetricName": "dtlb_mpki", + "MetricExpr": "((DTLB_WALK / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of data TLB Walks per thousand instructions executed.", + "MetricGroup": "MPKI;DTLB_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "DTLB_WALK / L1D_TLB", - "BriefDescription": "The rate of DTLB Walks to the overall L1D TLB lookups", - "MetricGroup": "TLB", - "MetricName": "dtlb_walk_rate", - "ScaleUnit": "100%" + "MetricName": "dtlb_walk_ratio", + "MetricExpr": "(DTLB_WALK / L1D_TLB)", + "BriefDescription": "This metric measures the ratio of data TLB Walks to the total number of data TLB accesses. This gives an indication of the effectiveness of the data TLB accesses.", + "MetricGroup": "Miss_Ratio;DTLB_Effectiveness", + "ScaleUnit": "1per TLB access" }, { - "MetricExpr": "ITLB_WALK / INST_RETIRED * 1000", - "BriefDescription": "The rate of TLB Walks per kilo instructions for instruction accesses", - "MetricGroup": "TLB", - "MetricName": "itlb_mpki", - "ScaleUnit": "1MPKI" + "ArchStdEvent": "frontend_bound", + "MetricExpr": "(100 * ((((STALL_SLOT_FRONTEND) if (strcmp_cpuid_str(0x410fd493) | strcmp_cpuid_str(0x410fd490) ^ 1) else (STALL_SLOT_FRONTEND - CPU_CYCLES)) / (CPU_CYCLES * #slots)) - (BR_MIS_PRED / CPU_CYCLES)))" }, { - "MetricExpr": "ITLB_WALK / L1I_TLB", - "BriefDescription": "The rate of ITLB Walks to the overall L1I TLB lookups", - "MetricGroup": "TLB", - "MetricName": "itlb_walk_rate", - "ScaleUnit": "100%" + "MetricName": "frontend_stalled_cycles", + "MetricExpr": "((STALL_FRONTEND / CPU_CYCLES) * 100)", + "BriefDescription": "This metric is the percentage of cycles that were stalled due to resource constraints in the frontend unit of the processor.", + "MetricGroup": "Cycle_Accounting", + "ScaleUnit": "1percent of cycles" }, { - "MetricExpr": "L1I_CACHE_REFILL / INST_RETIRED * 1000", - "BriefDescription": "The rate of L1 I-Cache misses per kilo instructions", - "MetricGroup": "Cache", - "MetricName": "l1i_cache_mpki", + "MetricName": "integer_dp_percentage", + "MetricExpr": "((DP_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures scalar integer operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" + }, + { + "MetricName": "ipc", + "MetricExpr": "(INST_RETIRED / CPU_CYCLES)", + "BriefDescription": "This metric measures the number of instructions retired per cycle.", + "MetricGroup": "General", + "ScaleUnit": "1per cycle" + }, + { + "MetricName": "itlb_mpki", + "MetricExpr": "((ITLB_WALK / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of instruction TLB Walks per thousand instructions executed.", + "MetricGroup": "MPKI;ITLB_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "L1I_CACHE_REFILL / L1I_CACHE", - "BriefDescription": "The rate of L1 I-Cache misses to the overall L1 I-Cache", - "MetricGroup": "Cache", - "MetricName": "l1i_cache_miss_rate", - "ScaleUnit": "100%" + "MetricName": "itlb_walk_ratio", + "MetricExpr": "(ITLB_WALK / L1I_TLB)", + "BriefDescription": "This metric measures the ratio of instruction TLB Walks to the total number of instruction TLB accesses. This gives an indication of the effectiveness of the instruction TLB accesses.", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness", + "ScaleUnit": "1per TLB access" + }, + { + "MetricName": "l1d_cache_miss_ratio", + "MetricExpr": "(L1D_CACHE_REFILL / L1D_CACHE)", + "BriefDescription": "This metric measures the ratio of level 1 data cache accesses missed to the total number of level 1 data cache accesses. This gives an indication of the effectiveness of the level 1 data cache.", + "MetricGroup": "Miss_Ratio;L1D_Cache_Effectiveness", + "ScaleUnit": "1per cache access" }, { - "MetricExpr": "L1D_CACHE_REFILL / INST_RETIRED * 1000", - "BriefDescription": "The rate of L1 D-Cache misses per kilo instructions", - "MetricGroup": "Cache", "MetricName": "l1d_cache_mpki", + "MetricExpr": "((L1D_CACHE_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 1 data cache accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;L1D_Cache_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "L1D_CACHE_REFILL / L1D_CACHE", - "BriefDescription": "The rate of L1 D-Cache misses to the overall L1 D-Cache", - "MetricGroup": "Cache", - "MetricName": "l1d_cache_miss_rate", - "ScaleUnit": "100%" + "MetricName": "l1d_tlb_miss_ratio", + "MetricExpr": "(L1D_TLB_REFILL / L1D_TLB)", + "BriefDescription": "This metric measures the ratio of level 1 data TLB accesses missed to the total number of level 1 data TLB accesses. This gives an indication of the effectiveness of the level 1 data TLB.", + "MetricGroup": "Miss_Ratio;DTLB_Effectiveness", + "ScaleUnit": "1per TLB access" }, { - "MetricExpr": "L2D_CACHE_REFILL / INST_RETIRED * 1000", - "BriefDescription": "The rate of L2 D-Cache misses per kilo instructions", - "MetricGroup": "Cache", - "MetricName": "l2d_cache_mpki", + "MetricName": "l1d_tlb_mpki", + "MetricExpr": "((L1D_TLB_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 1 instruction TLB accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;DTLB_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "L2D_CACHE_REFILL / L2D_CACHE", - "BriefDescription": "The rate of L2 D-Cache misses to the overall L2 D-Cache", - "MetricGroup": "Cache", - "MetricName": "l2d_cache_miss_rate", - "ScaleUnit": "100%" + "MetricName": "l1i_cache_miss_ratio", + "MetricExpr": "(L1I_CACHE_REFILL / L1I_CACHE)", + "BriefDescription": "This metric measures the ratio of level 1 instruction cache accesses missed to the total number of level 1 instruction cache accesses. This gives an indication of the effectiveness of the level 1 instruction cache.", + "MetricGroup": "Miss_Ratio;L1I_Cache_Effectiveness", + "ScaleUnit": "1per cache access" }, { - "MetricExpr": "L3D_CACHE_REFILL / INST_RETIRED * 1000", - "BriefDescription": "The rate of L3 D-Cache misses per kilo instructions", - "MetricGroup": "Cache", - "MetricName": "l3d_cache_mpki", + "MetricName": "l1i_cache_mpki", + "MetricExpr": "((L1I_CACHE_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 1 instruction cache accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;L1I_Cache_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "L3D_CACHE_REFILL / L3D_CACHE", - "BriefDescription": "The rate of L3 D-Cache misses to the overall L3 D-Cache", - "MetricGroup": "Cache", - "MetricName": "l3d_cache_miss_rate", - "ScaleUnit": "100%" + "MetricName": "l1i_tlb_miss_ratio", + "MetricExpr": "(L1I_TLB_REFILL / L1I_TLB)", + "BriefDescription": "This metric measures the ratio of level 1 instruction TLB accesses missed to the total number of level 1 instruction TLB accesses. This gives an indication of the effectiveness of the level 1 instruction TLB.", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness", + "ScaleUnit": "1per TLB access" }, { - "MetricExpr": "LL_CACHE_MISS_RD / INST_RETIRED * 1000", - "BriefDescription": "The rate of LL Cache read misses per kilo instructions", - "MetricGroup": "Cache", - "MetricName": "ll_cache_read_mpki", + "MetricName": "l1i_tlb_mpki", + "MetricExpr": "((L1I_TLB_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 1 instruction TLB accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;ITLB_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "LL_CACHE_MISS_RD / LL_CACHE_RD", - "BriefDescription": "The rate of LL Cache read misses to the overall LL Cache read", - "MetricGroup": "Cache", - "MetricName": "ll_cache_read_miss_rate", - "ScaleUnit": "100%" + "MetricName": "l2_cache_miss_ratio", + "MetricExpr": "(L2D_CACHE_REFILL / L2D_CACHE)", + "BriefDescription": "This metric measures the ratio of level 2 cache accesses missed to the total number of level 2 cache accesses. This gives an indication of the effectiveness of the level 2 cache, which is a unified cache that stores both data and instruction. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a unified cache.", + "MetricGroup": "Miss_Ratio;L2_Cache_Effectiveness", + "ScaleUnit": "1per cache access" }, { - "MetricExpr": "(LL_CACHE_RD - LL_CACHE_MISS_RD) / LL_CACHE_RD", - "BriefDescription": "The rate of LL Cache read hit to the overall LL Cache read", - "MetricGroup": "Cache", - "MetricName": "ll_cache_read_hit_rate", - "ScaleUnit": "100%" + "MetricName": "l2_cache_mpki", + "MetricExpr": "((L2D_CACHE_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 2 unified cache accesses missed per thousand instructions executed. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a unified cache.", + "MetricGroup": "MPKI;L2_Cache_Effectiveness", + "ScaleUnit": "1MPKI" }, { - "MetricExpr": "BR_MIS_PRED_RETIRED / INST_RETIRED * 1000", - "BriefDescription": "The rate of branches mis-predicted per kilo instructions", - "MetricGroup": "Branch", - "MetricName": "branch_mpki", + "MetricName": "l2_tlb_miss_ratio", + "MetricExpr": "(L2D_TLB_REFILL / L2D_TLB)", + "BriefDescription": "This metric measures the ratio of level 2 unified TLB accesses missed to the total number of level 2 unified TLB accesses. This gives an indication of the effectiveness of the level 2 TLB.", + "MetricGroup": "Miss_Ratio;ITLB_Effectiveness;DTLB_Effectiveness", + "ScaleUnit": "1per TLB access" + }, + { + "MetricName": "l2_tlb_mpki", + "MetricExpr": "((L2D_TLB_REFILL / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of level 2 unified TLB accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;ITLB_Effectiveness;DTLB_Effectiveness", "ScaleUnit": "1MPKI" }, { - "MetricExpr": "BR_RETIRED / INST_RETIRED * 1000", - "BriefDescription": "The rate of branches retired per kilo instructions", - "MetricGroup": "Branch", - "MetricName": "branch_pki", - "ScaleUnit": "1PKI" + "MetricName": "ll_cache_read_hit_ratio", + "MetricExpr": "((LL_CACHE_RD - LL_CACHE_MISS_RD) / LL_CACHE_RD)", + "BriefDescription": "This metric measures the ratio of last level cache read accesses hit in the cache to the total number of last level cache accesses. This gives an indication of the effectiveness of the last level cache for read traffic. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a system level cache.", + "MetricGroup": "LL_Cache_Effectiveness", + "ScaleUnit": "1per cache access" }, { - "MetricExpr": "BR_MIS_PRED_RETIRED / BR_RETIRED", - "BriefDescription": "The rate of branches mis-predited to the overall branches", - "MetricGroup": "Branch", - "MetricName": "branch_miss_pred_rate", - "ScaleUnit": "100%" + "MetricName": "ll_cache_read_miss_ratio", + "MetricExpr": "(LL_CACHE_MISS_RD / LL_CACHE_RD)", + "BriefDescription": "This metric measures the ratio of last level cache read accesses missed to the total number of last level cache accesses. This gives an indication of the effectiveness of the last level cache for read traffic. Note that cache accesses in this cache are either data memory access or instruction fetch as this is a system level cache.", + "MetricGroup": "Miss_Ratio;LL_Cache_Effectiveness", + "ScaleUnit": "1per cache access" }, { - "MetricExpr": "instructions / CPU_CYCLES", - "BriefDescription": "The average number of instructions executed for each cycle.", - "MetricGroup": "PEutilization", - "MetricName": "ipc" + "MetricName": "ll_cache_read_mpki", + "MetricExpr": "((LL_CACHE_MISS_RD / INST_RETIRED) * 1000)", + "BriefDescription": "This metric measures the number of last level cache read accesses missed per thousand instructions executed.", + "MetricGroup": "MPKI;LL_Cache_Effectiveness", + "ScaleUnit": "1MPKI" }, { - "MetricExpr": "ipc / 5", - "BriefDescription": "IPC percentage of peak. The peak of IPC is 5.", - "MetricGroup": "PEutilization", - "MetricName": "ipc_rate", - "ScaleUnit": "100%" + "MetricName": "load_percentage", + "MetricExpr": "((LD_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures load operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "INST_RETIRED / CPU_CYCLES", - "BriefDescription": "Architecturally executed Instructions Per Cycle (IPC)", - "MetricGroup": "PEutilization", - "MetricName": "retired_ipc" + "ArchStdEvent": "retiring", + "MetricExpr": "(100 * ((OP_RETIRED / OP_SPEC) * (1 - (((STALL_SLOT) if (strcmp_cpuid_str(0x410fd493) | strcmp_cpuid_str(0x410fd490) ^ 1) else (STALL_SLOT - CPU_CYCLES)) / (CPU_CYCLES * #slots)))))" }, { - "MetricExpr": "INST_SPEC / CPU_CYCLES", - "BriefDescription": "Speculatively executed Instructions Per Cycle (IPC)", - "MetricGroup": "PEutilization", - "MetricName": "spec_ipc" + "MetricName": "scalar_fp_percentage", + "MetricExpr": "((VFP_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures scalar floating point operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "OP_RETIRED / OP_SPEC", - "BriefDescription": "Of all the micro-operations issued, what percentage are retired(committed)", - "MetricGroup": "PEutilization", - "MetricName": "retired_rate", - "ScaleUnit": "100%" + "MetricName": "simd_percentage", + "MetricExpr": "((ASE_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures advanced SIMD operations as a percentage of total operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "1 - OP_RETIRED / OP_SPEC", - "BriefDescription": "Of all the micro-operations issued, what percentage are not retired(committed)", - "MetricGroup": "PEutilization", - "MetricName": "wasted_rate", - "ScaleUnit": "100%" + "MetricName": "store_percentage", + "MetricExpr": "((ST_SPEC / INST_SPEC) * 100)", + "BriefDescription": "This metric measures store operations as a percentage of operations speculatively executed.", + "MetricGroup": "Operation_Mix", + "ScaleUnit": "1percent of operations" }, { - "MetricExpr": "OP_RETIRED / OP_SPEC * (1 - (STALL_SLOT if (#slots - 5) else (STALL_SLOT - CPU_CYCLES)) / (#slots * CPU_CYCLES))", - "BriefDescription": "The truly effective ratio of micro-operations executed by the CPU, which means that misprediction and stall are not included", - "MetricGroup": "PEutilization", - "MetricName": "cpu_utilization", - "ScaleUnit": "100%" + "MetricExpr": "L3D_CACHE_REFILL / INST_RETIRED * 1000", + "BriefDescription": "The rate of L3 D-Cache misses per kilo instructions", + "MetricGroup": "MPKI;L3_Cache_Effectiveness", + "MetricName": "l3d_cache_mpki", + "ScaleUnit": "1MPKI" }, { - "MetricExpr": "LD_SPEC / INST_SPEC", - "BriefDescription": "The rate of load instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "load_spec_rate", + "MetricExpr": "L3D_CACHE_REFILL / L3D_CACHE", + "BriefDescription": "The rate of L3 D-Cache misses to the overall L3 D-Cache", + "MetricGroup": "Miss_Ratio;L3_Cache_Effectiveness", + "MetricName": "l3d_cache_miss_rate", "ScaleUnit": "100%" }, { - "MetricExpr": "ST_SPEC / INST_SPEC", - "BriefDescription": "The rate of store instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "store_spec_rate", - "ScaleUnit": "100%" + "MetricExpr": "BR_RETIRED / INST_RETIRED * 1000", + "BriefDescription": "The rate of branches retired per kilo instructions", + "MetricGroup": "MPKI;Branch_Effectiveness", + "MetricName": "branch_pki", + "ScaleUnit": "1PKI" }, { - "MetricExpr": "DP_SPEC / INST_SPEC", - "BriefDescription": "The rate of integer data-processing instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "data_process_spec_rate", + "MetricExpr": "ipc / #slots", + "BriefDescription": "IPC percentage of peak. The peak of IPC is the number of slots.", + "MetricGroup": "General", + "MetricName": "ipc_rate", "ScaleUnit": "100%" }, { - "MetricExpr": "ASE_SPEC / INST_SPEC", - "BriefDescription": "The rate of advanced SIMD instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "advanced_simd_spec_rate", - "ScaleUnit": "100%" + "MetricExpr": "INST_SPEC / CPU_CYCLES", + "BriefDescription": "Speculatively executed Instructions Per Cycle (IPC)", + "MetricGroup": "General", + "MetricName": "spec_ipc" }, { - "MetricExpr": "VFP_SPEC / INST_SPEC", - "BriefDescription": "The rate of floating point instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "float_point_spec_rate", + "MetricExpr": "OP_RETIRED / OP_SPEC", + "BriefDescription": "Of all the micro-operations issued, what percentage are retired(committed)", + "MetricGroup": "General", + "MetricName": "retired_rate", "ScaleUnit": "100%" }, { - "MetricExpr": "CRYPTO_SPEC / INST_SPEC", - "BriefDescription": "The rate of crypto instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", - "MetricName": "crypto_spec_rate", + "MetricExpr": "1 - OP_RETIRED / OP_SPEC", + "BriefDescription": "Of all the micro-operations issued, what percentage are not retired(committed)", + "MetricGroup": "General", + "MetricName": "wasted_rate", "ScaleUnit": "100%" }, { "MetricExpr": "BR_IMMED_SPEC / INST_SPEC", - "BriefDescription": "The rate of branch immediate instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", + "BriefDescription": "The rate of branch immediate instructions speculatively executed to overall instructions speculatively executed", + "MetricGroup": "Operation_Mix", "MetricName": "branch_immed_spec_rate", "ScaleUnit": "100%" }, { "MetricExpr": "BR_RETURN_SPEC / INST_SPEC", - "BriefDescription": "The rate of procedure return instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", + "BriefDescription": "The rate of procedure return instructions speculatively executed to overall instructions speculatively executed", + "MetricGroup": "Operation_Mix", "MetricName": "branch_return_spec_rate", "ScaleUnit": "100%" }, { "MetricExpr": "BR_INDIRECT_SPEC / INST_SPEC", - "BriefDescription": "The rate of indirect branch instructions speculatively executed to overall instructions speclatively executed", - "MetricGroup": "InstructionMix", + "BriefDescription": "The rate of indirect branch instructions speculatively executed to overall instructions speculatively executed", + "MetricGroup": "Operation_Mix", "MetricName": "branch_indirect_spec_rate", "ScaleUnit": "100%" } diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/pipeline.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/pipeline.json deleted file mode 100644 index f9fae15f7555..000000000000 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/pipeline.json +++ /dev/null @@ -1,23 +0,0 @@ -[ - { - "ArchStdEvent": "STALL_FRONTEND" - }, - { - "ArchStdEvent": "STALL_BACKEND" - }, - { - "ArchStdEvent": "STALL" - }, - { - "ArchStdEvent": "STALL_SLOT_BACKEND" - }, - { - "ArchStdEvent": "STALL_SLOT_FRONTEND" - }, - { - "ArchStdEvent": "STALL_SLOT" - }, - { - "ArchStdEvent": "STALL_BACKEND_MEM" - } -] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/retired.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/retired.json new file mode 100644 index 000000000000..f297b049b62f --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/retired.json @@ -0,0 +1,30 @@ +[ + { + "ArchStdEvent": "SW_INCR", + "PublicDescription": "Counts software writes to the PMSWINC_EL0 (software PMU increment) register. The PMSWINC_EL0 register is a manually updated counter for use by application software.\n\nThis event could be used to measure any user program event, such as accesses to a particular data structure (by writing to the PMSWINC_EL0 register each time the data structure is accessed).\n\nTo use the PMSWINC_EL0 register and event, developers must insert instructions that write to the PMSWINC_EL0 register into the source code.\n\nSince the SW_INCR event records writes to the PMSWINC_EL0 register, there is no need to do a read/increment/write sequence to the PMSWINC_EL0 register." + }, + { + "ArchStdEvent": "INST_RETIRED", + "PublicDescription": "Counts instructions that have been architecturally executed." + }, + { + "ArchStdEvent": "CID_WRITE_RETIRED", + "PublicDescription": "Counts architecturally executed writes to the CONTEXTIDR register, which usually contain the kernel PID and can be output with hardware trace." + }, + { + "ArchStdEvent": "TTBR_WRITE_RETIRED", + "PublicDescription": "Counts architectural writes to TTBR0/1_EL1. If virtualization host extensions are enabled (by setting the HCR_EL2.E2H bit to 1), then accesses to TTBR0/1_EL1 that are redirected to TTBR0/1_EL2, or accesses to TTBR0/1_EL12, are counted. TTBRn registers are typically updated when the kernel is swapping user-space threads or applications." + }, + { + "ArchStdEvent": "BR_RETIRED", + "PublicDescription": "Counts architecturally executed branches, whether the branch is taken or not. Instructions that explicitly write to the PC are also counted." + }, + { + "ArchStdEvent": "BR_MIS_PRED_RETIRED", + "PublicDescription": "Counts branches counted by BR_RETIRED which were mispredicted and caused a pipeline flush." + }, + { + "ArchStdEvent": "OP_RETIRED", + "PublicDescription": "Counts micro-operations that are architecturally executed. This is a count of number of micro-operations retired from the commit queue in a single cycle." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spe.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spe.json index 20f2165c85fe..5de8b0f3a440 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spe.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spe.json @@ -1,14 +1,18 @@ [ { - "ArchStdEvent": "SAMPLE_POP" + "ArchStdEvent": "SAMPLE_POP", + "PublicDescription": "Counts statistical profiling sample population, the count of all operations that could be sampled but may or may not be chosen for sampling." }, { - "ArchStdEvent": "SAMPLE_FEED" + "ArchStdEvent": "SAMPLE_FEED", + "PublicDescription": "Counts statistical profiling samples taken for sampling." }, { - "ArchStdEvent": "SAMPLE_FILTRATE" + "ArchStdEvent": "SAMPLE_FILTRATE", + "PublicDescription": "Counts statistical profiling samples taken which are not removed by filtering." }, { - "ArchStdEvent": "SAMPLE_COLLISION" + "ArchStdEvent": "SAMPLE_COLLISION", + "PublicDescription": "Counts statistical profiling samples that have collided with a previous sample and so therefore not taken." } ] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spec_operation.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spec_operation.json new file mode 100644 index 000000000000..1af961f8a6c8 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/spec_operation.json @@ -0,0 +1,110 @@ +[ + { + "ArchStdEvent": "BR_MIS_PRED", + "PublicDescription": "Counts branches which are speculatively executed and mispredicted." + }, + { + "ArchStdEvent": "BR_PRED", + "PublicDescription": "Counts branches speculatively executed and were predicted right." + }, + { + "ArchStdEvent": "INST_SPEC", + "PublicDescription": "Counts operations that have been speculatively executed." + }, + { + "ArchStdEvent": "OP_SPEC", + "PublicDescription": "Counts micro-operations speculatively executed. This is the count of the number of micro-operations dispatched in a cycle." + }, + { + "ArchStdEvent": "UNALIGNED_LD_SPEC", + "PublicDescription": "Counts unaligned memory read operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses. The event does not count preload operations (PLD, PLI)." + }, + { + "ArchStdEvent": "UNALIGNED_ST_SPEC", + "PublicDescription": "Counts unaligned memory write operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses." + }, + { + "ArchStdEvent": "UNALIGNED_LDST_SPEC", + "PublicDescription": "Counts unaligned memory operations issued by the CPU. This event counts unaligned accesses (as defined by the actual instruction), even if they are subsequently issued as multiple aligned accesses." + }, + { + "ArchStdEvent": "LDREX_SPEC", + "PublicDescription": "Counts Load-Exclusive operations that have been speculatively executed. Eg: LDREX, LDX" + }, + { + "ArchStdEvent": "STREX_PASS_SPEC", + "PublicDescription": "Counts store-exclusive operations that have been speculatively executed and have successfully completed the store operation." + }, + { + "ArchStdEvent": "STREX_FAIL_SPEC", + "PublicDescription": "Counts store-exclusive operations that have been speculatively executed and have not successfully completed the store operation." + }, + { + "ArchStdEvent": "STREX_SPEC", + "PublicDescription": "Counts store-exclusive operations that have been speculatively executed." + }, + { + "ArchStdEvent": "LD_SPEC", + "PublicDescription": "Counts speculatively executed load operations including Single Instruction Multiple Data (SIMD) load operations." + }, + { + "ArchStdEvent": "ST_SPEC", + "PublicDescription": "Counts speculatively executed store operations including Single Instruction Multiple Data (SIMD) store operations." + }, + { + "ArchStdEvent": "DP_SPEC", + "PublicDescription": "Counts speculatively executed logical or arithmetic instructions such as MOV/MVN operations." + }, + { + "ArchStdEvent": "ASE_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD operations excluding load, store and move micro-operations that move data to or from SIMD (vector) registers." + }, + { + "ArchStdEvent": "VFP_SPEC", + "PublicDescription": "Counts speculatively executed floating point operations. This event does not count operations that move data to or from floating point (vector) registers." + }, + { + "ArchStdEvent": "PC_WRITE_SPEC", + "PublicDescription": "Counts speculatively executed operations which cause software changes of the PC. Those operations include all taken branch operations." + }, + { + "ArchStdEvent": "CRYPTO_SPEC", + "PublicDescription": "Counts speculatively executed cryptographic operations except for PMULL and VMULL operations." + }, + { + "ArchStdEvent": "BR_IMMED_SPEC", + "PublicDescription": "Counts immediate branch operations which are speculatively executed." + }, + { + "ArchStdEvent": "BR_RETURN_SPEC", + "PublicDescription": "Counts procedure return operations (RET) which are speculatively executed." + }, + { + "ArchStdEvent": "BR_INDIRECT_SPEC", + "PublicDescription": "Counts indirect branch operations including procedure returns, which are speculatively executed. This includes operations that force a software change of the PC, other than exception-generating operations. Eg: BR Xn, RET" + }, + { + "ArchStdEvent": "ISB_SPEC", + "PublicDescription": "Counts ISB operations that are executed." + }, + { + "ArchStdEvent": "DSB_SPEC", + "PublicDescription": "Counts DSB operations that are speculatively issued to Load/Store unit in the CPU." + }, + { + "ArchStdEvent": "DMB_SPEC", + "PublicDescription": "Counts DMB operations that are speculatively issued to the Load/Store unit in the CPU. This event does not count implied barriers from load acquire/store release operations." + }, + { + "ArchStdEvent": "RC_LD_SPEC", + "PublicDescription": "Counts any load acquire operations that are speculatively executed. Eg: LDAR, LDARH, LDARB" + }, + { + "ArchStdEvent": "RC_ST_SPEC", + "PublicDescription": "Counts any store release operations that are speculatively executed. Eg: STLR, STLRH, STLRB'" + }, + { + "ArchStdEvent": "ASE_INST_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD operations." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/stall.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/stall.json new file mode 100644 index 000000000000..bbbebc805034 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/stall.json @@ -0,0 +1,30 @@ +[ + { + "ArchStdEvent": "STALL_FRONTEND", + "PublicDescription": "Counts cycles when frontend could not send any micro-operations to the rename stage because of frontend resource stalls caused by fetch memory latency or branch prediction flow stalls. All the frontend slots were empty during the cycle when this event counts." + }, + { + "ArchStdEvent": "STALL_BACKEND", + "PublicDescription": "Counts cycles whenever the rename unit is unable to send any micro-operations to the backend of the pipeline because of backend resource constraints. Backend resource constraints can include issue stage fullness, execution stage fullness, or other internal pipeline resource fullness. All the backend slots were empty during the cycle when this event counts." + }, + { + "ArchStdEvent": "STALL", + "PublicDescription": "Counts cycles when no operations are sent to the rename unit from the frontend or from the rename unit to the backend for any reason (either frontend or backend stall)." + }, + { + "ArchStdEvent": "STALL_SLOT_BACKEND", + "PublicDescription": "Counts slots per cycle in which no operations are sent from the rename unit to the backend due to backend resource constraints." + }, + { + "ArchStdEvent": "STALL_SLOT_FRONTEND", + "PublicDescription": "Counts slots per cycle in which no operations are sent to the rename unit from the frontend due to frontend resource constraints." + }, + { + "ArchStdEvent": "STALL_SLOT", + "PublicDescription": "Counts slots per cycle in which no operations are sent to the rename unit from the frontend or from the rename unit to the backend for any reason (either frontend or backend stall)." + }, + { + "ArchStdEvent": "STALL_BACKEND_MEM", + "PublicDescription": "Counts cycles when the backend is stalled because there is a pending demand load request in progress in the last level core cache." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/sve.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/sve.json new file mode 100644 index 000000000000..51dab48cb2ba --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/sve.json @@ -0,0 +1,50 @@ +[ + { + "ArchStdEvent": "SVE_INST_SPEC", + "PublicDescription": "Counts speculatively executed operations that are SVE operations." + }, + { + "ArchStdEvent": "SVE_PRED_SPEC", + "PublicDescription": "Counts speculatively executed predicated SVE operations." + }, + { + "ArchStdEvent": "SVE_PRED_EMPTY_SPEC", + "PublicDescription": "Counts speculatively executed predicated SVE operations with no active predicate elements." + }, + { + "ArchStdEvent": "SVE_PRED_FULL_SPEC", + "PublicDescription": "Counts speculatively executed predicated SVE operations with all predicate elements active." + }, + { + "ArchStdEvent": "SVE_PRED_PARTIAL_SPEC", + "PublicDescription": "Counts speculatively executed predicated SVE operations with at least one but not all active predicate elements." + }, + { + "ArchStdEvent": "SVE_PRED_NOT_FULL_SPEC", + "PublicDescription": "Counts speculatively executed predicated SVE operations with at least one non active predicate elements." + }, + { + "ArchStdEvent": "SVE_LDFF_SPEC", + "PublicDescription": "Counts speculatively executed SVE first fault or non-fault load operations." + }, + { + "ArchStdEvent": "SVE_LDFF_FAULT_SPEC", + "PublicDescription": "Counts speculatively executed SVE first fault or non-fault load operations that clear at least one bit in the FFR." + }, + { + "ArchStdEvent": "ASE_SVE_INT8_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type an 8-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT16_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 16-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT32_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 32-bit integer." + }, + { + "ArchStdEvent": "ASE_SVE_INT64_SPEC", + "PublicDescription": "Counts speculatively executed Advanced SIMD or SVE integer operations with the largest data type a 64-bit integer." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/tlb.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/tlb.json new file mode 100644 index 000000000000..b550af1831f5 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/tlb.json @@ -0,0 +1,66 @@ +[ + { + "ArchStdEvent": "L1I_TLB_REFILL", + "PublicDescription": "Counts level 1 instruction TLB refills from any Instruction fetch. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL", + "PublicDescription": "Counts level 1 data TLB accesses that resulted in TLB refills. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count on an access from an AT(address translation) instruction." + }, + { + "ArchStdEvent": "L1D_TLB", + "PublicDescription": "Counts level 1 data TLB accesses caused by any memory load or store operation. Note that load or store instructions can be broken up into multiple memory operations. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1I_TLB", + "PublicDescription": "Counts level 1 instruction TLB accesses, whether the access hits or misses in the TLB. This event counts both demand accesses and prefetch or preload generated accesses." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL", + "PublicDescription": "Counts level 2 TLB refills caused by memory operations from both data and instruction fetch, except for those caused by TLB maintenance operations and hardware prefetches." + }, + { + "ArchStdEvent": "L2D_TLB", + "PublicDescription": "Counts level 2 TLB accesses except those caused by TLB maintenance operations." + }, + { + "ArchStdEvent": "DTLB_WALK", + "PublicDescription": "Counts data memory translation table walks caused by a miss in the L2 TLB driven by a memory access. Note that partial translations that also cause a table walk are counted. This event does not count table walks caused by TLB maintenance operations." + }, + { + "ArchStdEvent": "ITLB_WALK", + "PublicDescription": "Counts instruction memory translation table walks caused by a miss in the L2 TLB driven by a memory access. Partial translations that also cause a table walk are counted. This event does not count table walks caused by TLB maintenance operations." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL_RD", + "PublicDescription": "Counts level 1 data TLB refills caused by memory read operations. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the translation table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count on an access from an Address Translation (AT) instruction." + }, + { + "ArchStdEvent": "L1D_TLB_REFILL_WR", + "PublicDescription": "Counts level 1 data TLB refills caused by data side memory write operations. If there are multiple misses in the TLB that are resolved by the refill, then this event only counts once. This event counts for refills caused by preload instructions or hardware prefetch accesses. This event counts regardless of whether the miss hits in L2 or results in a translation table walk. This event will not count if the table walk results in a fault (such as a translation or access fault), since there is no new translation created for the TLB. This event will not count with an access from an Address Translation (AT) instruction." + }, + { + "ArchStdEvent": "L1D_TLB_RD", + "PublicDescription": "Counts level 1 data TLB accesses caused by memory read operations. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L1D_TLB_WR", + "PublicDescription": "Counts any L1 data side TLB accesses caused by memory write operations. This event counts whether the access hits or misses in the TLB. This event does not count TLB maintenance operations." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL_RD", + "PublicDescription": "Counts level 2 TLB refills caused by memory read operations from both data and instruction fetch except for those caused by TLB maintenance operations or hardware prefetches." + }, + { + "ArchStdEvent": "L2D_TLB_REFILL_WR", + "PublicDescription": "Counts level 2 TLB refills caused by memory write operations from both data and instruction fetch except for those caused by TLB maintenance operations." + }, + { + "ArchStdEvent": "L2D_TLB_RD", + "PublicDescription": "Counts level 2 TLB accesses caused by memory read operations from both data and instruction fetch except for those caused by TLB maintenance operations." + }, + { + "ArchStdEvent": "L2D_TLB_WR", + "PublicDescription": "Counts level 2 TLB accesses caused by memory write operations from both data and instruction fetch except for those caused by TLB maintenance operations." + } +] diff --git a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/trace.json b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/trace.json index 3116135c59e2..98f6fabfebc7 100644 --- a/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/trace.json +++ b/tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/trace.json @@ -1,29 +1,38 @@ [ { - "ArchStdEvent": "TRB_WRAP" + "ArchStdEvent": "TRB_WRAP", + "PublicDescription": "This event is generated each time the current write pointer is wrapped to the base pointer." }, { - "ArchStdEvent": "TRCEXTOUT0" + "ArchStdEvent": "TRCEXTOUT0", + "PublicDescription": "This event is generated each time an event is signaled by ETE external event 0." }, { - "ArchStdEvent": "TRCEXTOUT1" + "ArchStdEvent": "TRCEXTOUT1", + "PublicDescription": "This event is generated each time an event is signaled by ETE external event 1." }, { - "ArchStdEvent": "TRCEXTOUT2" + "ArchStdEvent": "TRCEXTOUT2", + "PublicDescription": "This event is generated each time an event is signaled by ETE external event 2." }, { - "ArchStdEvent": "TRCEXTOUT3" + "ArchStdEvent": "TRCEXTOUT3", + "PublicDescription": "This event is generated each time an event is signaled by ETE external event 3." }, { - "ArchStdEvent": "CTI_TRIGOUT4" + "ArchStdEvent": "CTI_TRIGOUT4", + "PublicDescription": "This event is generated each time an event is signaled on CTI output trigger 4." }, { - "ArchStdEvent": "CTI_TRIGOUT5" + "ArchStdEvent": "CTI_TRIGOUT5", + "PublicDescription": "This event is generated each time an event is signaled on CTI output trigger 5." }, { - "ArchStdEvent": "CTI_TRIGOUT6" + "ArchStdEvent": "CTI_TRIGOUT6", + "PublicDescription": "This event is generated each time an event is signaled on CTI output trigger 6." }, { - "ArchStdEvent": "CTI_TRIGOUT7" + "ArchStdEvent": "CTI_TRIGOUT7", + "PublicDescription": "This event is generated each time an event is signaled on CTI output trigger 7." } ] diff --git a/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/ali_drw.json b/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/ali_drw.json new file mode 100644 index 000000000000..e21c469a8ef0 --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/ali_drw.json @@ -0,0 +1,373 @@ +[ + { + "BriefDescription": "A Write or Read Op at HIF interface. The unit is 64B.", + "ConfigCode": "0x0", + "EventName": "hif_rd_or_wr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Write Op at HIF interface. The unit is 64B.", + "ConfigCode": "0x1", + "EventName": "hif_wr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Read Op at HIF interface. The unit is 64B.", + "ConfigCode": "0x2", + "EventName": "hif_rd", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Read-Modify-Write Op at HIF interface. The unit is 64B.", + "ConfigCode": "0x3", + "EventName": "hif_rmw", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A high priority Read at HIF interface. The unit is 64B.", + "ConfigCode": "0x4", + "EventName": "hif_hi_pri_rd", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A write data cycle at DFI interface (to DRAM).", + "ConfigCode": "0x7", + "EventName": "dfi_wr_data_cycles", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A read data cycle at DFI interface (to DRAM).", + "ConfigCode": "0x8", + "EventName": "dfi_rd_data_cycles", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A high priority read becomes critical.", + "ConfigCode": "0x9", + "EventName": "hpr_xact_when_critical", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A low priority read becomes critical.", + "ConfigCode": "0xA", + "EventName": "lpr_xact_when_critical", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A write becomes critical.", + "ConfigCode": "0xB", + "EventName": "wr_xact_when_critical", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "An Activate(ACT) command to DRAM.", + "ConfigCode": "0xC", + "EventName": "op_is_activate", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Read or Write CAS command to DRAM.", + "ConfigCode": "0xD", + "EventName": "op_is_rd_or_wr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "An Activate(ACT) command for read to DRAM.", + "ConfigCode": "0xE", + "EventName": "op_is_rd_activate", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Read CAS command to DRAM.", + "ConfigCode": "0xF", + "EventName": "op_is_rd", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Write CAS command to DRAM.", + "ConfigCode": "0x10", + "EventName": "op_is_wr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Masked Write command to DRAM.", + "ConfigCode": "0x11", + "EventName": "op_is_mwr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Precharge(PRE) command to DRAM.", + "ConfigCode": "0x12", + "EventName": "op_is_precharge", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Precharge(PRE) required by read or write.", + "ConfigCode": "0x13", + "EventName": "precharge_for_rdwr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Precharge(PRE) required by other conditions.", + "ConfigCode": "0x14", + "EventName": "precharge_for_other", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A read-write turnaround.", + "ConfigCode": "0x15", + "EventName": "rdwr_transitions", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A write combine(merge) in write data buffer.", + "ConfigCode": "0x16", + "EventName": "write_combine", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Write-After-Read hazard.", + "ConfigCode": "0x17", + "EventName": "war_hazard", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Read-After-Write hazard.", + "ConfigCode": "0x18", + "EventName": "raw_hazard", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Write-After-Write hazard.", + "ConfigCode": "0x19", + "EventName": "waw_hazard", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank0 enters self-refresh(SRE).", + "ConfigCode": "0x1A", + "EventName": "op_is_enter_selfref_rk0", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank1 enters self-refresh(SRE).", + "ConfigCode": "0x1B", + "EventName": "op_is_enter_selfref_rk1", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank2 enters self-refresh(SRE).", + "ConfigCode": "0x1C", + "EventName": "op_is_enter_selfref_rk2", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank3 enters self-refresh(SRE).", + "ConfigCode": "0x1D", + "EventName": "op_is_enter_selfref_rk3", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank0 enters power-down(PDE).", + "ConfigCode": "0x1E", + "EventName": "op_is_enter_powerdown_rk0", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank1 enters power-down(PDE).", + "ConfigCode": "0x1F", + "EventName": "op_is_enter_powerdown_rk1", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank2 enters power-down(PDE).", + "ConfigCode": "0x20", + "EventName": "op_is_enter_powerdown_rk2", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "Rank3 enters power-down(PDE).", + "ConfigCode": "0x21", + "EventName": "op_is_enter_powerdown_rk3", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A cycle that Rank0 stays in self-refresh mode.", + "ConfigCode": "0x26", + "EventName": "selfref_mode_rk0", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A cycle that Rank1 stays in self-refresh mode.", + "ConfigCode": "0x27", + "EventName": "selfref_mode_rk1", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A cycle that Rank2 stays in self-refresh mode.", + "ConfigCode": "0x28", + "EventName": "selfref_mode_rk2", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A cycle that Rank3 stays in self-refresh mode.", + "ConfigCode": "0x29", + "EventName": "selfref_mode_rk3", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "An auto-refresh(REF) command to DRAM.", + "ConfigCode": "0x2A", + "EventName": "op_is_refresh", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A critical auto-refresh(REF) command to DRAM.", + "ConfigCode": "0x2B", + "EventName": "op_is_crit_ref", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "An MRR or MRW command to DRAM.", + "ConfigCode": "0x2D", + "EventName": "op_is_load_mode", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A ZQCal command to DRAM.", + "ConfigCode": "0x2E", + "EventName": "op_is_zqcl", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "At least one entry in read queue reaches the visible window limit.", + "ConfigCode": "0x30", + "EventName": "visible_window_limit_reached_rd", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "At least one entry in write queue reaches the visible window limit.", + "ConfigCode": "0x31", + "EventName": "visible_window_limit_reached_wr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A DQS Oscillator MPC command to DRAM.", + "ConfigCode": "0x34", + "EventName": "op_is_dqsosc_mpc", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A DQS Oscillator MRR command to DRAM.", + "ConfigCode": "0x35", + "EventName": "op_is_dqsosc_mrr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A Temperature Compensated Refresh(TCR) MRR command to DRAM.", + "ConfigCode": "0x36", + "EventName": "op_is_tcr_mrr", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A ZQCal Start command to DRAM.", + "ConfigCode": "0x37", + "EventName": "op_is_zqstart", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A ZQCal Latch command to DRAM.", + "ConfigCode": "0x38", + "EventName": "op_is_zqlatch", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A packet at CHI TXREQ interface (request).", + "ConfigCode": "0x39", + "EventName": "chi_txreq", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A packet at CHI TXDAT interface (read data).", + "ConfigCode": "0x3A", + "EventName": "chi_txdat", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A packet at CHI RXDAT interface (write data).", + "ConfigCode": "0x3B", + "EventName": "chi_rxdat", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A packet at CHI RXRSP interface.", + "ConfigCode": "0x3C", + "EventName": "chi_rxrsp", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "A violation detected in TZC.", + "ConfigCode": "0x3D", + "EventName": "tsz_vio", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "BriefDescription": "The ddr cycles.", + "ConfigCode": "0x80", + "EventName": "ddr_cycles", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/metrics.json b/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/metrics.json new file mode 100644 index 000000000000..bc865b374b6a --- /dev/null +++ b/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/metrics.json @@ -0,0 +1,20 @@ +[ + { + "MetricName": "ddr_read_bandwidth.all", + "BriefDescription": "The ddr read bandwidth(MB/s).", + "MetricGroup": "ali_drw", + "MetricExpr": "hif_rd * 64 / 1e6 / duration_time", + "ScaleUnit": "1MB/s", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + }, + { + "MetricName": "ddr_write_bandwidth.all", + "BriefDescription": "The ddr write bandwidth(MB/s).", + "MetricGroup": "ali_drw", + "MetricExpr": "(hif_wr + hif_rmw) * 64 / 1e6 / duration_time", + "ScaleUnit": "1MB/s", + "Unit": "ali_drw", + "Compat": "ali_drw_pmu" + } +] diff --git a/tools/perf/pmu-events/arch/arm64/sbsa.json b/tools/perf/pmu-events/arch/arm64/sbsa.json index f90b338261ac..4eed79a28f6e 100644 --- a/tools/perf/pmu-events/arch/arm64/sbsa.json +++ b/tools/perf/pmu-events/arch/arm64/sbsa.json @@ -1,34 +1,34 @@ [ { - "MetricExpr": "stall_slot_frontend / (#slots * cpu_cycles)", - "BriefDescription": "Frontend bound L1 topdown metric", + "MetricExpr": "100 * (stall_slot_frontend / (#slots * cpu_cycles))", + "BriefDescription": "This metric is the percentage of total slots that were stalled due to resource constraints in the frontend of the processor.", "DefaultMetricgroupName": "TopdownL1", "MetricGroup": "Default;TopdownL1", "MetricName": "frontend_bound", - "ScaleUnit": "100%" + "ScaleUnit": "1percent of slots" }, { - "MetricExpr": "(1 - op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))", - "BriefDescription": "Bad speculation L1 topdown metric", + "MetricExpr": "100 * ((1 - op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles)))", + "BriefDescription": "This metric is the percentage of total slots that executed operations and didn't retire due to a pipeline flush.\nThis indicates cycles that were utilized but inefficiently.", "DefaultMetricgroupName": "TopdownL1", "MetricGroup": "Default;TopdownL1", "MetricName": "bad_speculation", - "ScaleUnit": "100%" + "ScaleUnit": "1percent of slots" }, { - "MetricExpr": "(op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))", - "BriefDescription": "Retiring L1 topdown metric", + "MetricExpr": "100 * ((op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles)))", + "BriefDescription": "This metric is the percentage of total slots that retired operations, which indicates cycles that were utilized efficiently.", "DefaultMetricgroupName": "TopdownL1", "MetricGroup": "Default;TopdownL1", "MetricName": "retiring", - "ScaleUnit": "100%" + "ScaleUnit": "1percent of slots" }, { - "MetricExpr": "stall_slot_backend / (#slots * cpu_cycles)", - "BriefDescription": "Backend Bound L1 topdown metric", + "MetricExpr": "100 * (stall_slot_backend / (#slots * cpu_cycles))", + "BriefDescription": "This metric is the percentage of total slots that were stalled due to resource constraints in the backend of the processor.", "DefaultMetricgroupName": "TopdownL1", "MetricGroup": "Default;TopdownL1", "MetricName": "backend_bound", - "ScaleUnit": "100%" + "ScaleUnit": "1percent of slots" } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/cache.json b/tools/perf/pmu-events/arch/powerpc/power10/cache.json index 605be14f441c..839ae26945fb 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/cache.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/cache.json @@ -1,53 +1,8 @@ [ { - "EventCode": "0x1003C", - "EventName": "PM_EXEC_STALL_DMISS_L2L3", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from either the local L2 or local L3." - }, - { - "EventCode": "0x1E054", - "EventName": "PM_EXEC_STALL_DMISS_L21_L31", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from another core's L2 or L3 on the same chip." - }, - { - "EventCode": "0x34054", - "EventName": "PM_EXEC_STALL_DMISS_L2L3_NOCONFLICT", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local L2 or local L3, without a dispatch conflict." - }, - { - "EventCode": "0x34056", - "EventName": "PM_EXEC_STALL_LOAD_FINISH", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was finishing a load after its data was reloaded from a data source beyond the local L1; cycles in which the LSU was processing an L1-hit; cycles in which the NTF instruction merged with another load in the LMQ; cycles in which the NTF instruction is waiting for a data reload for a load miss, but the data comes back with a non-NTF instruction." - }, - { - "EventCode": "0x3006C", - "EventName": "PM_RUN_CYC_SMT2_MODE", - "BriefDescription": "Cycles when this thread's run latch is set and the core is in SMT2 mode." - }, - { "EventCode": "0x300F4", "EventName": "PM_RUN_INST_CMPL_CONC", - "BriefDescription": "PowerPC instructions completed by this thread when all threads in the core had the run-latch set." - }, - { - "EventCode": "0x4C016", - "EventName": "PM_EXEC_STALL_DMISS_L2L3_CONFLICT", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local L2 or local L3, with a dispatch conflict." - }, - { - "EventCode": "0x4D014", - "EventName": "PM_EXEC_STALL_LOAD", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a load instruction executing in the Load Store Unit." - }, - { - "EventCode": "0x4D016", - "EventName": "PM_EXEC_STALL_PTESYNC", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a PTESYNC instruction executing in the Load Store Unit." - }, - { - "EventCode": "0x401EA", - "EventName": "PM_THRESH_EXC_128", - "BriefDescription": "Threshold counter exceeded a value of 128." + "BriefDescription": "PowerPC instruction completed by this thread when all threads in the core had the run-latch set." }, { "EventCode": "0x400F6", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/floating_point.json b/tools/perf/pmu-events/arch/powerpc/power10/floating_point.json index 54acb55e2c8c..e816cd10c129 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/floating_point.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/floating_point.json @@ -1,7 +1,67 @@ [ { - "EventCode": "0x4016E", - "EventName": "PM_THRESH_NOT_MET", - "BriefDescription": "Threshold counter did not meet threshold." + "EventCode": "0x100F4", + "EventName": "PM_FLOP_CMPL", + "BriefDescription": "Floating Point Operations Completed. Includes any type. It counts once for each 1, 2, 4 or 8 flop instruction. Use PM_1|2|4|8_FLOP_CMPL events to count flops." + }, + { + "EventCode": "0x45050", + "EventName": "PM_1FLOP_CMPL", + "BriefDescription": "One floating point instruction completed (fadd, fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg)." + }, + { + "EventCode": "0x45052", + "EventName": "PM_4FLOP_CMPL", + "BriefDescription": "Four floating point instruction completed (fadd, fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg)." + }, + { + "EventCode": "0x45054", + "EventName": "PM_FMA_CMPL", + "BriefDescription": "Two floating point instruction completed (FMA class of instructions: fmadd, fnmadd, fmsub, fnmsub). Scalar instructions only." + }, + { + "EventCode": "0x45056", + "EventName": "PM_SCALAR_FLOP_CMPL", + "BriefDescription": "Scalar floating point instruction completed." + }, + { + "EventCode": "0x4505A", + "EventName": "PM_SP_FLOP_CMPL", + "BriefDescription": "Single Precision floating point instruction completed." + }, + { + "EventCode": "0x4505C", + "EventName": "PM_MATH_FLOP_CMPL", + "BriefDescription": "Math floating point instruction completed." + }, + { + "EventCode": "0x4D052", + "EventName": "PM_2FLOP_CMPL", + "BriefDescription": "Double Precision vector version of fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg completed." + }, + { + "EventCode": "0x4D054", + "EventName": "PM_8FLOP_CMPL", + "BriefDescription": "Four Double Precision vector instruction completed." + }, + { + "EventCode": "0x4D056", + "EventName": "PM_NON_FMA_FLOP_CMPL", + "BriefDescription": "Non FMA instruction completed." + }, + { + "EventCode": "0x4D058", + "EventName": "PM_VECTOR_FLOP_CMPL", + "BriefDescription": "Vector floating point instruction completed." + }, + { + "EventCode": "0x4D05A", + "EventName": "PM_NON_MATH_FLOP_CMPL", + "BriefDescription": "Non Math instruction completed." + }, + { + "EventCode": "0x4D05C", + "EventName": "PM_DPP_FLOP_CMPL", + "BriefDescription": "Double-Precision or Quad-Precision instruction completed." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json index 558f9530f54e..5977f5e64212 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json @@ -1,43 +1,13 @@ [ { - "EventCode": "0x10004", - "EventName": "PM_EXEC_STALL_TRANSLATION", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline suffered a TLB miss or ERAT miss and waited for it to resolve." + "EventCode": "0x1D054", + "EventName": "PM_DTLB_HIT_2M", + "BriefDescription": "Data TLB hit (DERAT reload) page size 2M. Implies radix translation. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x10006", - "EventName": "PM_DISP_STALL_HELD_OTHER_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch for any other reason." - }, - { - "EventCode": "0x10010", - "EventName": "PM_PMC4_OVERFLOW", - "BriefDescription": "The event selected for PMC4 caused the event counter to overflow." - }, - { - "EventCode": "0x10020", - "EventName": "PM_PMC4_REWIND", - "BriefDescription": "The speculative event selected for PMC4 rewinds and the counter for PMC4 is not charged." - }, - { - "EventCode": "0x10038", - "EventName": "PM_DISP_STALL_TRANSLATION", - "BriefDescription": "Cycles when dispatch was stalled for this thread because the MMU was handling a translation miss." - }, - { - "EventCode": "0x1003A", - "EventName": "PM_DISP_STALL_BR_MPRED_IC_L2", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L2 after suffering a branch mispredict." - }, - { - "EventCode": "0x1D05E", - "EventName": "PM_DISP_STALL_HELD_HALT_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch because of power management." - }, - { - "EventCode": "0x1E050", - "EventName": "PM_DISP_STALL_HELD_STF_MAPPER_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch because the STF mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR." + "EventCode": "0x1D058", + "EventName": "PM_ITLB_HIT_64K", + "BriefDescription": "Instruction TLB hit (IERAT reload) page size 64K. When MMCR1[17]=0 this event counts only for demand misses. When MMCR1[17]=1 this event includes demand misses and prefetches." }, { "EventCode": "0x1F054", @@ -45,21 +15,6 @@ "BriefDescription": "The PTE required by the instruction was resident in the TLB (data TLB access). When MMCR1[16]=0 this event counts only demand hits. When MMCR1[16]=1 this event includes demand and prefetch. Applies to both HPT and RPT." }, { - "EventCode": "0x10064", - "EventName": "PM_DISP_STALL_IC_L2", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L2." - }, - { - "EventCode": "0x101E8", - "EventName": "PM_THRESH_EXC_256", - "BriefDescription": "Threshold counter exceeded a count of 256." - }, - { - "EventCode": "0x101EC", - "EventName": "PM_THRESH_MET", - "BriefDescription": "Threshold exceeded." - }, - { "EventCode": "0x100F2", "EventName": "PM_1PLUS_PPC_CMPL", "BriefDescription": "Cycles in which at least one instruction is completed by this thread." @@ -67,57 +22,7 @@ { "EventCode": "0x100F6", "EventName": "PM_IERAT_MISS", - "BriefDescription": "IERAT Reloaded to satisfy an IERAT miss. All page sizes are counted by this event." - }, - { - "EventCode": "0x100F8", - "EventName": "PM_DISP_STALL_CYC", - "BriefDescription": "Cycles the ICT has no itags assigned to this thread (no instructions were dispatched during these cycles)." - }, - { - "EventCode": "0x20006", - "EventName": "PM_DISP_STALL_HELD_ISSQ_FULL_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch due to Issue queue full. Includes issue queue and branch queue." - }, - { - "EventCode": "0x20114", - "EventName": "PM_MRK_L2_RC_DISP", - "BriefDescription": "Marked instruction RC dispatched in L2." - }, - { - "EventCode": "0x2C010", - "EventName": "PM_EXEC_STALL_LSU", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the Load Store Unit. This does not include simple fixed point instructions." - }, - { - "EventCode": "0x2C016", - "EventName": "PM_DISP_STALL_IERAT_ONLY_MISS", - "BriefDescription": "Cycles when dispatch was stalled while waiting to resolve an instruction ERAT miss." - }, - { - "EventCode": "0x2C01E", - "EventName": "PM_DISP_STALL_BR_MPRED_IC_L3", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L3 after suffering a branch mispredict." - }, - { - "EventCode": "0x2D01A", - "EventName": "PM_DISP_STALL_IC_MISS", - "BriefDescription": "Cycles when dispatch was stalled for this thread due to an Icache Miss." - }, - { - "EventCode": "0x2E018", - "EventName": "PM_DISP_STALL_FETCH", - "BriefDescription": "Cycles when dispatch was stalled for this thread because Fetch was being held." - }, - { - "EventCode": "0x2E01A", - "EventName": "PM_DISP_STALL_HELD_XVFC_MAPPER_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch because the XVFC mapper/SRB was full." - }, - { - "EventCode": "0x2C142", - "EventName": "PM_MRK_XFER_FROM_SRC_PMC2", - "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[15:27]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." + "BriefDescription": "IERAT Reloaded to satisfy an IERAT miss. All page sizes are counted by this event. This event only counts instruction demand access." }, { "EventCode": "0x24050", @@ -135,11 +40,6 @@ "BriefDescription": "Branch Taken instruction completed." }, { - "EventCode": "0x30004", - "EventName": "PM_DISP_STALL_FLUSH", - "BriefDescription": "Cycles when dispatch was stalled because of a flush that happened to an instruction(s) that was not yet NTC. PM_EXEC_STALL_NTC_FLUSH only includes instructions that were flushed after becoming NTC." - }, - { "EventCode": "0x3000A", "EventName": "PM_DISP_STALL_ITLB_MISS", "BriefDescription": "Cycles when dispatch was stalled while waiting to resolve an instruction TLB miss." @@ -150,59 +50,24 @@ "BriefDescription": "The instruction that was next to complete (oldest in the pipeline) did not complete because it suffered a flush." }, { - "EventCode": "0x30014", - "EventName": "PM_EXEC_STALL_STORE", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a store instruction executing in the Load Store Unit." - }, - { - "EventCode": "0x30018", - "EventName": "PM_DISP_STALL_HELD_SCOREBOARD_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch while waiting on the Scoreboard. This event combines VSCR and FPSCR together." - }, - { - "EventCode": "0x30026", - "EventName": "PM_EXEC_STALL_STORE_MISS", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a store whose cache line was not resident in the L1 and was waiting for allocation of the missing line into the L1." - }, - { - "EventCode": "0x3012A", - "EventName": "PM_MRK_L2_RC_DONE", - "BriefDescription": "L2 RC machine completed the transaction for the marked instruction." - }, - { "EventCode": "0x3F046", "EventName": "PM_ITLB_HIT_1G", "BriefDescription": "Instruction TLB hit (IERAT reload) page size 1G, which implies Radix Page Table translation is in use. When MMCR1[17]=0 this event counts only for demand misses. When MMCR1[17]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x34058", - "EventName": "PM_DISP_STALL_BR_MPRED_ICMISS", - "BriefDescription": "Cycles when dispatch was stalled after a mispredicted branch resulted in an instruction cache miss." - }, - { - "EventCode": "0x3D05C", - "EventName": "PM_DISP_STALL_HELD_RENAME_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch because the mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR and XVFC." - }, - { - "EventCode": "0x3E052", - "EventName": "PM_DISP_STALL_IC_L3", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L3." + "EventCode": "0x3C05A", + "EventName": "PM_DTLB_HIT_64K", + "BriefDescription": "Data TLB hit (DERAT reload) page size 64K. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { "EventCode": "0x3E054", "EventName": "PM_LD_MISS_L1", - "BriefDescription": "Load Missed L1, counted at execution time (can be greater than loads finished). LMQ merges are not included in this count. i.e. if a load instruction misses on an address that is already allocated on the LMQ, this event will not increment for that load). Note that this count is per slice, so if a load spans multiple slices this event will increment multiple times for a single load." - }, - { - "EventCode": "0x301EA", - "EventName": "PM_THRESH_EXC_1024", - "BriefDescription": "Threshold counter exceeded a value of 1024." + "BriefDescription": "Load missed L1, counted at finish time. LMQ merges are not included in this count. i.e. if a load instruction misses on an address that is already allocated on the LMQ, this event will not increment for that load). Note that this count is per slice, so if a load spans multiple slices this event will increment multiple times for a single load." }, { "EventCode": "0x300FA", "EventName": "PM_INST_FROM_L3MISS", - "BriefDescription": "The processor's instruction cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss." + "BriefDescription": "The processor's instruction cache was reloaded from beyond the local core's L3 due to a demand miss." }, { "EventCode": "0x40006", @@ -210,38 +75,18 @@ "BriefDescription": "Cycles in which an instruction or group of instructions were cancelled after being issued. This event increments once per occurrence, regardless of how many instructions are included in the issue group." }, { - "EventCode": "0x40116", - "EventName": "PM_MRK_LARX_FIN", - "BriefDescription": "Marked load and reserve instruction (LARX) finished. LARX and STCX are instructions used to acquire a lock." - }, - { - "EventCode": "0x4C010", - "EventName": "PM_DISP_STALL_BR_MPRED_IC_L3MISS", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from sources beyond the local L3 after suffering a mispredicted branch." - }, - { - "EventCode": "0x4D01E", - "EventName": "PM_DISP_STALL_BR_MPRED", - "BriefDescription": "Cycles when dispatch was stalled for this thread due to a mispredicted branch." - }, - { - "EventCode": "0x4E010", - "EventName": "PM_DISP_STALL_IC_L3MISS", - "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from any source beyond the local L3." - }, - { - "EventCode": "0x4E01A", - "EventName": "PM_DISP_STALL_HELD_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch for any reason." + "EventCode": "0x44056", + "EventName": "PM_VECTOR_ST_CMPL", + "BriefDescription": "Vector store instruction completed." }, { - "EventCode": "0x4003C", - "EventName": "PM_DISP_STALL_HELD_SYNC_CYC", - "BriefDescription": "Cycles in which the NTC instruction is held at dispatch because of a synchronizing instruction that requires the ICT to be empty before dispatch." + "EventCode": "0x4E054", + "EventName": "PM_DTLB_HIT_1G", + "BriefDescription": "Data TLB hit (DERAT reload) page size 1G. Implies radix translation. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x44056", - "EventName": "PM_VECTOR_ST_CMPL", - "BriefDescription": "Vector store instructions completed." + "EventCode": "0x400FC", + "EventName": "PM_ITLB_MISS", + "BriefDescription": "Instruction TLB reload (after a miss), all page sizes. Includes only demand misses." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/marked.json b/tools/perf/pmu-events/arch/powerpc/power10/marked.json index 58b5dfe3a273..78f71a9eadfd 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/marked.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/marked.json @@ -1,15 +1,35 @@ [ { - "EventCode": "0x1002C", - "EventName": "PM_LD_PREFETCH_CACHE_LINE_MISS", - "BriefDescription": "The L1 cache was reloaded with a line that fulfills a prefetch request." - }, - { "EventCode": "0x10132", "EventName": "PM_MRK_INST_ISSUED", "BriefDescription": "Marked instruction issued. Note that stores always get issued twice, the address gets issued to the LSU and the data gets issued to the VSU. Also, issues can sometimes get killed/cancelled and cause multiple sequential issues for the same instruction." }, { + "EventCode": "0x10134", + "EventName": "PM_MRK_ST_DONE_L2", + "BriefDescription": "Marked store completed in L2." + }, + { + "EventCode": "0x1C142", + "EventName": "PM_MRK_XFER_FROM_SRC_PMC1", + "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[0:12]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." + }, + { + "EventCode": "0x1C144", + "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC1", + "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[0:12]." + }, + { + "EventCode": "0x1D15C", + "EventName": "PM_MRK_DTLB_MISS_1G", + "BriefDescription": "Marked Data TLB reload (after a miss) page size 1G. Implies radix translation was used. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + }, + { + "EventCode": "0x1F150", + "EventName": "PM_MRK_ST_L2_CYC", + "BriefDescription": "Cycles from L2 RC dispatch to L2 RC completion." + }, + { "EventCode": "0x101E0", "EventName": "PM_MRK_INST_DISP", "BriefDescription": "The thread has dispatched a randomly sampled marked instruction." @@ -20,14 +40,39 @@ "BriefDescription": "Marked Branch Taken instruction completed." }, { - "EventCode": "0x20112", - "EventName": "PM_MRK_NTF_FIN", - "BriefDescription": "The marked instruction became the oldest in the pipeline before it finished. It excludes instructions that finish at dispatch." + "EventCode": "0x101E4", + "EventName": "PM_MRK_L1_ICACHE_MISS", + "BriefDescription": "Marked instruction suffered an instruction cache miss." + }, + { + "EventCode": "0x101EA", + "EventName": "PM_MRK_L1_RELOAD_VALID", + "BriefDescription": "Marked demand reload." }, { - "EventCode": "0x2C01C", - "EventName": "PM_EXEC_STALL_DMISS_OFF_CHIP", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from a remote chip." + "EventCode": "0x20114", + "EventName": "PM_MRK_L2_RC_DISP", + "BriefDescription": "Marked instruction RC dispatched in L2." + }, + { + "EventCode": "0x2011C", + "EventName": "PM_MRK_NTF_CYC", + "BriefDescription": "Cycles in which the marked instruction is the oldest in the pipeline (next-to-finish or next-to-complete)." + }, + { + "EventCode": "0x20130", + "EventName": "PM_MRK_INST_DECODED", + "BriefDescription": "An instruction was marked at decode time. Random Instruction Sampling (RIS) only." + }, + { + "EventCode": "0x20132", + "EventName": "PM_MRK_DFU_ISSUE", + "BriefDescription": "The marked instruction was a decimal floating point operation issued to the VSU. Measured at issue time." + }, + { + "EventCode": "0x20134", + "EventName": "PM_MRK_FXU_ISSUE", + "BriefDescription": "The marked instruction was a fixed point operation issued to the VSU. Measured at issue time." }, { "EventCode": "0x20138", @@ -40,6 +85,16 @@ "BriefDescription": "Marked Branch instruction finished." }, { + "EventCode": "0x2013C", + "EventName": "PM_MRK_FX_LSU_FIN", + "BriefDescription": "The marked instruction was simple fixed point that was issued to the store unit. Measured at finish time." + }, + { + "EventCode": "0x2C142", + "EventName": "PM_MRK_XFER_FROM_SRC_PMC2", + "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[15:27]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." + }, + { "EventCode": "0x2C144", "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC2", "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[15:27]." @@ -60,19 +115,54 @@ "BriefDescription": "A marked branch completed. All branches are included." }, { - "EventCode": "0x200FD", - "EventName": "PM_L1_ICACHE_MISS", - "BriefDescription": "Demand iCache Miss." + "EventCode": "0x2D154", + "EventName": "PM_MRK_DERAT_MISS_64K", + "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 64K for a marked instruction. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + }, + { + "EventCode": "0x201E0", + "EventName": "PM_MRK_DATA_FROM_MEMORY", + "BriefDescription": "The processor's data cache was reloaded from local, remote, or distant memory due to a demand miss for a marked load." }, { - "EventCode": "0x30130", - "EventName": "PM_MRK_INST_FIN", - "BriefDescription": "marked instruction finished. Excludes instructions that finish at dispatch. Note that stores always finish twice since the address gets issued to the LSU and the data gets issued to the VSU." + "EventCode": "0x201E2", + "EventName": "PM_MRK_LD_MISS_L1", + "BriefDescription": "Marked demand data load miss counted at finish time." + }, + { + "EventCode": "0x201E4", + "EventName": "PM_MRK_DATA_FROM_L3MISS", + "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss for a marked load." + }, + { + "EventCode": "0x3012A", + "EventName": "PM_MRK_L2_RC_DONE", + "BriefDescription": "L2 RC machine completed the transaction for the marked instruction." + }, + { + "EventCode": "0x3012E", + "EventName": "PM_MRK_DTLB_MISS_2M", + "BriefDescription": "Marked Data TLB reload (after a miss) page size 2M, which implies Radix Page Table translation was used. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + }, + { + "EventCode": "0x30132", + "EventName": "PM_MRK_VSU_FIN", + "BriefDescription": "VSU marked instruction finished. Excludes simple FX instructions issued to the Store Unit." }, { "EventCode": "0x34146", "EventName": "PM_MRK_LD_CMPL", - "BriefDescription": "Marked loads completed." + "BriefDescription": "Marked load instruction completed." + }, + { + "EventCode": "0x3C142", + "EventName": "PM_MRK_XFER_FROM_SRC_PMC3", + "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[30:42]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." + }, + { + "EventCode": "0x3C144", + "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC3", + "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[30:42]." }, { "EventCode": "0x3E158", @@ -82,12 +172,22 @@ { "EventCode": "0x3E15A", "EventName": "PM_MRK_ST_FIN", - "BriefDescription": "The marked instruction was a store of any kind." + "BriefDescription": "Marked store instruction finished." + }, + { + "EventCode": "0x3F150", + "EventName": "PM_MRK_ST_DRAIN_CYC", + "BriefDescription": "Cycles in which the marked store drained from the core to the L2." }, { - "EventCode": "0x30068", - "EventName": "PM_L1_ICACHE_RELOADED_PREF", - "BriefDescription": "Counts all Icache prefetch reloads ( includes demand turned into prefetch)." + "EventCode": "0x30162", + "EventName": "PM_MRK_ISSUE_DEPENDENT_LOAD", + "BriefDescription": "The marked instruction was dependent on a load. It is eligible for issue kill." + }, + { + "EventCode": "0x301E2", + "EventName": "PM_MRK_ST_CMPL", + "BriefDescription": "Marked store completed and sent to nest. Note that this count excludes cache-inhibited stores." }, { "EventCode": "0x301E4", @@ -95,48 +195,78 @@ "BriefDescription": "Marked Branch Mispredicted. Includes direction and target." }, { - "EventCode": "0x300F6", - "EventName": "PM_LD_DEMAND_MISS_L1", - "BriefDescription": "The L1 cache was reloaded with a line that fulfills a demand miss request. Counted at reload time, before finish." + "EventCode": "0x301E6", + "EventName": "PM_MRK_DERAT_MISS", + "BriefDescription": "Marked Erat Miss (Data TLB Access) All page sizes. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + }, + { + "EventCode": "0x4010E", + "EventName": "PM_MRK_TLBIE_FIN", + "BriefDescription": "Marked TLBIE instruction finished. Includes TLBIE and TLBIEL instructions." + }, + { + "EventCode": "0x40116", + "EventName": "PM_MRK_LARX_FIN", + "BriefDescription": "Marked load and reserve instruction (LARX) finished. LARX and STCX are instructions used to acquire a lock." + }, + { + "EventCode": "0x40132", + "EventName": "PM_MRK_LSU_FIN", + "BriefDescription": "LSU marked instruction finish." + }, + { + "EventCode": "0x44146", + "EventName": "PM_MRK_STCX_CORE_CYC", + "BriefDescription": "Cycles spent in the core portion of a marked STCX instruction. It starts counting when the instruction is decoded and stops counting when it drains into the L2." }, { - "EventCode": "0x300FE", - "EventName": "PM_DATA_FROM_L3MISS", - "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss." + "EventCode": "0x4C142", + "EventName": "PM_MRK_XFER_FROM_SRC_PMC4", + "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[45:57]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." }, { - "EventCode": "0x40012", - "EventName": "PM_L1_ICACHE_RELOADED_ALL", - "BriefDescription": "Counts all Icache reloads includes demand, prefetch, prefetch turned into demand and demand turned into prefetch." + "EventCode": "0x4C144", + "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC4", + "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[45:57]." }, { - "EventCode": "0x40134", - "EventName": "PM_MRK_INST_TIMEO", - "BriefDescription": "Marked instruction finish timeout (instruction was lost)." + "EventCode": "0x4C15C", + "EventName": "PM_MRK_DERAT_MISS_1G", + "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 1G for a marked instruction. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x4505A", - "EventName": "PM_SP_FLOP_CMPL", - "BriefDescription": "Single Precision floating point instructions completed." + "EventCode": "0x4C15E", + "EventName": "PM_MRK_DTLB_MISS_64K", + "BriefDescription": "Marked Data TLB reload (after a miss) page size 64K. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x4D058", - "EventName": "PM_VECTOR_FLOP_CMPL", - "BriefDescription": "Vector floating point instructions completed." + "EventCode": "0x4E15E", + "EventName": "PM_MRK_INST_FLUSHED", + "BriefDescription": "The marked instruction was flushed." }, { - "EventCode": "0x4D05A", - "EventName": "PM_NON_MATH_FLOP_CMPL", - "BriefDescription": "Non Math instructions completed." + "EventCode": "0x40164", + "EventName": "PM_MRK_DERAT_MISS_2M", + "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 2M for a marked instruction. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { "EventCode": "0x401E0", "EventName": "PM_MRK_INST_CMPL", - "BriefDescription": "marked instruction completed." + "BriefDescription": "Marked instruction completed." + }, + { + "EventCode": "0x401E4", + "EventName": "PM_MRK_DTLB_MISS", + "BriefDescription": "The DPTEG required for the marked load/store instruction in execution was missing from the TLB. This event only counts for demand misses." + }, + { + "EventCode": "0x401E6", + "EventName": "PM_MRK_INST_FROM_L3MISS", + "BriefDescription": "The processor's instruction cache was reloaded from beyond the local core's L3 due to a demand miss for a marked instruction." }, { - "EventCode": "0x400FE", - "EventName": "PM_DATA_FROM_MEMORY", - "BriefDescription": "The processor's data cache was reloaded from local, remote, or distant memory due to a demand miss." + "EventCode": "0x401E8", + "EventName": "PM_MRK_DATA_FROM_L2MISS", + "BriefDescription": "The processor's L1 data cache was reloaded from a source beyond the local core's L2 due to a demand miss for a marked instruction." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/memory.json b/tools/perf/pmu-events/arch/powerpc/power10/memory.json index 843b51f531e9..885262957beb 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/memory.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/memory.json @@ -1,25 +1,10 @@ [ { - "EventCode": "0x1000A", - "EventName": "PM_PMC3_REWIND", - "BriefDescription": "The speculative event selected for PMC3 rewinds and the counter for PMC3 is not charged." - }, - { "EventCode": "0x1C040", "EventName": "PM_XFER_FROM_SRC_PMC1", "BriefDescription": "The processor's L1 data cache was reloaded from the source specified in MMCR3[0:12]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." }, { - "EventCode": "0x1C142", - "EventName": "PM_MRK_XFER_FROM_SRC_PMC1", - "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[0:12]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." - }, - { - "EventCode": "0x1C144", - "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC1", - "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[0:12]." - }, - { "EventCode": "0x1C056", "EventName": "PM_DERAT_MISS_4K", "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 4K. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." @@ -35,24 +20,9 @@ "BriefDescription": "Data TLB reload (after a miss) page size 2M. Implies radix translation was used. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x1E056", - "EventName": "PM_EXEC_STALL_STORE_PIPE", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the store unit. This does not include cycles spent handling store misses, PTESYNC instructions or TLBIE instructions." - }, - { - "EventCode": "0x1F150", - "EventName": "PM_MRK_ST_L2_CYC", - "BriefDescription": "Cycles from L2 RC dispatch to L2 RC completion." - }, - { "EventCode": "0x10062", "EventName": "PM_LD_L3MISS_PEND_CYC", - "BriefDescription": "Cycles L3 miss was pending for this thread." - }, - { - "EventCode": "0x20010", - "EventName": "PM_PMC1_OVERFLOW", - "BriefDescription": "The event selected for PMC1 caused the event counter to overflow." + "BriefDescription": "Cycles in which an L3 miss was pending for this thread." }, { "EventCode": "0x2001A", @@ -80,9 +50,9 @@ "BriefDescription": "Data TLB reload (after a miss) page size 4K. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x2D154", - "EventName": "PM_MRK_DERAT_MISS_64K", - "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 64K for a marked instruction. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + "EventCode": "0x2C05A", + "EventName": "PM_DERAT_MISS_1G", + "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 1G. Implies radix translation. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { "EventCode": "0x200F6", @@ -90,9 +60,9 @@ "BriefDescription": "DERAT Reloaded to satisfy a DERAT miss. All page sizes are counted by this event. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { - "EventCode": "0x30016", - "EventName": "PM_EXEC_STALL_DERAT_DTLB_MISS", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline suffered a TLB miss and waited for it resolve." + "EventCode": "0x34044", + "EventName": "PM_DERAT_MISS_PREF", + "BriefDescription": "DERAT miss (TLB access) while servicing a data prefetch." }, { "EventCode": "0x3C040", @@ -100,16 +70,6 @@ "BriefDescription": "The processor's L1 data cache was reloaded from the source specified in MMCR3[30:42]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." }, { - "EventCode": "0x3C142", - "EventName": "PM_MRK_XFER_FROM_SRC_PMC3", - "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[30:42]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." - }, - { - "EventCode": "0x3C144", - "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC3", - "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[30:42]." - }, - { "EventCode": "0x3C054", "EventName": "PM_DERAT_MISS_16M", "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 16M. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." @@ -125,24 +85,14 @@ "BriefDescription": "Load and reserve instruction (LARX) finished. LARX and STCX are instructions used to acquire a lock." }, { - "EventCode": "0x301E2", - "EventName": "PM_MRK_ST_CMPL", - "BriefDescription": "Marked store completed and sent to nest. Note that this count excludes cache-inhibited stores." - }, - { "EventCode": "0x300FC", "EventName": "PM_DTLB_MISS", - "BriefDescription": "The DPTEG required for the load/store instruction in execution was missing from the TLB. It includes pages of all sizes for demand and prefetch activity." - }, - { - "EventCode": "0x4D02C", - "EventName": "PM_PMC1_REWIND", - "BriefDescription": "The speculative event selected for PMC1 rewinds and the counter for PMC1 is not charged." + "BriefDescription": "The DPTEG required for the load/store instruction in execution was missing from the TLB. This event only counts for demand misses." }, { "EventCode": "0x4003E", "EventName": "PM_LD_CMPL", - "BriefDescription": "Loads completed." + "BriefDescription": "Load instruction completed." }, { "EventCode": "0x4C040", @@ -150,16 +100,6 @@ "BriefDescription": "The processor's L1 data cache was reloaded from the source specified in MMCR3[45:57]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." }, { - "EventCode": "0x4C142", - "EventName": "PM_MRK_XFER_FROM_SRC_PMC4", - "BriefDescription": "For a marked data transfer instruction, the processor's L1 data cache was reloaded from the source specified in MMCR3[45:57]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." - }, - { - "EventCode": "0x4C144", - "EventName": "PM_MRK_XFER_FROM_SRC_CYC_PMC4", - "BriefDescription": "Cycles taken for a marked demand miss to reload a line from the source specified in MMCR3[45:57]." - }, - { "EventCode": "0x4C056", "EventName": "PM_DTLB_MISS_16M", "BriefDescription": "Data TLB reload (after a miss) page size 16M. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." @@ -168,20 +108,5 @@ "EventCode": "0x4C05A", "EventName": "PM_DTLB_MISS_1G", "BriefDescription": "Data TLB reload (after a miss) page size 1G. Implies radix translation was used. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." - }, - { - "EventCode": "0x4C15E", - "EventName": "PM_MRK_DTLB_MISS_64K", - "BriefDescription": "Marked Data TLB reload (after a miss) page size 64K. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." - }, - { - "EventCode": "0x4D056", - "EventName": "PM_NON_FMA_FLOP_CMPL", - "BriefDescription": "Non FMA instruction completed." - }, - { - "EventCode": "0x40164", - "EventName": "PM_MRK_DERAT_MISS_2M", - "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 2M for a marked instruction. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/metrics.json b/tools/perf/pmu-events/arch/powerpc/power10/metrics.json index 6f53583a0c62..4d66b75c6ad5 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/metrics.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/metrics.json @@ -16,133 +16,139 @@ "BriefDescription": "Average cycles per completed instruction when dispatch was stalled for any reason", "MetricExpr": "PM_DISP_STALL_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI;CPI_STALL_RATIO", - "MetricName": "DISPATCHED_CPI" + "MetricName": "DISPATCH_STALL_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled because there was a flush", "MetricExpr": "PM_DISP_STALL_FLUSH / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_FLUSH_CPI" + "MetricName": "DISPATCH_STALL_FLUSH_CPI" + }, + { + "BriefDescription": "Average cycles per completed instruction when dispatch was stalled because Fetch was being held, so there was nothing in the pipeline for this thread", + "MetricExpr": "PM_DISP_STALL_FETCH / PM_RUN_INST_CMPL", + "MetricGroup": "CPI", + "MetricName": "DISPATCH_STALL_FETCH_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled because the MMU was handling a translation miss", "MetricExpr": "PM_DISP_STALL_TRANSLATION / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_TRANSLATION_CPI" + "MetricName": "DISPATCH_STALL_TRANSLATION_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled waiting to resolve an instruction ERAT miss", "MetricExpr": "PM_DISP_STALL_IERAT_ONLY_MISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_IERAT_ONLY_MISS_CPI" + "MetricName": "DISPATCH_STALL_IERAT_ONLY_MISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled waiting to resolve an instruction TLB miss", "MetricExpr": "PM_DISP_STALL_ITLB_MISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_ITLB_MISS_CPI" + "MetricName": "DISPATCH_STALL_ITLB_MISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled due to an icache miss", "MetricExpr": "PM_DISP_STALL_IC_MISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_IC_MISS_CPI" + "MetricName": "DISPATCH_STALL_IC_MISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while the instruction was fetched from the local L2", "MetricExpr": "PM_DISP_STALL_IC_L2 / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_IC_L2_CPI" + "MetricName": "DISPATCH_STALL_IC_L2_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while the instruction was fetched from the local L3", "MetricExpr": "PM_DISP_STALL_IC_L3 / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_IC_L3_CPI" + "MetricName": "DISPATCH_STALL_IC_L3_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while the instruction was fetched from any source beyond the local L3", "MetricExpr": "PM_DISP_STALL_IC_L3MISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_IC_L3MISS_CPI" + "MetricName": "DISPATCH_STALL_IC_L3MISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled due to an icache miss after a branch mispredict", "MetricExpr": "PM_DISP_STALL_BR_MPRED_ICMISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_BR_MPRED_ICMISS_CPI" + "MetricName": "DISPATCH_STALL_BR_MPRED_ICMISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while instruction was fetched from the local L2 after suffering a branch mispredict", "MetricExpr": "PM_DISP_STALL_BR_MPRED_IC_L2 / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_BR_MPRED_IC_L2_CPI" + "MetricName": "DISPATCH_STALL_BR_MPRED_IC_L2_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while instruction was fetched from the local L3 after suffering a branch mispredict", "MetricExpr": "PM_DISP_STALL_BR_MPRED_IC_L3 / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_BR_MPRED_IC_L3_CPI" + "MetricName": "DISPATCH_STALL_BR_MPRED_IC_L3_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled while instruction was fetched from any source beyond the local L3 after suffering a branch mispredict", "MetricExpr": "PM_DISP_STALL_BR_MPRED_IC_L3MISS / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_BR_MPRED_IC_L3MISS_CPI" + "MetricName": "DISPATCH_STALL_BR_MPRED_IC_L3MISS_CPI" }, { "BriefDescription": "Average cycles per completed instruction when dispatch was stalled due to a branch mispredict", "MetricExpr": "PM_DISP_STALL_BR_MPRED / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_BR_MPRED_CPI" + "MetricName": "DISPATCH_STALL_BR_MPRED_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch for any reason", "MetricExpr": "PM_DISP_STALL_HELD_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_CPI" + "MetricName": "DISPATCH_STALL_HELD_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch because of a synchronizing instruction that requires the ICT to be empty before dispatch", "MetricExpr": "PM_DISP_STALL_HELD_SYNC_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISP_HELD_STALL_SYNC_CPI" + "MetricName": "DISPATCH_STALL_HELD_SYNC_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch while waiting on the scoreboard", "MetricExpr": "PM_DISP_STALL_HELD_SCOREBOARD_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISP_HELD_STALL_SCOREBOARD_CPI" + "MetricName": "DISPATCH_STALL_HELD_SCOREBOARD_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch due to issue queue full", "MetricExpr": "PM_DISP_STALL_HELD_ISSQ_FULL_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISP_HELD_STALL_ISSQ_FULL_CPI" + "MetricName": "DISPATCH_STALL_HELD_ISSQ_FULL_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch because the mapper/SRB was full", "MetricExpr": "PM_DISP_STALL_HELD_RENAME_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_RENAME_CPI" + "MetricName": "DISPATCH_STALL_HELD_RENAME_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch because the STF mapper/SRB was full", "MetricExpr": "PM_DISP_STALL_HELD_STF_MAPPER_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_STF_MAPPER_CPI" + "MetricName": "DISPATCH_STALL_HELD_STF_MAPPER_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch because the XVFC mapper/SRB was full", "MetricExpr": "PM_DISP_STALL_HELD_XVFC_MAPPER_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_XVFC_MAPPER_CPI" + "MetricName": "DISPATCH_STALL_HELD_XVFC_MAPPER_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch for any other reason", "MetricExpr": "PM_DISP_STALL_HELD_OTHER_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_OTHER_CPI" + "MetricName": "DISPATCH_STALL_HELD_OTHER_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction has been dispatched but not issued for any reason", @@ -352,13 +358,13 @@ "BriefDescription": "Average cycles per completed instruction when dispatch was stalled because fetch was being held, so there was nothing in the pipeline for this thread", "MetricExpr": "PM_DISP_STALL_FETCH / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_FETCH_CPI" + "MetricName": "DISPATCH_STALL_FETCH_CPI" }, { "BriefDescription": "Average cycles per completed instruction when the NTC instruction was held at dispatch because of power management", "MetricExpr": "PM_DISP_STALL_HELD_HALT_CYC / PM_RUN_INST_CMPL", "MetricGroup": "CPI", - "MetricName": "DISPATCHED_HELD_HALT_CPI" + "MetricName": "DISPATCH_STALL_HELD_HALT_CPI" }, { "BriefDescription": "Percentage of flushes per completed instruction", @@ -395,6 +401,13 @@ "ScaleUnit": "1%" }, { + "BriefDescription": "Percentage of completed instructions that were stores that missed the L1", + "MetricExpr": "PM_ST_MISS_L1 * 100 / PM_RUN_INST_CMPL", + "MetricGroup": "Others", + "MetricName": "L1_ST_MISS_RATE", + "ScaleUnit": "1%" + }, + { "BriefDescription": "Percentage of completed instructions when the DPTEG required for the load/store instruction in execution was missing from the TLB", "MetricExpr": "PM_DTLB_MISS / PM_RUN_INST_CMPL * 100", "MetricGroup": "Others", @@ -454,12 +467,6 @@ "MetricName": "LOADS_PER_INST" }, { - "BriefDescription": "Average number of finished stores per completed instruction", - "MetricExpr": "PM_ST_FIN / PM_RUN_INST_CMPL", - "MetricGroup": "General", - "MetricName": "STORES_PER_INST" - }, - { "BriefDescription": "Percentage of demand loads that reloaded from beyond the L2 per completed instruction", "MetricExpr": "PM_DATA_FROM_L2MISS / PM_RUN_INST_CMPL * 100", "MetricGroup": "dL1_Reloads", @@ -474,6 +481,13 @@ "ScaleUnit": "1%" }, { + "BriefDescription": "Percentage of ITLB misses per completed run instruction", + "MetricExpr": "PM_ITLB_MISS / PM_RUN_INST_CMPL * 100", + "MetricGroup": "General", + "MetricName": "ITLB_MISS_RATE", + "ScaleUnit": "1%" + }, + { "BriefDescription": "Percentage of DERAT misses with 4k page size per completed instruction", "MetricExpr": "PM_DERAT_MISS_4K / PM_RUN_INST_CMPL * 100", "MetricGroup": "Translation", @@ -566,7 +580,7 @@ "BriefDescription": "Average number of STCX instructions finshed per completed instruction", "MetricExpr": "PM_STCX_FIN / PM_RUN_INST_CMPL", "MetricGroup": "General", - "MetricName": "STXC_PER_INST" + "MetricName": "STCX_PER_INST" }, { "BriefDescription": "Average number of LARX instructions finshed per completed instruction", @@ -629,6 +643,13 @@ "ScaleUnit": "1%" }, { + "BriefDescription": "Percentage of DERAT misses with 1G page size per completed run instruction", + "MetricExpr": "PM_DERAT_MISS_1G * 100 / PM_RUN_INST_CMPL", + "MetricGroup": "Translation", + "MetricName": "DERAT_1G_MISS_RATE", + "ScaleUnit": "1%" + }, + { "BriefDescription": "DERAT miss ratio for 4K page size", "MetricExpr": "PM_DERAT_MISS_4K / PM_DERAT_MISS", "MetricGroup": "Translation", @@ -647,6 +668,12 @@ "MetricName": "DERAT_16M_MISS_RATIO" }, { + "BriefDescription": "DERAT miss ratio for 1G page size", + "MetricExpr": "PM_DERAT_MISS_1G / PM_DERAT_MISS", + "MetricGroup": "Translation", + "MetricName": "DERAT_1G_MISS_RATIO" + }, + { "BriefDescription": "DERAT miss ratio for 64K page size", "MetricExpr": "PM_DERAT_MISS_64K / PM_DERAT_MISS", "MetricGroup": "Translation", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/others.json b/tools/perf/pmu-events/arch/powerpc/power10/others.json index a771e4b6bec5..0e21e7ba1959 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/others.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/others.json @@ -1,28 +1,13 @@ [ { - "EventCode": "0x10016", - "EventName": "PM_VSU0_ISSUE", - "BriefDescription": "VSU instructions issued to VSU pipe 0." - }, - { - "EventCode": "0x1001C", - "EventName": "PM_ULTRAVISOR_INST_CMPL", - "BriefDescription": "PowerPC instructions that completed while the thread was in ultravisor state." - }, - { - "EventCode": "0x100F0", - "EventName": "PM_CYC", - "BriefDescription": "Processor cycles." - }, - { - "EventCode": "0x10134", - "EventName": "PM_MRK_ST_DONE_L2", - "BriefDescription": "Marked stores completed in L2 (RC machine done)." + "EventCode": "0x1002C", + "EventName": "PM_LD_PREFETCH_CACHE_LINE_MISS", + "BriefDescription": "The L1 cache was reloaded with a line that fulfills a prefetch request." }, { "EventCode": "0x1505E", "EventName": "PM_LD_HIT_L1", - "BriefDescription": "Loads that finished without experiencing an L1 miss." + "BriefDescription": "Load finished without experiencing an L1 miss." }, { "EventCode": "0x1F056", @@ -30,9 +15,9 @@ "BriefDescription": "Cycles in which Superslice 0 dispatches either 1 or 2 instructions." }, { - "EventCode": "0x1F15C", - "EventName": "PM_MRK_STCX_L2_CYC", - "BriefDescription": "Cycles spent in the nest portion of a marked Stcx instruction. It starts counting when the operation starts to drain to the L2 and it stops counting when the instruction retires from the Instruction Completion Table (ICT) in the Instruction Sequencing Unit (ISU)." + "EventCode": "0x1F05A", + "EventName": "PM_DISP_HELD_SYNC_CYC", + "BriefDescription": "Cycles dispatch is held because of a synchronizing instruction that requires the ICT to be empty before dispatch." }, { "EventCode": "0x10066", @@ -40,39 +25,14 @@ "BriefDescription": "Cycles in which the thread is in Adjunct state. MSR[S HV PR] bits = 011." }, { - "EventCode": "0x101E4", - "EventName": "PM_MRK_L1_ICACHE_MISS", - "BriefDescription": "Marked Instruction suffered an icache Miss." - }, - { - "EventCode": "0x101EA", - "EventName": "PM_MRK_L1_RELOAD_VALID", - "BriefDescription": "Marked demand reload." - }, - { - "EventCode": "0x100F4", - "EventName": "PM_FLOP_CMPL", - "BriefDescription": "Floating Point Operations Completed. Includes any type. It counts once for each 1, 2, 4 or 8 flop instruction. Use PM_1|2|4|8_FLOP_CMPL events to count flops." - }, - { - "EventCode": "0x100FA", - "EventName": "PM_RUN_LATCH_ANY_THREAD_CYC", - "BriefDescription": "Cycles when at least one thread has the run latch set." - }, - { "EventCode": "0x100FC", "EventName": "PM_LD_REF_L1", "BriefDescription": "All L1 D cache load references counted at finish, gated by reject. In P9 and earlier this event counted only cacheable loads but in P10 both cacheable and non-cacheable loads are included." }, { - "EventCode": "0x2000C", - "EventName": "PM_RUN_LATCH_ALL_THREADS_CYC", - "BriefDescription": "Cycles when the run latch is set for all threads." - }, - { "EventCode": "0x2E010", "EventName": "PM_ADJUNCT_INST_CMPL", - "BriefDescription": "PowerPC instructions that completed while the thread is in Adjunct state." + "BriefDescription": "PowerPC instruction completed while the thread was in Adjunct state." }, { "EventCode": "0x2E014", @@ -80,26 +40,6 @@ "BriefDescription": "Conditional store instruction (STCX) finished. LARX and STCX are instructions used to acquire a lock." }, { - "EventCode": "0x20130", - "EventName": "PM_MRK_INST_DECODED", - "BriefDescription": "An instruction was marked at decode time. Random Instruction Sampling (RIS) only." - }, - { - "EventCode": "0x20132", - "EventName": "PM_MRK_DFU_ISSUE", - "BriefDescription": "The marked instruction was a decimal floating point operation issued to the VSU. Measured at issue time." - }, - { - "EventCode": "0x20134", - "EventName": "PM_MRK_FXU_ISSUE", - "BriefDescription": "The marked instruction was a fixed point operation issued to the VSU. Measured at issue time." - }, - { - "EventCode": "0x2505C", - "EventName": "PM_VSU_ISSUE", - "BriefDescription": "At least one VSU instruction was issued to one of the VSU pipes. Up to 4 per cycle. Includes fixed point operations." - }, - { "EventCode": "0x2F054", "EventName": "PM_DISP_SS1_2_INSTR_CYC", "BriefDescription": "Cycles in which Superslice 1 dispatches either 1 or 2 instructions." @@ -110,39 +50,14 @@ "BriefDescription": "Cycles in which Superslice 1 dispatches either 3 or 4 instructions." }, { - "EventCode": "0x2006C", - "EventName": "PM_RUN_CYC_SMT4_MODE", - "BriefDescription": "Cycles when this thread's run latch is set and the core is in SMT4 mode." - }, - { - "EventCode": "0x201E0", - "EventName": "PM_MRK_DATA_FROM_MEMORY", - "BriefDescription": "The processor's data cache was reloaded from local, remote, or distant memory due to a demand miss for a marked load." - }, - { - "EventCode": "0x201E4", - "EventName": "PM_MRK_DATA_FROM_L3MISS", - "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss for a marked load." - }, - { - "EventCode": "0x201E8", - "EventName": "PM_THRESH_EXC_512", - "BriefDescription": "Threshold counter exceeded a value of 512." - }, - { "EventCode": "0x200F2", "EventName": "PM_INST_DISP", - "BriefDescription": "PowerPC instructions dispatched." - }, - { - "EventCode": "0x30132", - "EventName": "PM_MRK_VSU_FIN", - "BriefDescription": "VSU marked instructions finished. Excludes simple FX instructions issued to the Store Unit." + "BriefDescription": "PowerPC instruction dispatched." }, { - "EventCode": "0x30038", - "EventName": "PM_EXEC_STALL_DMISS_LMEM", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local memory, local OpenCapp cache, or local OpenCapp memory." + "EventCode": "0x200FD", + "EventName": "PM_L1_ICACHE_MISS", + "BriefDescription": "Demand instruction cache miss." }, { "EventCode": "0x3F04A", @@ -152,12 +67,7 @@ { "EventCode": "0x3405A", "EventName": "PM_PRIVILEGED_INST_CMPL", - "BriefDescription": "PowerPC Instructions that completed while the thread is in Privileged state." - }, - { - "EventCode": "0x3F150", - "EventName": "PM_MRK_ST_DRAIN_CYC", - "BriefDescription": "cycles to drain st from core to L2." + "BriefDescription": "PowerPC instruction completed while the thread was in Privileged state." }, { "EventCode": "0x3F054", @@ -170,74 +80,29 @@ "BriefDescription": "Cycles in which Superslice 0 dispatches either 5, 6, 7 or 8 instructions." }, { - "EventCode": "0x30162", - "EventName": "PM_MRK_ISSUE_DEPENDENT_LOAD", - "BriefDescription": "The marked instruction was dependent on a load. It is eligible for issue kill." - }, - { - "EventCode": "0x40114", - "EventName": "PM_MRK_START_PROBE_NOP_DISP", - "BriefDescription": "Marked Start probe nop dispatched. Instruction AND R0,R0,R0." - }, - { - "EventCode": "0x4001C", - "EventName": "PM_VSU_FIN", - "BriefDescription": "VSU instructions finished." - }, - { - "EventCode": "0x4C01A", - "EventName": "PM_EXEC_STALL_DMISS_OFF_NODE", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from a distant chip." - }, - { - "EventCode": "0x4D012", - "EventName": "PM_PMC3_SAVED", - "BriefDescription": "The conditions for the speculative event selected for PMC3 are met and PMC3 is charged." - }, - { - "EventCode": "0x4D022", - "EventName": "PM_HYPERVISOR_INST_CMPL", - "BriefDescription": "PowerPC instructions that completed while the thread is in hypervisor state." - }, - { - "EventCode": "0x4D026", - "EventName": "PM_ULTRAVISOR_CYC", - "BriefDescription": "Cycles when the thread is in Ultravisor state. MSR[S HV PR]=110." + "EventCode": "0x30068", + "EventName": "PM_L1_ICACHE_RELOADED_PREF", + "BriefDescription": "Counts all instruction cache prefetch reloads (includes demand turned into prefetch)." }, { - "EventCode": "0x4D028", - "EventName": "PM_PRIVILEGED_CYC", - "BriefDescription": "Cycles when the thread is in Privileged state. MSR[S HV PR]=x00." + "EventCode": "0x300F6", + "EventName": "PM_LD_DEMAND_MISS_L1", + "BriefDescription": "The L1 cache was reloaded with a line that fulfills a demand miss request. Counted at reload time, before finish." }, { - "EventCode": "0x40030", - "EventName": "PM_INST_FIN", - "BriefDescription": "Instructions finished." + "EventCode": "0x300FE", + "EventName": "PM_DATA_FROM_L3MISS", + "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss." }, { - "EventCode": "0x44146", - "EventName": "PM_MRK_STCX_CORE_CYC", - "BriefDescription": "Cycles spent in the core portion of a marked Stcx instruction. It starts counting when the instruction is decoded and stops counting when it drains into the L2." + "EventCode": "0x40012", + "EventName": "PM_L1_ICACHE_RELOADED_ALL", + "BriefDescription": "Counts all instruction cache reloads includes demand, prefetch, prefetch turned into demand and demand turned into prefetch." }, { "EventCode": "0x44054", "EventName": "PM_VECTOR_LD_CMPL", - "BriefDescription": "Vector load instructions completed." - }, - { - "EventCode": "0x45054", - "EventName": "PM_FMA_CMPL", - "BriefDescription": "Two floating point instructions completed (FMA class of instructions: fmadd, fnmadd, fmsub, fnmsub). Scalar instructions only." - }, - { - "EventCode": "0x45056", - "EventName": "PM_SCALAR_FLOP_CMPL", - "BriefDescription": "Scalar floating point instructions completed." - }, - { - "EventCode": "0x4505C", - "EventName": "PM_MATH_FLOP_CMPL", - "BriefDescription": "Math floating point instructions completed." + "BriefDescription": "Vector load instruction completed." }, { "EventCode": "0x4D05E", @@ -245,28 +110,13 @@ "BriefDescription": "A branch completed. All branches are included." }, { - "EventCode": "0x4E15E", - "EventName": "PM_MRK_INST_FLUSHED", - "BriefDescription": "The marked instruction was flushed." - }, - { - "EventCode": "0x401E6", - "EventName": "PM_MRK_INST_FROM_L3MISS", - "BriefDescription": "The processor's instruction cache was reloaded from a source other than the local core's L1, L2, or L3 due to a demand miss for a marked instruction." - }, - { - "EventCode": "0x401E8", - "EventName": "PM_MRK_DATA_FROM_L2MISS", - "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1 or L2 due to a demand miss for a marked load." - }, - { "EventCode": "0x400F0", "EventName": "PM_LD_DEMAND_MISS_L1_FIN", - "BriefDescription": "Load Missed L1, counted at finish time." + "BriefDescription": "Load missed L1, counted at finish time." }, { - "EventCode": "0x500FA", - "EventName": "PM_RUN_INST_CMPL", - "BriefDescription": "Completed PowerPC instructions gated by the run latch." + "EventCode": "0x400FE", + "EventName": "PM_DATA_FROM_MEMORY", + "BriefDescription": "The processor's data cache was reloaded from local, remote, or distant memory due to a demand miss." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json b/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json index b8aded6045fa..21b23bb55d0d 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json @@ -1,8 +1,13 @@ [ { - "EventCode": "0x100FE", - "EventName": "PM_INST_CMPL", - "BriefDescription": "PowerPC instructions completed." + "EventCode": "0x10004", + "EventName": "PM_EXEC_STALL_TRANSLATION", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline suffered a TLB miss or ERAT miss and waited for it to resolve." + }, + { + "EventCode": "0x10006", + "EventName": "PM_DISP_STALL_HELD_OTHER_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch for any other reason." }, { "EventCode": "0x1000C", @@ -12,7 +17,7 @@ { "EventCode": "0x1000E", "EventName": "PM_MMA_ISSUED", - "BriefDescription": "MMA instructions issued." + "BriefDescription": "MMA instruction issued." }, { "EventCode": "0x10012", @@ -30,14 +35,24 @@ "BriefDescription": "Cycles in which an instruction reload is pending to satisfy a demand miss." }, { - "EventCode": "0x10022", - "EventName": "PM_PMC2_SAVED", - "BriefDescription": "The conditions for the speculative event selected for PMC2 are met and PMC2 is charged." + "EventCode": "0x10028", + "EventName": "PM_NTC_FLUSH", + "BriefDescription": "The instruction was flushed after becoming next-to-complete (NTC)." + }, + { + "EventCode": "0x10038", + "EventName": "PM_DISP_STALL_TRANSLATION", + "BriefDescription": "Cycles when dispatch was stalled for this thread because the MMU was handling a translation miss." + }, + { + "EventCode": "0x1003A", + "EventName": "PM_DISP_STALL_BR_MPRED_IC_L2", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L2 after suffering a branch mispredict." }, { - "EventCode": "0x10024", - "EventName": "PM_PMC5_OVERFLOW", - "BriefDescription": "The event selected for PMC5 caused the event counter to overflow." + "EventCode": "0x1003C", + "EventName": "PM_EXEC_STALL_DMISS_L2L3", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from either the local L2 or local L3." }, { "EventCode": "0x10058", @@ -55,11 +70,41 @@ "BriefDescription": "Data ERAT Miss (Data TLB Access) page size 2M. Implies radix translation. When MMCR1[16]=0 this event counts only DERAT reloads for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, { + "EventCode": "0x1D05E", + "EventName": "PM_DISP_STALL_HELD_HALT_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch because of power management." + }, + { + "EventCode": "0x1E050", + "EventName": "PM_DISP_STALL_HELD_STF_MAPPER_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch because the STF mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR." + }, + { + "EventCode": "0x1E054", + "EventName": "PM_EXEC_STALL_DMISS_L21_L31", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from another core's L2 or L3 on the same chip." + }, + { + "EventCode": "0x1E056", + "EventName": "PM_EXEC_STALL_STORE_PIPE", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the store unit. This does not include cycles spent handling store misses, PTESYNC instructions or TLBIE instructions." + }, + { "EventCode": "0x1E05A", "EventName": "PM_CMPL_STALL_LWSYNC", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a lwsync waiting to complete." }, { + "EventCode": "0x1F058", + "EventName": "PM_DISP_HELD_CYC", + "BriefDescription": "Cycles dispatch is held." + }, + { + "EventCode": "0x10064", + "EventName": "PM_DISP_STALL_IC_L2", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L2." + }, + { "EventCode": "0x10068", "EventName": "PM_BR_FIN", "BriefDescription": "A branch instruction finished. Includes predicted/mispredicted/unconditional." @@ -70,9 +115,9 @@ "BriefDescription": "Simple fixed point instruction issued to the store unit. Measured at finish time." }, { - "EventCode": "0x1006C", - "EventName": "PM_RUN_CYC_ST_MODE", - "BriefDescription": "Cycles when the run latch is set and the core is in ST mode." + "EventCode": "0x100F8", + "EventName": "PM_DISP_STALL_CYC", + "BriefDescription": "Cycles the ICT has no itags assigned to this thread (no instructions were dispatched during these cycles)." }, { "EventCode": "0x20004", @@ -80,9 +125,9 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline was dispatched but not issued yet." }, { - "EventCode": "0x2000A", - "EventName": "PM_HYPERVISOR_CYC", - "BriefDescription": "Cycles when the thread is in Hypervisor state. MSR[S HV PR]=010." + "EventCode": "0x20006", + "EventName": "PM_DISP_STALL_HELD_ISSQ_FULL_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch due to Issue queue full. Includes issue queue and branch queue." }, { "EventCode": "0x2000E", @@ -90,24 +135,59 @@ "BriefDescription": "LSU Finished an internal operation in LD1 port." }, { + "EventCode": "0x2C010", + "EventName": "PM_EXEC_STALL_LSU", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the Load Store Unit. This does not include simple fixed point instructions." + }, + { "EventCode": "0x2C014", "EventName": "PM_CMPL_STALL_SPECIAL", "BriefDescription": "Cycles in which the oldest instruction in the pipeline required special handling before completing." }, { + "EventCode": "0x2C016", + "EventName": "PM_DISP_STALL_IERAT_ONLY_MISS", + "BriefDescription": "Cycles when dispatch was stalled while waiting to resolve an instruction ERAT miss." + }, + { "EventCode": "0x2C018", "EventName": "PM_EXEC_STALL_DMISS_L3MISS", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from a source beyond the local L2 or local L3." }, { + "EventCode": "0x2C01C", + "EventName": "PM_EXEC_STALL_DMISS_OFF_CHIP", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from a remote chip." + }, + { + "EventCode": "0x2C01E", + "EventName": "PM_DISP_STALL_BR_MPRED_IC_L3", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L3 after suffering a branch mispredict." + }, + { "EventCode": "0x2D010", "EventName": "PM_LSU_ST1_FIN", "BriefDescription": "LSU Finished an internal operation in ST1 port." }, { + "EventCode": "0x10016", + "EventName": "PM_VSU0_ISSUE", + "BriefDescription": "VSU instruction issued to VSU pipe 0." + }, + { "EventCode": "0x2D012", "EventName": "PM_VSU1_ISSUE", - "BriefDescription": "VSU instructions issued to VSU pipe 1." + "BriefDescription": "VSU instruction issued to VSU pipe 1." + }, + { + "EventCode": "0x2505C", + "EventName": "PM_VSU_ISSUE", + "BriefDescription": "At least one VSU instruction was issued to one of the VSU pipes. Up to 4 per cycle. Includes fixed point operations." + }, + { + "EventCode": "0x4001C", + "EventName": "PM_VSU_FIN", + "BriefDescription": "VSU instruction finished." }, { "EventCode": "0x2D018", @@ -115,19 +195,34 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the VSU (includes FXU, VSU, CRU)." }, { + "EventCode": "0x2D01A", + "EventName": "PM_DISP_STALL_IC_MISS", + "BriefDescription": "Cycles when dispatch was stalled for this thread due to an instruction cache miss." + }, + { "EventCode": "0x2D01C", "EventName": "PM_CMPL_STALL_STCX", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a stcx waiting for resolution from the nest before completing." }, { - "EventCode": "0x2E01E", - "EventName": "PM_EXEC_STALL_NTC_FLUSH", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in any unit before it was flushed. Note that if the flush of the oldest instruction happens after finish, the cycles from dispatch to issue will be included in PM_DISP_STALL and the cycles from issue to finish will be included in PM_EXEC_STALL and its corresponding children. This event will also count cycles when the previous NTF instruction is still completing and the new NTF instruction is stalled at dispatch." + "EventCode": "0x2E018", + "EventName": "PM_DISP_STALL_FETCH", + "BriefDescription": "Cycles when dispatch was stalled for this thread because Fetch was being held." + }, + { + "EventCode": "0x2E01A", + "EventName": "PM_DISP_STALL_HELD_XVFC_MAPPER_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch because the XVFC mapper/SRB was full." + }, + { + "EventCode": "0x2E01C", + "EventName": "PM_EXEC_STALL_TLBIE", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a TLBIE instruction executing in the Load Store Unit." }, { - "EventCode": "0x2013C", - "EventName": "PM_MRK_FX_LSU_FIN", - "BriefDescription": "The marked instruction was simple fixed point that was issued to the store unit. Measured at finish time." + "EventCode": "0x2E01E", + "EventName": "PM_EXEC_STALL_NTC_FLUSH", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in any unit before it was flushed. Note that if the flush of the oldest instruction happens after finish, the cycles from dispatch to issue will be included in PM_DISP_STALL and the cycles from issue to finish will be included in PM_EXEC_STALL and its corresponding children. This event will also count cycles when the previous next-to-finish (NTF) instruction is still completing and the new NTF instruction is stalled at dispatch." }, { "EventCode": "0x2405A", @@ -135,14 +230,19 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline (NTC) finishes. Note that instructions can finish out of order, therefore not all the instructions that finish have a Next-to-complete status." }, { - "EventCode": "0x201E2", - "EventName": "PM_MRK_LD_MISS_L1", - "BriefDescription": "Marked DL1 Demand Miss counted at finish time." + "EventCode": "0x20066", + "EventName": "PM_DISP_HELD_OTHER_CYC", + "BriefDescription": "Cycles dispatch is held for any other reason." + }, + { + "EventCode": "0x2006A", + "EventName": "PM_DISP_HELD_STF_MAPPER_CYC", + "BriefDescription": "Cycles dispatch is held because the STF mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR." }, { - "EventCode": "0x200F4", - "EventName": "PM_RUN_CYC", - "BriefDescription": "Processor cycles gated by the run latch." + "EventCode": "0x30004", + "EventName": "PM_DISP_STALL_FLUSH", + "BriefDescription": "Cycles when dispatch was stalled because of a flush that happened to an instruction(s) that was not yet next-to-complete (NTC). PM_EXEC_STALL_NTC_FLUSH only includes instructions that were flushed after becoming NTC." }, { "EventCode": "0x30008", @@ -150,29 +250,34 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting to finish in one of the execution units (BRU, LSU, VSU). Only cycles between issue and finish are counted in this category." }, { - "EventCode": "0x3001A", - "EventName": "PM_LSU_ST2_FIN", - "BriefDescription": "LSU Finished an internal operation in ST2 port." + "EventCode": "0x30014", + "EventName": "PM_EXEC_STALL_STORE", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a store instruction executing in the Load Store Unit." + }, + { + "EventCode": "0x30016", + "EventName": "PM_EXEC_STALL_DERAT_DTLB_MISS", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline suffered a TLB miss and waited for it resolve." }, { - "EventCode": "0x30020", - "EventName": "PM_PMC2_REWIND", - "BriefDescription": "The speculative event selected for PMC2 rewinds and the counter for PMC2 is not charged." + "EventCode": "0x30018", + "EventName": "PM_DISP_STALL_HELD_SCOREBOARD_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch while waiting on the Scoreboard. This event combines VSCR and FPSCR together." }, { - "EventCode": "0x30022", - "EventName": "PM_PMC4_SAVED", - "BriefDescription": "The conditions for the speculative event selected for PMC4 are met and PMC4 is charged." + "EventCode": "0x3001A", + "EventName": "PM_LSU_ST2_FIN", + "BriefDescription": "LSU Finished an internal operation in ST2 port." }, { - "EventCode": "0x30024", - "EventName": "PM_PMC6_OVERFLOW", - "BriefDescription": "The event selected for PMC6 caused the event counter to overflow." + "EventCode": "0x30026", + "EventName": "PM_EXEC_STALL_STORE_MISS", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a store whose cache line was not resident in the L1 and was waiting for allocation of the missing line into the L1." }, { "EventCode": "0x30028", "EventName": "PM_CMPL_STALL_MEM_ECC", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for the non-speculative finish of either a stcx waiting for its result or a load waiting for non-critical sectors of data and ECC." + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for the non-speculative finish of either a STCX waiting for its result or a load waiting for non-critical sectors of data and ECC." }, { "EventCode": "0x30036", @@ -180,6 +285,11 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a simple fixed point instruction executing in the Load Store Unit." }, { + "EventCode": "0x30038", + "EventName": "PM_EXEC_STALL_DMISS_LMEM", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local memory, local OpenCAPI cache, or local OpenCAPI memory." + }, + { "EventCode": "0x3003A", "EventName": "PM_CMPL_STALL_EXCEPTION", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was not allowed to complete because it was interrupted by ANY exception, which has to be serviced before the instruction can complete." @@ -187,17 +297,42 @@ { "EventCode": "0x3F044", "EventName": "PM_VSU2_ISSUE", - "BriefDescription": "VSU instructions issued to VSU pipe 2." + "BriefDescription": "VSU instruction issued to VSU pipe 2." }, { "EventCode": "0x30058", "EventName": "PM_TLBIE_FIN", - "BriefDescription": "TLBIE instructions finished in the LSU. Two TLBIEs can finish each cycle. All will be counted." + "BriefDescription": "TLBIE instruction finished in the LSU. Two TLBIEs can finish each cycle. All will be counted." }, { - "EventCode": "0x3D058", - "EventName": "PM_SCALAR_FSQRT_FDIV_ISSUE", - "BriefDescription": "Scalar versions of four floating point operations: fdiv,fsqrt (xvdivdp, xvdivsp, xvsqrtdp, xvsqrtsp)." + "EventCode": "0x34054", + "EventName": "PM_EXEC_STALL_DMISS_L2L3_NOCONFLICT", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local L2 or local L3, without a dispatch conflict." + }, + { + "EventCode": "0x34056", + "EventName": "PM_EXEC_STALL_LOAD_FINISH", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was finishing a load after its data was reloaded from a data source beyond the local L1; cycles in which the LSU was processing an L1-hit; cycles in which the next-to-finish (NTF) instruction merged with another load in the LMQ; cycles in which the NTF instruction is waiting for a data reload for a load miss, but the data comes back with a non-NTF instruction." + }, + { + "EventCode": "0x34058", + "EventName": "PM_DISP_STALL_BR_MPRED_ICMISS", + "BriefDescription": "Cycles when dispatch was stalled after a mispredicted branch resulted in an instruction cache miss." + }, + { + "EventCode": "0x3D05C", + "EventName": "PM_DISP_STALL_HELD_RENAME_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch because the mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR and XVFC." + }, + { + "EventCode": "0x3E052", + "EventName": "PM_DISP_STALL_IC_L3", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L3." + }, + { + "EventCode": "0x30060", + "EventName": "PM_DISP_HELD_XVFC_MAPPER_CYC", + "BriefDescription": "Cycles dispatch is held because the XVFC mapper/SRB was full." }, { "EventCode": "0x30066", @@ -215,9 +350,9 @@ "BriefDescription": "Cycles in which both instructions in the ICT entry pair show as finished. These are the cycles between finish and completion for the oldest pair of instructions in the pipeline." }, { - "EventCode": "0x40010", - "EventName": "PM_PMC3_OVERFLOW", - "BriefDescription": "The event selected for PMC3 caused the event counter to overflow." + "EventCode": "0x4C010", + "EventName": "PM_DISP_STALL_BR_MPRED_IC_L3MISS", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from sources beyond the local L3 after suffering a mispredicted branch." }, { "EventCode": "0x4C012", @@ -225,16 +360,36 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline suffered an ERAT miss and waited for it resolve." }, { + "EventCode": "0x4C016", + "EventName": "PM_EXEC_STALL_DMISS_L2L3_CONFLICT", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from the local L2 or local L3, with a dispatch conflict." + }, + { "EventCode": "0x4C018", "EventName": "PM_CMPL_STALL", "BriefDescription": "Cycles in which the oldest instruction in the pipeline cannot complete because the thread was blocked for any reason." }, { + "EventCode": "0x4C01A", + "EventName": "PM_EXEC_STALL_DMISS_OFF_NODE", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was waiting for a load miss to resolve from a distant chip." + }, + { "EventCode": "0x4C01E", "EventName": "PM_LSU_ST3_FIN", "BriefDescription": "LSU Finished an internal operation in ST3 port." }, { + "EventCode": "0x4D014", + "EventName": "PM_EXEC_STALL_LOAD", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a load instruction executing in the Load Store Unit." + }, + { + "EventCode": "0x4D016", + "EventName": "PM_EXEC_STALL_PTESYNC", + "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a PTESYNC instruction executing in the Load Store Unit." + }, + { "EventCode": "0x4D018", "EventName": "PM_EXEC_STALL_BRU", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was executing in the Branch unit." @@ -250,9 +405,24 @@ "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a TLBIEL instruction executing in the Load Store Unit. TLBIEL instructions have lower overhead than TLBIE instructions because they don't get set to the nest." }, { + "EventCode": "0x4D01E", + "EventName": "PM_DISP_STALL_BR_MPRED", + "BriefDescription": "Cycles when dispatch was stalled for this thread due to a mispredicted branch." + }, + { + "EventCode": "0x4E010", + "EventName": "PM_DISP_STALL_IC_L3MISS", + "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from any source beyond the local L3." + }, + { "EventCode": "0x4E012", "EventName": "PM_EXEC_STALL_UNKNOWN", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline completed without an ntf_type pulse. The ntf_pulse was missed by the ISU because the NTF finishes and completions came too close together." + "BriefDescription": "Cycles in which the oldest instruction in the pipeline completed without an ntf_type pulse. The ntf_pulse was missed by the ISU because the next-to-finish (NTF) instruction finishes and completions came too close together." + }, + { + "EventCode": "0x4E01A", + "EventName": "PM_DISP_STALL_HELD_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch for any reason." }, { "EventCode": "0x4D020", @@ -260,24 +430,24 @@ "BriefDescription": "VSU instruction was issued to VSU pipe 3." }, { - "EventCode": "0x40132", - "EventName": "PM_MRK_LSU_FIN", - "BriefDescription": "LSU marked instruction finish." + "EventCode": "0x4003C", + "EventName": "PM_DISP_STALL_HELD_SYNC_CYC", + "BriefDescription": "Cycles in which the next-to-complete (NTC) instruction is held at dispatch because of a synchronizing instruction that requires the ICT to be empty before dispatch." }, { "EventCode": "0x45058", "EventName": "PM_IC_MISS_CMPL", - "BriefDescription": "Non-speculative icache miss, counted at completion." + "BriefDescription": "Non-speculative instruction cache miss, counted at completion." }, { - "EventCode": "0x4D050", - "EventName": "PM_VSU_NON_FLOP_CMPL", - "BriefDescription": "Non-floating point VSU instructions completed." + "EventCode": "0x40060", + "EventName": "PM_DISP_HELD_SCOREBOARD_CYC", + "BriefDescription": "Cycles dispatch is held while waiting on the Scoreboard. This event combines VSCR and FPSCR together." }, { - "EventCode": "0x4D052", - "EventName": "PM_2FLOP_CMPL", - "BriefDescription": "Double Precision vector version of fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg completed." + "EventCode": "0x40062", + "EventName": "PM_DISP_HELD_RENAME_CYC", + "BriefDescription": "Cycles dispatch is held because the mapper/SRB was full. Includes GPR (count, link, tar), VSR, VMR, FPR and XVFC." }, { "EventCode": "0x400F2", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json index b5d1bd39cfb2..c606ae03cd27 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json @@ -1,22 +1,202 @@ [ { + "EventCode": "0x100FE", + "EventName": "PM_INST_CMPL", + "BriefDescription": "PowerPC instruction completed." + }, + { + "EventCode": "0x1000A", + "EventName": "PM_PMC3_REWIND", + "BriefDescription": "The speculative event selected for PMC3 rewinds and the counter for PMC3 is not charged." + }, + { + "EventCode": "0x10010", + "EventName": "PM_PMC4_OVERFLOW", + "BriefDescription": "The event selected for PMC4 caused the event counter to overflow." + }, + { + "EventCode": "0x1001C", + "EventName": "PM_ULTRAVISOR_INST_CMPL", + "BriefDescription": "PowerPC instruction completed while the thread was in ultravisor state." + }, + { + "EventCode": "0x100F0", + "EventName": "PM_CYC", + "BriefDescription": "Processor cycles." + }, + { + "EventCode": "0x10020", + "EventName": "PM_PMC4_REWIND", + "BriefDescription": "The speculative event selected for PMC4 rewinds and the counter for PMC4 is not charged." + }, + { + "EventCode": "0x10022", + "EventName": "PM_PMC2_SAVED", + "BriefDescription": "The conditions for the speculative event selected for PMC2 are met and PMC2 is charged." + }, + { + "EventCode": "0x10024", + "EventName": "PM_PMC5_OVERFLOW", + "BriefDescription": "The event selected for PMC5 caused the event counter to overflow." + }, + { + "EventCode": "0x1002A", + "EventName": "PM_PMC3_HELD_CYC", + "BriefDescription": "Cycles when the speculative counter for PMC3 is frozen." + }, + { + "EventCode": "0x1F15E", + "EventName": "PM_MRK_START_PROBE_NOP_CMPL", + "BriefDescription": "Marked Start probe nop (AND R0,R0,R0) completed." + }, + { + "EventCode": "0x1006C", + "EventName": "PM_RUN_CYC_ST_MODE", + "BriefDescription": "Cycles when the run latch is set and the core is in ST mode." + }, + { + "EventCode": "0x101E8", + "EventName": "PM_THRESH_EXC_256", + "BriefDescription": "Threshold counter exceeded a count of 256." + }, + { + "EventCode": "0x101EC", + "EventName": "PM_THRESH_MET", + "BriefDescription": "Threshold exceeded." + }, + { + "EventCode": "0x100FA", + "EventName": "PM_RUN_LATCH_ANY_THREAD_CYC", + "BriefDescription": "Cycles when at least one thread has the run latch set." + }, + { + "EventCode": "0x2000A", + "EventName": "PM_HYPERVISOR_CYC", + "BriefDescription": "Cycles when the thread is in Hypervisor state. MSR[S HV PR]=010." + }, + { + "EventCode": "0x2000C", + "EventName": "PM_RUN_LATCH_ALL_THREADS_CYC", + "BriefDescription": "Cycles when the run latch is set for all threads." + }, + { + "EventCode": "0x20010", + "EventName": "PM_PMC1_OVERFLOW", + "BriefDescription": "The event selected for PMC1 caused the event counter to overflow." + }, + { + "EventCode": "0x2006C", + "EventName": "PM_RUN_CYC_SMT4_MODE", + "BriefDescription": "Cycles when this thread's run latch is set and the core is in SMT4 mode." + }, + { + "EventCode": "0x201E6", + "EventName": "PM_THRESH_EXC_32", + "BriefDescription": "Threshold counter exceeded a value of 32." + }, + { + "EventCode": "0x201E8", + "EventName": "PM_THRESH_EXC_512", + "BriefDescription": "Threshold counter exceeded a value of 512." + }, + { + "EventCode": "0x200F4", + "EventName": "PM_RUN_CYC", + "BriefDescription": "Processor cycles gated by the run latch." + }, + { + "EventCode": "0x30010", + "EventName": "PM_PMC2_OVERFLOW", + "BriefDescription": "The event selected for PMC2 caused the event counter to overflow." + }, + { + "EventCode": "0x30020", + "EventName": "PM_PMC2_REWIND", + "BriefDescription": "The speculative event selected for PMC2 rewinds and the counter for PMC2 is not charged." + }, + { + "EventCode": "0x30022", + "EventName": "PM_PMC4_SAVED", + "BriefDescription": "The conditions for the speculative event selected for PMC4 are met and PMC4 is charged." + }, + { + "EventCode": "0x30024", + "EventName": "PM_PMC6_OVERFLOW", + "BriefDescription": "The event selected for PMC6 caused the event counter to overflow." + }, + { + "EventCode": "0x3006C", + "EventName": "PM_RUN_CYC_SMT2_MODE", + "BriefDescription": "Cycles when this thread's run latch is set and the core is in SMT2 mode." + }, + { "EventCode": "0x301E8", "EventName": "PM_THRESH_EXC_64", "BriefDescription": "Threshold counter exceeded a value of 64." }, { - "EventCode": "0x45050", - "EventName": "PM_1FLOP_CMPL", - "BriefDescription": "One floating point instruction completed (fadd, fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg)." + "EventCode": "0x301EA", + "EventName": "PM_THRESH_EXC_1024", + "BriefDescription": "Threshold counter exceeded a value of 1024." + }, + { + "EventCode": "0x40010", + "EventName": "PM_PMC3_OVERFLOW", + "BriefDescription": "The event selected for PMC3 caused the event counter to overflow." + }, + { + "EventCode": "0x40114", + "EventName": "PM_MRK_START_PROBE_NOP_DISP", + "BriefDescription": "Marked Start probe nop dispatched. Instruction AND R0,R0,R0." + }, + { + "EventCode": "0x4D010", + "EventName": "PM_PMC1_SAVED", + "BriefDescription": "The conditions for the speculative event selected for PMC1 are met and PMC1 is charged." + }, + { + "EventCode": "0x4D012", + "EventName": "PM_PMC3_SAVED", + "BriefDescription": "The conditions for the speculative event selected for PMC3 are met and PMC3 is charged." + }, + { + "EventCode": "0x4D022", + "EventName": "PM_HYPERVISOR_INST_CMPL", + "BriefDescription": "PowerPC instruction completed while the thread was in hypervisor state." + }, + { + "EventCode": "0x4D026", + "EventName": "PM_ULTRAVISOR_CYC", + "BriefDescription": "Cycles when the thread is in Ultravisor state. MSR[S HV PR]=110." + }, + { + "EventCode": "0x4D028", + "EventName": "PM_PRIVILEGED_CYC", + "BriefDescription": "Cycles when the thread is in Privileged state. MSR[S HV PR]=x00." + }, + { + "EventCode": "0x4D02C", + "EventName": "PM_PMC1_REWIND", + "BriefDescription": "The speculative event selected for PMC1 rewinds and the counter for PMC1 is not charged." + }, + { + "EventCode": "0x40030", + "EventName": "PM_INST_FIN", + "BriefDescription": "Instruction finished." + }, + { + "EventCode": "0x40134", + "EventName": "PM_MRK_INST_TIMEO", + "BriefDescription": "Marked instruction finish timeout (instruction was lost)." }, { - "EventCode": "0x45052", - "EventName": "PM_4FLOP_CMPL", - "BriefDescription": "Four floating point instructions completed (fadd, fmul, fsub, fcmp, fsel, fabs, fnabs, fres, fsqrte, fneg)." + "EventCode": "0x401EA", + "EventName": "PM_THRESH_EXC_128", + "BriefDescription": "Threshold counter exceeded a value of 128." }, { - "EventCode": "0x4D054", - "EventName": "PM_8FLOP_CMPL", - "BriefDescription": "Four Double Precision vector instructions completed." + "EventCode": "0x400FA", + "EventName": "PM_RUN_INST_CMPL", + "BriefDescription": "PowerPC instruction completed while the run latch is set." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/translation.json b/tools/perf/pmu-events/arch/powerpc/power10/translation.json index db3766dca07c..ea73900d248a 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/translation.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/translation.json @@ -1,35 +1,10 @@ [ { - "EventCode": "0x1F15E", - "EventName": "PM_MRK_START_PROBE_NOP_CMPL", - "BriefDescription": "Marked Start probe nop (AND R0,R0,R0) completed." - }, - { - "EventCode": "0x20016", - "EventName": "PM_ST_FIN", - "BriefDescription": "Store finish count. Includes speculative activity." - }, - { "EventCode": "0x20018", "EventName": "PM_ST_FWD", "BriefDescription": "Store forwards that finished." }, { - "EventCode": "0x2011C", - "EventName": "PM_MRK_NTF_CYC", - "BriefDescription": "Cycles during which the marked instruction is the oldest in the pipeline (NTF or NTC)." - }, - { - "EventCode": "0x2E01C", - "EventName": "PM_EXEC_STALL_TLBIE", - "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a TLBIE instruction executing in the Load Store Unit." - }, - { - "EventCode": "0x201E6", - "EventName": "PM_THRESH_EXC_32", - "BriefDescription": "Threshold counter exceeded a value of 32." - }, - { "EventCode": "0x200F0", "EventName": "PM_ST_CMPL", "BriefDescription": "Stores completed from S2Q (2nd-level store queue). This event includes regular stores, stcx and cache inhibited stores. The following operations are excluded (pteupdate, snoop tlbie complete, store atomics, miso, load atomic payloads, tlbie, tlbsync, slbieg, isync, msgsnd, slbiag, cpabort, copy, tcheck, tend, stsync, dcbst, icbi, dcbf, hwsync, lwsync, ptesync, eieio, msgsync)." @@ -37,21 +12,11 @@ { "EventCode": "0x200FE", "EventName": "PM_DATA_FROM_L2MISS", - "BriefDescription": "The processor's data cache was reloaded from a source other than the local core's L1 or L2 due to a demand miss." - }, - { - "EventCode": "0x30010", - "EventName": "PM_PMC2_OVERFLOW", - "BriefDescription": "The event selected for PMC2 caused the event counter to overflow." - }, - { - "EventCode": "0x4D010", - "EventName": "PM_PMC1_SAVED", - "BriefDescription": "The conditions for the speculative event selected for PMC1 are met and PMC1 is charged." + "BriefDescription": "The processor's L1 data cache was reloaded from a source beyond the local core's L2 due to a demand miss." }, { - "EventCode": "0x4D05C", - "EventName": "PM_DPP_FLOP_CMPL", - "BriefDescription": "Double-Precision or Quad-Precision instructions completed." + "EventCode": "0x300F0", + "EventName": "PM_ST_MISS_L1", + "BriefDescription": "Store Missed L1." } ] diff --git a/tools/perf/pmu-events/arch/x86/alderlake/adl-metrics.json b/tools/perf/pmu-events/arch/x86/alderlake/adl-metrics.json index daf9458f0b77..c6780d5c456b 100644 --- a/tools/perf/pmu-events/arch/x86/alderlake/adl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/alderlake/adl-metrics.json @@ -558,6 +558,7 @@ }, { "BriefDescription": "Counts the number of cycles a core is stalled due to a demand load which hit in the Last Level Cache (LLC) or other core with HITE/F/M.", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "cpu_atom@MEM_BOUND_STALLS.LOAD_LLC_HIT@ / tma_info_core_clks - max((cpu_atom@MEM_BOUND_STALLS.LOAD@ - cpu_atom@LD_HEAD.L1_MISS_AT_RET@) / tma_info_core_clks, 0) * cpu_atom@MEM_BOUND_STALLS.LOAD_LLC_HIT@ / cpu_atom@MEM_BOUND_STALLS.LOAD@", "MetricGroup": "TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -800,6 +801,7 @@ }, { "BriefDescription": "Counts the number of cycles that the oldest load of the load buffer is stalled at retirement due to a store forward block.", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_HEAD.ST_ADDR_AT_RET / tma_info_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", @@ -1058,7 +1060,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -1230,6 +1231,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -1267,6 +1269,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_fb_full / (tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -1355,7 +1358,6 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu_core@FP_ARITH_INST_RETIRED.SCALAR_SINGLE@ + cpu_core@FP_ARITH_INST_RETIRED.SCALAR_DOUBLE@ + 2 * cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + 4 * (cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE@ + cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_DOUBLE@) + 8 * cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@) / tma_info_core_core_clks", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc", @@ -1363,7 +1365,6 @@ }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu_core@FP_ARITH_DISPATCHED.PORT_0@ + cpu_core@FP_ARITH_DISPATCHED.PORT_1@ + cpu_core@FP_ARITH_DISPATCHED.PORT_5@) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -1769,7 +1770,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu_core@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire", @@ -2002,6 +2002,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(cpu_core@MEMORY_ACTIVITY.STALLS_L2_MISS@ - cpu_core@MEMORY_ACTIVITY.STALLS_L3_MISS@) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -2375,6 +2376,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -2405,6 +2407,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * cpu_core@LD_BLOCKS.STORE_FORWARD@ / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/alderlaken/adln-metrics.json b/tools/perf/pmu-events/arch/x86/alderlaken/adln-metrics.json index 0f1628d698da..06e67e34e1bf 100644 --- a/tools/perf/pmu-events/arch/x86/alderlaken/adln-metrics.json +++ b/tools/perf/pmu-events/arch/x86/alderlaken/adln-metrics.json @@ -466,6 +466,7 @@ }, { "BriefDescription": "Counts the number of cycles a core is stalled due to a demand load which hit in the Last Level Cache (LLC) or other core with HITE/F/M.", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_BOUND_STALLS.LOAD_LLC_HIT / tma_info_core_clks - max((MEM_BOUND_STALLS.LOAD - LD_HEAD.L1_MISS_AT_RET) / tma_info_core_clks, 0) * MEM_BOUND_STALLS.LOAD_LLC_HIT / MEM_BOUND_STALLS.LOAD", "MetricGroup": "TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -682,6 +683,7 @@ }, { "BriefDescription": "Counts the number of cycles that the oldest load of the load buffer is stalled at retirement due to a store forward block.", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_HEAD.ST_ADDR_AT_RET / tma_info_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/icelake/icl-metrics.json b/tools/perf/pmu-events/arch/x86/icelake/icl-metrics.json index 8fcc05c4e0a1..a6eed0d9a26d 100644 --- a/tools/perf/pmu-events/arch/x86/icelake/icl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/icelake/icl-metrics.json @@ -85,6 +85,7 @@ }, { "BriefDescription": "This metric estimates how often memory load accesses were aliased by preceding stores (in program order) with a 4K address offset", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_4k_aliasing", @@ -319,7 +320,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -464,6 +464,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -497,6 +498,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_fb_full / (tma_4k_aliasing + tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -574,14 +576,12 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + 2 * FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE + 4 * cpu@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE\\,umask\\=0x18@ + 8 * cpu@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE\\,umask\\=0x60@ + 16 * FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE) / tma_info_core_core_clks", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc" }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + cpu@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE\\,umask\\=0xfc@) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -927,7 +927,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire" @@ -1100,6 +1099,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(CYCLE_ACTIVITY.STALLS_L2_MISS - CYCLE_ACTIVITY.STALLS_L3_MISS) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -1419,6 +1419,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -1446,6 +1447,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * LD_BLOCKS.STORE_FORWARD / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json b/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json index 9bb7e3f20f7f..7082ad5ba961 100644 --- a/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json +++ b/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json @@ -289,6 +289,7 @@ }, { "BriefDescription": "This metric estimates how often memory load accesses were aliased by preceding stores (in program order) with a 4K address offset", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_4k_aliasing", @@ -523,7 +524,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -668,6 +668,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -701,6 +702,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_fb_full / (tma_4k_aliasing + tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -778,14 +780,12 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + 2 * FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE + 4 * cpu@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE\\,umask\\=0x18@ + 8 * cpu@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE\\,umask\\=0x60@ + 16 * FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE) / tma_info_core_core_clks", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc" }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + cpu@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE\\,umask\\=0xfc@) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -1144,7 +1144,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire" @@ -1369,6 +1368,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(CYCLE_ACTIVITY.STALLS_L2_MISS - CYCLE_ACTIVITY.STALLS_L3_MISS) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -1715,6 +1715,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -1742,6 +1743,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * LD_BLOCKS.STORE_FORWARD / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/mapfile.csv b/tools/perf/pmu-events/arch/x86/mapfile.csv index 6650100830c4..3a8770e29fe8 100644 --- a/tools/perf/pmu-events/arch/x86/mapfile.csv +++ b/tools/perf/pmu-events/arch/x86/mapfile.csv @@ -19,12 +19,12 @@ GenuineIntel-6-3A,v24,ivybridge,core GenuineIntel-6-3E,v23,ivytown,core GenuineIntel-6-2D,v23,jaketown,core GenuineIntel-6-(57|85),v10,knightslanding,core -GenuineIntel-6-A[AC],v1.03,meteorlake,core +GenuineIntel-6-A[AC],v1.04,meteorlake,core GenuineIntel-6-1[AEF],v3,nehalemep,core GenuineIntel-6-2E,v3,nehalemex,core GenuineIntel-6-A7,v1.01,rocketlake,core GenuineIntel-6-2A,v19,sandybridge,core -GenuineIntel-6-(8F|CF),v1.14,sapphirerapids,core +GenuineIntel-6-(8F|CF),v1.15,sapphirerapids,core GenuineIntel-6-AF,v1.00,sierraforest,core GenuineIntel-6-(37|4A|4C|4D|5A),v15,silvermont,core GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v57,skylake,core diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json index e1ae7c92f38e..1de0200b32f6 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/cache.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/cache.json @@ -37,6 +37,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Number of cycles a demand request has waited due to L1D due to lack of L2 resources.", + "EventCode": "0x48", + "EventName": "L1D_PEND_MISS.L2_STALLS", + "PublicDescription": "Counts number of cycles a demand request has waited due to L1D due to lack of L2 resources. Demand requests include cacheable/uncacheable demand load, store, lock or SW prefetch accesses.", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { "BriefDescription": "Number of L1D misses that are outstanding", "EventCode": "0x48", "EventName": "L1D_PEND_MISS.PENDING", @@ -261,6 +270,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cycles when L1D is locked", + "EventCode": "0x42", + "EventName": "LOCK_CYCLES.CACHE_LOCK_DURATION", + "PublicDescription": "This event counts the number of cycles when the L1D is locked. It is a superset of the 0x1 mask (BUS_LOCK_CLOCKS.BUS_LOCK_DURATION).", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of cacheable memory requests that miss in the LLC. Counts on a per core basis.", "EventCode": "0x2e", "EventName": "LONGEST_LAT_CACHE.MISS", @@ -515,6 +533,17 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Retired load instructions whose data sources were L3 hit and cross-core snoop missed in on-pkg core cache.", + "Data_LA": "1", + "EventCode": "0xd2", + "EventName": "MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS", + "PEBS": "1", + "PublicDescription": "Counts the retired load instructions whose data sources were L3 hit and cross-core snoop missed in on-pkg core cache.", + "SampleAfterValue": "20011", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Retired load instructions whose data sources were hits in L3 without snoops required", "Data_LA": "1", "EventCode": "0xd2", @@ -731,6 +760,14 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "MEM_STORE_RETIRED.L2_HIT", + "EventCode": "0x44", + "EventName": "MEM_STORE_RETIRED.L2_HIT", + "SampleAfterValue": "200003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of load ops retired.", "Data_LA": "1", "EventCode": "0xd0", @@ -978,6 +1015,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cacheable and Non-Cacheable code read requests", + "EventCode": "0x21", + "EventName": "OFFCORE_REQUESTS.DEMAND_CODE_RD", + "PublicDescription": "Counts both cacheable and Non-Cacheable code read requests.", + "SampleAfterValue": "100003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Demand Data Read requests sent to uncore", "EventCode": "0x21", "EventName": "OFFCORE_REQUESTS.DEMAND_DATA_RD", @@ -996,6 +1042,89 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cycles when offcore outstanding cacheable Core Data Read transactions are present in SuperQueue (SQ), queue to uncore.", + "CounterMask": "1", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD", + "PublicDescription": "Counts cycles when offcore outstanding cacheable Core Data Read transactions are present in the super queue. A transaction is considered to be in the Offcore outstanding state between L2 miss and transaction completion sent to requestor (SQ de-allocation). See corresponding Umask under OFFCORE_REQUESTS.", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles with offcore outstanding Code Reads transactions in the SuperQueue (SQ), queue to uncore.", + "CounterMask": "1", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_CODE_RD", + "PublicDescription": "Counts the number of offcore outstanding Code Reads transactions in the super queue every cycle. The 'Offcore outstanding' state of the transaction lasts from the L2 miss until the sending transaction completion to requestor (SQ deallocation). See the corresponding Umask under OFFCORE_REQUESTS.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where at least 1 outstanding demand data read request is pending.", + "CounterMask": "1", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_DATA_RD", + "SampleAfterValue": "2000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles with offcore outstanding demand rfo reads transactions in SuperQueue (SQ), queue to uncore.", + "CounterMask": "1", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_RFO", + "PublicDescription": "Counts the number of offcore outstanding demand rfo Reads transactions in the super queue every cycle. The 'Offcore outstanding' state of the transaction lasts from the L2 miss until the sending transaction completion to requestor (SQ deallocation). See the corresponding Umask under OFFCORE_REQUESTS.", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { + "BriefDescription": "OFFCORE_REQUESTS_OUTSTANDING.DATA_RD", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.DATA_RD", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Offcore outstanding Code Reads transactions in the SuperQueue (SQ), queue to uncore, every cycle.", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.DEMAND_CODE_RD", + "PublicDescription": "Counts the number of offcore outstanding Code Reads transactions in the super queue every cycle. The 'Offcore outstanding' state of the transaction lasts from the L2 miss until the sending transaction completion to requestor (SQ deallocation). See the corresponding Umask under OFFCORE_REQUESTS.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "For every cycle, increments by the number of outstanding demand data read requests pending.", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD", + "PublicDescription": "For every cycle, increments by the number of outstanding demand data read requests pending. Requests are considered outstanding from the time they miss the core's L2 cache until the transaction completion message is sent to the requestor.", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles with at least 6 offcore outstanding Demand Data Read transactions in uncore queue.", + "CounterMask": "6", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD_GE_6", + "SampleAfterValue": "2000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Store Read transactions pending for off-core. Highly correlated.", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.DEMAND_RFO", + "PublicDescription": "Counts the number of off-core outstanding read-for-ownership (RFO) store transactions every cycle. An RFO transaction is considered to be in the Off-core outstanding state between L2 cache miss and transaction completion.", + "SampleAfterValue": "1000003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts bus locks, accounts for cache line split locks and UC locks.", "EventCode": "0x2c", "EventName": "SQ_MISC.BUS_LOCK", @@ -1005,6 +1134,42 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Number of PREFETCHNTA instructions executed.", + "EventCode": "0x40", + "EventName": "SW_PREFETCH_ACCESS.NTA", + "PublicDescription": "Counts the number of PREFETCHNTA instructions executed.", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Number of PREFETCHW instructions executed.", + "EventCode": "0x40", + "EventName": "SW_PREFETCH_ACCESS.PREFETCHW", + "PublicDescription": "Counts the number of PREFETCHW instructions executed.", + "SampleAfterValue": "100003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Number of PREFETCHT0 instructions executed.", + "EventCode": "0x40", + "EventName": "SW_PREFETCH_ACCESS.T0", + "PublicDescription": "Counts the number of PREFETCHT0 instructions executed.", + "SampleAfterValue": "100003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Number of PREFETCHT1 or PREFETCHT2 instructions executed.", + "EventCode": "0x40", + "EventName": "SW_PREFETCH_ACCESS.T1_T2", + "PublicDescription": "Counts the number of PREFETCHT1 or PREFETCHT2 instructions executed.", + "SampleAfterValue": "100003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of issue slots every cycle that were not delivered by the frontend due to an icache miss", "EventCode": "0x71", "EventName": "TOPDOWN_FE_BOUND.ICACHE", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json b/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json index 616489f0974a..f66506ee37ef 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/floating-point.json @@ -42,6 +42,14 @@ "Unit": "cpu_core" }, { + "BriefDescription": "FP_ARITH_DISPATCHED.PORT_5", + "EventCode": "0xb3", + "EventName": "FP_ARITH_DISPATCHED.PORT_5", + "SampleAfterValue": "2000003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts number of SSE/AVX computational 128-bit packed double precision floating-point instructions retired; some instructions will count twice as noted below. Each count represents 2 computation operations, one for each element. Applies to SSE* and AVX* packed double precision floating-point instructions: ADD SUB HADD HSUB SUBADD MUL DIV MIN MAX SQRT DPP FM(N)ADD/SUB. DPP and FM(N)ADD/SUB instructions count twice as they perform 2 calculations per element.", "EventCode": "0xc7", "EventName": "FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json index 0f064518d1c0..8264419500a5 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/frontend.json @@ -44,6 +44,14 @@ "Unit": "cpu_core" }, { + "BriefDescription": "DSB_FILL.FB_STALL_OT", + "EventCode": "0x62", + "EventName": "DSB_FILL.FB_STALL_OT", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { "BriefDescription": "Retired ANT branches", "EventCode": "0xc6", "EventName": "FRONTEND_RETIRED.ANY_ANT", @@ -56,6 +64,30 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Retired Instructions who experienced DSB miss.", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.ANY_DSB_MISS", + "MSRIndex": "0x3F7", + "MSRValue": "0x1", + "PEBS": "1", + "PublicDescription": "Counts retired Instructions that experienced DSB (Decode stream buffer i.e. the decoded instruction-cache) miss.", + "SampleAfterValue": "100007", + "UMask": "0x3", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Retired Instructions who experienced a critical DSB miss.", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.DSB_MISS", + "MSRIndex": "0x3F7", + "MSRValue": "0x11", + "PEBS": "1", + "PublicDescription": "Number of retired Instructions that experienced a critical DSB (Decode stream buffer i.e. the decoded instruction-cache) miss. Critical means stalls were exposed to the back-end as a result of the DSB miss.", + "SampleAfterValue": "100007", + "UMask": "0x3", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of instructions retired that were tagged because empty issue slots were seen before the uop due to ITLB miss", "EventCode": "0xc6", "EventName": "FRONTEND_RETIRED.ITLB_MISS", @@ -89,6 +121,18 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Retired Instructions who experienced Instruction L2 Cache true miss.", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.L2_MISS", + "MSRIndex": "0x3F7", + "MSRValue": "0x13", + "PEBS": "1", + "PublicDescription": "Counts retired Instructions who experienced Instruction L2 Cache true miss.", + "SampleAfterValue": "100007", + "UMask": "0x3", + "Unit": "cpu_core" + }, + { "BriefDescription": "Retired instructions after front-end starvation of at least 1 cycle", "EventCode": "0xc6", "EventName": "FRONTEND_RETIRED.LATENCY_GE_1", @@ -244,6 +288,18 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Retired Instructions who experienced STLB (2nd level TLB) true miss.", + "EventCode": "0xc6", + "EventName": "FRONTEND_RETIRED.STLB_MISS", + "MSRIndex": "0x3F7", + "MSRValue": "0x15", + "PEBS": "1", + "PublicDescription": "Counts retired Instructions that experienced STLB (2nd level TLB) true miss.", + "SampleAfterValue": "100007", + "UMask": "0x3", + "Unit": "cpu_core" + }, + { "BriefDescription": "FRONTEND_RETIRED.UNKNOWN_BRANCH", "EventCode": "0xc6", "EventName": "FRONTEND_RETIRED.UNKNOWN_BRANCH", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/memory.json b/tools/perf/pmu-events/arch/x86/meteorlake/memory.json index 67e949b4c789..2605e1d0ba9f 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/memory.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/memory.json @@ -67,6 +67,15 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "Number of machine clears due to memory ordering conflicts.", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.MEMORY_ORDERING", + "PublicDescription": "Counts the number of Machine Clears detected dye to memory ordering. Memory Ordering Machine Clears may apply when a memory read may not conform to the memory ordering rules of the x86 architecture", + "SampleAfterValue": "100003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Execution stalls while L1 cache miss demand load is outstanding.", "CounterMask": "3", "EventCode": "0x47", @@ -96,6 +105,35 @@ "Unit": "cpu_core" }, { + "BriefDescription": "MEMORY_ORDERING.MD_NUKE", + "EventCode": "0x09", + "EventName": "MEMORY_ORDERING.MD_NUKE", + "SampleAfterValue": "100003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts the number of memory ordering machine clears due to memory renaming.", + "EventCode": "0x09", + "EventName": "MEMORY_ORDERING.MRN_NUKE", + "SampleAfterValue": "100003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 1024 cycles.", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_1024", + "MSRIndex": "0x3F6", + "MSRValue": "0x400", + "PEBS": "2", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 1024 cycles. Reported latency may be longer than just the memory latency.", + "SampleAfterValue": "53", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 128 cycles.", "Data_LA": "1", "EventCode": "0xcd", @@ -122,6 +160,19 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 2048 cycles.", + "Data_LA": "1", + "EventCode": "0xcd", + "EventName": "MEM_TRANS_RETIRED.LOAD_LATENCY_GT_2048", + "MSRIndex": "0x3F6", + "MSRValue": "0x800", + "PEBS": "2", + "PublicDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 2048 cycles. Reported latency may be longer than just the memory latency.", + "SampleAfterValue": "23", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts randomly selected loads when the latency from first dispatch to completion is greater than 256 cycles.", "Data_LA": "1", "EventCode": "0xcd", @@ -235,5 +286,34 @@ "SampleAfterValue": "100003", "UMask": "0x10", "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where data return is pending for a Demand Data Read request who miss L3 cache.", + "CounterMask": "1", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_L3_MISS_DEMAND_DATA_RD", + "PublicDescription": "Cycles with at least 1 Demand Data Read requests who miss L3 cache in the superQ.", + "SampleAfterValue": "1000003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "For every cycle, increments by the number of demand data read requests pending that are known to have missed the L3 cache.", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD", + "PublicDescription": "For every cycle, increments by the number of demand data read requests pending that are known to have missed the L3 cache. Note that this does not capture all elapsed cycles while requests are outstanding - only cycles from when the requests were known by the requesting core to have missed the L3 cache.", + "SampleAfterValue": "2000003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles where the core is waiting on at least 6 outstanding demand data read requests known to have missed the L3 cache.", + "CounterMask": "6", + "EventCode": "0x20", + "EventName": "OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD_GE_6", + "PublicDescription": "Cycles where the core is waiting on at least 6 outstanding demand data read requests known to have missed the L3 cache. Note that this event does not capture all elapsed cycles while the requests are outstanding - only cycles from when the requests were known to have missed the L3 cache.", + "SampleAfterValue": "2000003", + "UMask": "0x10", + "Unit": "cpu_core" } ] diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/other.json b/tools/perf/pmu-events/arch/x86/meteorlake/other.json index 2ec57f487525..f4c603599df4 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/other.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/other.json @@ -1,5 +1,13 @@ [ { + "BriefDescription": "ASSISTS.PAGE_FAULT", + "EventCode": "0xc1", + "EventName": "ASSISTS.PAGE_FAULT", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts streaming stores that have any type of response.", "EventCode": "0x2A,0x2B", "EventName": "OCR.STREAMING_WR.ANY_RESPONSE", @@ -31,6 +39,14 @@ "Unit": "cpu_core" }, { + "BriefDescription": "RS.EMPTY_RESOURCE", + "EventCode": "0xa5", + "EventName": "RS.EMPTY_RESOURCE", + "SampleAfterValue": "1000003", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of issue slots in a UMWAIT or TPAUSE instruction where no uop issues due to the instruction putting the CPU into the C0.1 activity state. For Tremont, UMWAIT and TPAUSE will only put the CPU into C0.1 activity state (not C0.2 activity state)", "EventCode": "0x75", "EventName": "SERIALIZATION.C01_MS_SCB", diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json index eeaa7a97f71c..352c5efafc06 100644 --- a/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json +++ b/tools/perf/pmu-events/arch/x86/meteorlake/pipeline.json @@ -312,6 +312,16 @@ "Unit": "cpu_core" }, { + "BriefDescription": "This event counts the number of mispredicted ret instructions retired. Non PEBS", + "EventCode": "0xc5", + "EventName": "BR_MISP_RETIRED.RET", + "PEBS": "1", + "PublicDescription": "This is a non-precise version (that is, does not use PEBS) of the event that counts mispredicted return instructions retired.", + "SampleAfterValue": "100007", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of mispredicted near RET branch instructions retired.", "EventCode": "0xc5", "EventName": "BR_MISP_RETIRED.RETURN", @@ -330,6 +340,33 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Core clocks when the thread is in the C0.1 light-weight slower wakeup time but more power saving optimized state.", + "EventCode": "0xec", + "EventName": "CPU_CLK_UNHALTED.C01", + "PublicDescription": "Counts core clocks when the thread is in the C0.1 light-weight slower wakeup time but more power saving optimized state. This state can be entered via the TPAUSE or UMWAIT instructions.", + "SampleAfterValue": "2000003", + "UMask": "0x10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Core clocks when the thread is in the C0.2 light-weight faster wakeup time but less power saving optimized state.", + "EventCode": "0xec", + "EventName": "CPU_CLK_UNHALTED.C02", + "PublicDescription": "Counts core clocks when the thread is in the C0.2 light-weight faster wakeup time but less power saving optimized state. This state can be entered via the TPAUSE or UMWAIT instructions.", + "SampleAfterValue": "2000003", + "UMask": "0x20", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Core clocks when the thread is in the C0.1 or C0.2 or running a PAUSE in C0 ACPI state.", + "EventCode": "0xec", + "EventName": "CPU_CLK_UNHALTED.C0_WAIT", + "PublicDescription": "Counts core clocks when the thread is in the C0.1 or C0.2 power saving optimized states (TPAUSE or UMWAIT instructions) or running the PAUSE instruction.", + "SampleAfterValue": "2000003", + "UMask": "0x70", + "Unit": "cpu_core" + }, + { "BriefDescription": "Fixed Counter: Counts the number of unhalted core clock cycles", "EventName": "CPU_CLK_UNHALTED.CORE", "SampleAfterValue": "2000003", @@ -362,6 +399,24 @@ "Unit": "cpu_core" }, { + "BriefDescription": "CPU_CLK_UNHALTED.PAUSE", + "EventCode": "0xec", + "EventName": "CPU_CLK_UNHALTED.PAUSE", + "SampleAfterValue": "2000003", + "UMask": "0x40", + "Unit": "cpu_core" + }, + { + "BriefDescription": "CPU_CLK_UNHALTED.PAUSE_INST", + "CounterMask": "1", + "EdgeDetect": "1", + "EventCode": "0xec", + "EventName": "CPU_CLK_UNHALTED.PAUSE_INST", + "SampleAfterValue": "2000003", + "UMask": "0x40", + "Unit": "cpu_core" + }, + { "BriefDescription": "Core crystal clock cycles. Cycle counts are evenly distributed between active threads in the Core.", "EventCode": "0x3c", "EventName": "CPU_CLK_UNHALTED.REF_DISTRIBUTED", @@ -603,6 +658,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Retired NOP instructions.", + "EventCode": "0xc0", + "EventName": "INST_RETIRED.NOP", + "PublicDescription": "Counts all retired NOP or ENDBR32/64 or PREFETCHIT0/1 instructions", + "SampleAfterValue": "2000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Precise instruction retired with PEBS precise-distribution", "EventName": "INST_RETIRED.PREC_DIST", "PEBS": "1", @@ -612,6 +676,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Iterations of Repeat string retired instructions.", + "EventCode": "0xc0", + "EventName": "INST_RETIRED.REP_ITERATION", + "PublicDescription": "Number of iterations of Repeat (REP) string retired instructions such as MOVS, CMPS, and SCAS. Each has a byte, word, and doubleword version and string instructions can be repeated using a repetition prefix, REP, that allows their architectural execution to be repeated a number of times as specified by the RCX register. Note the number of iterations is implementation-dependent.", + "SampleAfterValue": "2000003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { "BriefDescription": "Cycles the Backend cluster is recovering after a miss-speculation or a Store Buffer or Load Buffer drain stall.", "CounterMask": "1", "EventCode": "0xad", @@ -622,6 +695,17 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Clears speculative count", + "CounterMask": "1", + "EdgeDetect": "1", + "EventCode": "0xad", + "EventName": "INT_MISC.CLEARS_COUNT", + "PublicDescription": "Counts the number of speculative clears due to any type of branch misprediction or machine clears", + "SampleAfterValue": "500009", + "UMask": "0x1", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts cycles after recovery from a branch misprediction or machine clear till the first uop is issued from the resteered path.", "EventCode": "0xad", "EventName": "INT_MISC.CLEAR_RESTEER_CYCLES", @@ -631,6 +715,15 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cycles when Resource Allocation Table (RAT) external stall is sent to Instruction Decode Queue (IDQ) for the thread", + "EventCode": "0xad", + "EventName": "INT_MISC.RAT_STALLS", + "PublicDescription": "This event counts the number of cycles during which Resource Allocation Table (RAT) external stall is sent to Instruction Decode Queue (IDQ) for the current thread. This also includes the cycles during which the Allocator is serving another thread.", + "SampleAfterValue": "1000003", + "UMask": "0x8", + "Unit": "cpu_core" + }, + { "BriefDescription": "Core cycles the allocator was stalled due to recovery from earlier clear event for this thread", "EventCode": "0xad", "EventName": "INT_MISC.RECOVERY_CYCLES", @@ -734,6 +827,15 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "False dependencies in MOB due to partial compare on address.", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.ADDRESS_ALIAS", + "PublicDescription": "Counts the number of times a load got blocked due to false dependencies in MOB due to partial compare on address.", + "SampleAfterValue": "100003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of retired loads that are blocked because its address exactly matches an older store whose data is not ready.", "EventCode": "0x03", "EventName": "LD_BLOCKS.DATA_UNKNOWN", @@ -743,6 +845,15 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "The number of times that split load operations are temporarily blocked because all resources for handling the split accesses are in use.", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.NO_SR", + "PublicDescription": "Counts the number of times that split load operations are temporarily blocked because all resources for handling the split accesses are in use.", + "SampleAfterValue": "100003", + "UMask": "0x88", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts the number of retired loads that are blocked because its address partially overlapped with an older store.", "EventCode": "0x03", "EventName": "LD_BLOCKS.STORE_FORWARD", @@ -752,6 +863,15 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "Loads blocked due to overlapping with a preceding store that cannot be forwarded.", + "EventCode": "0x03", + "EventName": "LD_BLOCKS.STORE_FORWARD", + "PublicDescription": "Counts the number of times where store forwarding was prevented for a load operation. The most common case is a load blocked due to the address of memory access (partially) overlapping with a preceding uncompleted store. Note: See the table of not supported store forwards in the Optimization Guide.", + "SampleAfterValue": "100003", + "UMask": "0x82", + "Unit": "cpu_core" + }, + { "BriefDescription": "Cycles Uops delivered by the LSD, but didn't come from the decoder.", "CounterMask": "1", "EventCode": "0xa8", @@ -824,6 +944,24 @@ "Unit": "cpu_atom" }, { + "BriefDescription": "Self-modifying code (SMC) detected.", + "EventCode": "0xc3", + "EventName": "MACHINE_CLEARS.SMC", + "PublicDescription": "Counts self-modifying code (SMC) detected, which causes a machine clear.", + "SampleAfterValue": "100003", + "UMask": "0x4", + "Unit": "cpu_core" + }, + { + "BriefDescription": "LFENCE instructions retired", + "EventCode": "0xe0", + "EventName": "MISC2_RETIRED.LFENCE", + "PublicDescription": "number of LFENCE retired instructions", + "SampleAfterValue": "400009", + "UMask": "0x20", + "Unit": "cpu_core" + }, + { "BriefDescription": "Counts cycles where the pipeline is stalled due to serializing operations.", "EventCode": "0xa2", "EventName": "RESOURCE_STALLS.SCOREBOARD", @@ -1261,6 +1399,16 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cycles with retired uop(s).", + "CounterMask": "1", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.CYCLES", + "PublicDescription": "Counts cycles where at least one uop has retired.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Retired uops except the last uop of each instruction.", "EventCode": "0xc2", "EventName": "UOPS_RETIRED.HEAVY", @@ -1307,6 +1455,17 @@ "Unit": "cpu_core" }, { + "BriefDescription": "Cycles without actually retired uops.", + "CounterMask": "1", + "EventCode": "0xc2", + "EventName": "UOPS_RETIRED.STALLS", + "Invert": "1", + "PublicDescription": "This event counts cycles without actually retired uops.", + "SampleAfterValue": "1000003", + "UMask": "0x2", + "Unit": "cpu_core" + }, + { "BriefDescription": "Cycles with less than 10 actually retired uops.", "CounterMask": "10", "EventCode": "0xc2", diff --git a/tools/perf/pmu-events/arch/x86/rocketlake/rkl-metrics.json b/tools/perf/pmu-events/arch/x86/rocketlake/rkl-metrics.json index 1bb9cededa56..a0191c8b708d 100644 --- a/tools/perf/pmu-events/arch/x86/rocketlake/rkl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/rocketlake/rkl-metrics.json @@ -85,6 +85,7 @@ }, { "BriefDescription": "This metric estimates how often memory load accesses were aliased by preceding stores (in program order) with a 4K address offset", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_4k_aliasing", @@ -319,7 +320,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -464,6 +464,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -497,6 +498,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_fb_full / (tma_4k_aliasing + tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -574,14 +576,12 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + 2 * FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE + 4 * cpu@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE\\,umask\\=0x18@ + 8 * cpu@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE\\,umask\\=0x60@ + 16 * FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE) / tma_info_core_core_clks", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc" }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + cpu@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE\\,umask\\=0xfc@) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -933,7 +933,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire" @@ -1126,6 +1125,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(CYCLE_ACTIVITY.STALLS_L2_MISS - CYCLE_ACTIVITY.STALLS_L3_MISS) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -1445,6 +1445,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -1472,6 +1473,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * LD_BLOCKS.STORE_FORWARD / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/other.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/other.json index 31b6be9fb8c7..442ef3807a9d 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/other.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/other.json @@ -77,6 +77,24 @@ "UMask": "0x1" }, { + "BriefDescription": "Counts demand data reads that were supplied by PMM attached to this socket, whether or not in Sub NUMA Cluster(SNC) Mode. In SNC Mode counts PMM accesses that are controlled by the close or distant SNC Cluster.", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_DATA_RD.LOCAL_SOCKET_PMM", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x700C00001", + "SampleAfterValue": "100003", + "UMask": "0x1" + }, + { + "BriefDescription": "Counts demand data reads that were supplied by PMM.", + "EventCode": "0x2A,0x2B", + "EventName": "OCR.DEMAND_DATA_RD.PMM", + "MSRIndex": "0x1a6,0x1a7", + "MSRValue": "0x703C00001", + "SampleAfterValue": "100003", + "UMask": "0x1" + }, + { "BriefDescription": "Counts demand data reads that were supplied by DRAM attached to another socket.", "EventCode": "0x2A,0x2B", "EventName": "OCR.DEMAND_DATA_RD.REMOTE_DRAM", diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json index c207c851a9f9..222212abd811 100644 --- a/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json +++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json @@ -553,7 +553,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector + tma_fp_amx", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -717,6 +716,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -750,6 +750,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_pmm_bound + tma_store_bound) * (tma_fb_full / (tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -827,14 +828,12 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + FP_ARITH_INST_RETIRED2.SCALAR_HALF + 2 * (FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE + FP_ARITH_INST_RETIRED2.COMPLEX_SCALAR_HALF) + 4 * cpu@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE\\,umask\\=0x18@ + 8 * (FP_ARITH_INST_RETIRED2.128B_PACKED_HALF + cpu@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE\\,umask\\=0x60@) + 16 * (FP_ARITH_INST_RETIRED2.256B_PACKED_HALF + FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE) + 32 * FP_ARITH_INST_RETIRED2.512B_PACKED_HALF + 4 * AMX_OPS_RETIRED.BF16", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc" }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(FP_ARITH_DISPATCHED.PORT_0 + FP_ARITH_DISPATCHED.PORT_1 + FP_ARITH_DISPATCHED.PORT_5) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -1216,7 +1215,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire" @@ -1467,6 +1465,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(MEMORY_ACTIVITY.STALLS_L2_MISS - MEMORY_ACTIVITY.STALLS_L3_MISS) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -1841,6 +1840,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -1868,6 +1868,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * LD_BLOCKS.STORE_FORWARD / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json b/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json index 94cb38540b5a..2795a404bb58 100644 --- a/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/skylake/skl-metrics.json @@ -923,7 +923,7 @@ }, { "BriefDescription": "Average number of parallel data read requests to external memory", - "MetricExpr": "UNC_ARB_TRK_OCCUPANCY.DATA_READ / UNC_ARB_TRK_OCCUPANCY.DATA_READ@thresh\\=1@", + "MetricExpr": "UNC_ARB_TRK_OCCUPANCY.DATA_READ / UNC_ARB_TRK_OCCUPANCY.DATA_READ@cmask\\=1@", "MetricGroup": "Mem;MemoryBW;SoC", "MetricName": "tma_info_system_mem_parallel_reads", "PublicDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches" diff --git a/tools/perf/pmu-events/arch/x86/tigerlake/tgl-metrics.json b/tools/perf/pmu-events/arch/x86/tigerlake/tgl-metrics.json index c7c2d6ab1a93..fab084e1bc69 100644 --- a/tools/perf/pmu-events/arch/x86/tigerlake/tgl-metrics.json +++ b/tools/perf/pmu-events/arch/x86/tigerlake/tgl-metrics.json @@ -79,6 +79,7 @@ }, { "BriefDescription": "This metric estimates how often memory load accesses were aliased by preceding stores (in program order) with a 4K address offset", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "LD_BLOCKS_PARTIAL.ADDRESS_ALIAS / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_4k_aliasing", @@ -313,7 +314,6 @@ }, { "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", "MetricName": "tma_fp_arith", @@ -458,6 +458,7 @@ }, { "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", "MetricName": "tma_info_botlnk_l2_ic_misses", @@ -491,6 +492,7 @@ }, { "BriefDescription": "Total pipeline cost of (external) Memory Bandwidth related bottlenecks", + "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "100 * tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full))) + tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_fb_full / (tma_4k_aliasing + tma_dtlb_load + tma_fb_full + tma_lock_latency + tma_split_loads + tma_store_fwd_blk))", "MetricGroup": "Mem;MemoryBW;Offcore;tma_issueBW", "MetricName": "tma_info_bottleneck_memory_bandwidth", @@ -568,14 +570,12 @@ }, { "BriefDescription": "Floating Point Operations Per Cycle", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + 2 * FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE + 4 * cpu@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE\\,umask\\=0x18@ + 8 * cpu@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE\\,umask\\=0x60@ + 16 * FP_ARITH_INST_RETIRED.512B_PACKED_SINGLE) / tma_info_core_core_clks", "MetricGroup": "Flops;Ret", "MetricName": "tma_info_core_flopc" }, { "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "(cpu@FP_ARITH_INST_RETIRED.SCALAR_SINGLE\\,umask\\=0x03@ + cpu@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE\\,umask\\=0xfc@) / (2 * tma_info_core_core_clks)", "MetricGroup": "Cor;Flops;HPC", "MetricName": "tma_info_core_fp_arith_utilization", @@ -927,7 +927,6 @@ }, { "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", - "MetricConstraint": "NO_GROUP_EVENTS", "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu@UOPS_RETIRED.SLOTS\\,cmask\\=1@", "MetricGroup": "Pipeline;Ret", "MetricName": "tma_info_pipeline_retire" @@ -1114,6 +1113,7 @@ }, { "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "(CYCLE_ACTIVITY.STALLS_L2_MISS - CYCLE_ACTIVITY.STALLS_L3_MISS) / tma_info_thread_clks", "MetricGroup": "CacheMisses;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", "MetricName": "tma_l3_bound", @@ -1433,6 +1433,7 @@ }, { "BriefDescription": "This metric represents rate of split store accesses", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "MEM_INST_RETIRED.SPLIT_STORES / tma_info_core_core_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", "MetricName": "tma_split_stores", @@ -1460,6 +1461,7 @@ }, { "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", "MetricExpr": "13 * LD_BLOCKS.STORE_FORWARD / tma_info_thread_clks", "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", "MetricName": "tma_store_fwd_blk", diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index a630c617e879..12bd043a05e3 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -266,19 +266,53 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { }, }; -int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn, - void *data) +int pmu_events_table__for_each_event(const struct pmu_events_table *table, struct perf_pmu *pmu, + pmu_event_iter_fn fn, void *data) { for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { - int ret = fn(pe, table, data); + int ret; + if (pmu && !pmu__name_match(pmu, pe->pmu)) + continue; + + ret = fn(pe, table, data); if (ret) return ret; } return 0; } -int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, +int pmu_events_table__find_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) +{ + for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { + if (pmu && !pmu__name_match(pmu, pe->pmu)) + continue; + + if (!strcasecmp(pe->name, name)) + return fn(pe, table, data); + } + return -1000; +} + +size_t pmu_events_table__num_events(const struct pmu_events_table *table, + struct perf_pmu *pmu) +{ + size_t count = 0; + + for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { + if (pmu && !pmu__name_match(pmu, pe->pmu)) + continue; + + count++; + } + return count; +} + +int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, void *data) { for (const struct pmu_metric *pm = &table->entries[0]; pm->metric_expr; pm++) { @@ -371,7 +405,8 @@ const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) { for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { - int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/ NULL, fn, data); if (ret) return ret; @@ -384,7 +419,7 @@ int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { - int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); if (ret) return ret; @@ -408,7 +443,7 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; tables->name; tables++) { - int ret = pmu_events_table_for_each_event(&tables->table, fn, data); + int ret = pmu_events_table__for_each_event(&tables->table, /*pmu=*/ NULL, fn, data); if (ret) return ret; diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 12e80bb7939b..72ba4a9239c6 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -42,7 +42,7 @@ _metricgroups = {} # Order specific JsonEvent attributes will be visited. _json_event_attributes = [ # cmp_sevent related attributes. - 'name', 'pmu', 'topic', 'desc', + 'name', 'topic', 'desc', # Seems useful, put it early. 'event', # Short things in alphabetical order. @@ -53,7 +53,7 @@ _json_event_attributes = [ # Attributes that are in pmu_metric rather than pmu_event. _json_metric_attributes = [ - 'pmu', 'metric_name', 'metric_group', 'metric_expr', 'metric_threshold', + 'metric_name', 'metric_group', 'metric_expr', 'metric_threshold', 'desc', 'long_desc', 'unit', 'compat', 'metricgroup_no_group', 'default_metricgroup_name', 'aggr_mode', 'event_grouping' ] @@ -113,13 +113,24 @@ class BigCString: strings: Set[str] big_string: Sequence[str] offsets: Dict[str, int] + insert_number: int + insert_point: Dict[str, int] + metrics: Set[str] def __init__(self): self.strings = set() + self.insert_number = 0; + self.insert_point = {} + self.metrics = set() - def add(self, s: str) -> None: + def add(self, s: str, metric: bool) -> None: """Called to add to the big string.""" - self.strings.add(s) + if s not in self.strings: + self.strings.add(s) + self.insert_point[s] = self.insert_number + self.insert_number += 1 + if metric: + self.metrics.add(s) def compute(self) -> None: """Called once all strings are added to compute the string and offsets.""" @@ -160,8 +171,11 @@ class BigCString: self.big_string = [] self.offsets = {} + def string_cmp_key(s: str) -> Tuple[bool, int, str]: + return (s in self.metrics, self.insert_point[s], s) + # Emit all strings that aren't folded in a sorted manner. - for s in sorted(self.strings): + for s in sorted(self.strings, key=string_cmp_key): if s not in folded_strings: self.offsets[s] = big_string_offset self.big_string.append(f'/* offset={big_string_offset} */ "') @@ -252,7 +266,7 @@ class JsonEvent: def unit_to_pmu(unit: str) -> Optional[str]: """Convert a JSON Unit to Linux PMU name.""" if not unit: - return None + return 'default_core' # Comment brought over from jevents.c: # it's not realistic to keep adding these, we need something more scalable ... table = { @@ -274,6 +288,7 @@ class JsonEvent: 'DFPMC': 'amd_df', 'cpu_core': 'cpu_core', 'cpu_atom': 'cpu_atom', + 'ali_drw': 'ali_drw', } return table[unit] if unit in table else f'uncore_{unit.lower()}' @@ -342,16 +357,15 @@ class JsonEvent: self.desc += extra_desc if self.long_desc and extra_desc: self.long_desc += extra_desc - if self.pmu: - if self.desc and not self.desc.endswith('. '): - self.desc += '. ' - self.desc = (self.desc if self.desc else '') + ('Unit: ' + self.pmu + ' ') - if arch_std and arch_std.lower() in _arch_std_events: - event = _arch_std_events[arch_std.lower()].event - # Copy from the architecture standard event to self for undefined fields. - for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): - if hasattr(self, attr) and not getattr(self, attr): - setattr(self, attr, value) + if arch_std: + if arch_std.lower() in _arch_std_events: + event = _arch_std_events[arch_std.lower()].event + # Copy from the architecture standard event to self for undefined fields. + for attr, value in _arch_std_events[arch_std.lower()].__dict__.items(): + if hasattr(self, attr) and not getattr(self, attr): + setattr(self, attr, value) + else: + raise argparse.ArgumentTypeError('Cannot find arch std event:', arch_std) self.event = real_event(self.name, event) @@ -433,13 +447,13 @@ def add_events_table_entries(item: os.DirEntry, topic: str) -> None: def print_pending_events() -> None: """Optionally close events table.""" - def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]: + def event_cmp_key(j: JsonEvent) -> Tuple[str, str, bool, str, str]: def fix_none(s: Optional[str]) -> str: if s is None: return '' return s - return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu), + return (fix_none(j.pmu).replace(',','_'), fix_none(j.name), j.desc is not None, fix_none(j.topic), fix_none(j.metric_name)) global _pending_events @@ -454,13 +468,36 @@ def print_pending_events() -> None: global event_tables _event_tables.append(_pending_events_tblname) - _args.output_file.write( - f'static const struct compact_pmu_event {_pending_events_tblname}[] = {{\n') - + first = True + last_pmu = None + pmus = set() for event in sorted(_pending_events, key=event_cmp_key): + if event.pmu != last_pmu: + if not first: + _args.output_file.write('};\n') + pmu_name = event.pmu.replace(',', '_') + _args.output_file.write( + f'static const struct compact_pmu_event {_pending_events_tblname}_{pmu_name}[] = {{\n') + first = False + last_pmu = event.pmu + pmus.add((event.pmu, pmu_name)) + _args.output_file.write(event.to_c_string(metric=False)) _pending_events = [] + _args.output_file.write(f""" +}}; + +const struct pmu_table_entry {_pending_events_tblname}[] = {{ +""") + for (pmu, tbl_pmu) in sorted(pmus): + pmu_name = f"{pmu}\\000" + _args.output_file.write(f"""{{ + .entries = {_pending_events_tblname}_{tbl_pmu}, + .num_entries = ARRAY_SIZE({_pending_events_tblname}_{tbl_pmu}), + .pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, +}}, +""") _args.output_file.write('};\n\n') def print_pending_metrics() -> None: @@ -486,13 +523,36 @@ def print_pending_metrics() -> None: global metric_tables _metric_tables.append(_pending_metrics_tblname) - _args.output_file.write( - f'static const struct compact_pmu_event {_pending_metrics_tblname}[] = {{\n') - + first = True + last_pmu = None + pmus = set() for metric in sorted(_pending_metrics, key=metric_cmp_key): + if metric.pmu != last_pmu: + if not first: + _args.output_file.write('};\n') + pmu_name = metric.pmu.replace(',', '_') + _args.output_file.write( + f'static const struct compact_pmu_event {_pending_metrics_tblname}_{pmu_name}[] = {{\n') + first = False + last_pmu = metric.pmu + pmus.add((metric.pmu, pmu_name)) + _args.output_file.write(metric.to_c_string(metric=True)) _pending_metrics = [] + _args.output_file.write(f""" +}}; + +const struct pmu_table_entry {_pending_metrics_tblname}[] = {{ +""") + for (pmu, tbl_pmu) in sorted(pmus): + pmu_name = f"{pmu}\\000" + _args.output_file.write(f"""{{ + .entries = {_pending_metrics_tblname}_{tbl_pmu}, + .num_entries = ARRAY_SIZE({_pending_metrics_tblname}_{tbl_pmu}), + .pmu_name = {{ {_bcs.offsets[pmu_name]} /* {pmu_name} */ }}, +}}, +""") _args.output_file.write('};\n\n') def get_topic(topic: str) -> str: @@ -521,17 +581,20 @@ def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: assert len(mgroup) > 1, parents description = f"{metricgroup_descriptions[mgroup]}\\000" mgroup = f"{mgroup}\\000" - _bcs.add(mgroup) - _bcs.add(description) + _bcs.add(mgroup, metric=True) + _bcs.add(description, metric=True) _metricgroups[mgroup] = description return topic = get_topic(item.name) for event in read_json_events(item.path, topic): + pmu_name = f"{event.pmu}\\000" if event.name: - _bcs.add(event.build_c_string(metric=False)) + _bcs.add(pmu_name, metric=False) + _bcs.add(event.build_c_string(metric=False), metric=False) if event.metric_name: - _bcs.add(event.build_c_string(metric=True)) + _bcs.add(pmu_name, metric=True) + _bcs.add(event.build_c_string(metric=True), metric=True) def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: """Process a JSON file during the main walk.""" @@ -573,14 +636,14 @@ def print_mapping_table(archs: Sequence[str]) -> None: _args.output_file.write(""" /* Struct used to make the PMU event table implementation opaque to callers. */ struct pmu_events_table { - const struct compact_pmu_event *entries; - size_t length; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { - const struct compact_pmu_event *entries; - size_t length; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* @@ -610,12 +673,12 @@ const struct pmu_events_map pmu_events_map[] = { \t.arch = "testarch", \t.cpuid = "testcpu", \t.event_table = { -\t\t.entries = pmu_events__test_soc_cpu, -\t\t.length = ARRAY_SIZE(pmu_events__test_soc_cpu), +\t\t.pmus = pmu_events__test_soc_cpu, +\t\t.num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), \t}, \t.metric_table = { -\t\t.entries = pmu_metrics__test_soc_cpu, -\t\t.length = ARRAY_SIZE(pmu_metrics__test_soc_cpu), +\t\t.pmus = pmu_metrics__test_soc_cpu, +\t\t.num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), \t} }, """) @@ -645,12 +708,12 @@ const struct pmu_events_map pmu_events_map[] = { \t.arch = "{arch}", \t.cpuid = "{cpuid}", \t.event_table = {{ -\t\t.entries = {event_tblname}, -\t\t.length = {event_size} +\t\t.pmus = {event_tblname}, +\t\t.num_pmus = {event_size} \t}}, \t.metric_table = {{ -\t\t.entries = {metric_tblname}, -\t\t.length = {metric_size} +\t\t.pmus = {metric_tblname}, +\t\t.num_pmus = {metric_size} \t}} }}, """) @@ -681,15 +744,15 @@ static const struct pmu_sys_events pmu_sys_event_tables[] = { for tblname in _sys_event_tables: _args.output_file.write(f"""\t{{ \t\t.event_table = {{ -\t\t\t.entries = {tblname}, -\t\t\t.length = ARRAY_SIZE({tblname}) +\t\t\t.pmus = {tblname}, +\t\t\t.num_pmus = ARRAY_SIZE({tblname}) \t\t}},""") metric_tblname = _sys_event_table_to_metric_table_mapping[tblname] if metric_tblname in _sys_metric_tables: _args.output_file.write(f""" \t\t.metric_table = {{ -\t\t\t.entries = {metric_tblname}, -\t\t\t.length = ARRAY_SIZE({metric_tblname}) +\t\t\t.pmus = {metric_tblname}, +\t\t\t.num_pmus = ARRAY_SIZE({metric_tblname}) \t\t}},""") printed_metric_tables.append(metric_tblname) _args.output_file.write(f""" @@ -749,15 +812,18 @@ static void decompress_metric(int offset, struct pmu_metric *pm) _args.output_file.write('\twhile (*p++);') _args.output_file.write("""} -int pmu_events_table_for_each_event(const struct pmu_events_table *table, - pmu_event_iter_fn fn, - void *data) +static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, + const struct pmu_table_entry *pmu, + pmu_event_iter_fn fn, + void *data) { - for (size_t i = 0; i < table->length; i++) { - struct pmu_event pe; - int ret; + int ret; + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; - decompress_event(table->entries[i].offset, &pe); + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_event(pmu->entries[i].offset, &pe); if (!pe.name) continue; ret = fn(&pe, table, data); @@ -765,17 +831,119 @@ int pmu_events_table_for_each_event(const struct pmu_events_table *table, return ret; } return 0; + } + +static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, + const struct pmu_table_entry *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) +{ + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + int low = 0, high = pmu->num_entries - 1; + + while (low <= high) { + int cmp, mid = (low + high) / 2; + + decompress_event(pmu->entries[mid].offset, &pe); + + if (!pe.name && !name) + goto do_call; + + if (!pe.name && name) { + low = mid + 1; + continue; + } + if (pe.name && !name) { + high = mid - 1; + continue; + } + + cmp = strcasecmp(pe.name, name); + if (cmp < 0) { + low = mid + 1; + continue; + } + if (cmp > 0) { + high = mid - 1; + continue; + } + do_call: + return fn ? fn(&pe, table, data) : 0; + } + return -1000; } -int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, - pmu_metric_iter_fn fn, - void *data) +int pmu_events_table__for_each_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + pmu_event_iter_fn fn, + void *data) +{ + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; + + if (pmu && !pmu__name_match(pmu, pmu_name)) + continue; + + ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); + if (pmu || ret) + return ret; + } + return 0; +} + +int pmu_events_table__find_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) { - for (size_t i = 0; i < table->length; i++) { - struct pmu_metric pm; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; int ret; - decompress_metric(table->entries[i].offset, &pm); + if (!pmu__name_match(pmu, pmu_name)) + continue; + + ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); + if (ret != -1000) + return ret; + } + return -1000; +} + +size_t pmu_events_table__num_events(const struct pmu_events_table *table, + struct perf_pmu *pmu) +{ + size_t count = 0; + + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + + if (pmu__name_match(pmu, pmu_name)) + count += table_pmu->num_entries; + } + return count; +} + +static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, + const struct pmu_table_entry *pmu, + pmu_metric_iter_fn fn, + void *data) +{ + int ret; + struct pmu_metric pm = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_metric(pmu->entries[i].offset, &pm); if (!pm.metric_expr) continue; ret = fn(&pm, table, data); @@ -785,11 +953,25 @@ int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, return 0; } +int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, + pmu_metric_iter_fn fn, + void *data) +{ + for (size_t i = 0; i < table->num_pmus; i++) { + int ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], + fn, data); + + if (ret) + return ret; + } + return 0; +} + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { const struct pmu_events_table *table = NULL; char *cpuid = perf_pmu__getcpuid(pmu); - int i; + size_t i; /* on some platforms which uses cpus map, cpuid can be NULL for * PMUs other than CORE PMUs. @@ -809,7 +991,17 @@ const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) } } free(cpuid); - return table; + if (!pmu || !table) + return table; + + for (i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + + if (pmu__name_match(pmu, pmu_name)) + return table; + } + return NULL; } const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) @@ -866,7 +1058,8 @@ int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { - int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/ NULL, fn, data); if (ret) return ret; @@ -879,7 +1072,7 @@ int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { - int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); if (ret) return ret; @@ -903,7 +1096,8 @@ int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; tables->name; tables++) { - int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/ NULL, fn, data); if (ret) return ret; @@ -916,7 +1110,7 @@ int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; tables->name; tables++) { - int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); if (ret) return ret; @@ -999,14 +1193,20 @@ such as "arm/cortex-a34".''', _args = ap.parse_args() _args.output_file.write(""" -#include "pmu-events/pmu-events.h" +#include <pmu-events/pmu-events.h> #include "util/header.h" #include "util/pmu.h" #include <string.h> #include <stddef.h> struct compact_pmu_event { - int offset; + int offset; +}; + +struct pmu_table_entry { + const struct compact_pmu_event *entries; + uint32_t num_entries; + struct compact_pmu_event pmu_name; }; """) diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py index 85a3545f5b6a..3e673f25d5fd 100644 --- a/tools/perf/pmu-events/metric.py +++ b/tools/perf/pmu-events/metric.py @@ -413,6 +413,10 @@ def has_event(event: Event) -> Function: # pylint: disable=invalid-name return Function('has_event', event) +def strcmp_cpuid_str(cpuid: Event) -> Function: + # pylint: disable=redefined-builtin + # pylint: disable=invalid-name + return Function('strcmp_cpuid_str', cpuid) class Metric: """An individual metric that will specifiable on the perf command line.""" @@ -541,14 +545,23 @@ def ParsePerfJson(orig: str) -> Expression: """ # pylint: disable=eval-used py = orig.strip() + # First try to convert everything that looks like a string (event name) into Event(r"EVENT_NAME"). + # This isn't very selective so is followed up by converting some unwanted conversions back again py = re.sub(r'([a-zA-Z][^-+/\* \\\(\),]*(?:\\.[^-+/\* \\\(\),]*)*)', r'Event(r"\1")', py) + # If it started with a # it should have been a literal, rather than an event name py = re.sub(r'#Event\(r"([^"]*)"\)', r'Literal("#\1")', py) + # Convert accidentally converted hex constants ("0Event(r"xDEADBEEF)"") back to a constant, + # but keep it wrapped in Event(), otherwise Python drops the 0x prefix and it gets interpreted as + # a double by the Bison parser + py = re.sub(r'0Event\(r"[xX]([0-9a-fA-F]*)"\)', r'Event("0x\1")', py) + # Convert accidentally converted scientific notation constants back py = re.sub(r'([0-9]+)Event\(r"(e[0-9]+)"\)', r'\1\2', py) - keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event'] + # Convert all the known keywords back from events to just the keyword + keywords = ['if', 'else', 'min', 'max', 'd_ratio', 'source_count', 'has_event', 'strcmp_cpuid_str', + 'cpuid_not_more_than'] for kw in keywords: py = re.sub(rf'Event\(r"{kw}"\)', kw, py) - try: parsed = ast.parse(py, mode='eval') except SyntaxError as e: diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index caf59f23cd64..f5aa96f1685c 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -3,6 +3,7 @@ #define PMU_EVENTS_H #include <stdbool.h> +#include <stddef.h> struct perf_pmu; @@ -77,9 +78,19 @@ typedef int (*pmu_metric_iter_fn)(const struct pmu_metric *pm, const struct pmu_metrics_table *table, void *data); -int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn, +int pmu_events_table__for_each_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + pmu_event_iter_fn fn, void *data); -int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, +int pmu_events_table__find_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data); +size_t pmu_events_table__num_events(const struct pmu_events_table *table, + struct perf_pmu *pmu); + +int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, void *data); const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu); diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Build b/tools/perf/scripts/python/Perf-Trace-Util/Build index 7d0e33ce6aba..5b0b5ff7e14a 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Build +++ b/tools/perf/scripts/python/Perf-Trace-Util/Build @@ -1,3 +1,4 @@ perf-y += Context.o -CFLAGS_Context.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs +# -Wno-declaration-after-statement: The python headers have mixed code with declarations (decls after asserts, for instance) +CFLAGS_Context.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-declaration-after-statement diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 7384dcb628c4..b75d31858e54 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -54,6 +54,7 @@ try: import audit machine_to_id = { 'x86_64': audit.MACH_86_64, + 'aarch64': audit.MACH_AARCH64, 'alpha' : audit.MACH_ALPHA, 'ia64' : audit.MACH_IA64, 'ppc' : audit.MACH_PPC, @@ -73,9 +74,9 @@ try: except: if not audit_package_warned: audit_package_warned = True - print("Install the audit-libs-python package to get syscall names.\n" - "For example:\n # apt-get install python-audit (Ubuntu)" - "\n # yum install audit-libs-python (Fedora)" + print("Install the python-audit package to get syscall names.\n" + "For example:\n # apt-get install python3-audit (Ubuntu)" + "\n # yum install python3-audit (Fedora)" "\n etc.\n") def syscall_name(id): diff --git a/tools/perf/scripts/python/bin/gecko-record b/tools/perf/scripts/python/bin/gecko-record new file mode 100644 index 000000000000..f0d1aa55f171 --- /dev/null +++ b/tools/perf/scripts/python/bin/gecko-record @@ -0,0 +1,2 @@ +#!/bin/bash +perf record -F 99 -g "$@" diff --git a/tools/perf/scripts/python/bin/gecko-report b/tools/perf/scripts/python/bin/gecko-report new file mode 100755 index 000000000000..1867ec8d9757 --- /dev/null +++ b/tools/perf/scripts/python/bin/gecko-report @@ -0,0 +1,7 @@ +#!/bin/bash +# description: create firefox gecko profile json format from perf.data +if [ "$*" = "-i -" ]; then +perf script -s "$PERF_EXEC_PATH"/scripts/python/gecko.py +else +perf script -s "$PERF_EXEC_PATH"/scripts/python/gecko.py -- "$@" +fi diff --git a/tools/perf/scripts/python/gecko.py b/tools/perf/scripts/python/gecko.py new file mode 100644 index 000000000000..bc5a72f94bfa --- /dev/null +++ b/tools/perf/scripts/python/gecko.py @@ -0,0 +1,395 @@ +# gecko.py - Convert perf record output to Firefox's gecko profile format +# SPDX-License-Identifier: GPL-2.0 +# +# The script converts perf.data to Gecko Profile Format, +# which can be read by https://profiler.firefox.com/. +# +# Usage: +# +# perf record -a -g -F 99 sleep 60 +# perf script report gecko +# +# Combined: +# +# perf script gecko -F 99 -a sleep 60 + +import os +import sys +import time +import json +import string +import random +import argparse +import threading +import webbrowser +import urllib.parse +from os import system +from functools import reduce +from dataclasses import dataclass, field +from http.server import HTTPServer, SimpleHTTPRequestHandler, test +from typing import List, Dict, Optional, NamedTuple, Set, Tuple, Any + +# Add the Perf-Trace-Util library to the Python path +sys.path.append(os.environ['PERF_EXEC_PATH'] + \ + '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') + +from perf_trace_context import * +from Core import * + +StringID = int +StackID = int +FrameID = int +CategoryID = int +Milliseconds = float + +# start_time is intialiazed only once for the all event traces. +start_time = None + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/profile.js#L425 +# Follow Brendan Gregg's Flamegraph convention: orange for kernel and yellow for user space by default. +CATEGORIES = None + +# The product name is used by the profiler UI to show the Operating system and Processor. +PRODUCT = os.popen('uname -op').read().strip() + +# store the output file +output_file = None + +# Here key = tid, value = Thread +tid_to_thread = dict() + +# The HTTP server is used to serve the profile to the profiler UI. +http_server_thread = None + +# The category index is used by the profiler UI to show the color of the flame graph. +USER_CATEGORY_INDEX = 0 +KERNEL_CATEGORY_INDEX = 1 + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156 +class Frame(NamedTuple): + string_id: StringID + relevantForJS: bool + innerWindowID: int + implementation: None + optimizations: None + line: None + column: None + category: CategoryID + subcategory: int + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216 +class Stack(NamedTuple): + prefix_id: Optional[StackID] + frame_id: FrameID + +# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90 +class Sample(NamedTuple): + stack_id: Optional[StackID] + time_ms: Milliseconds + responsiveness: int + +@dataclass +class Thread: + """A builder for a profile of the thread. + + Attributes: + comm: Thread command-line (name). + pid: process ID of containing process. + tid: thread ID. + samples: Timeline of profile samples. + frameTable: interned stack frame ID -> stack frame. + stringTable: interned string ID -> string. + stringMap: interned string -> string ID. + stackTable: interned stack ID -> stack. + stackMap: (stack prefix ID, leaf stack frame ID) -> interned Stack ID. + frameMap: Stack Frame string -> interned Frame ID. + comm: str + pid: int + tid: int + samples: List[Sample] = field(default_factory=list) + frameTable: List[Frame] = field(default_factory=list) + stringTable: List[str] = field(default_factory=list) + stringMap: Dict[str, int] = field(default_factory=dict) + stackTable: List[Stack] = field(default_factory=list) + stackMap: Dict[Tuple[Optional[int], int], int] = field(default_factory=dict) + frameMap: Dict[str, int] = field(default_factory=dict) + """ + comm: str + pid: int + tid: int + samples: List[Sample] = field(default_factory=list) + frameTable: List[Frame] = field(default_factory=list) + stringTable: List[str] = field(default_factory=list) + stringMap: Dict[str, int] = field(default_factory=dict) + stackTable: List[Stack] = field(default_factory=list) + stackMap: Dict[Tuple[Optional[int], int], int] = field(default_factory=dict) + frameMap: Dict[str, int] = field(default_factory=dict) + + def _intern_stack(self, frame_id: int, prefix_id: Optional[int]) -> int: + """Gets a matching stack, or saves the new stack. Returns a Stack ID.""" + key = f"{frame_id}" if prefix_id is None else f"{frame_id},{prefix_id}" + # key = (prefix_id, frame_id) + stack_id = self.stackMap.get(key) + if stack_id is None: + # return stack_id + stack_id = len(self.stackTable) + self.stackTable.append(Stack(prefix_id=prefix_id, frame_id=frame_id)) + self.stackMap[key] = stack_id + return stack_id + + def _intern_string(self, string: str) -> int: + """Gets a matching string, or saves the new string. Returns a String ID.""" + string_id = self.stringMap.get(string) + if string_id is not None: + return string_id + string_id = len(self.stringTable) + self.stringTable.append(string) + self.stringMap[string] = string_id + return string_id + + def _intern_frame(self, frame_str: str) -> int: + """Gets a matching stack frame, or saves the new frame. Returns a Frame ID.""" + frame_id = self.frameMap.get(frame_str) + if frame_id is not None: + return frame_id + frame_id = len(self.frameTable) + self.frameMap[frame_str] = frame_id + string_id = self._intern_string(frame_str) + + symbol_name_to_category = KERNEL_CATEGORY_INDEX if frame_str.find('kallsyms') != -1 \ + or frame_str.find('/vmlinux') != -1 \ + or frame_str.endswith('.ko)') \ + else USER_CATEGORY_INDEX + + self.frameTable.append(Frame( + string_id=string_id, + relevantForJS=False, + innerWindowID=0, + implementation=None, + optimizations=None, + line=None, + column=None, + category=symbol_name_to_category, + subcategory=None, + )) + return frame_id + + def _add_sample(self, comm: str, stack: List[str], time_ms: Milliseconds) -> None: + """Add a timestamped stack trace sample to the thread builder. + Args: + comm: command-line (name) of the thread at this sample + stack: sampled stack frames. Root first, leaf last. + time_ms: timestamp of sample in milliseconds. + """ + # Ihreads may not set their names right after they are created. + # Instead, they might do it later. In such situations, to use the latest name they have set. + if self.comm != comm: + self.comm = comm + + prefix_stack_id = reduce(lambda prefix_id, frame: self._intern_stack + (self._intern_frame(frame), prefix_id), stack, None) + if prefix_stack_id is not None: + self.samples.append(Sample(stack_id=prefix_stack_id, + time_ms=time_ms, + responsiveness=0)) + + def _to_json_dict(self) -> Dict: + """Converts current Thread to GeckoThread JSON format.""" + # Gecko profile format is row-oriented data as List[List], + # And a schema for interpreting each index. + # Schema: + # https://github.com/firefox-devtools/profiler/blob/main/docs-developer/gecko-profile-format.md + # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L230 + return { + "tid": self.tid, + "pid": self.pid, + "name": self.comm, + # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L51 + "markers": { + "schema": { + "name": 0, + "startTime": 1, + "endTime": 2, + "phase": 3, + "category": 4, + "data": 5, + }, + "data": [], + }, + + # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90 + "samples": { + "schema": { + "stack": 0, + "time": 1, + "responsiveness": 2, + }, + "data": self.samples + }, + + # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156 + "frameTable": { + "schema": { + "location": 0, + "relevantForJS": 1, + "innerWindowID": 2, + "implementation": 3, + "optimizations": 4, + "line": 5, + "column": 6, + "category": 7, + "subcategory": 8, + }, + "data": self.frameTable, + }, + + # https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216 + "stackTable": { + "schema": { + "prefix": 0, + "frame": 1, + }, + "data": self.stackTable, + }, + "stringTable": self.stringTable, + "registerTime": 0, + "unregisterTime": None, + "processType": "default", + } + +# Uses perf script python interface to parse each +# event and store the data in the thread builder. +def process_event(param_dict: Dict) -> None: + global start_time + global tid_to_thread + time_stamp = (param_dict['sample']['time'] // 1000) / 1000 + pid = param_dict['sample']['pid'] + tid = param_dict['sample']['tid'] + comm = param_dict['comm'] + + # Start time is the time of the first sample + if not start_time: + start_time = time_stamp + + # Parse and append the callchain of the current sample into a stack. + stack = [] + if param_dict['callchain']: + for call in param_dict['callchain']: + if 'sym' not in call: + continue + stack.append(f'{call["sym"]["name"]} (in {call["dso"]})') + if len(stack) != 0: + # Reverse the stack, as root come first and the leaf at the end. + stack = stack[::-1] + + # During perf record if -g is not used, the callchain is not available. + # In that case, the symbol and dso are available in the event parameters. + else: + func = param_dict['symbol'] if 'symbol' in param_dict else '[unknown]' + dso = param_dict['dso'] if 'dso' in param_dict else '[unknown]' + stack.append(f'{func} (in {dso})') + + # Add sample to the specific thread. + thread = tid_to_thread.get(tid) + if thread is None: + thread = Thread(comm=comm, pid=pid, tid=tid) + tid_to_thread[tid] = thread + thread._add_sample(comm=comm, stack=stack, time_ms=time_stamp) + +def trace_begin() -> None: + global output_file + if (output_file is None): + print("Staring Firefox Profiler on your default browser...") + global http_server_thread + http_server_thread = threading.Thread(target=test, args=(CORSRequestHandler, HTTPServer,)) + http_server_thread.daemon = True + http_server_thread.start() + +# Trace_end runs at the end and will be used to aggregate +# the data into the final json object and print it out to stdout. +def trace_end() -> None: + global output_file + threads = [thread._to_json_dict() for thread in tid_to_thread.values()] + + # Schema: https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L305 + gecko_profile_with_meta = { + "meta": { + "interval": 1, + "processType": 0, + "product": PRODUCT, + "stackwalk": 1, + "debug": 0, + "gcpoison": 0, + "asyncstack": 1, + "startTime": start_time, + "shutdownTime": None, + "version": 24, + "presymbolicated": True, + "categories": CATEGORIES, + "markerSchema": [], + }, + "libs": [], + "threads": threads, + "processes": [], + "pausedRanges": [], + } + # launch the profiler on local host if not specified --save-only args, otherwise print to file + if (output_file is None): + output_file = 'gecko_profile.json' + with open(output_file, 'w') as f: + json.dump(gecko_profile_with_meta, f, indent=2) + launchFirefox(output_file) + time.sleep(1) + print(f'[ perf gecko: Captured and wrote into {output_file} ]') + else: + print(f'[ perf gecko: Captured and wrote into {output_file} ]') + with open(output_file, 'w') as f: + json.dump(gecko_profile_with_meta, f, indent=2) + +# Used to enable Cross-Origin Resource Sharing (CORS) for requests coming from 'https://profiler.firefox.com', allowing it to access resources from this server. +class CORSRequestHandler(SimpleHTTPRequestHandler): + def end_headers (self): + self.send_header('Access-Control-Allow-Origin', 'https://profiler.firefox.com') + SimpleHTTPRequestHandler.end_headers(self) + +# start a local server to serve the gecko_profile.json file to the profiler.firefox.com +def launchFirefox(file): + safe_string = urllib.parse.quote_plus(f'http://localhost:8000/{file}') + url = 'https://profiler.firefox.com/from-url/' + safe_string + webbrowser.open(f'{url}') + +def main() -> None: + global output_file + global CATEGORIES + parser = argparse.ArgumentParser(description="Convert perf.data to Firefox\'s Gecko Profile format which can be uploaded to profiler.firefox.com for visualization") + + # Add the command-line options + # Colors must be defined according to this: + # https://github.com/firefox-devtools/profiler/blob/50124adbfa488adba6e2674a8f2618cf34b59cd2/res/css/categories.css + parser.add_argument('--user-color', default='yellow', help='Color for the User category', choices=['yellow', 'blue', 'purple', 'green', 'orange', 'red', 'grey', 'magenta']) + parser.add_argument('--kernel-color', default='orange', help='Color for the Kernel category', choices=['yellow', 'blue', 'purple', 'green', 'orange', 'red', 'grey', 'magenta']) + # If --save-only is specified, the output will be saved to a file instead of opening Firefox's profiler directly. + parser.add_argument('--save-only', help='Save the output to a file instead of opening Firefox\'s profiler') + + # Parse the command-line arguments + args = parser.parse_args() + # Access the values provided by the user + user_color = args.user_color + kernel_color = args.kernel_color + output_file = args.save_only + + CATEGORIES = [ + { + "name": 'User', + "color": user_color, + "subcategories": ['Other'] + }, + { + "name": 'Kernel', + "color": kernel_color, + "subcategories": ['Other'] + }, + ] + +if __name__ == '__main__': + main() diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore deleted file mode 100644 index d053b325f728..000000000000 --- a/tools/perf/tests/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -llvm-src-base.c -llvm-src-kbuild.c -llvm-src-prologue.c -llvm-src-relocation.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index fb9ac5dc4079..63d5e6d5f165 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -37,8 +37,6 @@ perf-y += sample-parsing.o perf-y += parse-no-sample-id-all.o perf-y += kmod-path.o perf-y += thread-map.o -perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o llvm-src-relocation.o -perf-y += bpf.o perf-y += topology.o perf-y += mem.o perf-y += cpumap.o @@ -51,7 +49,6 @@ perf-y += sdt.o perf-y += is_printable_array.o perf-y += bitmap.o perf-y += perf-hooks.o -perf-y += clang.o perf-y += unit_number__scnprintf.o perf-y += mem2node.o perf-y += maps.o @@ -70,34 +67,6 @@ perf-y += sigtrap.o perf-y += event_groups.o perf-y += symbols.o -$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build - $(call rule_mkdir) - $(Q)echo '#include <tests/llvm.h>' > $@ - $(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@ - $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ - $(Q)echo ';' >> $@ - -$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c tests/Build - $(call rule_mkdir) - $(Q)echo '#include <tests/llvm.h>' > $@ - $(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@ - $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ - $(Q)echo ';' >> $@ - -$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c tests/Build - $(call rule_mkdir) - $(Q)echo '#include <tests/llvm.h>' > $@ - $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@ - $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ - $(Q)echo ';' >> $@ - -$(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/Build - $(call rule_mkdir) - $(Q)echo '#include <tests/llvm.h>' > $@ - $(Q)echo 'const char test_llvm__bpf_test_relocation[] =' >> $@ - $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ - $(Q)echo ';' >> $@ - ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc)) perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c deleted file mode 100644 index b638cc99d5ae..000000000000 --- a/tools/perf/tests/bpf-script-example.c +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-script-example.c - * Test basic LLVM building - */ -#ifndef LINUX_VERSION_CODE -# error Need LINUX_VERSION_CODE -# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' -#endif -#define BPF_ANY 0 -#define BPF_MAP_TYPE_ARRAY 2 -#define BPF_FUNC_map_lookup_elem 1 -#define BPF_FUNC_map_update_elem 2 - -static void *(*bpf_map_lookup_elem)(void *map, void *key) = - (void *) BPF_FUNC_map_lookup_elem; -static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) = - (void *) BPF_FUNC_map_update_elem; - -/* - * Following macros are taken from tools/lib/bpf/bpf_helpers.h, - * and are used to create BTF defined maps. It is easier to take - * 2 simple macros, than being able to include above header in - * runtime. - * - * __uint - defines integer attribute of BTF map definition, - * Such attributes are represented using a pointer to an array, - * in which dimensionality of array encodes specified integer - * value. - * - * __type - defines pointer variable with typeof(val) type for - * attributes like key or value, which will be defined by the - * size of the type. - */ -#define __uint(name, val) int (*name)[val] -#define __type(name, val) typeof(val) *name - -#define SEC(NAME) __attribute__((section(NAME), used)) -struct { - __uint(type, BPF_MAP_TYPE_ARRAY); - __uint(max_entries, 1); - __type(key, int); - __type(value, int); -} flip_table SEC(".maps"); - -SEC("syscalls:sys_enter_epoll_pwait") -int bpf_func__SyS_epoll_pwait(void *ctx) -{ - int ind =0; - int *flag = bpf_map_lookup_elem(&flip_table, &ind); - int new_flag; - if (!flag) - return 0; - /* flip flag and store back */ - new_flag = !*flag; - bpf_map_update_elem(&flip_table, &ind, &new_flag, BPF_ANY); - return new_flag; -} -char _license[] SEC("license") = "GPL"; -int _version SEC("version") = LINUX_VERSION_CODE; diff --git a/tools/perf/tests/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c deleted file mode 100644 index 219673aa278f..000000000000 --- a/tools/perf/tests/bpf-script-test-kbuild.c +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-script-test-kbuild.c - * Test include from kernel header - */ -#ifndef LINUX_VERSION_CODE -# error Need LINUX_VERSION_CODE -# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' -#endif -#define SEC(NAME) __attribute__((section(NAME), used)) - -#include <uapi/linux/fs.h> - -SEC("func=vfs_llseek") -int bpf_func__vfs_llseek(void *ctx) -{ - return 0; -} - -char _license[] SEC("license") = "GPL"; -int _version SEC("version") = LINUX_VERSION_CODE; diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c deleted file mode 100644 index 91778b5c6125..000000000000 --- a/tools/perf/tests/bpf-script-test-prologue.c +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-script-test-prologue.c - * Test BPF prologue - */ -#ifndef LINUX_VERSION_CODE -# error Need LINUX_VERSION_CODE -# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' -#endif -#define SEC(NAME) __attribute__((section(NAME), used)) - -#include <uapi/linux/fs.h> - -/* - * If CONFIG_PROFILE_ALL_BRANCHES is selected, - * 'if' is redefined after include kernel header. - * Recover 'if' for BPF object code. - */ -#ifdef if -# undef if -#endif - -typedef unsigned int __bitwise fmode_t; - -#define FMODE_READ 0x1 -#define FMODE_WRITE 0x2 - -static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = - (void *) 6; - -SEC("func=null_lseek file->f_mode offset orig") -int bpf_func__null_lseek(void *ctx, int err, unsigned long _f_mode, - unsigned long offset, unsigned long orig) -{ - fmode_t f_mode = (fmode_t)_f_mode; - - if (err) - return 0; - if (f_mode & FMODE_WRITE) - return 0; - if (offset & 1) - return 0; - if (orig == SEEK_CUR) - return 0; - return 1; -} - -char _license[] SEC("license") = "GPL"; -int _version SEC("version") = LINUX_VERSION_CODE; diff --git a/tools/perf/tests/bpf-script-test-relocation.c b/tools/perf/tests/bpf-script-test-relocation.c deleted file mode 100644 index 74006e4b2d24..000000000000 --- a/tools/perf/tests/bpf-script-test-relocation.c +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-script-test-relocation.c - * Test BPF loader checking relocation - */ -#ifndef LINUX_VERSION_CODE -# error Need LINUX_VERSION_CODE -# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' -#endif -#define BPF_ANY 0 -#define BPF_MAP_TYPE_ARRAY 2 -#define BPF_FUNC_map_lookup_elem 1 -#define BPF_FUNC_map_update_elem 2 - -static void *(*bpf_map_lookup_elem)(void *map, void *key) = - (void *) BPF_FUNC_map_lookup_elem; -static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) = - (void *) BPF_FUNC_map_update_elem; - -struct bpf_map_def { - unsigned int type; - unsigned int key_size; - unsigned int value_size; - unsigned int max_entries; -}; - -#define SEC(NAME) __attribute__((section(NAME), used)) -struct bpf_map_def SEC("maps") my_table = { - .type = BPF_MAP_TYPE_ARRAY, - .key_size = sizeof(int), - .value_size = sizeof(int), - .max_entries = 1, -}; - -int this_is_a_global_val; - -SEC("func=sys_write") -int bpf_func__sys_write(void *ctx) -{ - int key = 0; - int value = 0; - - /* - * Incorrect relocation. Should not allow this program be - * loaded into kernel. - */ - bpf_map_update_elem(&this_is_a_global_val, &key, &value, 0); - return 0; -} -char _license[] SEC("license") = "GPL"; -int _version SEC("version") = LINUX_VERSION_CODE; diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c deleted file mode 100644 index 8beb46066034..000000000000 --- a/tools/perf/tests/bpf.c +++ /dev/null @@ -1,389 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/epoll.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <util/record.h> -#include <util/util.h> -#include <util/bpf-loader.h> -#include <util/evlist.h> -#include <linux/filter.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <api/fs/fs.h> -#include <perf/mmap.h> -#include "tests.h" -#include "llvm.h" -#include "debug.h" -#include "parse-events.h" -#include "util/mmap.h" -#define NR_ITERS 111 -#define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test" - -#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) -#include <linux/bpf.h> -#include <bpf/bpf.h> - -static int epoll_pwait_loop(void) -{ - int i; - - /* Should fail NR_ITERS times */ - for (i = 0; i < NR_ITERS; i++) - epoll_pwait(-(i + 1), NULL, 0, 0, NULL); - return 0; -} - -#ifdef HAVE_BPF_PROLOGUE - -static int llseek_loop(void) -{ - int fds[2], i; - - fds[0] = open("/dev/null", O_RDONLY); - fds[1] = open("/dev/null", O_RDWR); - - if (fds[0] < 0 || fds[1] < 0) - return -1; - - for (i = 0; i < NR_ITERS; i++) { - lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); - lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); - } - close(fds[0]); - close(fds[1]); - return 0; -} - -#endif - -static struct { - enum test_llvm__testcase prog_id; - const char *name; - const char *msg_compile_fail; - const char *msg_load_fail; - int (*target_func)(void); - int expect_result; - bool pin; -} bpf_testcase_table[] = { - { - .prog_id = LLVM_TESTCASE_BASE, - .name = "[basic_bpf_test]", - .msg_compile_fail = "fix 'perf test LLVM' first", - .msg_load_fail = "load bpf object failed", - .target_func = &epoll_pwait_loop, - .expect_result = (NR_ITERS + 1) / 2, - }, - { - .prog_id = LLVM_TESTCASE_BASE, - .name = "[bpf_pinning]", - .msg_compile_fail = "fix kbuild first", - .msg_load_fail = "check your vmlinux setting?", - .target_func = &epoll_pwait_loop, - .expect_result = (NR_ITERS + 1) / 2, - .pin = true, - }, -#ifdef HAVE_BPF_PROLOGUE - { - .prog_id = LLVM_TESTCASE_BPF_PROLOGUE, - .name = "[bpf_prologue_test]", - .msg_compile_fail = "fix kbuild first", - .msg_load_fail = "check your vmlinux setting?", - .target_func = &llseek_loop, - .expect_result = (NR_ITERS + 1) / 4, - }, -#endif -}; - -static int do_test(struct bpf_object *obj, int (*func)(void), - int expect) -{ - struct record_opts opts = { - .target = { - .uid = UINT_MAX, - .uses_mmap = true, - }, - .freq = 0, - .mmap_pages = 256, - .default_interval = 1, - }; - - char pid[16]; - char sbuf[STRERR_BUFSIZE]; - struct evlist *evlist; - int i, ret = TEST_FAIL, err = 0, count = 0; - - struct parse_events_state parse_state; - struct parse_events_error parse_error; - - parse_events_error__init(&parse_error); - bzero(&parse_state, sizeof(parse_state)); - parse_state.error = &parse_error; - INIT_LIST_HEAD(&parse_state.list); - - err = parse_events_load_bpf_obj(&parse_state, &parse_state.list, obj, NULL); - parse_events_error__exit(&parse_error); - if (err == -ENODATA) { - pr_debug("Failed to add events selected by BPF, debuginfo package not installed\n"); - return TEST_SKIP; - } - if (err || list_empty(&parse_state.list)) { - pr_debug("Failed to add events selected by BPF\n"); - return TEST_FAIL; - } - - snprintf(pid, sizeof(pid), "%d", getpid()); - pid[sizeof(pid) - 1] = '\0'; - opts.target.tid = opts.target.pid = pid; - - /* Instead of evlist__new_default, don't add default events */ - evlist = evlist__new(); - if (!evlist) { - pr_debug("Not enough memory to create evlist\n"); - return TEST_FAIL; - } - - err = evlist__create_maps(evlist, &opts.target); - if (err < 0) { - pr_debug("Not enough memory to create thread/cpu maps\n"); - goto out_delete_evlist; - } - - evlist__splice_list_tail(evlist, &parse_state.list); - - evlist__config(evlist, &opts, NULL); - - err = evlist__open(evlist); - if (err < 0) { - pr_debug("perf_evlist__open: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; - } - - err = evlist__mmap(evlist, opts.mmap_pages); - if (err < 0) { - pr_debug("evlist__mmap: %s\n", - str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_delete_evlist; - } - - evlist__enable(evlist); - (*func)(); - evlist__disable(evlist); - - for (i = 0; i < evlist->core.nr_mmaps; i++) { - union perf_event *event; - struct mmap *md; - - md = &evlist->mmap[i]; - if (perf_mmap__read_init(&md->core) < 0) - continue; - - while ((event = perf_mmap__read_event(&md->core)) != NULL) { - const u32 type = event->header.type; - - if (type == PERF_RECORD_SAMPLE) - count ++; - } - perf_mmap__read_done(&md->core); - } - - if (count != expect * evlist->core.nr_entries) { - pr_debug("BPF filter result incorrect, expected %d, got %d samples\n", expect * evlist->core.nr_entries, count); - goto out_delete_evlist; - } - - ret = TEST_OK; - -out_delete_evlist: - evlist__delete(evlist); - return ret; -} - -static struct bpf_object * -prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) -{ - struct bpf_object *obj; - - obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name); - if (IS_ERR(obj)) { - pr_debug("Compile BPF program failed.\n"); - return NULL; - } - return obj; -} - -static int __test__bpf(int idx) -{ - int ret; - void *obj_buf; - size_t obj_buf_sz; - struct bpf_object *obj; - - ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, - bpf_testcase_table[idx].prog_id, - false, NULL); - if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { - pr_debug("Unable to get BPF object, %s\n", - bpf_testcase_table[idx].msg_compile_fail); - if ((idx == 0) || (ret == TEST_SKIP)) - return TEST_SKIP; - else - return TEST_FAIL; - } - - obj = prepare_bpf(obj_buf, obj_buf_sz, - bpf_testcase_table[idx].name); - if ((!!bpf_testcase_table[idx].target_func) != (!!obj)) { - if (!obj) - pr_debug("Fail to load BPF object: %s\n", - bpf_testcase_table[idx].msg_load_fail); - else - pr_debug("Success unexpectedly: %s\n", - bpf_testcase_table[idx].msg_load_fail); - ret = TEST_FAIL; - goto out; - } - - if (obj) { - ret = do_test(obj, - bpf_testcase_table[idx].target_func, - bpf_testcase_table[idx].expect_result); - if (ret != TEST_OK) - goto out; - if (bpf_testcase_table[idx].pin) { - int err; - - if (!bpf_fs__mount()) { - pr_debug("BPF filesystem not mounted\n"); - ret = TEST_FAIL; - goto out; - } - err = mkdir(PERF_TEST_BPF_PATH, 0777); - if (err && errno != EEXIST) { - pr_debug("Failed to make perf_test dir: %s\n", - strerror(errno)); - ret = TEST_FAIL; - goto out; - } - if (bpf_object__pin(obj, PERF_TEST_BPF_PATH)) - ret = TEST_FAIL; - if (rm_rf(PERF_TEST_BPF_PATH)) - ret = TEST_FAIL; - } - } - -out: - free(obj_buf); - bpf__clear(); - return ret; -} - -static int check_env(void) -{ - LIBBPF_OPTS(bpf_prog_load_opts, opts); - int err; - char license[] = "GPL"; - - struct bpf_insn insns[] = { - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }; - - err = fetch_kernel_version(&opts.kern_version, NULL, 0); - if (err) { - pr_debug("Unable to get kernel version\n"); - return err; - } - err = bpf_prog_load(BPF_PROG_TYPE_KPROBE, NULL, license, insns, - ARRAY_SIZE(insns), &opts); - if (err < 0) { - pr_err("Missing basic BPF support, skip this test: %s\n", - strerror(errno)); - return err; - } - close(err); - - return 0; -} - -static int test__bpf(int i) -{ - int err; - - if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table)) - return TEST_FAIL; - - if (geteuid() != 0) { - pr_debug("Only root can run BPF test\n"); - return TEST_SKIP; - } - - if (check_env()) - return TEST_SKIP; - - err = __test__bpf(i); - return err; -} -#endif - -static int test__basic_bpf_test(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) - return test__bpf(0); -#else - pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); - return TEST_SKIP; -#endif -} - -static int test__bpf_pinning(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) - return test__bpf(1); -#else - pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); - return TEST_SKIP; -#endif -} - -static int test__bpf_prologue_test(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_BPF_PROLOGUE) && defined(HAVE_LIBTRACEEVENT) - return test__bpf(2); -#else - pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); - return TEST_SKIP; -#endif -} - - -static struct test_case bpf_tests[] = { -#if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) - TEST_CASE("Basic BPF filtering", basic_bpf_test), - TEST_CASE_REASON("BPF pinning", bpf_pinning, - "clang isn't installed or environment missing BPF support"), -#ifdef HAVE_BPF_PROLOGUE - TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, - "clang/debuginfo isn't installed or environment missing BPF support"), -#else - TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in"), -#endif -#else - TEST_CASE_REASON("Basic BPF filtering", basic_bpf_test, "not compiled in or missing libtraceevent support"), - TEST_CASE_REASON("BPF pinning", bpf_pinning, "not compiled in or missing libtraceevent support"), - TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in or missing libtraceevent support"), -#endif - { .name = NULL, } -}; - -struct test_suite suite__bpf = { - .desc = "BPF filter", - .test_cases = bpf_tests, -}; diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 1f6557ce3b0a..0ad18cf6dd22 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -33,9 +33,18 @@ static bool dont_fork; const char *dso_to_test; -struct test_suite *__weak arch_tests[] = { +/* + * List of architecture specific tests. Not a weak symbol as the array length is + * dependent on the initialization, as such GCC with LTO complains of + * conflicting definitions with a weak symbol. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) +extern struct test_suite *arch_tests[]; +#else +static struct test_suite *arch_tests[] = { NULL, }; +#endif static struct test_suite *generic_tests[] = { &suite__vmlinux_matches_kallsyms, @@ -83,9 +92,7 @@ static struct test_suite *generic_tests[] = { &suite__fdarray__add, &suite__kmod_path__parse, &suite__thread_map, - &suite__llvm, &suite__session_topology, - &suite__bpf, &suite__thread_map_synthesize, &suite__thread_map_remove, &suite__cpu_map, @@ -99,7 +106,6 @@ static struct test_suite *generic_tests[] = { &suite__is_printable_array, &suite__bitmap_print, &suite__perf_hooks, - &suite__clang, &suite__unit_number__scnprint, &suite__mem2node, &suite__time_utils, diff --git a/tools/perf/tests/clang.c b/tools/perf/tests/clang.c deleted file mode 100644 index a7111005d5b9..000000000000 --- a/tools/perf/tests/clang.c +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "tests.h" -#include "c++/clang-c.h" -#include <linux/kernel.h> - -#ifndef HAVE_LIBCLANGLLVM_SUPPORT -static int test__clang_to_IR(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ - return TEST_SKIP; -} - -static int test__clang_to_obj(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ - return TEST_SKIP; -} -#endif - -static struct test_case clang_tests[] = { - TEST_CASE_REASON("builtin clang compile C source to IR", clang_to_IR, - "not compiled in"), - TEST_CASE_REASON("builtin clang compile C source to ELF object", - clang_to_obj, - "not compiled in"), - { .name = NULL, } -}; - -struct test_suite suite__clang = { - .desc = "builtin clang support", - .test_cases = clang_tests, -}; diff --git a/tools/perf/tests/config-fragments/README b/tools/perf/tests/config-fragments/README new file mode 100644 index 000000000000..fe7de5d93674 --- /dev/null +++ b/tools/perf/tests/config-fragments/README @@ -0,0 +1,7 @@ +This folder is for kernel config fragments that can be merged with +defconfig to give full test coverage of a perf test run. This is only +an optimistic set as some features require hardware support in order to +pass and not skip. + +'config' is shared across all platforms, and for arch specific files, +the file name should match that used in the ARCH=... make option. diff --git a/tools/perf/tests/config-fragments/arm64 b/tools/perf/tests/config-fragments/arm64 new file mode 100644 index 000000000000..64c4ab17cd58 --- /dev/null +++ b/tools/perf/tests/config-fragments/arm64 @@ -0,0 +1 @@ +CONFIG_CORESIGHT_SOURCE_ETM4X=y diff --git a/tools/perf/tests/config-fragments/config b/tools/perf/tests/config-fragments/config new file mode 100644 index 000000000000..c340b3195fca --- /dev/null +++ b/tools/perf/tests/config-fragments/config @@ -0,0 +1,11 @@ +CONFIG_TRACEPOINTS=y +CONFIG_STACKTRACE=y +CONFIG_NOP_TRACER=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_FTRACE=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_BRANCH_PROFILE_NONE=y diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index 086fd2179e41..da3a9b50b1b1 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Test dlfilter C API. A perf.data file is synthesized and then processed - * by perf script with a dlfilter named dlfilter-test-api-v0.so. Also a C file + * by perf script with dlfilters named dlfilter-test-api-v*.so. Also a C file * is compiled to provide a dso to match the synthesized perf.data file. */ @@ -37,6 +37,8 @@ #define MAP_START 0x400000 +#define DLFILTER_TEST_NAME_MAX 128 + struct test_data { struct perf_tool tool; struct machine *machine; @@ -45,6 +47,8 @@ struct test_data { u64 bar; u64 ip; u64 addr; + char name[DLFILTER_TEST_NAME_MAX]; + char desc[DLFILTER_TEST_NAME_MAX]; char perf[PATH_MAX]; char perf_data_file_name[PATH_MAX]; char c_file_name[PATH_MAX]; @@ -215,7 +219,7 @@ static int write_prog(char *file_name) return err ? -1 : 0; } -static int get_dlfilters_path(char *buf, size_t sz) +static int get_dlfilters_path(const char *name, char *buf, size_t sz) { char perf[PATH_MAX]; char path[PATH_MAX]; @@ -224,12 +228,12 @@ static int get_dlfilters_path(char *buf, size_t sz) perf_exe(perf, sizeof(perf)); perf_path = dirname(perf); - snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", perf_path); + snprintf(path, sizeof(path), "%s/dlfilters/%s", perf_path, name); if (access(path, R_OK)) { exec_path = get_argv_exec_path(); if (!exec_path) return -1; - snprintf(path, sizeof(path), "%s/dlfilters/dlfilter-test-api-v0.so", exec_path); + snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, name); free(exec_path); if (access(path, R_OK)) return -1; @@ -244,9 +248,9 @@ static int check_filter_desc(struct test_data *td) char *desc = NULL; int ret; - if (get_filter_desc(td->dlfilters, "dlfilter-test-api-v0.so", &desc, &long_desc) && + if (get_filter_desc(td->dlfilters, td->name, &desc, &long_desc) && long_desc && !strcmp(long_desc, "Filter used by the 'dlfilter C API' perf test") && - desc && !strcmp(desc, "dlfilter to test v0 C API")) + desc && !strcmp(desc, td->desc)) ret = 0; else ret = -1; @@ -284,7 +288,7 @@ static int get_ip_addr(struct test_data *td) static int do_run_perf_script(struct test_data *td, int do_early) { return system_cmd("%s script -i %s " - "--dlfilter %s/dlfilter-test-api-v0.so " + "--dlfilter %s/%s " "--dlarg first " "--dlarg %d " "--dlarg %" PRIu64 " " @@ -292,7 +296,7 @@ static int do_run_perf_script(struct test_data *td, int do_early) "--dlarg %d " "--dlarg last", td->perf, td->perf_data_file_name, td->dlfilters, - verbose, td->ip, td->addr, do_early); + td->name, verbose, td->ip, td->addr, do_early); } static int run_perf_script(struct test_data *td) @@ -321,7 +325,7 @@ static int test__dlfilter_test(struct test_data *td) u64 id = 99; int err; - if (get_dlfilters_path(td->dlfilters, PATH_MAX)) + if (get_dlfilters_path(td->name, td->dlfilters, PATH_MAX)) return test_result("dlfilters not found", TEST_SKIP); if (check_filter_desc(td)) @@ -399,14 +403,18 @@ static void test_data__free(struct test_data *td) } } -static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +static int test__dlfilter_ver(int ver) { struct test_data td = {.fd = -1}; int pid = getpid(); int err; + pr_debug("\n-- Testing version %d API --\n", ver); + perf_exe(td.perf, sizeof(td.perf)); + snprintf(td.name, sizeof(td.name), "dlfilter-test-api-v%d.so", ver); + snprintf(td.desc, sizeof(td.desc), "dlfilter to test v%d C API", ver); snprintf(td.perf_data_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-perf-data", pid); snprintf(td.c_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog.c", pid); snprintf(td.prog_file_name, PATH_MAX, "/tmp/dlfilter-test-%u-prog", pid); @@ -416,4 +424,14 @@ static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __ return err; } +static int test__dlfilter(struct test_suite *test __maybe_unused, int subtest __maybe_unused) +{ + int err = test__dlfilter_ver(0); + + if (err) + return err; + /* No test for version 1 */ + return test__dlfilter_ver(2); +} + DEFINE_SUITE("dlfilter C API", dlfilter); diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c index c1c3fcbc2753..81229fa4f1e9 100644 --- a/tools/perf/tests/expr.c +++ b/tools/perf/tests/expr.c @@ -70,7 +70,7 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u { struct expr_id_data *val_ptr; const char *p; - double val, num_cpus, num_cores, num_dies, num_packages; + double val, num_cpus_online, num_cpus, num_cores, num_dies, num_packages; int ret; struct expr_parse_ctx *ctx; bool is_intel = false; @@ -227,7 +227,10 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u /* Test toplogy constants appear well ordered. */ expr__ctx_clear(ctx); + TEST_ASSERT_VAL("#num_cpus_online", + expr__parse(&num_cpus_online, ctx, "#num_cpus_online") == 0); TEST_ASSERT_VAL("#num_cpus", expr__parse(&num_cpus, ctx, "#num_cpus") == 0); + TEST_ASSERT_VAL("#num_cpus >= #num_cpus_online", num_cpus >= num_cpus_online); TEST_ASSERT_VAL("#num_cores", expr__parse(&num_cores, ctx, "#num_cores") == 0); TEST_ASSERT_VAL("#num_cpus >= #num_cores", num_cpus >= num_cores); TEST_ASSERT_VAL("#num_dies", expr__parse(&num_dies, ctx, "#num_dies") == 0); diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c deleted file mode 100644 index 0bc25a56cfef..000000000000 --- a/tools/perf/tests/llvm.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "tests.h" -#include "debug.h" - -#ifdef HAVE_LIBBPF_SUPPORT -#include <bpf/libbpf.h> -#include <util/llvm-utils.h> -#include "llvm.h" -static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) -{ - struct bpf_object *obj; - - obj = bpf_object__open_mem(obj_buf, obj_buf_sz, NULL); - if (libbpf_get_error(obj)) - return TEST_FAIL; - bpf_object__close(obj); - return TEST_OK; -} - -static struct { - const char *source; - const char *desc; - bool should_load_fail; -} bpf_source_table[__LLVM_TESTCASE_MAX] = { - [LLVM_TESTCASE_BASE] = { - .source = test_llvm__bpf_base_prog, - .desc = "Basic BPF llvm compile", - }, - [LLVM_TESTCASE_KBUILD] = { - .source = test_llvm__bpf_test_kbuild_prog, - .desc = "kbuild searching", - }, - [LLVM_TESTCASE_BPF_PROLOGUE] = { - .source = test_llvm__bpf_test_prologue_prog, - .desc = "Compile source for BPF prologue generation", - }, - [LLVM_TESTCASE_BPF_RELOCATION] = { - .source = test_llvm__bpf_test_relocation, - .desc = "Compile source for BPF relocation", - .should_load_fail = true, - }, -}; - -int -test_llvm__fetch_bpf_obj(void **p_obj_buf, - size_t *p_obj_buf_sz, - enum test_llvm__testcase idx, - bool force, - bool *should_load_fail) -{ - const char *source; - const char *desc; - const char *tmpl_old, *clang_opt_old; - char *tmpl_new = NULL, *clang_opt_new = NULL; - int err, old_verbose, ret = TEST_FAIL; - - if (idx >= __LLVM_TESTCASE_MAX) - return TEST_FAIL; - - source = bpf_source_table[idx].source; - desc = bpf_source_table[idx].desc; - if (should_load_fail) - *should_load_fail = bpf_source_table[idx].should_load_fail; - - /* - * Skip this test if user's .perfconfig doesn't set [llvm] section - * and clang is not found in $PATH - */ - if (!force && (!llvm_param.user_set_param && - llvm__search_clang())) { - pr_debug("No clang, skip this test\n"); - return TEST_SKIP; - } - - /* - * llvm is verbosity when error. Suppress all error output if - * not 'perf test -v'. - */ - old_verbose = verbose; - if (verbose == 0) - verbose = -1; - - *p_obj_buf = NULL; - *p_obj_buf_sz = 0; - - if (!llvm_param.clang_bpf_cmd_template) - goto out; - - if (!llvm_param.clang_opt) - llvm_param.clang_opt = strdup(""); - - err = asprintf(&tmpl_new, "echo '%s' | %s%s", source, - llvm_param.clang_bpf_cmd_template, - old_verbose ? "" : " 2>/dev/null"); - if (err < 0) - goto out; - err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); - if (err < 0) - goto out; - - tmpl_old = llvm_param.clang_bpf_cmd_template; - llvm_param.clang_bpf_cmd_template = tmpl_new; - clang_opt_old = llvm_param.clang_opt; - llvm_param.clang_opt = clang_opt_new; - - err = llvm__compile_bpf("-", p_obj_buf, p_obj_buf_sz); - - llvm_param.clang_bpf_cmd_template = tmpl_old; - llvm_param.clang_opt = clang_opt_old; - - verbose = old_verbose; - if (err) - goto out; - - ret = TEST_OK; -out: - free(tmpl_new); - free(clang_opt_new); - if (ret != TEST_OK) - pr_debug("Failed to compile test case: '%s'\n", desc); - return ret; -} - -static int test__llvm(int subtest) -{ - int ret; - void *obj_buf = NULL; - size_t obj_buf_sz = 0; - bool should_load_fail = false; - - if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) - return TEST_FAIL; - - ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, - subtest, false, &should_load_fail); - - if (ret == TEST_OK && !should_load_fail) { - ret = test__bpf_parsing(obj_buf, obj_buf_sz); - if (ret != TEST_OK) { - pr_debug("Failed to parse test case '%s'\n", - bpf_source_table[subtest].desc); - } - } - free(obj_buf); - - return ret; -} -#endif //HAVE_LIBBPF_SUPPORT - -static int test__llvm__bpf_base_prog(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#ifdef HAVE_LIBBPF_SUPPORT - return test__llvm(LLVM_TESTCASE_BASE); -#else - pr_debug("Skip LLVM test because BPF support is not compiled\n"); - return TEST_SKIP; -#endif -} - -static int test__llvm__bpf_test_kbuild_prog(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#ifdef HAVE_LIBBPF_SUPPORT - return test__llvm(LLVM_TESTCASE_KBUILD); -#else - pr_debug("Skip LLVM test because BPF support is not compiled\n"); - return TEST_SKIP; -#endif -} - -static int test__llvm__bpf_test_prologue_prog(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#ifdef HAVE_LIBBPF_SUPPORT - return test__llvm(LLVM_TESTCASE_BPF_PROLOGUE); -#else - pr_debug("Skip LLVM test because BPF support is not compiled\n"); - return TEST_SKIP; -#endif -} - -static int test__llvm__bpf_test_relocation(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ -#ifdef HAVE_LIBBPF_SUPPORT - return test__llvm(LLVM_TESTCASE_BPF_RELOCATION); -#else - pr_debug("Skip LLVM test because BPF support is not compiled\n"); - return TEST_SKIP; -#endif -} - - -static struct test_case llvm_tests[] = { -#ifdef HAVE_LIBBPF_SUPPORT - TEST_CASE("Basic BPF llvm compile", llvm__bpf_base_prog), - TEST_CASE("kbuild searching", llvm__bpf_test_kbuild_prog), - TEST_CASE("Compile source for BPF prologue generation", - llvm__bpf_test_prologue_prog), - TEST_CASE("Compile source for BPF relocation", llvm__bpf_test_relocation), -#else - TEST_CASE_REASON("Basic BPF llvm compile", llvm__bpf_base_prog, "not compiled in"), - TEST_CASE_REASON("kbuild searching", llvm__bpf_test_kbuild_prog, "not compiled in"), - TEST_CASE_REASON("Compile source for BPF prologue generation", - llvm__bpf_test_prologue_prog, "not compiled in"), - TEST_CASE_REASON("Compile source for BPF relocation", - llvm__bpf_test_relocation, "not compiled in"), -#endif - { .name = NULL, } -}; - -struct test_suite suite__llvm = { - .desc = "LLVM search and compile", - .test_cases = llvm_tests, -}; diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h deleted file mode 100644 index f68b0d9b8ae2..000000000000 --- a/tools/perf/tests/llvm.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef PERF_TEST_LLVM_H -#define PERF_TEST_LLVM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stddef.h> /* for size_t */ -#include <stdbool.h> /* for bool */ - -extern const char test_llvm__bpf_base_prog[]; -extern const char test_llvm__bpf_test_kbuild_prog[]; -extern const char test_llvm__bpf_test_prologue_prog[]; -extern const char test_llvm__bpf_test_relocation[]; - -enum test_llvm__testcase { - LLVM_TESTCASE_BASE, - LLVM_TESTCASE_KBUILD, - LLVM_TESTCASE_BPF_PROLOGUE, - LLVM_TESTCASE_BPF_RELOCATION, - __LLVM_TESTCASE_MAX, -}; - -int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, - enum test_llvm__testcase index, bool force, - bool *should_load_fail); -#ifdef __cplusplus -} -#endif -#endif diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 58cf96d762d0..ea4c341f5af1 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -95,7 +95,6 @@ make_with_babeltrace:= LIBBABELTRACE=1 make_with_coresight := CORESIGHT=1 make_no_sdt := NO_SDT=1 make_no_syscall_tbl := NO_SYSCALL_TABLE=1 -make_with_clangllvm := LIBCLANGLLVM=1 make_no_libpfm4 := NO_LIBPFM4=1 make_with_gtk2 := GTK2=1 make_refcnt_check := EXTRA_CFLAGS="-DREFCNT_CHECKING=1" diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index e68ca6229756..886a13a77a16 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -284,7 +284,8 @@ static struct test_case tests__basic_mmap[] = { "permissions"), TEST_CASE_REASON("User space counter reading of instructions", mmap_user_read_instr, -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__riscv) && __riscv_xlen == 64) "permissions" #else "unsupported" @@ -292,7 +293,8 @@ static struct test_case tests__basic_mmap[] = { ), TEST_CASE_REASON("User space counter reading of cycles", mmap_user_read_cycles, -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__riscv) && __riscv_xlen == 64) "permissions" #else "unsupported" diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 658fb9599d95..d47f1f871164 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2170,7 +2170,7 @@ static const struct evlist_test test__events[] = { static const struct evlist_test test__events_pmu[] = { { - .name = "cpu/config=10,config1,config2=3,period=1000/u", + .name = "cpu/config=10,config1=1,config2=3,period=1000/u", .valid = test__pmu_cpu_valid, .check = test__checkevent_pmu, /* 0 */ @@ -2472,7 +2472,7 @@ static int test_term(const struct terms_test *t) INIT_LIST_HEAD(&terms); - ret = parse_events_terms(&terms, t->str); + ret = parse_events_terms(&terms, t->str, /*input=*/ NULL); if (ret) { pr_debug("failed to parse terms '%s', err %d\n", t->str , ret); diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index 64383fc34ef1..f5321fbdee79 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -44,6 +44,7 @@ struct perf_pmu_test_pmu { static const struct perf_pmu_test_event bp_l1_btb_correct = { .event = { + .pmu = "default_core", .name = "bp_l1_btb_correct", .event = "event=0x8a", .desc = "L1 BTB Correction", @@ -55,6 +56,7 @@ static const struct perf_pmu_test_event bp_l1_btb_correct = { static const struct perf_pmu_test_event bp_l2_btb_correct = { .event = { + .pmu = "default_core", .name = "bp_l2_btb_correct", .event = "event=0x8b", .desc = "L2 BTB Correction", @@ -66,6 +68,7 @@ static const struct perf_pmu_test_event bp_l2_btb_correct = { static const struct perf_pmu_test_event segment_reg_loads_any = { .event = { + .pmu = "default_core", .name = "segment_reg_loads.any", .event = "event=0x6,period=200000,umask=0x80", .desc = "Number of segment register loads", @@ -77,6 +80,7 @@ static const struct perf_pmu_test_event segment_reg_loads_any = { static const struct perf_pmu_test_event dispatch_blocked_any = { .event = { + .pmu = "default_core", .name = "dispatch_blocked.any", .event = "event=0x9,period=200000,umask=0x20", .desc = "Memory cluster signals to block micro-op dispatch for any reason", @@ -88,6 +92,7 @@ static const struct perf_pmu_test_event dispatch_blocked_any = { static const struct perf_pmu_test_event eist_trans = { .event = { + .pmu = "default_core", .name = "eist_trans", .event = "event=0x3a,period=200000,umask=0x0", .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", @@ -99,6 +104,7 @@ static const struct perf_pmu_test_event eist_trans = { static const struct perf_pmu_test_event l3_cache_rd = { .event = { + .pmu = "default_core", .name = "l3_cache_rd", .event = "event=0x40", .desc = "L3 cache access, read", @@ -123,7 +129,7 @@ static const struct perf_pmu_test_event uncore_hisi_ddrc_flux_wcmd = { .event = { .name = "uncore_hisi_ddrc.flux_wcmd", .event = "event=0x2", - .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ", + .desc = "DDRC write commands", .topic = "uncore", .long_desc = "DDRC write commands", .pmu = "hisi_sccl,ddrc", @@ -137,7 +143,7 @@ static const struct perf_pmu_test_event unc_cbo_xsnp_response_miss_eviction = { .event = { .name = "unc_cbo_xsnp_response.miss_eviction", .event = "event=0x22,umask=0x81", - .desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core. Unit: uncore_cbox ", + .desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", .topic = "uncore", .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", .pmu = "uncore_cbox", @@ -151,7 +157,7 @@ static const struct perf_pmu_test_event uncore_hyphen = { .event = { .name = "event-hyphen", .event = "event=0xe0,umask=0x00", - .desc = "UNC_CBO_HYPHEN. Unit: uncore_cbox ", + .desc = "UNC_CBO_HYPHEN", .topic = "uncore", .long_desc = "UNC_CBO_HYPHEN", .pmu = "uncore_cbox", @@ -165,7 +171,7 @@ static const struct perf_pmu_test_event uncore_two_hyph = { .event = { .name = "event-two-hyph", .event = "event=0xc0,umask=0x00", - .desc = "UNC_CBO_TWO_HYPH. Unit: uncore_cbox ", + .desc = "UNC_CBO_TWO_HYPH", .topic = "uncore", .long_desc = "UNC_CBO_TWO_HYPH", .pmu = "uncore_cbox", @@ -179,7 +185,7 @@ static const struct perf_pmu_test_event uncore_hisi_l3c_rd_hit_cpipe = { .event = { .name = "uncore_hisi_l3c.rd_hit_cpipe", .event = "event=0x7", - .desc = "Total read hits. Unit: hisi_sccl,l3c ", + .desc = "Total read hits", .topic = "uncore", .long_desc = "Total read hits", .pmu = "hisi_sccl,l3c", @@ -193,7 +199,7 @@ static const struct perf_pmu_test_event uncore_imc_free_running_cache_miss = { .event = { .name = "uncore_imc_free_running.cache_miss", .event = "event=0x12", - .desc = "Total cache misses. Unit: uncore_imc_free_running ", + .desc = "Total cache misses", .topic = "uncore", .long_desc = "Total cache misses", .pmu = "uncore_imc_free_running", @@ -207,7 +213,7 @@ static const struct perf_pmu_test_event uncore_imc_cache_hits = { .event = { .name = "uncore_imc.cache_hits", .event = "event=0x34", - .desc = "Total cache hits. Unit: uncore_imc ", + .desc = "Total cache hits", .topic = "uncore", .long_desc = "Total cache hits", .pmu = "uncore_imc", @@ -232,13 +238,13 @@ static const struct perf_pmu_test_event sys_ddr_pmu_write_cycles = { .event = { .name = "sys_ddr_pmu.write_cycles", .event = "event=0x2b", - .desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", + .desc = "ddr write-cycles event", .topic = "uncore", .pmu = "uncore_sys_ddr_pmu", .compat = "v8", }, .alias_str = "event=0x2b", - .alias_long_desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", + .alias_long_desc = "ddr write-cycles event", .matching_pmu = "uncore_sys_ddr_pmu", }; @@ -246,13 +252,13 @@ static const struct perf_pmu_test_event sys_ccn_pmu_read_cycles = { .event = { .name = "sys_ccn_pmu.read_cycles", .event = "config=0x2c", - .desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", + .desc = "ccn read-cycles event", .topic = "uncore", .pmu = "uncore_sys_ccn_pmu", .compat = "0x01", }, .alias_str = "config=0x2c", - .alias_long_desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", + .alias_long_desc = "ccn read-cycles event", .matching_pmu = "uncore_sys_ccn_pmu", }; @@ -341,7 +347,7 @@ static int compare_pmu_events(const struct pmu_event *e1, const struct pmu_event return 0; } -static int compare_alias_to_test_event(struct perf_pmu_alias *alias, +static int compare_alias_to_test_event(struct pmu_event_info *alias, struct perf_pmu_test_event const *test_event, char const *pmu_name) { @@ -385,8 +391,8 @@ static int compare_alias_to_test_event(struct perf_pmu_alias *alias, return -1; } - - if (!is_same(alias->pmu_name, test_event->event.pmu)) { + if (!is_same(alias->pmu_name, test_event->event.pmu) && + !is_same(alias->pmu_name, "default_core")) { pr_debug("testing aliases PMU %s: mismatched pmu_name, %s vs %s\n", pmu_name, alias->pmu_name, test_event->event.pmu); return -1; @@ -403,7 +409,7 @@ static int test__pmu_event_table_core_callback(const struct pmu_event *pe, struct perf_pmu_test_event const **test_event_table; bool found = false; - if (pe->pmu) + if (strcmp(pe->pmu, "default_core")) test_event_table = &uncore_events[0]; else test_event_table = &core_events[0]; @@ -477,12 +483,14 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused, if (!table || !sys_event_table) return -1; - err = pmu_events_table_for_each_event(table, test__pmu_event_table_core_callback, + err = pmu_events_table__for_each_event(table, /*pmu=*/ NULL, + test__pmu_event_table_core_callback, &map_events); if (err) return err; - err = pmu_events_table_for_each_event(sys_event_table, test__pmu_event_table_sys_callback, + err = pmu_events_table__for_each_event(sys_event_table, /*pmu=*/ NULL, + test__pmu_event_table_sys_callback, &map_events); if (err) return err; @@ -496,26 +504,30 @@ static int test__pmu_event_table(struct test_suite *test __maybe_unused, return 0; } -static struct perf_pmu_alias *find_alias(const char *test_event, struct list_head *aliases) -{ - struct perf_pmu_alias *alias; +struct test_core_pmu_event_aliases_cb_args { + struct perf_pmu_test_event const *test_event; + int *count; +}; - list_for_each_entry(alias, aliases, list) - if (!strcmp(test_event, alias->name)) - return alias; +static int test_core_pmu_event_aliases_cb(void *state, struct pmu_event_info *alias) +{ + struct test_core_pmu_event_aliases_cb_args *args = state; - return NULL; + if (compare_alias_to_test_event(alias, args->test_event, alias->pmu->name)) + return -1; + (*args->count)++; + pr_debug2("testing aliases core PMU %s: matched event %s\n", + alias->pmu_name, alias->name); + return 0; } /* Verify aliases are as expected */ -static int __test_core_pmu_event_aliases(char *pmu_name, int *count) +static int __test_core_pmu_event_aliases(const char *pmu_name, int *count) { struct perf_pmu_test_event const **test_event_table; struct perf_pmu *pmu; - LIST_HEAD(aliases); int res = 0; const struct pmu_events_table *table = find_core_events_table("testarch", "testcpu"); - struct perf_pmu_alias *a, *tmp; if (!table) return -1; @@ -526,37 +538,40 @@ static int __test_core_pmu_event_aliases(char *pmu_name, int *count) if (!pmu) return -1; - pmu->name = pmu_name; - - pmu_add_cpu_aliases_table(&aliases, pmu, table); - + INIT_LIST_HEAD(&pmu->format); + INIT_LIST_HEAD(&pmu->aliases); + INIT_LIST_HEAD(&pmu->caps); + INIT_LIST_HEAD(&pmu->list); + pmu->name = strdup(pmu_name); + pmu->is_core = true; + + pmu->events_table = table; + pmu_add_cpu_aliases_table(pmu, table); + pmu->cpu_aliases_added = true; + pmu->sysfs_aliases_loaded = true; + + res = pmu_events_table__find_event(table, pmu, "bp_l1_btb_correct", NULL, NULL); + if (res != 0) { + pr_debug("Missing test event in test architecture"); + return res; + } for (; *test_event_table; test_event_table++) { - struct perf_pmu_test_event const *test_event = *test_event_table; - struct pmu_event const *event = &test_event->event; - struct perf_pmu_alias *alias = find_alias(event->name, &aliases); - - if (!alias) { - pr_debug("testing aliases core PMU %s: no alias, alias_table->name=%s\n", - pmu_name, event->name); - res = -1; - break; - } - - if (compare_alias_to_test_event(alias, test_event, pmu_name)) { - res = -1; - break; - } - - (*count)++; - pr_debug2("testing aliases core PMU %s: matched event %s\n", - pmu_name, alias->name); + struct perf_pmu_test_event test_event = **test_event_table; + struct pmu_event const *event = &test_event.event; + struct test_core_pmu_event_aliases_cb_args args = { + .test_event = &test_event, + .count = count, + }; + int err; + + test_event.event.pmu = pmu_name; + err = perf_pmu__find_event(pmu, event->name, &args, + test_core_pmu_event_aliases_cb); + if (err) + res = err; } + perf_pmu__delete(pmu); - list_for_each_entry_safe(a, tmp, &aliases, list) { - list_del(&a->list); - perf_pmu_free_alias(a); - } - free(pmu); return res; } @@ -566,20 +581,20 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) struct perf_pmu_test_event const **table; struct perf_pmu *pmu = &test_pmu->pmu; const char *pmu_name = pmu->name; - struct perf_pmu_alias *a, *tmp, *alias; const struct pmu_events_table *events_table; - LIST_HEAD(aliases); int res = 0; events_table = find_core_events_table("testarch", "testcpu"); if (!events_table) return -1; - pmu_add_cpu_aliases_table(&aliases, pmu, events_table); - pmu_add_sys_aliases(&aliases, pmu); + pmu->events_table = events_table; + pmu_add_cpu_aliases_table(pmu, events_table); + pmu->cpu_aliases_added = true; + pmu->sysfs_aliases_loaded = true; + pmu_add_sys_aliases(pmu); /* Count how many aliases we generated */ - list_for_each_entry(alias, &aliases, list) - alias_count++; + alias_count = perf_pmu__num_events(pmu); /* Count how many aliases we expect from the known table */ for (table = &test_pmu->aliases[0]; *table; table++) @@ -588,33 +603,25 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) if (alias_count != to_match_count) { pr_debug("testing aliases uncore PMU %s: mismatch expected aliases (%d) vs found (%d)\n", pmu_name, to_match_count, alias_count); - res = -1; - goto out; + return -1; } - list_for_each_entry(alias, &aliases, list) { - bool matched = false; - - for (table = &test_pmu->aliases[0]; *table; table++) { - struct perf_pmu_test_event const *test_event = *table; - struct pmu_event const *event = &test_event->event; - - if (!strcmp(event->name, alias->name)) { - if (compare_alias_to_test_event(alias, - test_event, - pmu_name)) { - continue; - } - matched = true; - matched_count++; - } - } - - if (matched == false) { + for (table = &test_pmu->aliases[0]; *table; table++) { + struct perf_pmu_test_event test_event = **table; + struct pmu_event const *event = &test_event.event; + int err; + struct test_core_pmu_event_aliases_cb_args args = { + .test_event = &test_event, + .count = &matched_count, + }; + + err = perf_pmu__find_event(pmu, event->name, &args, + test_core_pmu_event_aliases_cb); + if (err) { + res = err; pr_debug("testing aliases uncore PMU %s: could not match alias %s\n", - pmu_name, alias->name); - res = -1; - goto out; + pmu_name, event->name); + return -1; } } @@ -623,19 +630,13 @@ static int __test_uncore_pmu_event_aliases(struct perf_pmu_test_pmu *test_pmu) pmu_name, matched_count, alias_count); res = -1; } - -out: - list_for_each_entry_safe(a, tmp, &aliases, list) { - list_del(&a->list); - perf_pmu_free_alias(a); - } return res; } static struct perf_pmu_test_pmu test_pmus[] = { { .pmu = { - .name = (char *)"hisi_sccl1_ddrc2", + .name = "hisi_sccl1_ddrc2", .is_uncore = 1, }, .aliases = { @@ -644,7 +645,7 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"uncore_cbox_0", + .name = "uncore_cbox_0", .is_uncore = 1, }, .aliases = { @@ -655,7 +656,7 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"hisi_sccl3_l3c7", + .name = "hisi_sccl3_l3c7", .is_uncore = 1, }, .aliases = { @@ -664,7 +665,7 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"uncore_imc_free_running_0", + .name = "uncore_imc_free_running_0", .is_uncore = 1, }, .aliases = { @@ -673,7 +674,7 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"uncore_imc_0", + .name = "uncore_imc_0", .is_uncore = 1, }, .aliases = { @@ -682,9 +683,9 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"uncore_sys_ddr_pmu0", + .name = "uncore_sys_ddr_pmu0", .is_uncore = 1, - .id = (char *)"v8", + .id = "v8", }, .aliases = { &sys_ddr_pmu_write_cycles, @@ -692,9 +693,9 @@ static struct perf_pmu_test_pmu test_pmus[] = { }, { .pmu = { - .name = (char *)"uncore_sys_ccn_pmu4", + .name = "uncore_sys_ccn_pmu4", .is_uncore = 1, - .id = (char *)"0x01", + .id = "0x01", }, .aliases = { &sys_ccn_pmu_read_cycles, @@ -732,8 +733,13 @@ static int test__aliases(struct test_suite *test __maybe_unused, } for (i = 0; i < ARRAY_SIZE(test_pmus); i++) { - int res = __test_uncore_pmu_event_aliases(&test_pmus[i]); + int res; + + INIT_LIST_HEAD(&test_pmus[i].pmu.format); + INIT_LIST_HEAD(&test_pmus[i].pmu.aliases); + INIT_LIST_HEAD(&test_pmus[i].pmu.caps); + res = __test_uncore_pmu_event_aliases(&test_pmus[i]); if (res) return res; } diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index a4452639a3d4..eb60e5f66859 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -7,6 +7,7 @@ #include <stdio.h> #include <linux/kernel.h> #include <linux/limits.h> +#include <linux/zalloc.h> /* Simulated format definitions. */ static struct test_format { @@ -27,55 +28,55 @@ static struct test_format { /* Simulated users input. */ static struct parse_events_term test_terms[] = { { - .config = (char *) "krava01", + .config = "krava01", .val.num = 15, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava02", + .config = "krava02", .val.num = 170, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava03", + .config = "krava03", .val.num = 1, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava11", + .config = "krava11", .val.num = 27, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava12", + .config = "krava12", .val.num = 1, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava13", + .config = "krava13", .val.num = 2, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava21", + .config = "krava21", .val.num = 119, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava22", + .config = "krava22", .val.num = 11, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava23", + .config = "krava23", .val.num = 2, .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = PARSE_EVENTS__TERM_TYPE_USER, @@ -141,48 +142,55 @@ static struct list_head *test_terms_list(void) static int test__pmu(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { char dir[PATH_MAX]; - char *format = test_format_dir_get(dir, sizeof(dir)); - LIST_HEAD(formats); + char *format; struct list_head *terms = test_terms_list(); + struct perf_event_attr attr; + struct perf_pmu *pmu; + int fd; int ret; - if (!format) - return -EINVAL; - - do { - struct perf_event_attr attr; - int fd; - - memset(&attr, 0, sizeof(attr)); - - fd = open(format, O_DIRECTORY); - if (fd < 0) { - ret = fd; - break; - } - ret = perf_pmu__format_parse(fd, &formats); - if (ret) - break; - - ret = perf_pmu__config_terms("perf-pmu-test", &formats, &attr, - terms, false, NULL); - if (ret) - break; + pmu = zalloc(sizeof(*pmu)); + if (!pmu) + return -ENOMEM; - ret = -EINVAL; + INIT_LIST_HEAD(&pmu->format); + INIT_LIST_HEAD(&pmu->aliases); + INIT_LIST_HEAD(&pmu->caps); + format = test_format_dir_get(dir, sizeof(dir)); + if (!format) { + free(pmu); + return -EINVAL; + } - if (attr.config != 0xc00000000002a823) - break; - if (attr.config1 != 0x8000400000000145) - break; - if (attr.config2 != 0x0400000020041d07) - break; + memset(&attr, 0, sizeof(attr)); - ret = 0; - } while (0); + fd = open(format, O_DIRECTORY); + if (fd < 0) { + ret = fd; + goto out; + } - perf_pmu__del_formats(&formats); + pmu->name = strdup("perf-pmu-test"); + ret = perf_pmu__format_parse(pmu, fd, /*eager_load=*/true); + if (ret) + goto out; + + ret = perf_pmu__config_terms(pmu, &attr, terms, /*zero=*/false, /*err=*/NULL); + if (ret) + goto out; + + ret = -EINVAL; + if (attr.config != 0xc00000000002a823) + goto out; + if (attr.config1 != 0x8000400000000145) + goto out; + if (attr.config2 != 0x0400000020041d07) + goto out; + + ret = 0; +out: test_format_dir_put(format); + perf_pmu__delete(pmu); return ret; } diff --git a/tools/perf/tests/shell/coresight/asm_pure_loop.sh b/tools/perf/tests/shell/coresight/asm_pure_loop.sh index 569e9d46162b..779bc8608e1e 100755 --- a/tools/perf/tests/shell/coresight/asm_pure_loop.sh +++ b/tools/perf/tests/shell/coresight/asm_pure_loop.sh @@ -5,7 +5,7 @@ # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 TEST="asm_pure_loop" -. $(dirname $0)/../lib/coresight.sh +. "$(dirname $0)"/../lib/coresight.sh ARGS="" DATV="out" DATA="$DATD/perf-$TEST-$DATV.data" diff --git a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh index d21ba8545938..08a44e52ce9b 100755 --- a/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh +++ b/tools/perf/tests/shell/coresight/memcpy_thread_16k_10.sh @@ -5,7 +5,7 @@ # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 TEST="memcpy_thread" -. $(dirname $0)/../lib/coresight.sh +. "$(dirname $0)"/../lib/coresight.sh ARGS="16 10 1" DATV="16k_10" DATA="$DATD/perf-$TEST-$DATV.data" diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh index 7c13636fc778..c83a200dede4 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_10.sh @@ -5,7 +5,7 @@ # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 TEST="thread_loop" -. $(dirname $0)/../lib/coresight.sh +. "$(dirname $0)"/../lib/coresight.sh ARGS="10 1" DATV="check-tid-10th" DATA="$DATD/perf-$TEST-$DATV.data" diff --git a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh index a067145af43c..6346fd5e87c8 100755 --- a/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh +++ b/tools/perf/tests/shell/coresight/thread_loop_check_tid_2.sh @@ -5,7 +5,7 @@ # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 TEST="thread_loop" -. $(dirname $0)/../lib/coresight.sh +. "$(dirname $0)"/../lib/coresight.sh ARGS="2 20" DATV="check-tid-2th" DATA="$DATD/perf-$TEST-$DATV.data" diff --git a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh index f48c85230b15..7304e3d3a6ff 100755 --- a/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh +++ b/tools/perf/tests/shell/coresight/unroll_loop_thread_10.sh @@ -5,7 +5,7 @@ # Carsten Haitzler <carsten.haitzler@arm.com>, 2021 TEST="unroll_loop_thread" -. $(dirname $0)/../lib/coresight.sh +. "$(dirname $0)"/../lib/coresight.sh ARGS="10" DATV="10" DATA="$DATD/perf-$TEST-$DATV.data" diff --git a/tools/perf/tests/shell/lib/probe.sh b/tools/perf/tests/shell/lib/probe.sh index 51e3f60baba0..5aa6e2ec5734 100644 --- a/tools/perf/tests/shell/lib/probe.sh +++ b/tools/perf/tests/shell/lib/probe.sh @@ -1,3 +1,4 @@ +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index 60c5e34f90c4..bf4c1fb71c4b 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -1,3 +1,4 @@ +#!/bin/sh # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 perf probe -l 2>&1 | grep -q probe:vfs_getname @@ -10,11 +11,11 @@ cleanup_probe_vfs_getname() { } add_probe_vfs_getname() { - local verbose=$1 + add_probe_verbose=$1 if [ $had_vfs_getname -eq 1 ] ; then line=$(perf probe -L getname_flags 2>&1 | grep -E 'result.*=.*filename;' | sed -r 's/[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*/\1/') perf probe -q "vfs_getname=getname_flags:${line} pathname=result->name:string" || \ - perf probe $verbose "vfs_getname=getname_flags:${line} pathname=filename:ustring" + perf probe $add_probe_verbose "vfs_getname=getname_flags:${line} pathname=filename:ustring" fi } diff --git a/tools/perf/tests/shell/lib/stat_output.sh b/tools/perf/tests/shell/lib/stat_output.sh index 698343f0ecf9..3cc158a64326 100644 --- a/tools/perf/tests/shell/lib/stat_output.sh +++ b/tools/perf/tests/shell/lib/stat_output.sh @@ -1,3 +1,4 @@ +#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # Return true if perf_event_paranoid is > $1 and not running as root. diff --git a/tools/perf/tests/shell/lib/waiting.sh b/tools/perf/tests/shell/lib/waiting.sh index e7a39134a68e..bdd5a7c71591 100644 --- a/tools/perf/tests/shell/lib/waiting.sh +++ b/tools/perf/tests/shell/lib/waiting.sh @@ -1,3 +1,4 @@ +#!/bin/sh # SPDX-License-Identifier: GPL-2.0 tenths=date\ +%s%1N diff --git a/tools/perf/tests/shell/lock_contention.sh b/tools/perf/tests/shell/lock_contention.sh index 4a194420416e..d120e83db7d9 100755 --- a/tools/perf/tests/shell/lock_contention.sh +++ b/tools/perf/tests/shell/lock_contention.sh @@ -21,7 +21,7 @@ trap_cleanup() { trap trap_cleanup EXIT TERM INT check() { - if [ `id -u` != 0 ]; then + if [ "$(id -u)" != 0 ]; then echo "[Skip] No root permission" err=2 exit @@ -157,10 +157,10 @@ test_lock_filter() perf lock contention -i ${perfdata} -L tasklist_lock -q 2> ${result} # find out the type of tasklist_lock - local type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//') + test_lock_filter_type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//') - if [ "$(grep -c -v "${type}" "${result}")" != "0" ]; then - echo "[Fail] Recorded result should not have non-${type} locks:" "$(cat "${result}")" + if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then + echo "[Fail] Recorded result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")" err=1 exit fi @@ -170,8 +170,8 @@ test_lock_filter() fi perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging > /dev/null 2> ${result} - if [ "$(grep -c -v "${type}" "${result}")" != "0" ]; then - echo "[Fail] BPF result should not have non-${type} locks:" "$(cat "${result}")" + if [ "$(grep -c -v "${test_lock_filter_type}" "${result}")" != "0" ]; then + echo "[Fail] BPF result should not have non-${test_lock_filter_type} locks:" "$(cat "${result}")" err=1 exit fi diff --git a/tools/perf/tests/shell/probe_vfs_getname.sh b/tools/perf/tests/shell/probe_vfs_getname.sh index 5d1b63d3f3e1..871243d6d03a 100755 --- a/tools/perf/tests/shell/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/probe_vfs_getname.sh @@ -4,11 +4,11 @@ # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 -. $(dirname $0)/lib/probe.sh +. "$(dirname $0)"/lib/probe.sh skip_if_no_perf_probe || exit 2 -. $(dirname $0)/lib/probe_vfs_getname.sh +. "$(dirname $0)"/lib/probe_vfs_getname.sh add_probe_vfs_getname || skip_if_no_debuginfo err=$? diff --git a/tools/perf/tests/shell/record+zstd_comp_decomp.sh b/tools/perf/tests/shell/record+zstd_comp_decomp.sh index 49bd875d5122..8929046e9057 100755 --- a/tools/perf/tests/shell/record+zstd_comp_decomp.sh +++ b/tools/perf/tests/shell/record+zstd_comp_decomp.sh @@ -13,25 +13,25 @@ skip_if_no_z_record() { collect_z_record() { echo "Collecting compressed record file:" [ "$(uname -m)" != s390x ] && gflag='-g' - $perf_tool record -o $trace_file $gflag -z -F 5000 -- \ + $perf_tool record -o "$trace_file" $gflag -z -F 5000 -- \ dd count=500 if=/dev/urandom of=/dev/null } check_compressed_stats() { echo "Checking compressed events stats:" - $perf_tool report -i $trace_file --header --stats | \ + $perf_tool report -i "$trace_file" --header --stats | \ grep -E "(# compressed : Zstd,)|(COMPRESSED events:)" } check_compressed_output() { - $perf_tool inject -i $trace_file -o $trace_file.decomp && - $perf_tool report -i $trace_file --stdio -F comm,dso,sym | head -n -3 > $trace_file.comp.output && - $perf_tool report -i $trace_file.decomp --stdio -F comm,dso,sym | head -n -3 > $trace_file.decomp.output && - diff $trace_file.comp.output $trace_file.decomp.output + $perf_tool inject -i "$trace_file" -o "$trace_file.decomp" && + $perf_tool report -i "$trace_file" --stdio -F comm,dso,sym | head -n -3 > "$trace_file.comp.output" && + $perf_tool report -i "$trace_file.decomp" --stdio -F comm,dso,sym | head -n -3 > "$trace_file.decomp.output" && + diff "$trace_file.comp.output" "$trace_file.decomp.output" } skip_if_no_z_record || exit 2 collect_z_record && check_compressed_stats && check_compressed_output err=$? -rm -f $trace_file* +rm -f "$trace_file*" exit $err diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh new file mode 100755 index 000000000000..31c593966e8c --- /dev/null +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# perf record sample filtering (by BPF) tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +err=0 +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + +cleanup() { + rm -f "${perfdata}" + rm -f "${perfdata}".old + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +test_bpf_filter_priv() { + echo "Checking BPF-filter privilege" + + if [ "$(id -u)" != 0 ] + then + echo "bpf-filter test [Skipped permission]" + err=2 + return + fi + if ! perf record -e task-clock --filter 'period > 1' \ + -o /dev/null --quiet true 2>&1 + then + echo "bpf-filter test [Skipped missing BPF support]" + err=2 + return + fi +} + +test_bpf_filter_basic() { + echo "Basic bpf-filter test" + + if ! perf record -e task-clock -c 10000 --filter 'ip < 0xffffffff00000000' \ + -o "${perfdata}" true 2> /dev/null + then + echo "Basic bpf-filter test [Failed record]" + err=1 + return + fi + if perf script -i "${perfdata}" -F ip | grep 'ffffffff[0-9a-f]*' + then + if uname -r | grep -q ^6.2 + then + echo "Basic bpf-filter test [Skipped unsupported kernel]" + err=2 + return + fi + echo "Basic bpf-filter test [Failed invalid output]" + err=1 + return + fi + echo "Basic bpf-filter test [Success]" +} + +test_bpf_filter_fail() { + echo "Failing bpf-filter test" + + # 'cpu' requires PERF_SAMPLE_CPU flag + if ! perf record -e task-clock --filter 'cpu > 0' \ + -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + then + echo "Failing bpf-filter test [Failed forbidden CPU]" + err=1 + return + fi + + if ! perf record --sample-cpu -e task-clock --filter 'cpu > 0' \ + -o /dev/null true 2>/dev/null + then + echo "Failing bpf-filter test [Failed should succeed]" + err=1 + return + fi + + echo "Failing bpf-filter test [Success]" +} + +test_bpf_filter_group() { + echo "Group bpf-filter test" + + if ! perf record -e task-clock --filter 'period > 1000 || ip > 0' \ + -o /dev/null true 2>/dev/null + then + echo "Group bpf-filter test [Failed should succeed]" + err=1 + return + fi + + if ! perf record -e task-clock --filter 'cpu > 0 || ip > 0' \ + -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + then + echo "Group bpf-filter test [Failed forbidden CPU]" + err=1 + return + fi + + if ! perf record -e task-clock --filter 'period > 0 || code_pgsz > 4096' \ + -o /dev/null true 2>&1 | grep PERF_SAMPLE_CODE_PAGE_SIZE + then + echo "Group bpf-filter test [Failed forbidden CODE_PAGE_SIZE]" + err=1 + return + fi + + echo "Group bpf-filter test [Success]" +} + + +test_bpf_filter_priv + +if [ $err = 0 ]; then + test_bpf_filter_basic +fi + +if [ $err = 0 ]; then + test_bpf_filter_fail +fi + +if [ $err = 0 ]; then + test_bpf_filter_group +fi + +cleanup +exit $err diff --git a/tools/perf/tests/shell/record_offcpu.sh b/tools/perf/tests/shell/record_offcpu.sh index f062ae9a95e1..a0d14cd0aa79 100755 --- a/tools/perf/tests/shell/record_offcpu.sh +++ b/tools/perf/tests/shell/record_offcpu.sh @@ -10,19 +10,19 @@ perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) cleanup() { rm -f ${perfdata} rm -f ${perfdata}.old - trap - exit term int + trap - EXIT TERM INT } trap_cleanup() { cleanup exit 1 } -trap trap_cleanup exit term int +trap trap_cleanup EXIT TERM INT test_offcpu_priv() { echo "Checking off-cpu privilege" - if [ `id -u` != 0 ] + if [ "$(id -u)" != 0 ] then echo "off-cpu test [Skipped permission]" err=2 diff --git a/tools/perf/tests/shell/stat+csv_output.sh b/tools/perf/tests/shell/stat+csv_output.sh index 34a0701fee05..d890eb26e914 100755 --- a/tools/perf/tests/shell/stat+csv_output.sh +++ b/tools/perf/tests/shell/stat+csv_output.sh @@ -6,7 +6,7 @@ set -e -. $(dirname $0)/lib/stat_output.sh +. "$(dirname $0)"/lib/stat_output.sh csv_sep=@ diff --git a/tools/perf/tests/shell/stat+csv_summary.sh b/tools/perf/tests/shell/stat+csv_summary.sh index 5571ff75eb42..8bae9c8a835e 100755 --- a/tools/perf/tests/shell/stat+csv_summary.sh +++ b/tools/perf/tests/shell/stat+csv_summary.sh @@ -10,7 +10,7 @@ set -e # perf stat -e cycles -x' ' -I1000 --interval-count 1 --summary 2>&1 | \ grep -e summary | \ -while read summary num event run pct +while read summary _num _event _run _pct do if [ $summary != "summary" ]; then exit 1 @@ -23,7 +23,7 @@ done # perf stat -e cycles -x' ' -I1000 --interval-count 1 --summary --no-csv-summary 2>&1 | \ grep -e summary | \ -while read num event run pct +while read _num _event _run _pct do exit 1 done diff --git a/tools/perf/tests/shell/stat+shadow_stat.sh b/tools/perf/tests/shell/stat+shadow_stat.sh index 0e9cba84e757..a1918a15e36a 100755 --- a/tools/perf/tests/shell/stat+shadow_stat.sh +++ b/tools/perf/tests/shell/stat+shadow_stat.sh @@ -14,7 +14,7 @@ test_global_aggr() { perf stat -a --no-big-num -e cycles,instructions sleep 1 2>&1 | \ grep -e cycles -e instructions | \ - while read num evt hash ipc rest + while read num evt _hash ipc rest do # skip not counted events if [ "$num" = "<not" ]; then @@ -45,7 +45,7 @@ test_no_aggr() { perf stat -a -A --no-big-num -e cycles,instructions sleep 1 2>&1 | \ grep ^CPU | \ - while read cpu num evt hash ipc rest + while read cpu num evt _hash ipc rest do # skip not counted events if [ "$num" = "<not" ]; then diff --git a/tools/perf/tests/shell/stat+std_output.sh b/tools/perf/tests/shell/stat+std_output.sh index f972b31fa0c2..fb2b10547a11 100755 --- a/tools/perf/tests/shell/stat+std_output.sh +++ b/tools/perf/tests/shell/stat+std_output.sh @@ -6,7 +6,7 @@ set -e -. $(dirname $0)/lib/stat_output.sh +. "$(dirname $0)"/lib/stat_output.sh stat_output=$(mktemp /tmp/__perf_test.stat_output.std.XXXXX) @@ -28,7 +28,6 @@ trap trap_cleanup EXIT TERM INT function commachecker() { - local -i cnt=0 local prefix=1 case "$1" diff --git a/tools/perf/tests/shell/stat_bpf_counters.sh b/tools/perf/tests/shell/stat_bpf_counters.sh index 13473aeba489..a87bb2814b4c 100755 --- a/tools/perf/tests/shell/stat_bpf_counters.sh +++ b/tools/perf/tests/shell/stat_bpf_counters.sh @@ -22,21 +22,21 @@ compare_number() } # skip if --bpf-counters is not supported -if ! perf stat --bpf-counters true > /dev/null 2>&1; then +if ! perf stat -e cycles --bpf-counters true > /dev/null 2>&1; then if [ "$1" = "-v" ]; then echo "Skipping: --bpf-counters not supported" - perf --no-pager stat --bpf-counters true || true + perf --no-pager stat -e cycles --bpf-counters true || true fi exit 2 fi base_cycles=$(perf stat --no-big-num -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}') -if [ "$base_cycles" == "<not" ]; then +if [ "$base_cycles" = "<not" ]; then echo "Skipping: cycles event not counted" exit 2 fi bpf_cycles=$(perf stat --no-big-num --bpf-counters -e cycles -- perf bench sched messaging -g 1 -l 100 -t 2>&1 | awk '/cycles/ {print $1}') -if [ "$bpf_cycles" == "<not" ]; then +if [ "$bpf_cycles" = "<not" ]; then echo "Failed: cycles not counted with --bpf-counters" exit 1 fi diff --git a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh index d724855d097c..e75d0780dc78 100755 --- a/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh +++ b/tools/perf/tests/shell/stat_bpf_counters_cgrp.sh @@ -25,22 +25,22 @@ check_bpf_counter() find_cgroups() { # try usual systemd slices first - if [ -d /sys/fs/cgroup/system.slice -a -d /sys/fs/cgroup/user.slice ]; then + if [ -d /sys/fs/cgroup/system.slice ] && [ -d /sys/fs/cgroup/user.slice ]; then test_cgroups="system.slice,user.slice" return fi # try root and self cgroups - local self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) - if [ -z ${self_cgrp} ]; then + find_cgroups_self_cgrp=$(grep perf_event /proc/self/cgroup | cut -d: -f3) + if [ -z ${find_cgroups_self_cgrp} ]; then # cgroup v2 doesn't specify perf_event - self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) + find_cgroups_self_cgrp=$(grep ^0: /proc/self/cgroup | cut -d: -f3) fi - if [ -z ${self_cgrp} ]; then + if [ -z ${find_cgroups_self_cgrp} ]; then test_cgroups="/" else - test_cgroups="/,${self_cgrp}" + test_cgroups="/,${find_cgroups_self_cgrp}" fi } @@ -48,13 +48,11 @@ find_cgroups() # Just check if it runs without failure and has non-zero results. check_system_wide_counted() { - local output - - output=$(perf stat -a --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, sleep 1 2>&1) - if echo ${output} | grep -q -F "<not "; then + check_system_wide_counted_output=$(perf stat -a --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, sleep 1 2>&1) + if echo ${check_system_wide_counted_output} | grep -q -F "<not "; then echo "Some system-wide events are not counted" if [ "${verbose}" = "1" ]; then - echo ${output} + echo ${check_system_wide_counted_output} fi exit 1 fi @@ -62,13 +60,11 @@ check_system_wide_counted() check_cpu_list_counted() { - local output - - output=$(perf stat -C 1 --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, taskset -c 1 sleep 1 2>&1) - if echo ${output} | grep -q -F "<not "; then + check_cpu_list_counted_output=$(perf stat -C 0,1 --bpf-counters --for-each-cgroup ${test_cgroups} -e cpu-clock -x, taskset -c 1 sleep 1 2>&1) + if echo ${check_cpu_list_counted_output} | grep -q -F "<not "; then echo "Some CPU events are not counted" if [ "${verbose}" = "1" ]; then - echo ${output} + echo ${check_cpu_list_counted_output} fi exit 1 fi diff --git a/tools/perf/tests/shell/test_arm_spe_fork.sh b/tools/perf/tests/shell/test_arm_spe_fork.sh index fad361675a1d..1a7e6a82d0e3 100755 --- a/tools/perf/tests/shell/test_arm_spe_fork.sh +++ b/tools/perf/tests/shell/test_arm_spe_fork.sh @@ -22,7 +22,7 @@ cleanup_files() rm -f ${PERF_DATA} } -trap cleanup_files exit term int +trap cleanup_files EXIT TERM INT echo "Recording workload..." perf record -o ${PERF_DATA} -e arm_spe/period=65536/ -vvv -- $TEST_PROGRAM > ${PERF_RECORD_LOG} 2>&1 & diff --git a/tools/perf/tests/shell/test_perf_data_converter_json.sh b/tools/perf/tests/shell/test_perf_data_converter_json.sh index 72ac6c83231c..6ded58f98f55 100755 --- a/tools/perf/tests/shell/test_perf_data_converter_json.sh +++ b/tools/perf/tests/shell/test_perf_data_converter_json.sh @@ -39,7 +39,7 @@ test_json_converter_command() echo "Testing Perf Data Convertion Command to JSON" perf record -o "$perfdata" -F 99 -g -- perf test -w noploop > /dev/null 2>&1 perf data convert --to-json "$result" --force -i "$perfdata" >/dev/null 2>&1 - if [ $(cat "${result}" | wc -l) -gt "0" ] ; then + if [ "$(cat ${result} | wc -l)" -gt "0" ] ; then echo "Perf Data Converter Command to JSON [SUCCESS]" else echo "Perf Data Converter Command to JSON [FAILED]" diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 0095abbe20ca..92d15154ba79 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -52,7 +52,7 @@ find_str_or_fail() { # check if perf is compiled with libtraceevent support skip_no_probe_record_support() { - perf record -e "sched:sched_switch" -a -- sleep 1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2 + perf version --build-options | grep -q " OFF .* HAVE_LIBTRACEEVENT" && return 2 return 0 } diff --git a/tools/perf/tests/shell/trace+probe_vfs_getname.sh b/tools/perf/tests/shell/trace+probe_vfs_getname.sh index 0a4bac3dd77e..4014487cf4d9 100755 --- a/tools/perf/tests/shell/trace+probe_vfs_getname.sh +++ b/tools/perf/tests/shell/trace+probe_vfs_getname.sh @@ -10,17 +10,17 @@ # SPDX-License-Identifier: GPL-2.0 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 -. $(dirname $0)/lib/probe.sh +. "$(dirname $0)"/lib/probe.sh skip_if_no_perf_probe || exit 2 skip_if_no_perf_trace || exit 2 -. $(dirname $0)/lib/probe_vfs_getname.sh +. "$(dirname $0)"/lib/probe_vfs_getname.sh trace_open_vfs_getname() { - evts=$(echo $(perf list syscalls:sys_enter_open* 2>/dev/null | grep -E 'open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/') | sed 's/ /,/') + evts="$(echo "$(perf list syscalls:sys_enter_open* 2>/dev/null | grep -E 'open(at)? ' | sed -r 's/.*sys_enter_([a-z]+) +\[.*$/\1/')" | sed ':a;N;s:\n:,:g')" perf trace -e $evts touch $file 2>&1 | \ - grep -E " +[0-9]+\.[0-9]+ +\( +[0-9]+\.[0-9]+ ms\): +touch\/[0-9]+ open(at)?\((dfd: +CWD, +)?filename: +${file}, +flags: CREAT\|NOCTTY\|NONBLOCK\|WRONLY, +mode: +IRUGO\|IWUGO\) += +[0-9]+$" + grep -E " +[0-9]+\.[0-9]+ +\( +[0-9]+\.[0-9]+ ms\): +touch/[0-9]+ open(at)?\((dfd: +CWD, +)?filename: +\"?${file}\"?, +flags: CREAT\|NOCTTY\|NONBLOCK\|WRONLY, +mode: +IRUGO\|IWUGO\) += +[0-9]+$" } diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index 500974040fe3..706780fb5695 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -27,7 +27,7 @@ static int process_stat_config_event(struct perf_tool *tool __maybe_unused, struct machine *machine __maybe_unused) { struct perf_record_stat_config *config = &event->stat_config; - struct perf_stat_config stat_config; + struct perf_stat_config stat_config = {}; #define HAS(term, val) \ has_term(config, PERF_STAT_CONFIG_TERM__##term, val) diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index f424c0b7f43f..f33cfc3c19a4 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -113,7 +113,6 @@ DECLARE_SUITE(fdarray__filter); DECLARE_SUITE(fdarray__add); DECLARE_SUITE(kmod_path__parse); DECLARE_SUITE(thread_map); -DECLARE_SUITE(llvm); DECLARE_SUITE(bpf); DECLARE_SUITE(session_topology); DECLARE_SUITE(thread_map_synthesize); @@ -129,7 +128,6 @@ DECLARE_SUITE(sdt_event); DECLARE_SUITE(is_printable_array); DECLARE_SUITE(bitmap_print); DECLARE_SUITE(perf_hooks); -DECLARE_SUITE(clang); DECLARE_SUITE(unit_number__scnprint); DECLARE_SUITE(mem2node); DECLARE_SUITE(maps__merge_in); diff --git a/tools/perf/trace/beauty/arch_errno_names.sh b/tools/perf/trace/beauty/arch_errno_names.sh index 37c53bac5f56..cc09dcaa891e 100755 --- a/tools/perf/trace/beauty/arch_errno_names.sh +++ b/tools/perf/trace/beauty/arch_errno_names.sh @@ -17,8 +17,7 @@ arch_string() asm_errno_file() { - local arch="$1" - local header + arch="$1" header="$toolsdir/arch/$arch/include/uapi/asm/errno.h" if test -r "$header"; then @@ -30,8 +29,7 @@ asm_errno_file() create_errno_lookup_func() { - local arch=$(arch_string "$1") - local nr name + arch=$(arch_string "$1") printf "static const char *errno_to_name__%s(int err)\n{\n\tswitch (err) {\n" $arch @@ -44,8 +42,8 @@ create_errno_lookup_func() process_arch() { - local arch="$1" - local asm_errno=$(asm_errno_file "$arch") + arch="$1" + asm_errno=$(asm_errno_file "$arch") $gcc $CFLAGS $include_path -E -dM -x c $asm_errno \ |grep -hE '^#define[[:blank:]]+(E[^[:blank:]]+)[[:blank:]]+([[:digit:]]+).*' \ @@ -56,9 +54,8 @@ process_arch() create_arch_errno_table_func() { - local archlist="$1" - local default="$2" - local arch + archlist="$1" + default="$2" printf 'const char *arch_syscalls__strerrno(const char *arch, int err)\n' printf '{\n' diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 3d12bf0f6d07..788e8f6bd90e 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -67,15 +67,14 @@ extern struct strarray strarray__socket_level; /** * augmented_arg: extra payload for syscall pointer arguments - * If perf_sample->raw_size is more than what a syscall sys_enter_FOO puts, - * then its the arguments contents, so that we can show more than just a + * If perf_sample->raw_size is more than what a syscall sys_enter_FOO puts, then + * its the arguments contents, so that we can show more than just a * pointer. This will be done initially with eBPF, the start of that is at the - * tools/perf/examples/bpf/augmented_syscalls.c example for the openat, but - * will eventually be done automagically caching the running kernel tracefs - * events data into an eBPF C script, that then gets compiled and its .o file - * cached for subsequent use. For char pointers like the ones for 'open' like - * syscalls its easy, for the rest we should use DWARF or better, BTF, much - * more compact. + * tools/perf/util/bpf_skel/augmented_syscalls.bpf.c that will eventually be + * done automagically caching the running kernel tracefs events data into an + * eBPF C script, that then gets compiled and its .o file cached for subsequent + * use. For char pointers like the ones for 'open' like syscalls its easy, for + * the rest we should use DWARF or better, BTF, much more compact. * * @size: 8 if all we need is an integer, otherwise all of the augmented arg. * @int_arg: will be used for integer like pointer contents, like 'accept's 'upeer_addrlen' diff --git a/tools/perf/trace/beauty/mmap_flags.sh b/tools/perf/trace/beauty/mmap_flags.sh index 3022597c8c17..6ecdb3c5a99e 100755 --- a/tools/perf/trace/beauty/mmap_flags.sh +++ b/tools/perf/trace/beauty/mmap_flags.sh @@ -19,6 +19,7 @@ arch_mman=${arch_header_dir}/mman.h printf "static const char *mmap_flags[] = {\n" regex='^[[:space:]]*#[[:space:]]*define[[:space:]]+MAP_([[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*' +test -f ${arch_mman} && \ grep -E -q $regex ${arch_mman} && \ (grep -E $regex ${arch_mman} | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ @@ -28,12 +29,14 @@ grep -E -q $regex ${linux_mman} && \ grep -E -vw 'MAP_(UNINITIALIZED|TYPE|SHARED_VALIDATE)' | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n#ifndef MAP_%s\n#define MAP_%s %s\n#endif\n") -([ ! -f ${arch_mman} ] || grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.*' ${arch_mman}) && +( ! test -f ${arch_mman} || \ +grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.*' ${arch_mman}) && (grep -E $regex ${header_dir}/mman-common.h | \ grep -E -vw 'MAP_(UNINITIALIZED|TYPE|SHARED_VALIDATE)' | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n#ifndef MAP_%s\n#define MAP_%s %s\n#endif\n") -([ ! -f ${arch_mman} ] || grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.h>.*' ${arch_mman}) && +( ! test -f ${arch_mman} || \ +grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.h>.*' ${arch_mman}) && (grep -E $regex ${header_dir}/mman.h | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n#ifndef MAP_%s\n#define MAP_%s %s\n#endif\n") diff --git a/tools/perf/trace/beauty/mmap_prot.sh b/tools/perf/trace/beauty/mmap_prot.sh index 49e8c865214b..4436fcd6e861 100755 --- a/tools/perf/trace/beauty/mmap_prot.sh +++ b/tools/perf/trace/beauty/mmap_prot.sh @@ -17,12 +17,13 @@ prefix="PROT" printf "static const char *mmap_prot[] = {\n" regex=`printf '^[[:space:]]*#[[:space:]]*define[[:space:]]+%s_([[:alnum:]_]+)[[:space:]]+(0x[[:xdigit:]]+)[[:space:]]*.*' ${prefix}` -([ ! -f ${arch_mman} ] || grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.*' ${arch_mman}) && +( ! test -f ${arch_mman} \ +|| grep -E -q '#[[:space:]]*include[[:space:]]+.*uapi/asm-generic/mman.*' ${arch_mman}) && (grep -E $regex ${common_mman} | \ grep -E -vw PROT_NONE | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ xargs printf "\t[ilog2(%s) + 1] = \"%s\",\n#ifndef ${prefix}_%s\n#define ${prefix}_%s %s\n#endif\n") -[ -f ${arch_mman} ] && grep -E -q $regex ${arch_mman} && +test -f ${arch_mman} && grep -E -q $regex ${arch_mman} && (grep -E $regex ${arch_mman} | \ grep -E -vw PROT_NONE | \ sed -r "s/$regex/\2 \1 \1 \1 \2/g" | \ diff --git a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh index eed9ce0fcbe6..87dc68c7de0c 100755 --- a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh +++ b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh @@ -12,7 +12,7 @@ x86_irq_vectors=${arch_x86_header_dir}/irq_vectors.h # FIRST_EXTERNAL_VECTOR is not that useful, find what is its number # and then replace whatever is using it and that is useful, which at -# the time of writing of this script was: IRQ_MOVE_CLEANUP_VECTOR. +# the time of writing of this script was: 0x20. first_external_regex='^#define[[:space:]]+FIRST_EXTERNAL_VECTOR[[:space:]]+(0x[[:xdigit:]]+)$' first_external_vector=$(grep -E ${first_external_regex} ${x86_irq_vectors} | sed -r "s/${first_external_regex}/\1/g") diff --git a/tools/perf/trace/beauty/x86_arch_prctl.sh b/tools/perf/trace/beauty/x86_arch_prctl.sh index fd5c740512c5..b1596df251f0 100755 --- a/tools/perf/trace/beauty/x86_arch_prctl.sh +++ b/tools/perf/trace/beauty/x86_arch_prctl.sh @@ -7,9 +7,9 @@ prctl_arch_header=${x86_header_dir}/prctl.h print_range () { - local idx=$1 - local prefix=$2 - local first_entry=$3 + idx=$1 + prefix=$2 + first_entry=$3 printf "#define x86_arch_prctl_codes_%d_offset %s\n" $idx $first_entry printf "static const char *x86_arch_prctl_codes_%d[] = {\n" $idx diff --git a/tools/perf/ui/Build b/tools/perf/ui/Build index 3aff83c3275f..6b6d7143a37b 100644 --- a/tools/perf/ui/Build +++ b/tools/perf/ui/Build @@ -10,5 +10,3 @@ CFLAGS_setup.o += -DLIBDIR="BUILD_STR($(LIBDIR))" perf-$(CONFIG_SLANG) += browser.o perf-$(CONFIG_SLANG) += browsers/ perf-$(CONFIG_SLANG) += tui/ - -CFLAGS_browser.o += -DENABLE_SLFUTURE_CONST diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 78fb01d6ad63..603d11283cbd 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -57,12 +57,12 @@ void ui_browser__gotorc(struct ui_browser *browser, int y, int x) void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg, unsigned int width) { - slsmg_write_nstring(msg, width); + SLsmg_write_nstring(msg, width); } void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args) { - slsmg_vprintf(fmt, args); + SLsmg_vprintf(fmt, args); } void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...) @@ -808,6 +808,6 @@ void ui_browser__init(void) while (ui_browser__colorsets[i].name) { struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; - sltt_set_color(c->colorset, c->name, c->fg, c->bg); + SLtt_set_color(c->colorset, c->name, c->fg, c->bg); } } diff --git a/tools/perf/ui/browsers/Build b/tools/perf/ui/browsers/Build index fdf86f7981ca..7a1d5ddaf688 100644 --- a/tools/perf/ui/browsers/Build +++ b/tools/perf/ui/browsers/Build @@ -4,8 +4,3 @@ perf-y += map.o perf-y += scripts.o perf-y += header.o perf-y += res_sample.o - -CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST -CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST -CFLAGS_map.o += -DENABLE_SLFUTURE_CONST -CFLAGS_scripts.o += -DENABLE_SLFUTURE_CONST diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index c7ad9e003080..70db5a717905 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -407,11 +407,6 @@ static bool hist_browser__selection_has_children(struct hist_browser *browser) return container_of(ms, struct callchain_list, ms)->has_children; } -static bool hist_browser__he_selection_unfolded(struct hist_browser *browser) -{ - return browser->he_selection ? browser->he_selection->unfolded : false; -} - static bool hist_browser__selection_unfolded(struct hist_browser *browser) { struct hist_entry *he = browser->he_selection; @@ -584,8 +579,8 @@ static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, return n; } -static void __hist_entry__set_folding(struct hist_entry *he, - struct hist_browser *hb, bool unfold) +static void hist_entry__set_folding(struct hist_entry *he, + struct hist_browser *hb, bool unfold) { hist_entry__init_have_children(he); he->unfolded = unfold ? he->has_children : false; @@ -603,34 +598,12 @@ static void __hist_entry__set_folding(struct hist_entry *he, he->nr_rows = 0; } -static void hist_entry__set_folding(struct hist_entry *he, - struct hist_browser *browser, bool unfold) -{ - double percent; - - percent = hist_entry__get_percent_limit(he); - if (he->filtered || percent < browser->min_pcnt) - return; - - __hist_entry__set_folding(he, browser, unfold); - - if (!he->depth || unfold) - browser->nr_hierarchy_entries++; - if (he->leaf) - browser->nr_callchain_rows += he->nr_rows; - else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { - browser->nr_hierarchy_entries++; - he->has_no_entry = true; - he->nr_rows = 1; - } else - he->has_no_entry = false; -} - static void __hist_browser__set_folding(struct hist_browser *browser, bool unfold) { struct rb_node *nd; struct hist_entry *he; + double percent; nd = rb_first_cached(&browser->hists->entries); while (nd) { @@ -640,6 +613,21 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold) nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); hist_entry__set_folding(he, browser, unfold); + + percent = hist_entry__get_percent_limit(he); + if (he->filtered || percent < browser->min_pcnt) + continue; + + if (!he->depth || unfold) + browser->nr_hierarchy_entries++; + if (he->leaf) + browser->nr_callchain_rows += he->nr_rows; + else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { + browser->nr_hierarchy_entries++; + he->has_no_entry = true; + he->nr_rows = 1; + } else + he->has_no_entry = false; } } @@ -659,8 +647,10 @@ static void hist_browser__set_folding_selected(struct hist_browser *browser, boo if (!browser->he_selection) return; - hist_entry__set_folding(browser->he_selection, browser, unfold); - browser->b.nr_entries = hist_browser__nr_entries(browser); + if (unfold == browser->he_selection->unfolded) + return; + + hist_browser__toggle_fold(browser); } static void ui_browser__warn_lost_events(struct ui_browser *browser) @@ -732,8 +722,8 @@ static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_l hist_browser__set_folding(browser, true); break; case 'e': - /* Expand the selected entry. */ - hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser)); + /* Toggle expand/collapse the selected entry. */ + hist_browser__toggle_fold(browser); break; case 'H': browser->show_headers = !browser->show_headers; @@ -1779,7 +1769,7 @@ static void hists_browser__hierarchy_headers(struct hist_browser *browser) hists_browser__scnprintf_hierarchy_headers(browser, headers, sizeof(headers)); - ui_browser__gotorc(&browser->b, 0, 0); + ui_browser__gotorc_title(&browser->b, 0, 0); ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); } diff --git a/tools/perf/ui/libslang.h b/tools/perf/ui/libslang.h index 991e692b9b46..1dff3020e9d5 100644 --- a/tools/perf/ui/libslang.h +++ b/tools/perf/ui/libslang.h @@ -11,28 +11,16 @@ #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG #endif +/* Enable future slang's corrected function prototypes. */ +#define ENABLE_SLFUTURE_CONST 1 +#define ENABLE_SLFUTURE_VOID 1 + #ifdef HAVE_SLANG_INCLUDE_SUBDIR #include <slang/slang.h> #else #include <slang.h> #endif -#if SLANG_VERSION < 20104 -#define slsmg_printf(msg, args...) \ - SLsmg_printf((char *)(msg), ##args) -#define slsmg_vprintf(msg, vargs) \ - SLsmg_vprintf((char *)(msg), vargs) -#define slsmg_write_nstring(msg, len) \ - SLsmg_write_nstring((char *)(msg), len) -#define sltt_set_color(obj, name, fg, bg) \ - SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg)) -#else -#define slsmg_printf SLsmg_printf -#define slsmg_vprintf SLsmg_vprintf -#define slsmg_write_nstring SLsmg_write_nstring -#define sltt_set_color SLtt_set_color -#endif - #define SL_KEY_UNTAB 0x1000 #endif /* _PERF_UI_SLANG_H_ */ diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c index db4952f5990b..b39451314f43 100644 --- a/tools/perf/ui/tui/helpline.c +++ b/tools/perf/ui/tui/helpline.c @@ -22,7 +22,7 @@ static void tui_helpline__push(const char *msg) SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); SLsmg_set_color(0); - SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); + SLsmg_write_nstring(msg, SLtt_Screen_Cols); SLsmg_refresh(); strlcpy(ui_helpline__current, msg, sz); } diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index c1886aa184b3..605d9e175ea7 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -142,7 +142,7 @@ int ui__init(void) goto out; } - SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); + SLkp_define_keysym("^(kB)", SL_KEY_UNTAB); signal(SIGSEGV, ui__signal_backtrace); signal(SIGFPE, ui__signal_backtrace); diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c index 3c5174854ac8..e4d322ce0b54 100644 --- a/tools/perf/ui/tui/util.c +++ b/tools/perf/ui/tui/util.c @@ -106,7 +106,7 @@ int ui_browser__input_window(const char *title, const char *text, char *input, SLsmg_draw_box(y, x++, nr_lines, max_len); if (title) { SLsmg_gotorc(y, x + 1); - SLsmg_write_string((char *)title); + SLsmg_write_string(title); } SLsmg_gotorc(++y, x); nr_lines -= 7; @@ -117,12 +117,12 @@ int ui_browser__input_window(const char *title, const char *text, char *input, len = 5; while (len--) { SLsmg_gotorc(y + len - 1, x); - SLsmg_write_nstring((char *)" ", max_len); + SLsmg_write_nstring(" ", max_len); } SLsmg_draw_box(y++, x + 1, 3, max_len - 2); SLsmg_gotorc(y + 3, x); - SLsmg_write_nstring((char *)exit_msg, max_len); + SLsmg_write_nstring(exit_msg, max_len); SLsmg_refresh(); mutex_unlock(&ui__lock); @@ -197,7 +197,7 @@ void __ui__info_window(const char *title, const char *text, const char *exit_msg SLsmg_draw_box(y, x++, nr_lines, max_len); if (title) { SLsmg_gotorc(y, x + 1); - SLsmg_write_string((char *)title); + SLsmg_write_string(title); } SLsmg_gotorc(++y, x); if (exit_msg) @@ -207,9 +207,9 @@ void __ui__info_window(const char *title, const char *text, const char *exit_msg nr_lines, max_len, 1); if (exit_msg) { SLsmg_gotorc(y + nr_lines - 2, x); - SLsmg_write_nstring((char *)" ", max_len); + SLsmg_write_nstring(" ", max_len); SLsmg_gotorc(y + nr_lines - 1, x); - SLsmg_write_nstring((char *)exit_msg, max_len); + SLsmg_write_nstring(exit_msg, max_len); } } diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 96f4ea1d45c5..6d657c9927f7 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -1,3 +1,6 @@ +include $(srctree)/tools/scripts/Makefile.include +include $(srctree)/tools/scripts/utilities.mak + perf-y += arm64-frame-pointer-unwind-support.o perf-y += addr_location.o perf-y += annotate.o @@ -20,13 +23,13 @@ perf-y += evswitch.o perf-y += find_bit.o perf-y += get_current_dir_name.o perf-y += levenshtein.o -perf-y += llvm-utils.o perf-y += mmap.o perf-y += memswap.o perf-y += parse-events.o perf-y += print-events.o perf-y += tracepoint.o perf-y += perf_regs.o +perf-y += perf-regs-arch/ perf-y += path.o perf-y += print_binary.o perf-y += rlimit.o @@ -147,7 +150,6 @@ perf-y += list_sort.o perf-y += mutex.o perf-y += sharded_mutex.o -perf-$(CONFIG_LIBBPF) += bpf-loader.o perf-$(CONFIG_LIBBPF) += bpf_map.o perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o @@ -165,7 +167,6 @@ ifeq ($(CONFIG_LIBTRACEEVENT),y) perf-$(CONFIG_PERF_BPF_SKEL) += bpf_kwork.o endif -perf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o perf-$(CONFIG_LIBELF) += symbol-elf.o perf-$(CONFIG_LIBELF) += probe-file.o perf-$(CONFIG_LIBELF) += probe-event.o @@ -229,12 +230,9 @@ perf-y += perf-hooks.o perf-$(CONFIG_LIBBPF) += bpf-event.o perf-$(CONFIG_LIBBPF) += bpf-utils.o -perf-$(CONFIG_CXX) += c++/ - perf-$(CONFIG_LIBPFM4) += pfm.o CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" -CFLAGS_llvm-utils.o += -DLIBBPF_INCLUDE_DIR="BUILD_STR($(libbpf_include_dir_SQ))" # avoid compiler warnings in 32-bit mode CFLAGS_genelf_debug.o += -Wno-packed @@ -246,7 +244,7 @@ $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-flex.h: util/parse- $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/parse-events-bison.h: util/parse-events.y $(call rule_mkdir) - $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \ + $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) $(BISON_FALLBACK_FLAGS) \ -o $(OUTPUT)util/parse-events-bison.c -p parse_events_ $(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-flex.h: util/expr.l $(OUTPUT)util/expr-bison.c @@ -279,28 +277,58 @@ $(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filt $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \ -o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_ -FLEX_GE_26 := $(shell expr $(shell $(FLEX) --version | sed -e 's/flex \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 26) -ifeq ($(FLEX_GE_26),1) - flex_flags := -Wno-switch-enum -Wno-switch-default -Wno-unused-function -Wno-redundant-decls -Wno-sign-compare -Wno-unused-parameter -Wno-missing-prototypes -Wno-missing-declarations - CC_HASNT_MISLEADING_INDENTATION := $(shell echo "int main(void) { return 0 }" | $(CC) -Werror -Wno-misleading-indentation -o /dev/null -xc - 2>&1 | grep -q -- -Wno-misleading-indentation ; echo $$?) - ifeq ($(CC_HASNT_MISLEADING_INDENTATION), 1) - flex_flags += -Wno-misleading-indentation +FLEX_VERSION := $(shell $(FLEX) --version | cut -d' ' -f2) + +FLEX_GE_260 := $(call version-ge3,$(FLEX_VERSION),2.6.0) +ifeq ($(FLEX_GE_260),1) + flex_flags := -Wno-redundant-decls -Wno-switch-default -Wno-unused-function -Wno-misleading-indentation + + # Some newer clang and gcc version complain about this + # util/parse-events-bison.c:1317:9: error: variable 'parse_events_nerrs' set but not used [-Werror,-Wunused-but-set-variable] + # int yynerrs = 0; + + flex_flags += -Wno-unused-but-set-variable + + FLEX_LT_262 := $(call version-lt3,$(FLEX_VERSION),2.6.2) + ifeq ($(FLEX_LT_262),1) + flex_flags += -Wno-sign-compare endif else flex_flags := -w endif -CFLAGS_parse-events-flex.o += $(flex_flags) -CFLAGS_pmu-flex.o += $(flex_flags) -CFLAGS_expr-flex.o += $(flex_flags) -CFLAGS_bpf-filter-flex.o += $(flex_flags) -bison_flags := -DYYENABLE_NLS=0 -BISON_GE_35 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 35) -ifeq ($(BISON_GE_35),1) - bison_flags += -Wno-unused-parameter -Wno-nested-externs -Wno-implicit-function-declaration -Wno-switch-enum -Wno-unused-but-set-variable -Wno-unknown-warning-option +# Some newer clang and gcc version complain about this +# util/parse-events-bison.c:1317:9: error: variable 'parse_events_nerrs' set but not used [-Werror,-Wunused-but-set-variable] +# int yynerrs = 0; + +bison_flags := -DYYENABLE_NLS=0 -Wno-unused-but-set-variable + +# Old clangs don't grok -Wno-unused-but-set-variable, remove it +ifeq ($(CC_NO_CLANG), 0) + CLANG_VERSION := $(shell $(CLANG) --version | head -1 | sed 's/.*clang version \([[:digit:]]\+.[[:digit:]]\+.[[:digit:]]\+\).*/\1/g') + ifeq ($(call version-lt3,$(CLANG_VERSION),13.0.0),1) + bison_flags := $(subst -Wno-unused-but-set-variable,,$(bison_flags)) + flex_flags := $(subst -Wno-unused-but-set-variable,,$(flex_flags)) + endif +endif + +BISON_GE_382 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \>\= 382) +ifeq ($(BISON_GE_382),1) + bison_flags += -Wno-switch-enum else bison_flags += -w endif + +BISON_LT_381 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \< 381) +ifeq ($(BISON_LT_381),1) + bison_flags += -DYYNOMEM=YYABORT +endif + +CFLAGS_parse-events-flex.o += $(flex_flags) -Wno-unused-label +CFLAGS_pmu-flex.o += $(flex_flags) +CFLAGS_expr-flex.o += $(flex_flags) +CFLAGS_bpf-filter-flex.o += $(flex_flags) + CFLAGS_parse-events-bison.o += $(bison_flags) CFLAGS_pmu-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags) CFLAGS_expr-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags) @@ -316,8 +344,6 @@ CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" -CFLAGS_parse-events.o += -Wno-redundant-decls -CFLAGS_expr.o += -Wno-redundant-decls CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/ diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c index 6a6ddba76c75..9d0ce88e90e4 100644 --- a/tools/perf/util/amd-sample-raw.c +++ b/tools/perf/util/amd-sample-raw.c @@ -15,7 +15,6 @@ #include "session.h" #include "evlist.h" #include "sample-raw.h" -#include "pmu-events/pmu-events.h" #include "util/sample.h" static u32 cpu_family, cpu_model, ibs_fetch_type, ibs_op_type; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index ba988a13dacb..82956adf9963 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1846,8 +1846,11 @@ static int symbol__disassemble_bpf(struct symbol *sym, perf_exe(tpath, sizeof(tpath)); bfdf = bfd_openr(tpath, NULL); - assert(bfdf); - assert(bfd_check_format(bfdf, bfd_object)); + if (bfdf == NULL) + abort(); + + if (!bfd_check_format(bfdf, bfd_object)) + abort(); s = open_memstream(&buf, &buf_size); if (!s) { @@ -1895,7 +1898,8 @@ static int symbol__disassemble_bpf(struct symbol *sym, #else disassemble = disassembler(bfdf); #endif - assert(disassemble); + if (disassemble == NULL) + abort(); fflush(s); do { diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 0b30688d78a7..b51544996046 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -9,8 +9,8 @@ #include "util/evsel.h" #include "util/bpf-filter.h" -#include "util/bpf-filter-flex.h" -#include "util/bpf-filter-bison.h" +#include <util/bpf-filter-flex.h> +#include <util/bpf-filter-bison.h> #include "bpf_skel/sample-filter.h" #include "bpf_skel/sample_filter.skel.h" @@ -62,6 +62,16 @@ static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr * if (evsel->core.attr.sample_type & expr->sample_flags) return 0; + if (expr->op == PBF_OP_GROUP_BEGIN) { + struct perf_bpf_filter_expr *group; + + list_for_each_entry(group, &expr->groups, list) { + if (check_sample_flags(evsel, group) < 0) + return -1; + } + return 0; + } + info = get_sample_info(expr->sample_flags); if (info == NULL) { pr_err("Error: %s event does not have sample flags %lx\n", diff --git a/tools/perf/util/bpf-filter.y b/tools/perf/util/bpf-filter.y index 07d6c7926c13..5dfa948fc986 100644 --- a/tools/perf/util/bpf-filter.y +++ b/tools/perf/util/bpf-filter.y @@ -9,6 +9,8 @@ #include <linux/list.h> #include "bpf-filter.h" +int perf_bpf_filter_lex(void); + static void perf_bpf_filter_error(struct list_head *expr __maybe_unused, char const *msg) { diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c deleted file mode 100644 index 44cde27d6389..000000000000 --- a/tools/perf/util/bpf-loader.c +++ /dev/null @@ -1,2110 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-loader.c - * - * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2015 Huawei Inc. - */ - -#include <linux/bpf.h> -#include <bpf/libbpf.h> -#include <bpf/bpf.h> -#include <linux/filter.h> -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/zalloc.h> -#include <errno.h> -#include <stdlib.h> -#include "debug.h" -#include "evlist.h" -#include "bpf-loader.h" -#include "bpf-prologue.h" -#include "probe-event.h" -#include "probe-finder.h" // for MAX_PROBES -#include "parse-events.h" -#include "strfilter.h" -#include "util.h" -#include "llvm-utils.h" -#include "c++/clang-c.h" -#include "util/hashmap.h" -#include "asm/bug.h" - -#include <internal/xyarray.h> - -/* temporarily disable libbpf deprecation warnings */ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)), - const char *fmt, va_list args) -{ - return veprintf(1, verbose, pr_fmt(fmt), args); -} - -struct bpf_prog_priv { - bool is_tp; - char *sys_name; - char *evt_name; - struct perf_probe_event pev; - bool need_prologue; - struct bpf_insn *insns_buf; - int nr_types; - int *type_mapping; - int *prologue_fds; -}; - -struct bpf_perf_object { - struct list_head list; - struct bpf_object *obj; -}; - -struct bpf_preproc_result { - struct bpf_insn *new_insn_ptr; - int new_insn_cnt; -}; - -static LIST_HEAD(bpf_objects_list); -static struct hashmap *bpf_program_hash; -static struct hashmap *bpf_map_hash; - -static struct bpf_perf_object * -bpf_perf_object__next(struct bpf_perf_object *prev) -{ - if (!prev) { - if (list_empty(&bpf_objects_list)) - return NULL; - - return list_first_entry(&bpf_objects_list, struct bpf_perf_object, list); - } - if (list_is_last(&prev->list, &bpf_objects_list)) - return NULL; - - return list_next_entry(prev, list); -} - -#define bpf_perf_object__for_each(perf_obj, tmp) \ - for ((perf_obj) = bpf_perf_object__next(NULL), \ - (tmp) = bpf_perf_object__next(perf_obj); \ - (perf_obj) != NULL; \ - (perf_obj) = (tmp), (tmp) = bpf_perf_object__next(tmp)) - -static bool libbpf_initialized; -static int libbpf_sec_handler; - -static int bpf_perf_object__add(struct bpf_object *obj) -{ - struct bpf_perf_object *perf_obj = zalloc(sizeof(*perf_obj)); - - if (perf_obj) { - INIT_LIST_HEAD(&perf_obj->list); - perf_obj->obj = obj; - list_add_tail(&perf_obj->list, &bpf_objects_list); - } - return perf_obj ? 0 : -ENOMEM; -} - -static void *program_priv(const struct bpf_program *prog) -{ - void *priv; - - if (IS_ERR_OR_NULL(bpf_program_hash)) - return NULL; - if (!hashmap__find(bpf_program_hash, prog, &priv)) - return NULL; - return priv; -} - -static struct bpf_insn prologue_init_insn[] = { - BPF_MOV64_IMM(BPF_REG_2, 0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_MOV64_IMM(BPF_REG_5, 0), -}; - -static int libbpf_prog_prepare_load_fn(struct bpf_program *prog, - struct bpf_prog_load_opts *opts __maybe_unused, - long cookie __maybe_unused) -{ - size_t init_size_cnt = ARRAY_SIZE(prologue_init_insn); - size_t orig_insn_cnt, insn_cnt, init_size, orig_size; - struct bpf_prog_priv *priv = program_priv(prog); - const struct bpf_insn *orig_insn; - struct bpf_insn *insn; - - if (IS_ERR_OR_NULL(priv)) { - pr_debug("bpf: failed to get private field\n"); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (!priv->need_prologue) - return 0; - - /* prepend initialization code to program instructions */ - orig_insn = bpf_program__insns(prog); - orig_insn_cnt = bpf_program__insn_cnt(prog); - init_size = init_size_cnt * sizeof(*insn); - orig_size = orig_insn_cnt * sizeof(*insn); - - insn_cnt = orig_insn_cnt + init_size_cnt; - insn = malloc(insn_cnt * sizeof(*insn)); - if (!insn) - return -ENOMEM; - - memcpy(insn, prologue_init_insn, init_size); - memcpy((char *) insn + init_size, orig_insn, orig_size); - bpf_program__set_insns(prog, insn, insn_cnt); - return 0; -} - -static int libbpf_init(void) -{ - LIBBPF_OPTS(libbpf_prog_handler_opts, handler_opts, - .prog_prepare_load_fn = libbpf_prog_prepare_load_fn, - ); - - if (libbpf_initialized) - return 0; - - libbpf_set_print(libbpf_perf_print); - libbpf_sec_handler = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_KPROBE, - 0, &handler_opts); - if (libbpf_sec_handler < 0) { - pr_debug("bpf: failed to register libbpf section handler: %d\n", - libbpf_sec_handler); - return -BPF_LOADER_ERRNO__INTERNAL; - } - libbpf_initialized = true; - return 0; -} - -struct bpf_object * -bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) -{ - LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = name); - struct bpf_object *obj; - int err; - - err = libbpf_init(); - if (err) - return ERR_PTR(err); - - obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts); - if (IS_ERR_OR_NULL(obj)) { - pr_debug("bpf: failed to load buffer\n"); - return ERR_PTR(-EINVAL); - } - - if (bpf_perf_object__add(obj)) { - bpf_object__close(obj); - return ERR_PTR(-ENOMEM); - } - - return obj; -} - -static void bpf_perf_object__close(struct bpf_perf_object *perf_obj) -{ - list_del(&perf_obj->list); - bpf_object__close(perf_obj->obj); - free(perf_obj); -} - -struct bpf_object *bpf__prepare_load(const char *filename, bool source) -{ - LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = filename); - struct bpf_object *obj; - int err; - - err = libbpf_init(); - if (err) - return ERR_PTR(err); - - if (source) { - void *obj_buf; - size_t obj_buf_sz; - - perf_clang__init(); - err = perf_clang__compile_bpf(filename, &obj_buf, &obj_buf_sz); - perf_clang__cleanup(); - if (err) { - pr_debug("bpf: builtin compilation failed: %d, try external compiler\n", err); - err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); - if (err) - return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); - } else - pr_debug("bpf: successful builtin compilation\n"); - obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts); - - if (!IS_ERR_OR_NULL(obj) && llvm_param.dump_obj) - llvm__dump_obj(filename, obj_buf, obj_buf_sz); - - free(obj_buf); - } else { - obj = bpf_object__open(filename); - } - - if (IS_ERR_OR_NULL(obj)) { - pr_debug("bpf: failed to load %s\n", filename); - return obj; - } - - if (bpf_perf_object__add(obj)) { - bpf_object__close(obj); - return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); - } - - return obj; -} - -static void close_prologue_programs(struct bpf_prog_priv *priv) -{ - struct perf_probe_event *pev; - int i, fd; - - if (!priv->need_prologue) - return; - pev = &priv->pev; - for (i = 0; i < pev->ntevs; i++) { - fd = priv->prologue_fds[i]; - if (fd != -1) - close(fd); - } -} - -static void -clear_prog_priv(const struct bpf_program *prog __maybe_unused, - void *_priv) -{ - struct bpf_prog_priv *priv = _priv; - - close_prologue_programs(priv); - cleanup_perf_probe_events(&priv->pev, 1); - zfree(&priv->insns_buf); - zfree(&priv->prologue_fds); - zfree(&priv->type_mapping); - zfree(&priv->sys_name); - zfree(&priv->evt_name); - free(priv); -} - -static void bpf_program_hash_free(void) -{ - struct hashmap_entry *cur; - size_t bkt; - - if (IS_ERR_OR_NULL(bpf_program_hash)) - return; - - hashmap__for_each_entry(bpf_program_hash, cur, bkt) - clear_prog_priv(cur->pkey, cur->pvalue); - - hashmap__free(bpf_program_hash); - bpf_program_hash = NULL; -} - -static void bpf_map_hash_free(void); - -void bpf__clear(void) -{ - struct bpf_perf_object *perf_obj, *tmp; - - bpf_perf_object__for_each(perf_obj, tmp) { - bpf__unprobe(perf_obj->obj); - bpf_perf_object__close(perf_obj); - } - - bpf_program_hash_free(); - bpf_map_hash_free(); -} - -static size_t ptr_hash(const long __key, void *ctx __maybe_unused) -{ - return __key; -} - -static bool ptr_equal(long key1, long key2, void *ctx __maybe_unused) -{ - return key1 == key2; -} - -static int program_set_priv(struct bpf_program *prog, void *priv) -{ - void *old_priv; - - /* - * Should not happen, we warn about it in the - * caller function - config_bpf_program - */ - if (IS_ERR(bpf_program_hash)) - return PTR_ERR(bpf_program_hash); - - if (!bpf_program_hash) { - bpf_program_hash = hashmap__new(ptr_hash, ptr_equal, NULL); - if (IS_ERR(bpf_program_hash)) - return PTR_ERR(bpf_program_hash); - } - - old_priv = program_priv(prog); - if (old_priv) { - clear_prog_priv(prog, old_priv); - return hashmap__set(bpf_program_hash, prog, priv, NULL, NULL); - } - return hashmap__add(bpf_program_hash, prog, priv); -} - -static int -prog_config__exec(const char *value, struct perf_probe_event *pev) -{ - pev->uprobes = true; - pev->target = strdup(value); - if (!pev->target) - return -ENOMEM; - return 0; -} - -static int -prog_config__module(const char *value, struct perf_probe_event *pev) -{ - pev->uprobes = false; - pev->target = strdup(value); - if (!pev->target) - return -ENOMEM; - return 0; -} - -static int -prog_config__bool(const char *value, bool *pbool, bool invert) -{ - int err; - bool bool_value; - - if (!pbool) - return -EINVAL; - - err = strtobool(value, &bool_value); - if (err) - return err; - - *pbool = invert ? !bool_value : bool_value; - return 0; -} - -static int -prog_config__inlines(const char *value, - struct perf_probe_event *pev __maybe_unused) -{ - return prog_config__bool(value, &probe_conf.no_inlines, true); -} - -static int -prog_config__force(const char *value, - struct perf_probe_event *pev __maybe_unused) -{ - return prog_config__bool(value, &probe_conf.force_add, false); -} - -static struct { - const char *key; - const char *usage; - const char *desc; - int (*func)(const char *, struct perf_probe_event *); -} bpf_prog_config_terms[] = { - { - .key = "exec", - .usage = "exec=<full path of file>", - .desc = "Set uprobe target", - .func = prog_config__exec, - }, - { - .key = "module", - .usage = "module=<module name> ", - .desc = "Set kprobe module", - .func = prog_config__module, - }, - { - .key = "inlines", - .usage = "inlines=[yes|no] ", - .desc = "Probe at inline symbol", - .func = prog_config__inlines, - }, - { - .key = "force", - .usage = "force=[yes|no] ", - .desc = "Forcibly add events with existing name", - .func = prog_config__force, - }, -}; - -static int -do_prog_config(const char *key, const char *value, - struct perf_probe_event *pev) -{ - unsigned int i; - - pr_debug("config bpf program: %s=%s\n", key, value); - for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) - if (strcmp(key, bpf_prog_config_terms[i].key) == 0) - return bpf_prog_config_terms[i].func(value, pev); - - pr_debug("BPF: ERROR: invalid program config option: %s=%s\n", - key, value); - - pr_debug("\nHint: Valid options are:\n"); - for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) - pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage, - bpf_prog_config_terms[i].desc); - pr_debug("\n"); - - return -BPF_LOADER_ERRNO__PROGCONF_TERM; -} - -static const char * -parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) -{ - char *text = strdup(config_str); - char *sep, *line; - const char *main_str = NULL; - int err = 0; - - if (!text) { - pr_debug("Not enough memory: dup config_str failed\n"); - return ERR_PTR(-ENOMEM); - } - - line = text; - while ((sep = strchr(line, ';'))) { - char *equ; - - *sep = '\0'; - equ = strchr(line, '='); - if (!equ) { - pr_warning("WARNING: invalid config in BPF object: %s\n", - line); - pr_warning("\tShould be 'key=value'.\n"); - goto nextline; - } - *equ = '\0'; - - err = do_prog_config(line, equ + 1, pev); - if (err) - break; -nextline: - line = sep + 1; - } - - if (!err) - main_str = config_str + (line - text); - free(text); - - return err ? ERR_PTR(err) : main_str; -} - -static int -parse_prog_config(const char *config_str, const char **p_main_str, - bool *is_tp, struct perf_probe_event *pev) -{ - int err; - const char *main_str = parse_prog_config_kvpair(config_str, pev); - - if (IS_ERR(main_str)) - return PTR_ERR(main_str); - - *p_main_str = main_str; - if (!strchr(main_str, '=')) { - /* Is a tracepoint event? */ - const char *s = strchr(main_str, ':'); - - if (!s) { - pr_debug("bpf: '%s' is not a valid tracepoint\n", - config_str); - return -BPF_LOADER_ERRNO__CONFIG; - } - - *is_tp = true; - return 0; - } - - *is_tp = false; - err = parse_perf_probe_command(main_str, pev); - if (err < 0) { - pr_debug("bpf: '%s' is not a valid config string\n", - config_str); - /* parse failed, don't need clear pev. */ - return -BPF_LOADER_ERRNO__CONFIG; - } - return 0; -} - -static int -config_bpf_program(struct bpf_program *prog) -{ - struct perf_probe_event *pev = NULL; - struct bpf_prog_priv *priv = NULL; - const char *config_str, *main_str; - bool is_tp = false; - int err; - - /* Initialize per-program probing setting */ - probe_conf.no_inlines = false; - probe_conf.force_add = false; - - priv = calloc(sizeof(*priv), 1); - if (!priv) { - pr_debug("bpf: failed to alloc priv\n"); - return -ENOMEM; - } - pev = &priv->pev; - - config_str = bpf_program__section_name(prog); - pr_debug("bpf: config program '%s'\n", config_str); - err = parse_prog_config(config_str, &main_str, &is_tp, pev); - if (err) - goto errout; - - if (is_tp) { - char *s = strchr(main_str, ':'); - - priv->is_tp = true; - priv->sys_name = strndup(main_str, s - main_str); - priv->evt_name = strdup(s + 1); - goto set_priv; - } - - if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { - pr_debug("bpf: '%s': group for event is set and not '%s'.\n", - config_str, PERF_BPF_PROBE_GROUP); - err = -BPF_LOADER_ERRNO__GROUP; - goto errout; - } else if (!pev->group) - pev->group = strdup(PERF_BPF_PROBE_GROUP); - - if (!pev->group) { - pr_debug("bpf: strdup failed\n"); - err = -ENOMEM; - goto errout; - } - - if (!pev->event) { - pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n", - config_str); - err = -BPF_LOADER_ERRNO__EVENTNAME; - goto errout; - } - pr_debug("bpf: config '%s' is ok\n", config_str); - -set_priv: - err = program_set_priv(prog, priv); - if (err) { - pr_debug("Failed to set priv for program '%s'\n", config_str); - goto errout; - } - - return 0; - -errout: - if (pev) - clear_perf_probe_event(pev); - free(priv); - return err; -} - -static int bpf__prepare_probe(void) -{ - static int err = 0; - static bool initialized = false; - - /* - * Make err static, so if init failed the first, bpf__prepare_probe() - * fails each time without calling init_probe_symbol_maps multiple - * times. - */ - if (initialized) - return err; - - initialized = true; - err = init_probe_symbol_maps(false); - if (err < 0) - pr_debug("Failed to init_probe_symbol_maps\n"); - probe_conf.max_probes = MAX_PROBES; - return err; -} - -static int -preproc_gen_prologue(struct bpf_program *prog, int n, - const struct bpf_insn *orig_insns, int orig_insns_cnt, - struct bpf_preproc_result *res) -{ - struct bpf_prog_priv *priv = program_priv(prog); - struct probe_trace_event *tev; - struct perf_probe_event *pev; - struct bpf_insn *buf; - size_t prologue_cnt = 0; - int i, err; - - if (IS_ERR_OR_NULL(priv) || priv->is_tp) - goto errout; - - pev = &priv->pev; - - if (n < 0 || n >= priv->nr_types) - goto errout; - - /* Find a tev belongs to that type */ - for (i = 0; i < pev->ntevs; i++) { - if (priv->type_mapping[i] == n) - break; - } - - if (i >= pev->ntevs) { - pr_debug("Internal error: prologue type %d not found\n", n); - return -BPF_LOADER_ERRNO__PROLOGUE; - } - - tev = &pev->tevs[i]; - - buf = priv->insns_buf; - err = bpf__gen_prologue(tev->args, tev->nargs, - buf, &prologue_cnt, - BPF_MAXINSNS - orig_insns_cnt); - if (err) { - const char *title; - - title = bpf_program__section_name(prog); - pr_debug("Failed to generate prologue for program %s\n", - title); - return err; - } - - memcpy(&buf[prologue_cnt], orig_insns, - sizeof(struct bpf_insn) * orig_insns_cnt); - - res->new_insn_ptr = buf; - res->new_insn_cnt = prologue_cnt + orig_insns_cnt; - return 0; - -errout: - pr_debug("Internal error in preproc_gen_prologue\n"); - return -BPF_LOADER_ERRNO__PROLOGUE; -} - -/* - * compare_tev_args is reflexive, transitive and antisymmetric. - * I can proof it but this margin is too narrow to contain. - */ -static int compare_tev_args(const void *ptev1, const void *ptev2) -{ - int i, ret; - const struct probe_trace_event *tev1 = - *(const struct probe_trace_event **)ptev1; - const struct probe_trace_event *tev2 = - *(const struct probe_trace_event **)ptev2; - - ret = tev2->nargs - tev1->nargs; - if (ret) - return ret; - - for (i = 0; i < tev1->nargs; i++) { - struct probe_trace_arg *arg1, *arg2; - struct probe_trace_arg_ref *ref1, *ref2; - - arg1 = &tev1->args[i]; - arg2 = &tev2->args[i]; - - ret = strcmp(arg1->value, arg2->value); - if (ret) - return ret; - - ref1 = arg1->ref; - ref2 = arg2->ref; - - while (ref1 && ref2) { - ret = ref2->offset - ref1->offset; - if (ret) - return ret; - - ref1 = ref1->next; - ref2 = ref2->next; - } - - if (ref1 || ref2) - return ref2 ? 1 : -1; - } - - return 0; -} - -/* - * Assign a type number to each tevs in a pev. - * mapping is an array with same slots as tevs in that pev. - * nr_types will be set to number of types. - */ -static int map_prologue(struct perf_probe_event *pev, int *mapping, - int *nr_types) -{ - int i, type = 0; - struct probe_trace_event **ptevs; - - size_t array_sz = sizeof(*ptevs) * pev->ntevs; - - ptevs = malloc(array_sz); - if (!ptevs) { - pr_debug("Not enough memory: alloc ptevs failed\n"); - return -ENOMEM; - } - - pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs); - for (i = 0; i < pev->ntevs; i++) - ptevs[i] = &pev->tevs[i]; - - qsort(ptevs, pev->ntevs, sizeof(*ptevs), - compare_tev_args); - - for (i = 0; i < pev->ntevs; i++) { - int n; - - n = ptevs[i] - pev->tevs; - if (i == 0) { - mapping[n] = type; - pr_debug("mapping[%d]=%d\n", n, type); - continue; - } - - if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0) - mapping[n] = type; - else - mapping[n] = ++type; - - pr_debug("mapping[%d]=%d\n", n, mapping[n]); - } - free(ptevs); - *nr_types = type + 1; - - return 0; -} - -static int hook_load_preprocessor(struct bpf_program *prog) -{ - struct bpf_prog_priv *priv = program_priv(prog); - struct perf_probe_event *pev; - bool need_prologue = false; - int i; - - if (IS_ERR_OR_NULL(priv)) { - pr_debug("Internal error when hook preprocessor\n"); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (priv->is_tp) { - priv->need_prologue = false; - return 0; - } - - pev = &priv->pev; - for (i = 0; i < pev->ntevs; i++) { - struct probe_trace_event *tev = &pev->tevs[i]; - - if (tev->nargs > 0) { - need_prologue = true; - break; - } - } - - /* - * Since all tevs don't have argument, we don't need generate - * prologue. - */ - if (!need_prologue) { - priv->need_prologue = false; - return 0; - } - - priv->need_prologue = true; - priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); - if (!priv->insns_buf) { - pr_debug("Not enough memory: alloc insns_buf failed\n"); - return -ENOMEM; - } - - priv->prologue_fds = malloc(sizeof(int) * pev->ntevs); - if (!priv->prologue_fds) { - pr_debug("Not enough memory: alloc prologue fds failed\n"); - return -ENOMEM; - } - memset(priv->prologue_fds, -1, sizeof(int) * pev->ntevs); - - priv->type_mapping = malloc(sizeof(int) * pev->ntevs); - if (!priv->type_mapping) { - pr_debug("Not enough memory: alloc type_mapping failed\n"); - return -ENOMEM; - } - memset(priv->type_mapping, -1, - sizeof(int) * pev->ntevs); - - return map_prologue(pev, priv->type_mapping, &priv->nr_types); -} - -int bpf__probe(struct bpf_object *obj) -{ - int err = 0; - struct bpf_program *prog; - struct bpf_prog_priv *priv; - struct perf_probe_event *pev; - - err = bpf__prepare_probe(); - if (err) { - pr_debug("bpf__prepare_probe failed\n"); - return err; - } - - bpf_object__for_each_program(prog, obj) { - err = config_bpf_program(prog); - if (err) - goto out; - - priv = program_priv(prog); - if (IS_ERR_OR_NULL(priv)) { - if (!priv) - err = -BPF_LOADER_ERRNO__INTERNAL; - else - err = PTR_ERR(priv); - goto out; - } - - if (priv->is_tp) { - bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT); - continue; - } - - bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE); - pev = &priv->pev; - - err = convert_perf_probe_events(pev, 1); - if (err < 0) { - pr_debug("bpf_probe: failed to convert perf probe events\n"); - goto out; - } - - err = apply_perf_probe_events(pev, 1); - if (err < 0) { - pr_debug("bpf_probe: failed to apply perf probe events\n"); - goto out; - } - - /* - * After probing, let's consider prologue, which - * adds program fetcher to BPF programs. - * - * hook_load_preprocessor() hooks pre-processor - * to bpf_program, let it generate prologue - * dynamically during loading. - */ - err = hook_load_preprocessor(prog); - if (err) - goto out; - } -out: - return err < 0 ? err : 0; -} - -#define EVENTS_WRITE_BUFSIZE 4096 -int bpf__unprobe(struct bpf_object *obj) -{ - int err, ret = 0; - struct bpf_program *prog; - - bpf_object__for_each_program(prog, obj) { - struct bpf_prog_priv *priv = program_priv(prog); - int i; - - if (IS_ERR_OR_NULL(priv) || priv->is_tp) - continue; - - for (i = 0; i < priv->pev.ntevs; i++) { - struct probe_trace_event *tev = &priv->pev.tevs[i]; - char name_buf[EVENTS_WRITE_BUFSIZE]; - struct strfilter *delfilter; - - snprintf(name_buf, EVENTS_WRITE_BUFSIZE, - "%s:%s", tev->group, tev->event); - name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0'; - - delfilter = strfilter__new(name_buf, NULL); - if (!delfilter) { - pr_debug("Failed to create filter for unprobing\n"); - ret = -ENOMEM; - continue; - } - - err = del_perf_probe_events(delfilter); - strfilter__delete(delfilter); - if (err) { - pr_debug("Failed to delete %s\n", name_buf); - ret = err; - continue; - } - } - } - return ret; -} - -static int bpf_object__load_prologue(struct bpf_object *obj) -{ - int init_cnt = ARRAY_SIZE(prologue_init_insn); - const struct bpf_insn *orig_insns; - struct bpf_preproc_result res; - struct perf_probe_event *pev; - struct bpf_program *prog; - int orig_insns_cnt; - - bpf_object__for_each_program(prog, obj) { - struct bpf_prog_priv *priv = program_priv(prog); - int err, i, fd; - - if (IS_ERR_OR_NULL(priv)) { - pr_debug("bpf: failed to get private field\n"); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (!priv->need_prologue) - continue; - - /* - * For each program that needs prologue we do following: - * - * - take its current instructions and use them - * to generate the new code with prologue - * - load new instructions with bpf_prog_load - * and keep the fd in prologue_fds - * - new fd will be used in bpf__foreach_event - * to connect this program with perf evsel - */ - orig_insns = bpf_program__insns(prog); - orig_insns_cnt = bpf_program__insn_cnt(prog); - - pev = &priv->pev; - for (i = 0; i < pev->ntevs; i++) { - /* - * Skipping artificall prologue_init_insn instructions - * (init_cnt), so the prologue can be generated instead - * of them. - */ - err = preproc_gen_prologue(prog, i, - orig_insns + init_cnt, - orig_insns_cnt - init_cnt, - &res); - if (err) - return err; - - fd = bpf_prog_load(bpf_program__get_type(prog), - bpf_program__name(prog), "GPL", - res.new_insn_ptr, - res.new_insn_cnt, NULL); - if (fd < 0) { - char bf[128]; - - libbpf_strerror(-errno, bf, sizeof(bf)); - pr_debug("bpf: load objects with prologue failed: err=%d: (%s)\n", - -errno, bf); - return -errno; - } - priv->prologue_fds[i] = fd; - } - /* - * We no longer need the original program, - * we can unload it. - */ - bpf_program__unload(prog); - } - return 0; -} - -int bpf__load(struct bpf_object *obj) -{ - int err; - - err = bpf_object__load(obj); - if (err) { - char bf[128]; - libbpf_strerror(err, bf, sizeof(bf)); - pr_debug("bpf: load objects failed: err=%d: (%s)\n", err, bf); - return err; - } - return bpf_object__load_prologue(obj); -} - -int bpf__foreach_event(struct bpf_object *obj, - bpf_prog_iter_callback_t func, - void *arg) -{ - struct bpf_program *prog; - int err; - - bpf_object__for_each_program(prog, obj) { - struct bpf_prog_priv *priv = program_priv(prog); - struct probe_trace_event *tev; - struct perf_probe_event *pev; - int i, fd; - - if (IS_ERR_OR_NULL(priv)) { - pr_debug("bpf: failed to get private field\n"); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (priv->is_tp) { - fd = bpf_program__fd(prog); - err = (*func)(priv->sys_name, priv->evt_name, fd, obj, arg); - if (err) { - pr_debug("bpf: tracepoint call back failed, stop iterate\n"); - return err; - } - continue; - } - - pev = &priv->pev; - for (i = 0; i < pev->ntevs; i++) { - tev = &pev->tevs[i]; - - if (priv->need_prologue) - fd = priv->prologue_fds[i]; - else - fd = bpf_program__fd(prog); - - if (fd < 0) { - pr_debug("bpf: failed to get file descriptor\n"); - return fd; - } - - err = (*func)(tev->group, tev->event, fd, obj, arg); - if (err) { - pr_debug("bpf: call back failed, stop iterate\n"); - return err; - } - } - } - return 0; -} - -enum bpf_map_op_type { - BPF_MAP_OP_SET_VALUE, - BPF_MAP_OP_SET_EVSEL, -}; - -enum bpf_map_key_type { - BPF_MAP_KEY_ALL, - BPF_MAP_KEY_RANGES, -}; - -struct bpf_map_op { - struct list_head list; - enum bpf_map_op_type op_type; - enum bpf_map_key_type key_type; - union { - struct parse_events_array array; - } k; - union { - u64 value; - struct evsel *evsel; - } v; -}; - -struct bpf_map_priv { - struct list_head ops_list; -}; - -static void -bpf_map_op__delete(struct bpf_map_op *op) -{ - if (!list_empty(&op->list)) - list_del_init(&op->list); - if (op->key_type == BPF_MAP_KEY_RANGES) - parse_events__clear_array(&op->k.array); - free(op); -} - -static void -bpf_map_priv__purge(struct bpf_map_priv *priv) -{ - struct bpf_map_op *pos, *n; - - list_for_each_entry_safe(pos, n, &priv->ops_list, list) { - list_del_init(&pos->list); - bpf_map_op__delete(pos); - } -} - -static void -bpf_map_priv__clear(const struct bpf_map *map __maybe_unused, - void *_priv) -{ - struct bpf_map_priv *priv = _priv; - - bpf_map_priv__purge(priv); - free(priv); -} - -static void *map_priv(const struct bpf_map *map) -{ - void *priv; - - if (IS_ERR_OR_NULL(bpf_map_hash)) - return NULL; - if (!hashmap__find(bpf_map_hash, map, &priv)) - return NULL; - return priv; -} - -static void bpf_map_hash_free(void) -{ - struct hashmap_entry *cur; - size_t bkt; - - if (IS_ERR_OR_NULL(bpf_map_hash)) - return; - - hashmap__for_each_entry(bpf_map_hash, cur, bkt) - bpf_map_priv__clear(cur->pkey, cur->pvalue); - - hashmap__free(bpf_map_hash); - bpf_map_hash = NULL; -} - -static int map_set_priv(struct bpf_map *map, void *priv) -{ - void *old_priv; - - if (WARN_ON_ONCE(IS_ERR(bpf_map_hash))) - return PTR_ERR(bpf_program_hash); - - if (!bpf_map_hash) { - bpf_map_hash = hashmap__new(ptr_hash, ptr_equal, NULL); - if (IS_ERR(bpf_map_hash)) - return PTR_ERR(bpf_map_hash); - } - - old_priv = map_priv(map); - if (old_priv) { - bpf_map_priv__clear(map, old_priv); - return hashmap__set(bpf_map_hash, map, priv, NULL, NULL); - } - return hashmap__add(bpf_map_hash, map, priv); -} - -static int -bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term) -{ - op->key_type = BPF_MAP_KEY_ALL; - if (!term) - return 0; - - if (term->array.nr_ranges) { - size_t memsz = term->array.nr_ranges * - sizeof(op->k.array.ranges[0]); - - op->k.array.ranges = memdup(term->array.ranges, memsz); - if (!op->k.array.ranges) { - pr_debug("Not enough memory to alloc indices for map\n"); - return -ENOMEM; - } - op->key_type = BPF_MAP_KEY_RANGES; - op->k.array.nr_ranges = term->array.nr_ranges; - } - return 0; -} - -static struct bpf_map_op * -bpf_map_op__new(struct parse_events_term *term) -{ - struct bpf_map_op *op; - int err; - - op = zalloc(sizeof(*op)); - if (!op) { - pr_debug("Failed to alloc bpf_map_op\n"); - return ERR_PTR(-ENOMEM); - } - INIT_LIST_HEAD(&op->list); - - err = bpf_map_op_setkey(op, term); - if (err) { - free(op); - return ERR_PTR(err); - } - return op; -} - -static struct bpf_map_op * -bpf_map_op__clone(struct bpf_map_op *op) -{ - struct bpf_map_op *newop; - - newop = memdup(op, sizeof(*op)); - if (!newop) { - pr_debug("Failed to alloc bpf_map_op\n"); - return NULL; - } - - INIT_LIST_HEAD(&newop->list); - if (op->key_type == BPF_MAP_KEY_RANGES) { - size_t memsz = op->k.array.nr_ranges * - sizeof(op->k.array.ranges[0]); - - newop->k.array.ranges = memdup(op->k.array.ranges, memsz); - if (!newop->k.array.ranges) { - pr_debug("Failed to alloc indices for map\n"); - free(newop); - return NULL; - } - } - - return newop; -} - -static struct bpf_map_priv * -bpf_map_priv__clone(struct bpf_map_priv *priv) -{ - struct bpf_map_priv *newpriv; - struct bpf_map_op *pos, *newop; - - newpriv = zalloc(sizeof(*newpriv)); - if (!newpriv) { - pr_debug("Not enough memory to alloc map private\n"); - return NULL; - } - INIT_LIST_HEAD(&newpriv->ops_list); - - list_for_each_entry(pos, &priv->ops_list, list) { - newop = bpf_map_op__clone(pos); - if (!newop) { - bpf_map_priv__purge(newpriv); - return NULL; - } - list_add_tail(&newop->list, &newpriv->ops_list); - } - - return newpriv; -} - -static int -bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) -{ - const char *map_name = bpf_map__name(map); - struct bpf_map_priv *priv = map_priv(map); - - if (IS_ERR(priv)) { - pr_debug("Failed to get private from map %s\n", map_name); - return PTR_ERR(priv); - } - - if (!priv) { - priv = zalloc(sizeof(*priv)); - if (!priv) { - pr_debug("Not enough memory to alloc map private\n"); - return -ENOMEM; - } - INIT_LIST_HEAD(&priv->ops_list); - - if (map_set_priv(map, priv)) { - free(priv); - return -BPF_LOADER_ERRNO__INTERNAL; - } - } - - list_add_tail(&op->list, &priv->ops_list); - return 0; -} - -static struct bpf_map_op * -bpf_map__add_newop(struct bpf_map *map, struct parse_events_term *term) -{ - struct bpf_map_op *op; - int err; - - op = bpf_map_op__new(term); - if (IS_ERR(op)) - return op; - - err = bpf_map__add_op(map, op); - if (err) { - bpf_map_op__delete(op); - return ERR_PTR(err); - } - return op; -} - -static int -__bpf_map__config_value(struct bpf_map *map, - struct parse_events_term *term) -{ - struct bpf_map_op *op; - const char *map_name = bpf_map__name(map); - - if (!map) { - pr_debug("Map '%s' is invalid\n", map_name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (bpf_map__type(map) != BPF_MAP_TYPE_ARRAY) { - pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n", - map_name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; - } - if (bpf_map__key_size(map) < sizeof(unsigned int)) { - pr_debug("Map %s has incorrect key size\n", map_name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE; - } - switch (bpf_map__value_size(map)) { - case 1: - case 2: - case 4: - case 8: - break; - default: - pr_debug("Map %s has incorrect value size\n", map_name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE; - } - - op = bpf_map__add_newop(map, term); - if (IS_ERR(op)) - return PTR_ERR(op); - op->op_type = BPF_MAP_OP_SET_VALUE; - op->v.value = term->val.num; - return 0; -} - -static int -bpf_map__config_value(struct bpf_map *map, - struct parse_events_term *term, - struct evlist *evlist __maybe_unused) -{ - if (!term->err_val) { - pr_debug("Config value not set\n"); - return -BPF_LOADER_ERRNO__OBJCONF_CONF; - } - - if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) { - pr_debug("ERROR: wrong value type for 'value'\n"); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE; - } - - return __bpf_map__config_value(map, term); -} - -static int -__bpf_map__config_event(struct bpf_map *map, - struct parse_events_term *term, - struct evlist *evlist) -{ - struct bpf_map_op *op; - const char *map_name = bpf_map__name(map); - struct evsel *evsel = evlist__find_evsel_by_str(evlist, term->val.str); - - if (!evsel) { - pr_debug("Event (for '%s') '%s' doesn't exist\n", - map_name, term->val.str); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT; - } - - if (!map) { - pr_debug("Map '%s' is invalid\n", map_name); - return PTR_ERR(map); - } - - /* - * No need to check key_size and value_size: - * kernel has already checked them. - */ - if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { - pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", - map_name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; - } - - op = bpf_map__add_newop(map, term); - if (IS_ERR(op)) - return PTR_ERR(op); - op->op_type = BPF_MAP_OP_SET_EVSEL; - op->v.evsel = evsel; - return 0; -} - -static int -bpf_map__config_event(struct bpf_map *map, - struct parse_events_term *term, - struct evlist *evlist) -{ - if (!term->err_val) { - pr_debug("Config value not set\n"); - return -BPF_LOADER_ERRNO__OBJCONF_CONF; - } - - if (term->type_val != PARSE_EVENTS__TERM_TYPE_STR) { - pr_debug("ERROR: wrong value type for 'event'\n"); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE; - } - - return __bpf_map__config_event(map, term, evlist); -} - -struct bpf_obj_config__map_func { - const char *config_opt; - int (*config_func)(struct bpf_map *, struct parse_events_term *, - struct evlist *); -}; - -struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = { - {"value", bpf_map__config_value}, - {"event", bpf_map__config_event}, -}; - -static int -config_map_indices_range_check(struct parse_events_term *term, - struct bpf_map *map, - const char *map_name) -{ - struct parse_events_array *array = &term->array; - unsigned int i; - - if (!array->nr_ranges) - return 0; - if (!array->ranges) { - pr_debug("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n", - map_name, (int)array->nr_ranges); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (!map) { - pr_debug("Map '%s' is invalid\n", map_name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - for (i = 0; i < array->nr_ranges; i++) { - unsigned int start = array->ranges[i].start; - size_t length = array->ranges[i].length; - unsigned int idx = start + length - 1; - - if (idx >= bpf_map__max_entries(map)) { - pr_debug("ERROR: index %d too large\n", idx); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG; - } - } - return 0; -} - -static int -bpf__obj_config_map(struct bpf_object *obj, - struct parse_events_term *term, - struct evlist *evlist, - int *key_scan_pos) -{ - /* key is "map:<mapname>.<config opt>" */ - char *map_name = strdup(term->config + sizeof("map:") - 1); - struct bpf_map *map; - int err = -BPF_LOADER_ERRNO__OBJCONF_OPT; - char *map_opt; - size_t i; - - if (!map_name) - return -ENOMEM; - - map_opt = strchr(map_name, '.'); - if (!map_opt) { - pr_debug("ERROR: Invalid map config: %s\n", map_name); - goto out; - } - - *map_opt++ = '\0'; - if (*map_opt == '\0') { - pr_debug("ERROR: Invalid map option: %s\n", term->config); - goto out; - } - - map = bpf_object__find_map_by_name(obj, map_name); - if (!map) { - pr_debug("ERROR: Map %s doesn't exist\n", map_name); - err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST; - goto out; - } - - *key_scan_pos += strlen(map_opt); - err = config_map_indices_range_check(term, map, map_name); - if (err) - goto out; - *key_scan_pos -= strlen(map_opt); - - for (i = 0; i < ARRAY_SIZE(bpf_obj_config__map_funcs); i++) { - struct bpf_obj_config__map_func *func = - &bpf_obj_config__map_funcs[i]; - - if (strcmp(map_opt, func->config_opt) == 0) { - err = func->config_func(map, term, evlist); - goto out; - } - } - - pr_debug("ERROR: Invalid map config option '%s'\n", map_opt); - err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT; -out: - if (!err) - *key_scan_pos += strlen(map_opt); - - free(map_name); - return err; -} - -int bpf__config_obj(struct bpf_object *obj, - struct parse_events_term *term, - struct evlist *evlist, - int *error_pos) -{ - int key_scan_pos = 0; - int err; - - if (!obj || !term || !term->config) - return -EINVAL; - - if (strstarts(term->config, "map:")) { - key_scan_pos = sizeof("map:") - 1; - err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos); - goto out; - } - err = -BPF_LOADER_ERRNO__OBJCONF_OPT; -out: - if (error_pos) - *error_pos = key_scan_pos; - return err; - -} - -typedef int (*map_config_func_t)(const char *name, int map_fd, - const struct bpf_map *map, - struct bpf_map_op *op, - void *pkey, void *arg); - -static int -foreach_key_array_all(map_config_func_t func, - void *arg, const char *name, - int map_fd, const struct bpf_map *map, - struct bpf_map_op *op) -{ - unsigned int i; - int err; - - for (i = 0; i < bpf_map__max_entries(map); i++) { - err = func(name, map_fd, map, op, &i, arg); - if (err) { - pr_debug("ERROR: failed to insert value to %s[%u]\n", - name, i); - return err; - } - } - return 0; -} - -static int -foreach_key_array_ranges(map_config_func_t func, void *arg, - const char *name, int map_fd, - const struct bpf_map *map, - struct bpf_map_op *op) -{ - unsigned int i, j; - int err; - - for (i = 0; i < op->k.array.nr_ranges; i++) { - unsigned int start = op->k.array.ranges[i].start; - size_t length = op->k.array.ranges[i].length; - - for (j = 0; j < length; j++) { - unsigned int idx = start + j; - - err = func(name, map_fd, map, op, &idx, arg); - if (err) { - pr_debug("ERROR: failed to insert value to %s[%u]\n", - name, idx); - return err; - } - } - } - return 0; -} - -static int -bpf_map_config_foreach_key(struct bpf_map *map, - map_config_func_t func, - void *arg) -{ - int err, map_fd, type; - struct bpf_map_op *op; - const char *name = bpf_map__name(map); - struct bpf_map_priv *priv = map_priv(map); - - if (IS_ERR(priv)) { - pr_debug("ERROR: failed to get private from map %s\n", name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - if (!priv || list_empty(&priv->ops_list)) { - pr_debug("INFO: nothing to config for map %s\n", name); - return 0; - } - - if (!map) { - pr_debug("Map '%s' is invalid\n", name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - map_fd = bpf_map__fd(map); - if (map_fd < 0) { - pr_debug("ERROR: failed to get fd from map %s\n", name); - return map_fd; - } - - type = bpf_map__type(map); - list_for_each_entry(op, &priv->ops_list, list) { - switch (type) { - case BPF_MAP_TYPE_ARRAY: - case BPF_MAP_TYPE_PERF_EVENT_ARRAY: - switch (op->key_type) { - case BPF_MAP_KEY_ALL: - err = foreach_key_array_all(func, arg, name, - map_fd, map, op); - break; - case BPF_MAP_KEY_RANGES: - err = foreach_key_array_ranges(func, arg, name, - map_fd, map, op); - break; - default: - pr_debug("ERROR: keytype for map '%s' invalid\n", - name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - if (err) - return err; - break; - default: - pr_debug("ERROR: type of '%s' incorrect\n", name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; - } - } - - return 0; -} - -static int -apply_config_value_for_key(int map_fd, void *pkey, - size_t val_size, u64 val) -{ - int err = 0; - - switch (val_size) { - case 1: { - u8 _val = (u8)(val); - err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); - break; - } - case 2: { - u16 _val = (u16)(val); - err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); - break; - } - case 4: { - u32 _val = (u32)(val); - err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); - break; - } - case 8: { - err = bpf_map_update_elem(map_fd, pkey, &val, BPF_ANY); - break; - } - default: - pr_debug("ERROR: invalid value size\n"); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE; - } - if (err && errno) - err = -errno; - return err; -} - -static int -apply_config_evsel_for_key(const char *name, int map_fd, void *pkey, - struct evsel *evsel) -{ - struct xyarray *xy = evsel->core.fd; - struct perf_event_attr *attr; - unsigned int key, events; - bool check_pass = false; - int *evt_fd; - int err; - - if (!xy) { - pr_debug("ERROR: evsel not ready for map %s\n", name); - return -BPF_LOADER_ERRNO__INTERNAL; - } - - if (xy->row_size / xy->entry_size != 1) { - pr_debug("ERROR: Dimension of target event is incorrect for map %s\n", - name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM; - } - - attr = &evsel->core.attr; - if (attr->inherit) { - pr_debug("ERROR: Can't put inherit event into map %s\n", name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH; - } - - if (evsel__is_bpf_output(evsel)) - check_pass = true; - if (attr->type == PERF_TYPE_RAW) - check_pass = true; - if (attr->type == PERF_TYPE_HARDWARE) - check_pass = true; - if (!check_pass) { - pr_debug("ERROR: Event type is wrong for map %s\n", name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE; - } - - events = xy->entries / (xy->row_size / xy->entry_size); - key = *((unsigned int *)pkey); - if (key >= events) { - pr_debug("ERROR: there is no event %d for map %s\n", - key, name); - return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE; - } - evt_fd = xyarray__entry(xy, key, 0); - err = bpf_map_update_elem(map_fd, pkey, evt_fd, BPF_ANY); - if (err && errno) - err = -errno; - return err; -} - -static int -apply_obj_config_map_for_key(const char *name, int map_fd, - const struct bpf_map *map, - struct bpf_map_op *op, - void *pkey, void *arg __maybe_unused) -{ - int err; - - switch (op->op_type) { - case BPF_MAP_OP_SET_VALUE: - err = apply_config_value_for_key(map_fd, pkey, - bpf_map__value_size(map), - op->v.value); - break; - case BPF_MAP_OP_SET_EVSEL: - err = apply_config_evsel_for_key(name, map_fd, pkey, - op->v.evsel); - break; - default: - pr_debug("ERROR: unknown value type for '%s'\n", name); - err = -BPF_LOADER_ERRNO__INTERNAL; - } - return err; -} - -static int -apply_obj_config_map(struct bpf_map *map) -{ - return bpf_map_config_foreach_key(map, - apply_obj_config_map_for_key, - NULL); -} - -static int -apply_obj_config_object(struct bpf_object *obj) -{ - struct bpf_map *map; - int err; - - bpf_object__for_each_map(map, obj) { - err = apply_obj_config_map(map); - if (err) - return err; - } - return 0; -} - -int bpf__apply_obj_config(void) -{ - struct bpf_perf_object *perf_obj, *tmp; - int err; - - bpf_perf_object__for_each(perf_obj, tmp) { - err = apply_obj_config_object(perf_obj->obj); - if (err) - return err; - } - - return 0; -} - -#define bpf__perf_for_each_map(map, pobj, tmp) \ - bpf_perf_object__for_each(pobj, tmp) \ - bpf_object__for_each_map(map, pobj->obj) - -#define bpf__perf_for_each_map_named(map, pobj, pobjtmp, name) \ - bpf__perf_for_each_map(map, pobj, pobjtmp) \ - if (bpf_map__name(map) && (strcmp(name, bpf_map__name(map)) == 0)) - -struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name) -{ - struct bpf_map_priv *tmpl_priv = NULL; - struct bpf_perf_object *perf_obj, *tmp; - struct evsel *evsel = NULL; - struct bpf_map *map; - int err; - bool need_init = false; - - bpf__perf_for_each_map_named(map, perf_obj, tmp, name) { - struct bpf_map_priv *priv = map_priv(map); - - if (IS_ERR(priv)) - return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL); - - /* - * No need to check map type: type should have been - * verified by kernel. - */ - if (!need_init && !priv) - need_init = !priv; - if (!tmpl_priv && priv) - tmpl_priv = priv; - } - - if (!need_init) - return NULL; - - if (!tmpl_priv) { - char *event_definition = NULL; - - if (asprintf(&event_definition, "bpf-output/no-inherit=1,name=%s/", name) < 0) - return ERR_PTR(-ENOMEM); - - err = parse_event(evlist, event_definition); - free(event_definition); - - if (err) { - pr_debug("ERROR: failed to create the \"%s\" bpf-output event\n", name); - return ERR_PTR(-err); - } - - evsel = evlist__last(evlist); - } - - bpf__perf_for_each_map_named(map, perf_obj, tmp, name) { - struct bpf_map_priv *priv = map_priv(map); - - if (IS_ERR(priv)) - return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL); - if (priv) - continue; - - if (tmpl_priv) { - priv = bpf_map_priv__clone(tmpl_priv); - if (!priv) - return ERR_PTR(-ENOMEM); - - err = map_set_priv(map, priv); - if (err) { - bpf_map_priv__clear(map, priv); - return ERR_PTR(err); - } - } else if (evsel) { - struct bpf_map_op *op; - - op = bpf_map__add_newop(map, NULL); - if (IS_ERR(op)) - return ERR_CAST(op); - op->op_type = BPF_MAP_OP_SET_EVSEL; - op->v.evsel = evsel; - } - } - - return evsel; -} - -int bpf__setup_stdout(struct evlist *evlist) -{ - struct evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__"); - return PTR_ERR_OR_ZERO(evsel); -} - -#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) -#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) -#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) - -static const char *bpf_loader_strerror_table[NR_ERRNO] = { - [ERRCODE_OFFSET(CONFIG)] = "Invalid config string", - [ERRCODE_OFFSET(GROUP)] = "Invalid group name", - [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", - [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", - [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", - [ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string", - [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", - [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", - [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", - [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option", - [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (missing '=')", - [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object map config option", - [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map doesn't exist", - [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map", - [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type", - [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size", - [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size", - [ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting", - [ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting", - [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large", - [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event", - [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map", - [ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large", -}; - -static int -bpf_loader_strerror(int err, char *buf, size_t size) -{ - char sbuf[STRERR_BUFSIZE]; - const char *msg; - - if (!buf || !size) - return -1; - - err = err > 0 ? err : -err; - - if (err >= __LIBBPF_ERRNO__START) - return libbpf_strerror(err, buf, size); - - if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) { - msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)]; - snprintf(buf, size, "%s", msg); - buf[size - 1] = '\0'; - return 0; - } - - if (err >= __BPF_LOADER_ERRNO__END) - snprintf(buf, size, "Unknown bpf loader error %d", err); - else - snprintf(buf, size, "%s", - str_error_r(err, sbuf, sizeof(sbuf))); - - buf[size - 1] = '\0'; - return -1; -} - -#define bpf__strerror_head(err, buf, size) \ - char sbuf[STRERR_BUFSIZE], *emsg;\ - if (!size)\ - return 0;\ - if (err < 0)\ - err = -err;\ - bpf_loader_strerror(err, sbuf, sizeof(sbuf));\ - emsg = sbuf;\ - switch (err) {\ - default:\ - scnprintf(buf, size, "%s", emsg);\ - break; - -#define bpf__strerror_entry(val, fmt...)\ - case val: {\ - scnprintf(buf, size, fmt);\ - break;\ - } - -#define bpf__strerror_end(buf, size)\ - }\ - buf[size - 1] = '\0'; - -int bpf__strerror_prepare_load(const char *filename, bool source, - int err, char *buf, size_t size) -{ - size_t n; - int ret; - - n = snprintf(buf, size, "Failed to load %s%s: ", - filename, source ? " from source" : ""); - if (n >= size) { - buf[size - 1] = '\0'; - return 0; - } - buf += n; - size -= n; - - ret = bpf_loader_strerror(err, buf, size); - buf[size - 1] = '\0'; - return ret; -} - -int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, - int err, char *buf, size_t size) -{ - bpf__strerror_head(err, buf, size); - case BPF_LOADER_ERRNO__PROGCONF_TERM: { - scnprintf(buf, size, "%s (add -v to see detail)", emsg); - break; - } - bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'"); - bpf__strerror_entry(EACCES, "You need to be root"); - bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); - bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file"); - bpf__strerror_end(buf, size); - return 0; -} - -int bpf__strerror_load(struct bpf_object *obj, - int err, char *buf, size_t size) -{ - bpf__strerror_head(err, buf, size); - case LIBBPF_ERRNO__KVER: { - unsigned int obj_kver = bpf_object__kversion(obj); - unsigned int real_kver; - - if (fetch_kernel_version(&real_kver, NULL, 0)) { - scnprintf(buf, size, "Unable to fetch kernel version"); - break; - } - - if (obj_kver != real_kver) { - scnprintf(buf, size, - "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")", - KVER_PARAM(obj_kver), - KVER_PARAM(real_kver)); - break; - } - - scnprintf(buf, size, "Failed to load program for unknown reason"); - break; - } - bpf__strerror_end(buf, size); - return 0; -} - -int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused, - struct parse_events_term *term __maybe_unused, - struct evlist *evlist __maybe_unused, - int *error_pos __maybe_unused, int err, - char *buf, size_t size) -{ - bpf__strerror_head(err, buf, size); - bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, - "Can't use this config term with this map type"); - bpf__strerror_end(buf, size); - return 0; -} - -int bpf__strerror_apply_obj_config(int err, char *buf, size_t size) -{ - bpf__strerror_head(err, buf, size); - bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, - "Cannot set event to BPF map in multi-thread tracing"); - bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, - "%s (Hint: use -i to turn off inherit)", emsg); - bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, - "Can only put raw, hardware and BPF output event into a BPF map"); - bpf__strerror_end(buf, size); - return 0; -} - -int bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused, - int err, char *buf, size_t size) -{ - bpf__strerror_head(err, buf, size); - bpf__strerror_end(buf, size); - return 0; -} diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h deleted file mode 100644 index 5d1c725cea29..000000000000 --- a/tools/perf/util/bpf-loader.h +++ /dev/null @@ -1,216 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2015, Huawei Inc. - */ -#ifndef __BPF_LOADER_H -#define __BPF_LOADER_H - -#include <linux/compiler.h> -#include <linux/err.h> - -#ifdef HAVE_LIBBPF_SUPPORT -#include <bpf/libbpf.h> - -enum bpf_loader_errno { - __BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100, - /* Invalid config string */ - BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START, - BPF_LOADER_ERRNO__GROUP, /* Invalid group name */ - BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */ - BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ - BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ - BPF_LOADER_ERRNO__PROGCONF_TERM,/* Invalid program config term in config string */ - BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */ - BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */ - BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */ - BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */ - BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */ - BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object map config option */ - BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */ - BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */ - BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */ - BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */ - BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */ - BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT, /* Event not found for map setting */ - BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE, /* Invalid map size for event setting */ - BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */ - BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */ - BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */ - BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG, /* Index too large */ - __BPF_LOADER_ERRNO__END, -}; -#endif // HAVE_LIBBPF_SUPPORT - -struct evsel; -struct evlist; -struct bpf_object; -struct parse_events_term; -#define PERF_BPF_PROBE_GROUP "perf_bpf_probe" - -typedef int (*bpf_prog_iter_callback_t)(const char *group, const char *event, - int fd, struct bpf_object *obj, void *arg); - -#ifdef HAVE_LIBBPF_SUPPORT -struct bpf_object *bpf__prepare_load(const char *filename, bool source); -int bpf__strerror_prepare_load(const char *filename, bool source, - int err, char *buf, size_t size); - -struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, - const char *name); - -void bpf__clear(void); - -int bpf__probe(struct bpf_object *obj); -int bpf__unprobe(struct bpf_object *obj); -int bpf__strerror_probe(struct bpf_object *obj, int err, - char *buf, size_t size); - -int bpf__load(struct bpf_object *obj); -int bpf__strerror_load(struct bpf_object *obj, int err, - char *buf, size_t size); -int bpf__foreach_event(struct bpf_object *obj, - bpf_prog_iter_callback_t func, void *arg); - -int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term, - struct evlist *evlist, int *error_pos); -int bpf__strerror_config_obj(struct bpf_object *obj, - struct parse_events_term *term, - struct evlist *evlist, - int *error_pos, int err, char *buf, - size_t size); -int bpf__apply_obj_config(void); -int bpf__strerror_apply_obj_config(int err, char *buf, size_t size); - -int bpf__setup_stdout(struct evlist *evlist); -struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name); -int bpf__strerror_setup_output_event(struct evlist *evlist, int err, char *buf, size_t size); -#else -#include <errno.h> -#include <string.h> -#include "debug.h" - -static inline struct bpf_object * -bpf__prepare_load(const char *filename __maybe_unused, - bool source __maybe_unused) -{ - pr_debug("ERROR: eBPF object loading is disabled during compiling.\n"); - return ERR_PTR(-ENOTSUP); -} - -static inline struct bpf_object * -bpf__prepare_load_buffer(void *obj_buf __maybe_unused, - size_t obj_buf_sz __maybe_unused) -{ - return ERR_PTR(-ENOTSUP); -} - -static inline void bpf__clear(void) { } - -static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} -static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;} -static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; } - -static inline int -bpf__foreach_event(struct bpf_object *obj __maybe_unused, - bpf_prog_iter_callback_t func __maybe_unused, - void *arg __maybe_unused) -{ - return 0; -} - -static inline int -bpf__config_obj(struct bpf_object *obj __maybe_unused, - struct parse_events_term *term __maybe_unused, - struct evlist *evlist __maybe_unused, - int *error_pos __maybe_unused) -{ - return 0; -} - -static inline int -bpf__apply_obj_config(void) -{ - return 0; -} - -static inline int -bpf__setup_stdout(struct evlist *evlist __maybe_unused) -{ - return 0; -} - -static inline struct evsel * -bpf__setup_output_event(struct evlist *evlist __maybe_unused, const char *name __maybe_unused) -{ - return NULL; -} - -static inline int -__bpf_strerror(char *buf, size_t size) -{ - if (!size) - return 0; - strncpy(buf, - "ERROR: eBPF object loading is disabled during compiling.\n", - size); - buf[size - 1] = '\0'; - return 0; -} - -static inline -int bpf__strerror_prepare_load(const char *filename __maybe_unused, - bool source __maybe_unused, - int err __maybe_unused, - char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -static inline int -bpf__strerror_probe(struct bpf_object *obj __maybe_unused, - int err __maybe_unused, - char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused, - int err __maybe_unused, - char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -static inline int -bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused, - struct parse_events_term *term __maybe_unused, - struct evlist *evlist __maybe_unused, - int *error_pos __maybe_unused, - int err __maybe_unused, - char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -static inline int -bpf__strerror_apply_obj_config(int err __maybe_unused, - char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -static inline int -bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused, - int err __maybe_unused, char *buf, size_t size) -{ - return __bpf_strerror(buf, size); -} - -#endif - -static inline int bpf__strerror_setup_stdout(struct evlist *evlist, int err, char *buf, size_t size) -{ - return bpf__strerror_setup_output_event(evlist, err, buf, size); -} -#endif diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c deleted file mode 100644 index 9887ae09242d..000000000000 --- a/tools/perf/util/bpf-prologue.c +++ /dev/null @@ -1,508 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * bpf-prologue.c - * - * Copyright (C) 2015 He Kuang <hekuang@huawei.com> - * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2015 Huawei Inc. - */ - -#include <bpf/libbpf.h> -#include "debug.h" -#include "bpf-loader.h" -#include "bpf-prologue.h" -#include "probe-finder.h" -#include <errno.h> -#include <stdlib.h> -#include <dwarf-regs.h> -#include <linux/filter.h> - -#define BPF_REG_SIZE 8 - -#define JMP_TO_ERROR_CODE -1 -#define JMP_TO_SUCCESS_CODE -2 -#define JMP_TO_USER_CODE -3 - -struct bpf_insn_pos { - struct bpf_insn *begin; - struct bpf_insn *end; - struct bpf_insn *pos; -}; - -static inline int -pos_get_cnt(struct bpf_insn_pos *pos) -{ - return pos->pos - pos->begin; -} - -static int -append_insn(struct bpf_insn new_insn, struct bpf_insn_pos *pos) -{ - if (!pos->pos) - return -BPF_LOADER_ERRNO__PROLOGUE2BIG; - - if (pos->pos + 1 >= pos->end) { - pr_err("bpf prologue: prologue too long\n"); - pos->pos = NULL; - return -BPF_LOADER_ERRNO__PROLOGUE2BIG; - } - - *(pos->pos)++ = new_insn; - return 0; -} - -static int -check_pos(struct bpf_insn_pos *pos) -{ - if (!pos->pos || pos->pos >= pos->end) - return -BPF_LOADER_ERRNO__PROLOGUE2BIG; - return 0; -} - -/* - * Convert type string (u8/u16/u32/u64/s8/s16/s32/s64 ..., see - * Documentation/trace/kprobetrace.rst) to size field of BPF_LDX_MEM - * instruction (BPF_{B,H,W,DW}). - */ -static int -argtype_to_ldx_size(const char *type) -{ - int arg_size = type ? atoi(&type[1]) : 64; - - switch (arg_size) { - case 8: - return BPF_B; - case 16: - return BPF_H; - case 32: - return BPF_W; - case 64: - default: - return BPF_DW; - } -} - -static const char * -insn_sz_to_str(int insn_sz) -{ - switch (insn_sz) { - case BPF_B: - return "BPF_B"; - case BPF_H: - return "BPF_H"; - case BPF_W: - return "BPF_W"; - case BPF_DW: - return "BPF_DW"; - default: - return "UNKNOWN"; - } -} - -/* Give it a shorter name */ -#define ins(i, p) append_insn((i), (p)) - -/* - * Give a register name (in 'reg'), generate instruction to - * load register into an eBPF register rd: - * 'ldd target_reg, offset(ctx_reg)', where: - * ctx_reg is pre initialized to pointer of 'struct pt_regs'. - */ -static int -gen_ldx_reg_from_ctx(struct bpf_insn_pos *pos, int ctx_reg, - const char *reg, int target_reg) -{ - int offset = regs_query_register_offset(reg); - - if (offset < 0) { - pr_err("bpf: prologue: failed to get register %s\n", - reg); - return offset; - } - ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos); - - return check_pos(pos); -} - -/* - * Generate a BPF_FUNC_probe_read function call. - * - * src_base_addr_reg is a register holding base address, - * dst_addr_reg is a register holding dest address (on stack), - * result is: - * - * *[dst_addr_reg] = *([src_base_addr_reg] + offset) - * - * Arguments of BPF_FUNC_probe_read: - * ARG1: ptr to stack (dest) - * ARG2: size (8) - * ARG3: unsafe ptr (src) - */ -static int -gen_read_mem(struct bpf_insn_pos *pos, - int src_base_addr_reg, - int dst_addr_reg, - long offset, - int probeid) -{ - /* mov arg3, src_base_addr_reg */ - if (src_base_addr_reg != BPF_REG_ARG3) - ins(BPF_MOV64_REG(BPF_REG_ARG3, src_base_addr_reg), pos); - /* add arg3, #offset */ - if (offset) - ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, offset), pos); - - /* mov arg2, #reg_size */ - ins(BPF_ALU64_IMM(BPF_MOV, BPF_REG_ARG2, BPF_REG_SIZE), pos); - - /* mov arg1, dst_addr_reg */ - if (dst_addr_reg != BPF_REG_ARG1) - ins(BPF_MOV64_REG(BPF_REG_ARG1, dst_addr_reg), pos); - - /* Call probe_read */ - ins(BPF_EMIT_CALL(probeid), pos); - /* - * Error processing: if read fail, goto error code, - * will be relocated. Target should be the start of - * error processing code. - */ - ins(BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, JMP_TO_ERROR_CODE), - pos); - - return check_pos(pos); -} - -/* - * Each arg should be bare register. Fetch and save them into argument - * registers (r3 - r5). - * - * BPF_REG_1 should have been initialized with pointer to - * 'struct pt_regs'. - */ -static int -gen_prologue_fastpath(struct bpf_insn_pos *pos, - struct probe_trace_arg *args, int nargs) -{ - int i, err = 0; - - for (i = 0; i < nargs; i++) { - err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value, - BPF_PROLOGUE_START_ARG_REG + i); - if (err) - goto errout; - } - - return check_pos(pos); -errout: - return err; -} - -/* - * Slow path: - * At least one argument has the form of 'offset($rx)'. - * - * Following code first stores them into stack, then loads all of then - * to r2 - r5. - * Before final loading, the final result should be: - * - * low address - * BPF_REG_FP - 24 ARG3 - * BPF_REG_FP - 16 ARG2 - * BPF_REG_FP - 8 ARG1 - * BPF_REG_FP - * high address - * - * For each argument (described as: offn(...off2(off1(reg)))), - * generates following code: - * - * r7 <- fp - * r7 <- r7 - stack_offset // Ideal code should initialize r7 using - * // fp before generating args. However, - * // eBPF won't regard r7 as stack pointer - * // if it is generated by minus 8 from - * // another stack pointer except fp. - * // This is why we have to set r7 - * // to fp for each variable. - * r3 <- value of 'reg'-> generated using gen_ldx_reg_from_ctx() - * (r7) <- r3 // skip following instructions for bare reg - * r3 <- r3 + off1 . // skip if off1 == 0 - * r2 <- 8 \ - * r1 <- r7 |-> generated by gen_read_mem() - * call probe_read / - * jnei r0, 0, err ./ - * r3 <- (r7) - * r3 <- r3 + off2 . // skip if off2 == 0 - * r2 <- 8 \ // r2 may be broken by probe_read, so set again - * r1 <- r7 |-> generated by gen_read_mem() - * call probe_read / - * jnei r0, 0, err ./ - * ... - */ -static int -gen_prologue_slowpath(struct bpf_insn_pos *pos, - struct probe_trace_arg *args, int nargs) -{ - int err, i, probeid; - - for (i = 0; i < nargs; i++) { - struct probe_trace_arg *arg = &args[i]; - const char *reg = arg->value; - struct probe_trace_arg_ref *ref = NULL; - int stack_offset = (i + 1) * -8; - - pr_debug("prologue: fetch arg %d, base reg is %s\n", - i, reg); - - /* value of base register is stored into ARG3 */ - err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg, - BPF_REG_ARG3); - if (err) { - pr_err("prologue: failed to get offset of register %s\n", - reg); - goto errout; - } - - /* Make r7 the stack pointer. */ - ins(BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP), pos); - /* r7 += -8 */ - ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, stack_offset), pos); - /* - * Store r3 (base register) onto stack - * Ensure fp[offset] is set. - * fp is the only valid base register when storing - * into stack. We are not allowed to use r7 as base - * register here. - */ - ins(BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3, - stack_offset), pos); - - ref = arg->ref; - probeid = BPF_FUNC_probe_read_kernel; - while (ref) { - pr_debug("prologue: arg %d: offset %ld\n", - i, ref->offset); - - if (ref->user_access) - probeid = BPF_FUNC_probe_read_user; - - err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7, - ref->offset, probeid); - if (err) { - pr_err("prologue: failed to generate probe_read function call\n"); - goto errout; - } - - ref = ref->next; - /* - * Load previous result into ARG3. Use - * BPF_REG_FP instead of r7 because verifier - * allows FP based addressing only. - */ - if (ref) - ins(BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, - BPF_REG_FP, stack_offset), pos); - } - } - - /* Final pass: read to registers */ - for (i = 0; i < nargs; i++) { - int insn_sz = (args[i].ref) ? argtype_to_ldx_size(args[i].type) : BPF_DW; - - pr_debug("prologue: load arg %d, insn_sz is %s\n", - i, insn_sz_to_str(insn_sz)); - ins(BPF_LDX_MEM(insn_sz, BPF_PROLOGUE_START_ARG_REG + i, - BPF_REG_FP, -BPF_REG_SIZE * (i + 1)), pos); - } - - ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_SUCCESS_CODE), pos); - - return check_pos(pos); -errout: - return err; -} - -static int -prologue_relocate(struct bpf_insn_pos *pos, struct bpf_insn *error_code, - struct bpf_insn *success_code, struct bpf_insn *user_code) -{ - struct bpf_insn *insn; - - if (check_pos(pos)) - return -BPF_LOADER_ERRNO__PROLOGUE2BIG; - - for (insn = pos->begin; insn < pos->pos; insn++) { - struct bpf_insn *target; - u8 class = BPF_CLASS(insn->code); - u8 opcode; - - if (class != BPF_JMP) - continue; - opcode = BPF_OP(insn->code); - if (opcode == BPF_CALL) - continue; - - switch (insn->off) { - case JMP_TO_ERROR_CODE: - target = error_code; - break; - case JMP_TO_SUCCESS_CODE: - target = success_code; - break; - case JMP_TO_USER_CODE: - target = user_code; - break; - default: - pr_err("bpf prologue: internal error: relocation failed\n"); - return -BPF_LOADER_ERRNO__PROLOGUE; - } - - insn->off = target - (insn + 1); - } - return 0; -} - -int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, - struct bpf_insn *new_prog, size_t *new_cnt, - size_t cnt_space) -{ - struct bpf_insn *success_code = NULL; - struct bpf_insn *error_code = NULL; - struct bpf_insn *user_code = NULL; - struct bpf_insn_pos pos; - bool fastpath = true; - int err = 0, i; - - if (!new_prog || !new_cnt) - return -EINVAL; - - if (cnt_space > BPF_MAXINSNS) - cnt_space = BPF_MAXINSNS; - - pos.begin = new_prog; - pos.end = new_prog + cnt_space; - pos.pos = new_prog; - - if (!nargs) { - ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), - &pos); - - if (check_pos(&pos)) - goto errout; - - *new_cnt = pos_get_cnt(&pos); - return 0; - } - - if (nargs > BPF_PROLOGUE_MAX_ARGS) { - pr_warning("bpf: prologue: %d arguments are dropped\n", - nargs - BPF_PROLOGUE_MAX_ARGS); - nargs = BPF_PROLOGUE_MAX_ARGS; - } - - /* First pass: validation */ - for (i = 0; i < nargs; i++) { - struct probe_trace_arg_ref *ref = args[i].ref; - - if (args[i].value[0] == '@') { - /* TODO: fetch global variable */ - pr_err("bpf: prologue: global %s%+ld not support\n", - args[i].value, ref ? ref->offset : 0); - return -ENOTSUP; - } - - while (ref) { - /* fastpath is true if all args has ref == NULL */ - fastpath = false; - - /* - * Instruction encodes immediate value using - * s32, ref->offset is long. On systems which - * can't fill long in s32, refuse to process if - * ref->offset too large (or small). - */ -#ifdef __LP64__ -#define OFFSET_MAX ((1LL << 31) - 1) -#define OFFSET_MIN ((1LL << 31) * -1) - if (ref->offset > OFFSET_MAX || - ref->offset < OFFSET_MIN) { - pr_err("bpf: prologue: offset out of bound: %ld\n", - ref->offset); - return -BPF_LOADER_ERRNO__PROLOGUEOOB; - } -#endif - ref = ref->next; - } - } - pr_debug("prologue: pass validation\n"); - - if (fastpath) { - /* If all variables are registers... */ - pr_debug("prologue: fast path\n"); - err = gen_prologue_fastpath(&pos, args, nargs); - if (err) - goto errout; - } else { - pr_debug("prologue: slow path\n"); - - /* Initialization: move ctx to a callee saved register. */ - ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos); - - err = gen_prologue_slowpath(&pos, args, nargs); - if (err) - goto errout; - /* - * start of ERROR_CODE (only slow pass needs error code) - * mov r2 <- 1 // r2 is error number - * mov r3 <- 0 // r3, r4... should be touched or - * // verifier would complain - * mov r4 <- 0 - * ... - * goto usercode - */ - error_code = pos.pos; - ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1), - &pos); - - for (i = 0; i < nargs; i++) - ins(BPF_ALU64_IMM(BPF_MOV, - BPF_PROLOGUE_START_ARG_REG + i, - 0), - &pos); - ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE), - &pos); - } - - /* - * start of SUCCESS_CODE: - * mov r2 <- 0 - * goto usercode // skip - */ - success_code = pos.pos; - ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos); - - /* - * start of USER_CODE: - * Restore ctx to r1 - */ - user_code = pos.pos; - if (!fastpath) { - /* - * Only slow path needs restoring of ctx. In fast path, - * register are loaded directly from r1. - */ - ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos); - err = prologue_relocate(&pos, error_code, success_code, - user_code); - if (err) - goto errout; - } - - err = check_pos(&pos); - if (err) - goto errout; - - *new_cnt = pos_get_cnt(&pos); - return 0; -errout: - return err; -} diff --git a/tools/perf/examples/bpf/augmented_raw_syscalls.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 9a03189d33d3..939ec769bf4a 100644 --- a/tools/perf/examples/bpf/augmented_raw_syscalls.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -2,24 +2,30 @@ /* * Augment the raw_syscalls tracepoints with the contents of the pointer arguments. * - * Test it with: - * - * perf trace -e tools/perf/examples/bpf/augmented_raw_syscalls.c cat /etc/passwd > /dev/null - * * This exactly matches what is marshalled into the raw_syscall:sys_enter * payload expected by the 'perf trace' beautifiers. - * - * For now it just uses the existing tracepoint augmentation code in 'perf - * trace', in the next csets we'll hook up these with the sys_enter/sys_exit - * code that will combine entry/exit in a strace like way. */ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/limits.h> +/** + * is_power_of_2() - check if a value is a power of two + * @n: the value to check + * + * Determine whether some value is a power of two, where zero is *not* + * considered a power of two. Return: true if @n is a power of 2, otherwise + * false. + */ +#define is_power_of_2(n) (n != 0 && ((n & (n - 1)) == 0)) + +#define MAX_CPUS 4096 + // FIXME: These should come from system headers +#ifndef bool typedef char bool; +#endif typedef int pid_t; typedef long long int __s64; typedef __s64 time64_t; @@ -34,7 +40,7 @@ struct __augmented_syscalls__ { __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); __type(key, int); __type(value, __u32); - __uint(max_entries, __NR_CPUS__); + __uint(max_entries, MAX_CPUS); } __augmented_syscalls__ SEC(".maps"); /* @@ -156,6 +162,7 @@ unsigned int augmented_arg__read_str(struct augmented_arg *augmented_arg, const */ if (string_len > 0) { augmented_len -= sizeof(augmented_arg->value) - string_len; + _Static_assert(is_power_of_2(sizeof(augmented_arg->value)), "sizeof(augmented_arg->value) needs to be a power of two"); augmented_len &= sizeof(augmented_arg->value) - 1; augmented_arg->size = string_len; } else { @@ -170,7 +177,7 @@ unsigned int augmented_arg__read_str(struct augmented_arg *augmented_arg, const return augmented_len; } -SEC("!raw_syscalls:unaugmented") +SEC("tp/raw_syscalls/sys_enter") int syscall_unaugmented(struct syscall_enter_args *args) { return 1; @@ -182,7 +189,7 @@ int syscall_unaugmented(struct syscall_enter_args *args) * on from there, reading the first syscall arg as a string, i.e. open's * filename. */ -SEC("!syscalls:sys_enter_connect") +SEC("tp/syscalls/sys_enter_connect") int sys_enter_connect(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -193,15 +200,15 @@ int sys_enter_connect(struct syscall_enter_args *args) if (augmented_args == NULL) return 1; /* Failure: don't filter */ - if (socklen > sizeof(augmented_args->saddr)) - socklen = sizeof(augmented_args->saddr); + _Static_assert(is_power_of_2(sizeof(augmented_args->saddr)), "sizeof(augmented_args->saddr) needs to be a power of two"); + socklen &= sizeof(augmented_args->saddr) - 1; bpf_probe_read(&augmented_args->saddr, socklen, sockaddr_arg); return augmented__output(args, augmented_args, len + socklen); } -SEC("!syscalls:sys_enter_sendto") +SEC("tp/syscalls/sys_enter_sendto") int sys_enter_sendto(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -212,15 +219,14 @@ int sys_enter_sendto(struct syscall_enter_args *args) if (augmented_args == NULL) return 1; /* Failure: don't filter */ - if (socklen > sizeof(augmented_args->saddr)) - socklen = sizeof(augmented_args->saddr); + socklen &= sizeof(augmented_args->saddr) - 1; bpf_probe_read(&augmented_args->saddr, socklen, sockaddr_arg); return augmented__output(args, augmented_args, len + socklen); } -SEC("!syscalls:sys_enter_open") +SEC("tp/syscalls/sys_enter_open") int sys_enter_open(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -235,7 +241,7 @@ int sys_enter_open(struct syscall_enter_args *args) return augmented__output(args, augmented_args, len); } -SEC("!syscalls:sys_enter_openat") +SEC("tp/syscalls/sys_enter_openat") int sys_enter_openat(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -250,7 +256,7 @@ int sys_enter_openat(struct syscall_enter_args *args) return augmented__output(args, augmented_args, len); } -SEC("!syscalls:sys_enter_rename") +SEC("tp/syscalls/sys_enter_rename") int sys_enter_rename(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -267,7 +273,7 @@ int sys_enter_rename(struct syscall_enter_args *args) return augmented__output(args, augmented_args, len); } -SEC("!syscalls:sys_enter_renameat") +SEC("tp/syscalls/sys_enter_renameat") int sys_enter_renameat(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -295,7 +301,7 @@ struct perf_event_attr_size { __u32 size; }; -SEC("!syscalls:sys_enter_perf_event_open") +SEC("tp/syscalls/sys_enter_perf_event_open") int sys_enter_perf_event_open(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -327,7 +333,7 @@ failure: return 1; /* Failure: don't filter */ } -SEC("!syscalls:sys_enter_clock_nanosleep") +SEC("tp/syscalls/sys_enter_clock_nanosleep") int sys_enter_clock_nanosleep(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); @@ -358,7 +364,7 @@ static bool pid_filter__has(struct pids_filtered *pids, pid_t pid) return bpf_map_lookup_elem(pids, &pid) != NULL; } -SEC("raw_syscalls:sys_enter") +SEC("tp/raw_syscalls/sys_enter") int sys_enter(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args; @@ -371,7 +377,6 @@ int sys_enter(struct syscall_enter_args *args) * We'll add to this as we add augmented syscalls right after that * initial, non-augmented raw_syscalls:sys_enter payload. */ - unsigned int len = sizeof(augmented_args->args); if (pid_filter__has(&pids_filtered, getpid())) return 0; @@ -393,7 +398,7 @@ int sys_enter(struct syscall_enter_args *args) return 0; } -SEC("raw_syscalls:sys_exit") +SEC("tp/raw_syscalls/sys_exit") int sys_exit(struct syscall_exit_args *args) { struct syscall_exit_args exit_args; diff --git a/tools/perf/util/bpf_skel/bench_uprobe.bpf.c b/tools/perf/util/bpf_skel/bench_uprobe.bpf.c new file mode 100644 index 000000000000..2c55896bb33c --- /dev/null +++ b/tools/perf/util/bpf_skel/bench_uprobe.bpf.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +// Copyright (c) 2023 Red Hat +#include "vmlinux.h" +#include <bpf/bpf_tracing.h> + +unsigned int nr_uprobes; + +SEC("uprobe") +int BPF_UPROBE(empty) +{ + return 0; +} + +SEC("uprobe") +int BPF_UPROBE(trace_printk) +{ + char fmt[] = "perf bench uprobe %u"; + + bpf_trace_printk(fmt, sizeof(fmt), ++nr_uprobes); + return 0; +} + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 36728222a5b4..03c64b85383b 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -560,7 +560,7 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name, struct nsinfo *nsi, bool is_kallsyms, bool is_vdso) { - char *realname = (char *)name, *filename; + char *realname = NULL, *filename; bool slash = is_kallsyms || is_vdso; if (!slash) @@ -571,9 +571,7 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name, sbuild_id ? "/" : "", sbuild_id ?: "") < 0) filename = NULL; - if (!slash) - free(realname); - + free(realname); return filename; } diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build deleted file mode 100644 index 613ecfd76527..000000000000 --- a/tools/perf/util/c++/Build +++ /dev/null @@ -1,2 +0,0 @@ -perf-$(CONFIG_CLANGLLVM) += clang.o -perf-$(CONFIG_CLANGLLVM) += clang-test.o diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h deleted file mode 100644 index d3731a876b6c..000000000000 --- a/tools/perf/util/c++/clang-c.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef PERF_UTIL_CLANG_C_H -#define PERF_UTIL_CLANG_C_H - -#include <stddef.h> /* for size_t */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef HAVE_LIBCLANGLLVM_SUPPORT -extern void perf_clang__init(void); -extern void perf_clang__cleanup(void); - -struct test_suite; -extern int test__clang_to_IR(struct test_suite *test, int subtest); -extern int test__clang_to_obj(struct test_suite *test, int subtest); - -extern int perf_clang__compile_bpf(const char *filename, - void **p_obj_buf, - size_t *p_obj_buf_sz); -#else - -#include <errno.h> -#include <linux/compiler.h> /* for __maybe_unused */ - -static inline void perf_clang__init(void) { } -static inline void perf_clang__cleanup(void) { } - -static inline int -perf_clang__compile_bpf(const char *filename __maybe_unused, - void **p_obj_buf __maybe_unused, - size_t *p_obj_buf_sz __maybe_unused) -{ - return -ENOTSUP; -} - -#endif - -#ifdef __cplusplus -} -#endif -#endif diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp deleted file mode 100644 index a4683ca53697..000000000000 --- a/tools/perf/util/c++/clang-test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "clang.h" -#include "clang-c.h" -extern "C" { -#include "../util.h" -} -#include "llvm/IR/Function.h" -#include "llvm/IR/LLVMContext.h" - -#include <tests/llvm.h> -#include <string> - -class perf_clang_scope { -public: - explicit perf_clang_scope() {perf_clang__init();} - ~perf_clang_scope() {perf_clang__cleanup();} -}; - -static std::unique_ptr<llvm::Module> -__test__clang_to_IR(void) -{ - unsigned int kernel_version; - - if (fetch_kernel_version(&kernel_version, NULL, 0)) - return std::unique_ptr<llvm::Module>(nullptr); - - std::string cflag_kver("-DLINUX_VERSION_CODE=" + - std::to_string(kernel_version)); - - std::unique_ptr<llvm::Module> M = - perf::getModuleFromSource({cflag_kver.c_str()}, - "perf-test.c", - test_llvm__bpf_base_prog); - return M; -} - -extern "C" { -int test__clang_to_IR(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ - perf_clang_scope _scope; - - auto M = __test__clang_to_IR(); - if (!M) - return -1; - for (llvm::Function& F : *M) - if (F.getName() == "bpf_func__SyS_epoll_pwait") - return 0; - return -1; -} - -int test__clang_to_obj(struct test_suite *test __maybe_unused, - int subtest __maybe_unused) -{ - perf_clang_scope _scope; - - auto M = __test__clang_to_IR(); - if (!M) - return -1; - - auto Buffer = perf::getBPFObjectFromModule(&*M); - if (!Buffer) - return -1; - return 0; -} - -} diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp deleted file mode 100644 index 1aad7d6d34aa..000000000000 --- a/tools/perf/util/c++/clang.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * llvm C frontend for perf. Support dynamically compile C file - * - * Inspired by clang example code: - * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp - * - * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2016 Huawei Inc. - */ - -#include "clang/Basic/Version.h" -#include "clang/CodeGen/CodeGenAction.h" -#include "clang/Frontend/CompilerInvocation.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/Option/Option.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/ManagedStatic.h" -#if CLANG_VERSION_MAJOR >= 14 -#include "llvm/MC/TargetRegistry.h" -#else -#include "llvm/Support/TargetRegistry.h" -#endif -#include "llvm/Support/TargetSelect.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" -#include <memory> - -#include "clang.h" -#include "clang-c.h" - -namespace perf { - -static std::unique_ptr<llvm::LLVMContext> LLVMCtx; - -using namespace clang; - -static CompilerInvocation * -createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, - DiagnosticsEngine& Diags) -{ - llvm::opt::ArgStringList CCArgs { - "-cc1", - "-triple", "bpf-pc-linux", - "-fsyntax-only", - "-O2", - "-nostdsysteminc", - "-nobuiltininc", - "-vectorize-loops", - "-vectorize-slp", - "-Wno-unused-value", - "-Wno-pointer-sign", - "-x", "c"}; - - CCArgs.append(CFlags.begin(), CFlags.end()); - CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs -#if CLANG_VERSION_MAJOR >= 11 - ,/*BinaryName=*/nullptr -#endif - ); - - FrontendOptions& Opts = CI->getFrontendOpts(); - Opts.Inputs.clear(); - Opts.Inputs.emplace_back(Path, - FrontendOptions::getInputKindForExtension("c")); - return CI; -} - -static std::unique_ptr<llvm::Module> -getModuleFromSource(llvm::opt::ArgStringList CFlags, - StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) -{ - CompilerInstance Clang; - Clang.createDiagnostics(); - -#if CLANG_VERSION_MAJOR < 9 - Clang.setVirtualFileSystem(&*VFS); -#else - Clang.createFileManager(&*VFS); -#endif - -#if CLANG_VERSION_MAJOR < 4 - IntrusiveRefCntPtr<CompilerInvocation> CI = - createCompilerInvocation(std::move(CFlags), Path, - Clang.getDiagnostics()); - Clang.setInvocation(&*CI); -#else - std::shared_ptr<CompilerInvocation> CI( - createCompilerInvocation(std::move(CFlags), Path, - Clang.getDiagnostics())); - Clang.setInvocation(CI); -#endif - - std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); - if (!Clang.ExecuteAction(*Act)) - return std::unique_ptr<llvm::Module>(nullptr); - - return Act->takeModule(); -} - -std::unique_ptr<llvm::Module> -getModuleFromSource(llvm::opt::ArgStringList CFlags, - StringRef Name, StringRef Content) -{ - using namespace vfs; - - llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( - new OverlayFileSystem(getRealFileSystem())); - llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( - new InMemoryFileSystem(true)); - - /* - * pushOverlay helps setting working dir for MemFS. Must call - * before addFile. - */ - OverlayFS->pushOverlay(MemFS); - MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); - - return getModuleFromSource(std::move(CFlags), Name, OverlayFS); -} - -std::unique_ptr<llvm::Module> -getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) -{ - IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); - return getModuleFromSource(std::move(CFlags), Path, VFS); -} - -std::unique_ptr<llvm::SmallVectorImpl<char>> -getBPFObjectFromModule(llvm::Module *Module) -{ - using namespace llvm; - - std::string TargetTriple("bpf-pc-linux"); - std::string Error; - const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); - if (!Target) { - llvm::errs() << Error; - return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); - } - - llvm::TargetOptions Opt; - TargetMachine *TargetMachine = - Target->createTargetMachine(TargetTriple, - "generic", "", - Opt, Reloc::Static); - - Module->setDataLayout(TargetMachine->createDataLayout()); - Module->setTargetTriple(TargetTriple); - - std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); - raw_svector_ostream ostream(*Buffer); - - legacy::PassManager PM; - bool NotAdded; - NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream -#if CLANG_VERSION_MAJOR >= 7 - , /*DwoOut=*/nullptr -#endif -#if CLANG_VERSION_MAJOR < 10 - , TargetMachine::CGFT_ObjectFile -#else - , llvm::CGFT_ObjectFile -#endif - ); - if (NotAdded) { - llvm::errs() << "TargetMachine can't emit a file of this type\n"; - return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); - } - PM.run(*Module); - - return Buffer; -} - -} - -extern "C" { -void perf_clang__init(void) -{ - perf::LLVMCtx.reset(new llvm::LLVMContext()); - LLVMInitializeBPFTargetInfo(); - LLVMInitializeBPFTarget(); - LLVMInitializeBPFTargetMC(); - LLVMInitializeBPFAsmPrinter(); -} - -void perf_clang__cleanup(void) -{ - perf::LLVMCtx.reset(nullptr); - llvm::llvm_shutdown(); -} - -int perf_clang__compile_bpf(const char *filename, - void **p_obj_buf, - size_t *p_obj_buf_sz) -{ - using namespace perf; - - if (!p_obj_buf || !p_obj_buf_sz) - return -EINVAL; - - llvm::opt::ArgStringList CFlags; - auto M = getModuleFromSource(std::move(CFlags), filename); - if (!M) - return -EINVAL; - auto O = getBPFObjectFromModule(&*M); - if (!O) - return -EINVAL; - - size_t size = O->size_in_bytes(); - void *buffer; - - buffer = malloc(size); - if (!buffer) - return -ENOMEM; - memcpy(buffer, O->data(), size); - *p_obj_buf = buffer; - *p_obj_buf_sz = size; - return 0; -} -} diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h deleted file mode 100644 index 6ce33e22f23c..000000000000 --- a/tools/perf/util/c++/clang.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef PERF_UTIL_CLANG_H -#define PERF_UTIL_CLANG_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/Option/Option.h" -#include <memory> - -namespace perf { - -using namespace llvm; - -std::unique_ptr<Module> -getModuleFromSource(opt::ArgStringList CFlags, - StringRef Name, StringRef Content); - -std::unique_ptr<Module> -getModuleFromSource(opt::ArgStringList CFlags, - StringRef Path); - -std::unique_ptr<llvm::SmallVectorImpl<char>> -getBPFObjectFromModule(llvm::Module *Module); - -} -#endif diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 46f144c46827..7a650de0db83 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -16,7 +16,6 @@ #include <subcmd/exec-cmd.h> #include "util/event.h" /* proc_map_timeout */ #include "util/hist.h" /* perf_hist_config */ -#include "util/llvm-utils.h" /* perf_llvm_config */ #include "util/stat.h" /* perf_stat__set_big_num */ #include "util/evsel.h" /* evsel__hw_names, evsel__use_bpf_counters */ #include "util/srcline.h" /* addr2line_timeout_ms */ @@ -486,9 +485,6 @@ int perf_default_config(const char *var, const char *value, if (strstarts(var, "call-graph.")) return perf_callchain_config(var, value); - if (strstarts(var, "llvm.")) - return perf_llvm_config(var, value); - if (strstarts(var, "buildid.")) return perf_buildid_config(var, value); diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 1419b40dfbe8..9729d006550d 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -6,10 +6,11 @@ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> */ +#include <linux/kernel.h> +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/coresight-pmu.h> #include <linux/err.h> -#include <linux/kernel.h> #include <linux/log2.h> #include <linux/types.h> #include <linux/zalloc.h> @@ -282,17 +283,6 @@ static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata) } /* - * FIELD_GET (linux/bitfield.h) not available outside kernel code, - * and the header contains too many dependencies to just copy over, - * so roll our own based on the original - */ -#define __bf_shf(x) (__builtin_ffsll(x) - 1) -#define FIELD_GET(_mask, _reg) \ - ({ \ - (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ - }) - -/* * Get a metadata for a specific cpu from an array. * */ diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index 46f74b2344db..1dbf27822ee2 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -10,6 +10,8 @@ #include <subcmd/exec-cmd.h> #include <linux/zalloc.h> #include <linux/build_bug.h> +#include <linux/kernel.h> +#include <linux/string.h> #include "debug.h" #include "event.h" @@ -63,6 +65,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al) d_al->addr = al->addr; d_al->comm = NULL; d_al->filtered = 0; + d_al->priv = NULL; } static struct addr_location *get_al(struct dlfilter *d) @@ -151,6 +154,11 @@ static char **dlfilter__args(void *ctx, int *dlargc) return d->dlargv; } +static bool has_priv(struct perf_dlfilter_al *d_al_p) +{ + return d_al_p->size >= offsetof(struct perf_dlfilter_al, priv) + sizeof(d_al_p->priv); +} + static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p) { struct dlfilter *d = (struct dlfilter *)ctx; @@ -166,6 +174,7 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlf if (!thread) return -1; + addr_location__init(&al); thread__find_symbol_fb(thread, d->sample->cpumode, address, &al); al_to_d_al(&al, &d_al); @@ -176,9 +185,31 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlf memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al))); d_al_p->size = sz; + if (has_priv(d_al_p)) + d_al_p->priv = memdup(&al, sizeof(al)); + else /* Avoid leak for v0 API */ + addr_location__exit(&al); + return 0; } +static void dlfilter__al_cleanup(void *ctx __maybe_unused, struct perf_dlfilter_al *d_al_p) +{ + struct addr_location *al; + + /* Ensure backward compatibility */ + if (!has_priv(d_al_p) || !d_al_p->priv) + return; + + al = d_al_p->priv; + + d_al_p->priv = NULL; + + addr_location__exit(al); + + free(al); +} + static const __u8 *dlfilter__insn(void *ctx, __u32 *len) { struct dlfilter *d = (struct dlfilter *)ctx; @@ -296,6 +327,7 @@ static const struct perf_dlfilter_fns perf_dlfilter_fns = { .resolve_addr = dlfilter__resolve_addr, .args = dlfilter__args, .resolve_address = dlfilter__resolve_address, + .al_cleanup = dlfilter__al_cleanup, .insn = dlfilter__insn, .srcline = dlfilter__srcline, .attr = dlfilter__attr, diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 9eabf3ec56e9..a164164001fb 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -324,11 +324,9 @@ int perf_env__read_pmu_mappings(struct perf_env *env) u32 pmu_num = 0; struct strbuf sb; - while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->name) - continue; + while ((pmu = perf_pmus__scan(pmu))) pmu_num++; - } + if (!pmu_num) { pr_debug("pmu mappings not available\n"); return -ENOENT; @@ -339,8 +337,6 @@ int perf_env__read_pmu_mappings(struct perf_env *env) return -ENOMEM; while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->name) - continue; if (strbuf_addf(&sb, "%u:%s", pmu->type, pmu->name) < 0) goto error; /* include a NULL character at the end */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 4cbb092e0684..923c0fb15122 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -93,8 +93,8 @@ struct process_symbol_args { u64 start; }; -static int find_symbol_cb(void *arg, const char *name, char type, - u64 start) +static int find_func_symbol_cb(void *arg, const char *name, char type, + u64 start) { struct process_symbol_args *args = arg; @@ -110,12 +110,36 @@ static int find_symbol_cb(void *arg, const char *name, char type, return 1; } +static int find_any_symbol_cb(void *arg, const char *name, + char type __maybe_unused, u64 start) +{ + struct process_symbol_args *args = arg; + + if (strcmp(name, args->name)) + return 0; + + args->start = start; + return 1; +} + int kallsyms__get_function_start(const char *kallsyms_filename, const char *symbol_name, u64 *addr) { struct process_symbol_args args = { .name = symbol_name, }; - if (kallsyms__parse(kallsyms_filename, &args, find_symbol_cb) <= 0) + if (kallsyms__parse(kallsyms_filename, &args, find_func_symbol_cb) <= 0) + return -1; + + *addr = args.start; + return 0; +} + +int kallsyms__get_symbol_start(const char *kallsyms_filename, + const char *symbol_name, u64 *addr) +{ + struct process_symbol_args args = { .name = symbol_name, }; + + if (kallsyms__parse(kallsyms_filename, &args, find_any_symbol_cb) <= 0) return -1; *addr = args.start; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index de20e01c9d72..d8bcee2e9b93 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -360,6 +360,8 @@ size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FIL int kallsyms__get_function_start(const char *kallsyms_filename, const char *symbol_name, u64 *addr); +int kallsyms__get_symbol_start(const char *kallsyms_filename, + const char *symbol_name, u64 *addr); void event_attr_init(struct perf_event_attr *attr); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 762e2b2634a5..a8a5ff87cc1f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -845,6 +845,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o { bool function = evsel__is_function_event(evsel); struct perf_event_attr *attr = &evsel->core.attr; + const char *arch = perf_env__arch(evsel__env(evsel)); evsel__set_sample_bit(evsel, CALLCHAIN); @@ -877,8 +878,9 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o if (!function) { evsel__set_sample_bit(evsel, REGS_USER); evsel__set_sample_bit(evsel, STACK_USER); - if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS; + if (opts->sample_user_regs && + DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) { + attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " "specifying a subset with --user-regs may render DWARF unwinding unreliable, " "so the minimal registers set (IP, SP) is explicitly forced.\n"); @@ -1474,6 +1476,7 @@ void evsel__exit(struct evsel *evsel) perf_thread_map__put(evsel->core.threads); zfree(&evsel->group_name); zfree(&evsel->name); + zfree(&evsel->filter); zfree(&evsel->pmu_name); zfree(&evsel->group_pmu_name); zfree(&evsel->unit); @@ -2826,9 +2829,6 @@ u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *n { struct tep_format_field *field = evsel__field(evsel, name); - if (!field) - return 0; - return field ? format_field__intval(field, sample, evsel->needs_swap) : 0; } #endif diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index 4814262e3805..4488f306de78 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -10,9 +10,11 @@ #include "debug.h" #include "evlist.h" #include "expr.h" -#include "expr-bison.h" -#include "expr-flex.h" +#include <util/expr-bison.h> +#include <util/expr-flex.h> #include "util/hashmap.h" +#include "util/header.h" +#include "util/pmu.h" #include "smt.h" #include "tsc.h" #include <api/fs/fs.h> @@ -425,6 +427,13 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx result = cpu__max_present_cpu().cpu; goto out; } + if (!strcmp("#num_cpus_online", literal)) { + struct perf_cpu_map *online = cpu_map__online(); + + if (online) + result = perf_cpu_map__nr(online); + goto out; + } if (!strcasecmp("#system_tsc_freq", literal)) { result = arch_get_tsc_freq(); @@ -495,3 +504,19 @@ double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const evlist__delete(tmp); return ret; } + +double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx __maybe_unused, + bool compute_ids __maybe_unused, const char *test_id) +{ + double ret; + struct perf_pmu *pmu = pmu__find_core_pmu(); + char *cpuid = perf_pmu__getcpuid(pmu); + + if (!cpuid) + return NAN; + + ret = !strcmp_cpuid_str(test_id, cpuid); + + free(cpuid); + return ret; +} diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h index 3c1e49b3e35d..c0cec29ddc29 100644 --- a/tools/perf/util/expr.h +++ b/tools/perf/util/expr.h @@ -55,5 +55,6 @@ double expr_id_data__value(const struct expr_id_data *data); double expr_id_data__source_count(const struct expr_id_data *data); double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx); double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); +double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id); #endif diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l index dbb117414710..0feef0726c48 100644 --- a/tools/perf/util/expr.l +++ b/tools/perf/util/expr.l @@ -114,6 +114,7 @@ if { return IF; } else { return ELSE; } source_count { return SOURCE_COUNT; } has_event { return HAS_EVENT; } +strcmp_cpuid_str { return STRCMP_CPUID_STR; } {literal} { return literal(yyscanner, sctx); } {number} { return value(yyscanner); } {symbol} { return str(yyscanner, ID, sctx->runtime); } diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index dd504afd8f36..6c93b358cc2d 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -7,6 +7,8 @@ #include "util/debug.h" #define IN_EXPR_Y 1 #include "expr.h" +#include "expr-bison.h" +int expr_lex(YYSTYPE * yylval_param , void *yyscanner); %} %define api.pure full @@ -37,7 +39,7 @@ } ids; } -%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR +%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR %left MIN MAX IF %left '|' %left '^' @@ -56,7 +58,7 @@ static void expr_error(double *final_val __maybe_unused, struct expr_parse_ctx *ctx __maybe_unused, bool compute_ids __maybe_unused, - void *scanner, + void *scanner __maybe_unused, const char *s) { pr_debug("%s\n", s); @@ -205,6 +207,12 @@ expr: NUMBER $$.ids = NULL; free($3); } +| STRCMP_CPUID_STR '(' ID ')' +{ + $$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3); + $$.ids = NULL; + free($3); +} | expr '|' expr { if (is_const($1.val) && is_const($3.val)) { diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h index 0a5bf1937a7c..c12f8320e668 100644 --- a/tools/perf/util/hashmap.h +++ b/tools/perf/util/hashmap.h @@ -80,16 +80,6 @@ struct hashmap { size_t sz; }; -#define HASHMAP_INIT(hash_fn, equal_fn, ctx) { \ - .hash_fn = (hash_fn), \ - .equal_fn = (equal_fn), \ - .ctx = (ctx), \ - .buckets = NULL, \ - .cap = 0, \ - .cap_bits = 0, \ - .sz = 0, \ -} - void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn, hashmap_equal_fn equal_fn, void *ctx); struct hashmap *hashmap__new(hashmap_hash_fn hash_fn, diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 52fbf526fe74..d812e1e371a7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -456,6 +456,8 @@ static int write_cpudesc(struct feat_fd *ff, #define CPUINFO_PROC { "Processor", } #elif defined(__xtensa__) #define CPUINFO_PROC { "core ID", } +#elif defined(__loongarch__) +#define CPUINFO_PROC { "Model Name", } #else #define CPUINFO_PROC { "model name", } #endif @@ -746,20 +748,14 @@ static int write_pmu_mappings(struct feat_fd *ff, * Do a first pass to count number of pmu to avoid lseek so this * works in pipe mode as well. */ - while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->name) - continue; + while ((pmu = perf_pmus__scan(pmu))) pmu_num++; - } ret = do_write(ff, &pmu_num, sizeof(pmu_num)); if (ret < 0) return ret; while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->name) - continue; - ret = do_write(ff, &pmu->type, sizeof(pmu->type)); if (ret < 0) return ret; @@ -1605,8 +1601,15 @@ static int write_pmu_caps(struct feat_fd *ff, int ret; while ((pmu = perf_pmus__scan(pmu))) { - if (!pmu->name || !strcmp(pmu->name, "cpu") || - perf_pmu__caps_parse(pmu) <= 0) + if (!strcmp(pmu->name, "cpu")) { + /* + * The "cpu" PMU is special and covered by + * HEADER_CPU_PMU_CAPS. Note, core PMUs are + * counted/written here for ARM, s390 and Intel hybrid. + */ + continue; + } + if (perf_pmu__caps_parse(pmu) <= 0) continue; nr_pmu++; } @@ -1619,23 +1622,17 @@ static int write_pmu_caps(struct feat_fd *ff, return 0; /* - * Write hybrid pmu caps first to maintain compatibility with - * older perf tool. + * Note older perf tools assume core PMUs come first, this is a property + * of perf_pmus__scan. */ - if (perf_pmus__num_core_pmus() > 1) { - pmu = NULL; - while ((pmu = perf_pmus__scan_core(pmu))) { - ret = __write_pmu_caps(ff, pmu, true); - if (ret < 0) - return ret; - } - } - pmu = NULL; while ((pmu = perf_pmus__scan(pmu))) { - if (pmu->is_core || !pmu->nr_caps) + if (!strcmp(pmu->name, "cpu")) { + /* Skip as above. */ + continue; + } + if (perf_pmu__caps_parse(pmu) <= 0) continue; - ret = __write_pmu_caps(ff, pmu, true); if (ret < 0) return ret; @@ -4381,7 +4378,8 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) { - u32 i, ids, n_ids; + u32 i, n_ids; + u64 *ids; struct evsel *evsel; struct evlist *evlist = *pevlist; @@ -4397,9 +4395,8 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, evlist__add(evlist, evsel); - ids = event->header.size; - ids -= (void *)&event->attr.id - (void *)event; - n_ids = ids / sizeof(u64); + n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size; + n_ids = n_ids / sizeof(u64); /* * We don't have the cpu and thread maps on the header, so * for allocating the perf_sample_id table we fake 1 cpu and @@ -4408,8 +4405,9 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, if (perf_evsel__alloc_id(&evsel->core, 1, n_ids)) return -ENOMEM; + ids = perf_record_header_attr_id(event); for (i = 0; i < n_ids; i++) { - perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, event->attr.id[i]); + perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]); } return 0; diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c index 014d82159656..37ecef0c53b9 100644 --- a/tools/perf/util/libunwind/arm64.c +++ b/tools/perf/util/libunwind/arm64.c @@ -18,8 +18,6 @@ * defined before including "unwind.h" */ #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum) -#define LIBUNWIND__ARCH_REG_IP PERF_REG_ARM64_PC -#define LIBUNWIND__ARCH_REG_SP PERF_REG_ARM64_SP #include "unwind.h" #include "libunwind-aarch64.h" diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c index b2b92d030aef..1697dece1b74 100644 --- a/tools/perf/util/libunwind/x86_32.c +++ b/tools/perf/util/libunwind/x86_32.c @@ -18,8 +18,6 @@ * defined before including "unwind.h" */ #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum) -#define LIBUNWIND__ARCH_REG_IP PERF_REG_X86_IP -#define LIBUNWIND__ARCH_REG_SP PERF_REG_X86_SP #include "unwind.h" #include "libunwind-x86.h" diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c deleted file mode 100644 index c6c9c2228578..000000000000 --- a/tools/perf/util/llvm-utils.c +++ /dev/null @@ -1,612 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2015, Huawei Inc. - */ - -#include <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <linux/err.h> -#include <linux/string.h> -#include <linux/zalloc.h> -#include "debug.h" -#include "llvm-utils.h" -#include "config.h" -#include "util.h" -#include <sys/wait.h> -#include <subcmd/exec-cmd.h> - -#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ - "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ - "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ - "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ - "-Wno-unused-value -Wno-pointer-sign " \ - "-working-directory $WORKING_DIR " \ - "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -g -O2 -o - $LLVM_OPTIONS_PIPE" - -struct llvm_param llvm_param = { - .clang_path = "clang", - .llc_path = "llc", - .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE, - .clang_opt = NULL, - .opts = NULL, - .kbuild_dir = NULL, - .kbuild_opts = NULL, - .user_set_param = false, -}; - -static void version_notice(void); - -int perf_llvm_config(const char *var, const char *value) -{ - if (!strstarts(var, "llvm.")) - return 0; - var += sizeof("llvm.") - 1; - - if (!strcmp(var, "clang-path")) - llvm_param.clang_path = strdup(value); - else if (!strcmp(var, "clang-bpf-cmd-template")) - llvm_param.clang_bpf_cmd_template = strdup(value); - else if (!strcmp(var, "clang-opt")) - llvm_param.clang_opt = strdup(value); - else if (!strcmp(var, "kbuild-dir")) - llvm_param.kbuild_dir = strdup(value); - else if (!strcmp(var, "kbuild-opts")) - llvm_param.kbuild_opts = strdup(value); - else if (!strcmp(var, "dump-obj")) - llvm_param.dump_obj = !!perf_config_bool(var, value); - else if (!strcmp(var, "opts")) - llvm_param.opts = strdup(value); - else { - pr_debug("Invalid LLVM config option: %s\n", value); - return -1; - } - llvm_param.user_set_param = true; - return 0; -} - -static int -search_program(const char *def, const char *name, - char *output) -{ - char *env, *path, *tmp = NULL; - char buf[PATH_MAX]; - int ret; - - output[0] = '\0'; - if (def && def[0] != '\0') { - if (def[0] == '/') { - if (access(def, F_OK) == 0) { - strlcpy(output, def, PATH_MAX); - return 0; - } - } else if (def[0] != '\0') - name = def; - } - - env = getenv("PATH"); - if (!env) - return -1; - env = strdup(env); - if (!env) - return -1; - - ret = -ENOENT; - path = strtok_r(env, ":", &tmp); - while (path) { - scnprintf(buf, sizeof(buf), "%s/%s", path, name); - if (access(buf, F_OK) == 0) { - strlcpy(output, buf, PATH_MAX); - ret = 0; - break; - } - path = strtok_r(NULL, ":", &tmp); - } - - free(env); - return ret; -} - -static int search_program_and_warn(const char *def, const char *name, - char *output) -{ - int ret = search_program(def, name, output); - - if (ret) { - pr_err("ERROR:\tunable to find %s.\n" - "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" - " \tand '%s-path' option in [llvm] section of ~/.perfconfig.\n", - name, name); - version_notice(); - } - return ret; -} - -#define READ_SIZE 4096 -static int -read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) -{ - int err = 0; - void *buf = NULL; - FILE *file = NULL; - size_t read_sz = 0, buf_sz = 0; - char serr[STRERR_BUFSIZE]; - - file = popen(cmd, "r"); - if (!file) { - pr_err("ERROR: unable to popen cmd: %s\n", - str_error_r(errno, serr, sizeof(serr))); - return -EINVAL; - } - - while (!feof(file) && !ferror(file)) { - /* - * Make buf_sz always have obe byte extra space so we - * can put '\0' there. - */ - if (buf_sz - read_sz < READ_SIZE + 1) { - void *new_buf; - - buf_sz = read_sz + READ_SIZE + 1; - new_buf = realloc(buf, buf_sz); - - if (!new_buf) { - pr_err("ERROR: failed to realloc memory\n"); - err = -ENOMEM; - goto errout; - } - - buf = new_buf; - } - read_sz += fread(buf + read_sz, 1, READ_SIZE, file); - } - - if (buf_sz - read_sz < 1) { - pr_err("ERROR: internal error\n"); - err = -EINVAL; - goto errout; - } - - if (ferror(file)) { - pr_err("ERROR: error occurred when reading from pipe: %s\n", - str_error_r(errno, serr, sizeof(serr))); - err = -EIO; - goto errout; - } - - err = WEXITSTATUS(pclose(file)); - file = NULL; - if (err) { - err = -EINVAL; - goto errout; - } - - /* - * If buf is string, give it terminal '\0' to make our life - * easier. If buf is not string, that '\0' is out of space - * indicated by read_sz so caller won't even notice it. - */ - ((char *)buf)[read_sz] = '\0'; - - if (!p_buf) - free(buf); - else - *p_buf = buf; - - if (p_read_sz) - *p_read_sz = read_sz; - return 0; - -errout: - if (file) - pclose(file); - free(buf); - if (p_buf) - *p_buf = NULL; - if (p_read_sz) - *p_read_sz = 0; - return err; -} - -static inline void -force_set_env(const char *var, const char *value) -{ - if (value) { - setenv(var, value, 1); - pr_debug("set env: %s=%s\n", var, value); - } else { - unsetenv(var); - pr_debug("unset env: %s\n", var); - } -} - -static void -version_notice(void) -{ - pr_err( -" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" -" \tYou may want to try git trunk:\n" -" \t\tgit clone http://llvm.org/git/llvm.git\n" -" \t\t and\n" -" \t\tgit clone http://llvm.org/git/clang.git\n\n" -" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" -" \tdebian/ubuntu:\n" -" \t\thttps://apt.llvm.org/\n\n" -" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" -" \toption in [llvm] section of ~/.perfconfig to:\n\n" -" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS \\\n" -" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" -" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" -" \t(Replace /path/to/llc with path to your llc)\n\n" -); -} - -static int detect_kbuild_dir(char **kbuild_dir) -{ - const char *test_dir = llvm_param.kbuild_dir; - const char *prefix_dir = ""; - const char *suffix_dir = ""; - - /* _UTSNAME_LENGTH is 65 */ - char release[128]; - - char *autoconf_path; - - int err; - - if (!test_dir) { - err = fetch_kernel_version(NULL, release, - sizeof(release)); - if (err) - return -EINVAL; - - test_dir = release; - prefix_dir = "/lib/modules/"; - suffix_dir = "/build"; - } - - err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h", - prefix_dir, test_dir, suffix_dir); - if (err < 0) - return -ENOMEM; - - if (access(autoconf_path, R_OK) == 0) { - free(autoconf_path); - - err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir, - suffix_dir); - if (err < 0) - return -ENOMEM; - return 0; - } - pr_debug("%s: Couldn't find \"%s\", missing kernel-devel package?.\n", - __func__, autoconf_path); - free(autoconf_path); - return -ENOENT; -} - -static const char *kinc_fetch_script = -"#!/usr/bin/env sh\n" -"if ! test -d \"$KBUILD_DIR\"\n" -"then\n" -" exit 1\n" -"fi\n" -"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n" -"then\n" -" exit 1\n" -"fi\n" -"TMPDIR=`mktemp -d`\n" -"if test -z \"$TMPDIR\"\n" -"then\n" -" exit 1\n" -"fi\n" -"cat << EOF > $TMPDIR/Makefile\n" -"obj-y := dummy.o\n" -"\\$(obj)/%.o: \\$(src)/%.c\n" -"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n" -"\t\\$(CC) -c -o \\$@ \\$<\n" -"EOF\n" -"touch $TMPDIR/dummy.c\n" -"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n" -"RET=$?\n" -"rm -rf $TMPDIR\n" -"exit $RET\n"; - -void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) -{ - static char *saved_kbuild_dir; - static char *saved_kbuild_include_opts; - int err; - - if (!kbuild_dir || !kbuild_include_opts) - return; - - *kbuild_dir = NULL; - *kbuild_include_opts = NULL; - - if (saved_kbuild_dir && saved_kbuild_include_opts && - !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) { - *kbuild_dir = strdup(saved_kbuild_dir); - *kbuild_include_opts = strdup(saved_kbuild_include_opts); - - if (*kbuild_dir && *kbuild_include_opts) - return; - - zfree(kbuild_dir); - zfree(kbuild_include_opts); - /* - * Don't fall through: it may breaks saved_kbuild_dir and - * saved_kbuild_include_opts if detect them again when - * memory is low. - */ - return; - } - - if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { - pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); - pr_debug("Skip kbuild options detection.\n"); - goto errout; - } - - err = detect_kbuild_dir(kbuild_dir); - if (err) { - pr_warning( -"WARNING:\tunable to get correct kernel building directory.\n" -"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" -" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" -" \tdetection.\n\n"); - goto errout; - } - - pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); - force_set_env("KBUILD_DIR", *kbuild_dir); - force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts); - err = read_from_pipe(kinc_fetch_script, - (void **)kbuild_include_opts, - NULL); - if (err) { - pr_warning( -"WARNING:\tunable to get kernel include directories from '%s'\n" -"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n" -" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n" -" \toption in [llvm] to \"\" to suppress this detection.\n\n", - *kbuild_dir); - - zfree(kbuild_dir); - goto errout; - } - - pr_debug("include option is set to %s\n", *kbuild_include_opts); - - saved_kbuild_dir = strdup(*kbuild_dir); - saved_kbuild_include_opts = strdup(*kbuild_include_opts); - - if (!saved_kbuild_dir || !saved_kbuild_include_opts) { - zfree(&saved_kbuild_dir); - zfree(&saved_kbuild_include_opts); - } - return; -errout: - saved_kbuild_dir = ERR_PTR(-EINVAL); - saved_kbuild_include_opts = ERR_PTR(-EINVAL); -} - -int llvm__get_nr_cpus(void) -{ - static int nr_cpus_avail = 0; - char serr[STRERR_BUFSIZE]; - - if (nr_cpus_avail > 0) - return nr_cpus_avail; - - nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); - if (nr_cpus_avail <= 0) { - pr_err( -"WARNING:\tunable to get available CPUs in this system: %s\n" -" \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); - nr_cpus_avail = 128; - } - return nr_cpus_avail; -} - -void llvm__dump_obj(const char *path, void *obj_buf, size_t size) -{ - char *obj_path = strdup(path); - FILE *fp; - char *p; - - if (!obj_path) { - pr_warning("WARNING: Not enough memory, skip object dumping\n"); - return; - } - - p = strrchr(obj_path, '.'); - if (!p || (strcmp(p, ".c") != 0)) { - pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n", - obj_path); - goto out; - } - - p[1] = 'o'; - fp = fopen(obj_path, "wb"); - if (!fp) { - pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n", - obj_path, strerror(errno)); - goto out; - } - - pr_debug("LLVM: dumping %s\n", obj_path); - if (fwrite(obj_buf, size, 1, fp) != 1) - pr_debug("WARNING: failed to write to file '%s': %s, skip object dumping\n", obj_path, strerror(errno)); - fclose(fp); -out: - free(obj_path); -} - -int llvm__compile_bpf(const char *path, void **p_obj_buf, - size_t *p_obj_buf_sz) -{ - size_t obj_buf_sz; - void *obj_buf = NULL; - int err, nr_cpus_avail; - unsigned int kernel_version; - char linux_version_code_str[64]; - const char *clang_opt = llvm_param.clang_opt; - char clang_path[PATH_MAX], llc_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64]; - char serr[STRERR_BUFSIZE]; - char *kbuild_dir = NULL, *kbuild_include_opts = NULL, - *perf_bpf_include_opts = NULL; - const char *template = llvm_param.clang_bpf_cmd_template; - char *pipe_template = NULL; - const char *opts = llvm_param.opts; - char *command_echo = NULL, *command_out; - char *libbpf_include_dir = system_path(LIBBPF_INCLUDE_DIR); - - if (path[0] != '-' && realpath(path, abspath) == NULL) { - err = errno; - pr_err("ERROR: problems with path %s: %s\n", - path, str_error_r(err, serr, sizeof(serr))); - return -err; - } - - if (!template) - template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; - - err = search_program_and_warn(llvm_param.clang_path, - "clang", clang_path); - if (err) - return -ENOENT; - - /* - * This is an optional work. Even it fail we can continue our - * work. Needn't check error return. - */ - llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); - - nr_cpus_avail = llvm__get_nr_cpus(); - snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", - nr_cpus_avail); - - if (fetch_kernel_version(&kernel_version, NULL, 0)) - kernel_version = 0; - - snprintf(linux_version_code_str, sizeof(linux_version_code_str), - "0x%x", kernel_version); - if (asprintf(&perf_bpf_include_opts, "-I%s/", libbpf_include_dir) < 0) - goto errout; - force_set_env("NR_CPUS", nr_cpus_avail_str); - force_set_env("LINUX_VERSION_CODE", linux_version_code_str); - force_set_env("CLANG_EXEC", clang_path); - force_set_env("CLANG_OPTIONS", clang_opt); - force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); - force_set_env("PERF_BPF_INC_OPTIONS", perf_bpf_include_opts); - force_set_env("WORKING_DIR", kbuild_dir ? : "."); - - if (opts) { - err = search_program_and_warn(llvm_param.llc_path, "llc", llc_path); - if (err) - goto errout; - - err = -ENOMEM; - if (asprintf(&pipe_template, "%s -emit-llvm | %s -march=bpf %s -filetype=obj -o -", - template, llc_path, opts) < 0) { - pr_err("ERROR:\tnot enough memory to setup command line\n"); - goto errout; - } - - template = pipe_template; - - } - - /* - * Since we may reset clang's working dir, path of source file - * should be transferred into absolute path, except we want - * stdin to be source file (testing). - */ - force_set_env("CLANG_SOURCE", - (path[0] == '-') ? path : abspath); - - pr_debug("llvm compiling command template: %s\n", template); - - /* - * Below, substitute control characters for values that can cause the - * echo to misbehave, then substitute the values back. - */ - err = -ENOMEM; - if (asprintf(&command_echo, "echo -n \a%s\a", template) < 0) - goto errout; - -#define SWAP_CHAR(a, b) do { if (*p == a) *p = b; } while (0) - for (char *p = command_echo; *p; p++) { - SWAP_CHAR('<', '\001'); - SWAP_CHAR('>', '\002'); - SWAP_CHAR('"', '\003'); - SWAP_CHAR('\'', '\004'); - SWAP_CHAR('|', '\005'); - SWAP_CHAR('&', '\006'); - SWAP_CHAR('\a', '"'); - } - err = read_from_pipe(command_echo, (void **) &command_out, NULL); - if (err) - goto errout; - - for (char *p = command_out; *p; p++) { - SWAP_CHAR('\001', '<'); - SWAP_CHAR('\002', '>'); - SWAP_CHAR('\003', '"'); - SWAP_CHAR('\004', '\''); - SWAP_CHAR('\005', '|'); - SWAP_CHAR('\006', '&'); - } -#undef SWAP_CHAR - pr_debug("llvm compiling command : %s\n", command_out); - - err = read_from_pipe(template, &obj_buf, &obj_buf_sz); - if (err) { - pr_err("ERROR:\tunable to compile %s\n", path); - pr_err("Hint:\tCheck error message shown above.\n"); - pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); - pr_err(" \t\tclang --target=bpf -O2 -c %s\n", path); - pr_err(" \twith proper -I and -D options.\n"); - goto errout; - } - - free(command_echo); - free(command_out); - free(kbuild_dir); - free(kbuild_include_opts); - free(perf_bpf_include_opts); - free(libbpf_include_dir); - - if (!p_obj_buf) - free(obj_buf); - else - *p_obj_buf = obj_buf; - - if (p_obj_buf_sz) - *p_obj_buf_sz = obj_buf_sz; - return 0; -errout: - free(command_echo); - free(kbuild_dir); - free(kbuild_include_opts); - free(obj_buf); - free(perf_bpf_include_opts); - free(libbpf_include_dir); - free(pipe_template); - if (p_obj_buf) - *p_obj_buf = NULL; - if (p_obj_buf_sz) - *p_obj_buf_sz = 0; - return err; -} - -int llvm__search_clang(void) -{ - char clang_path[PATH_MAX]; - - return search_program_and_warn(llvm_param.clang_path, "clang", clang_path); -} diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h deleted file mode 100644 index 7878a0e3fa98..000000000000 --- a/tools/perf/util/llvm-utils.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> - * Copyright (C) 2015, Huawei Inc. - */ -#ifndef __LLVM_UTILS_H -#define __LLVM_UTILS_H - -#include <stdbool.h> - -struct llvm_param { - /* Path of clang executable */ - const char *clang_path; - /* Path of llc executable */ - const char *llc_path; - /* - * Template of clang bpf compiling. 5 env variables - * can be used: - * $CLANG_EXEC: Path to clang. - * $CLANG_OPTIONS: Extra options to clang. - * $KERNEL_INC_OPTIONS: Kernel include directories. - * $WORKING_DIR: Kernel source directory. - * $CLANG_SOURCE: Source file to be compiled. - */ - const char *clang_bpf_cmd_template; - /* Will be filled in $CLANG_OPTIONS */ - const char *clang_opt; - /* - * If present it'll add -emit-llvm to $CLANG_OPTIONS to pipe - * the clang output to llc, useful for new llvm options not - * yet selectable via 'clang -mllvm option', such as -mattr=dwarfris - * in clang 6.0/llvm 7 - */ - const char *opts; - /* Where to find kbuild system */ - const char *kbuild_dir; - /* - * Arguments passed to make, like 'ARCH=arm' if doing cross - * compiling. Should not be used for dynamic compiling. - */ - const char *kbuild_opts; - /* - * Default is false. If set to true, write compiling result - * to object file. - */ - bool dump_obj; - /* - * Default is false. If one of the above fields is set by user - * explicitly then user_set_llvm is set to true. This is used - * for perf test. If user doesn't set anything in .perfconfig - * and clang is not found, don't trigger llvm test. - */ - bool user_set_param; -}; - -extern struct llvm_param llvm_param; -int perf_llvm_config(const char *var, const char *value); - -int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz); - -/* This function is for test__llvm() use only */ -int llvm__search_clang(void); - -/* Following functions are reused by builtin clang support */ -void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts); -int llvm__get_nr_cpus(void); - -void llvm__dump_obj(const char *path, void *obj_buf, size_t size); -#endif diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c index 51424cdc3b68..af9a97612f9d 100644 --- a/tools/perf/util/lzma.c +++ b/tools/perf/util/lzma.c @@ -45,15 +45,13 @@ int lzma_decompress_to_file(const char *input, int output_fd) infile = fopen(input, "rb"); if (!infile) { - pr_err("lzma: fopen failed on %s: '%s'\n", - input, strerror(errno)); + pr_debug("lzma: fopen failed on %s: '%s'\n", input, strerror(errno)); return -1; } ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); if (ret != LZMA_OK) { - pr_err("lzma: lzma_stream_decoder failed %s (%d)\n", - lzma_strerror(ret), ret); + pr_debug("lzma: lzma_stream_decoder failed %s (%d)\n", lzma_strerror(ret), ret); goto err_fclose; } @@ -68,7 +66,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile); if (ferror(infile)) { - pr_err("lzma: read error: %s\n", strerror(errno)); + pr_debug("lzma: read error: %s\n", strerror(errno)); goto err_lzma_end; } @@ -82,7 +80,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) ssize_t write_size = sizeof(buf_out) - strm.avail_out; if (writen(output_fd, buf_out, write_size) != write_size) { - pr_err("lzma: write error: %s\n", strerror(errno)); + pr_debug("lzma: write error: %s\n", strerror(errno)); goto err_lzma_end; } @@ -94,7 +92,7 @@ int lzma_decompress_to_file(const char *input, int output_fd) if (ret == LZMA_STREAM_END) break; - pr_err("lzma: failed %s\n", lzma_strerror(ret)); + pr_debug("lzma: failed %s\n", lzma_strerror(ret)); goto err_lzma_end; } } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index f4cb41ee23cd..88f31b3a63ac 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1215,7 +1215,9 @@ static int machine__get_running_kernel_start(struct machine *machine, *start = addr; - err = kallsyms__get_function_start(filename, "_etext", &addr); + err = kallsyms__get_symbol_start(filename, "_edata", &addr); + if (err) + err = kallsyms__get_function_start(filename, "_etext", &addr); if (!err) *end = addr; diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index c07fe3a90722..39ffe8ceb380 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -37,7 +37,7 @@ struct perf_mem_event * __weak perf_mem_events__ptr(int i) return &perf_mem_events[i]; } -char * __weak perf_mem_events__name(int i, char *pmu_name __maybe_unused) +const char * __weak perf_mem_events__name(int i, const char *pmu_name __maybe_unused) { struct perf_mem_event *e = perf_mem_events__ptr(i); @@ -53,7 +53,7 @@ char * __weak perf_mem_events__name(int i, char *pmu_name __maybe_unused) return mem_loads_name; } - return (char *)e->name; + return e->name; } __weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused) @@ -186,7 +186,6 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, int i = *argv_nr, k = 0; struct perf_mem_event *e; struct perf_pmu *pmu; - char *s; for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) { e = perf_mem_events__ptr(j); @@ -209,15 +208,16 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr, } while ((pmu = perf_pmus__scan(pmu)) != NULL) { + const char *s = perf_mem_events__name(j, pmu->name); + rec_argv[i++] = "-e"; - s = perf_mem_events__name(j, pmu->name); if (s) { - s = strdup(s); - if (!s) + char *copy = strdup(s); + if (!copy) return -1; - rec_argv[i++] = s; - rec_tmp[k++] = s; + rec_argv[i++] = copy; + rec_tmp[k++] = copy; } } } diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index 12372309d60e..b40ad6ea93fc 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -38,7 +38,7 @@ extern unsigned int perf_mem_events__loads_ldlat; int perf_mem_events__parse(const char *str); int perf_mem_events__init(void); -char *perf_mem_events__name(int i, char *pmu_name); +const char *perf_mem_events__name(int i, const char *pmu_name); struct perf_mem_event *perf_mem_events__ptr(int i); bool is_mem_loads_aux_event(struct evsel *leader); diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index a6a5ed44a679..6231044a491e 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -527,7 +527,7 @@ void metricgroup__print(const struct print_callbacks *print_cb, void *print_stat groups.node_delete = mep_delete; table = pmu_metrics_table__find(); if (table) { - pmu_metrics_table_for_each_metric(table, + pmu_metrics_table__for_each_metric(table, metricgroup__add_to_mep_groups_callback, &groups); } @@ -1069,7 +1069,7 @@ static bool metricgroup__find_metric(const char *pmu, .pm = pm, }; - return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data) + return pmu_metrics_table__for_each_metric(table, metricgroup__find_metric_callback, &data) ? true : false; } @@ -1255,7 +1255,7 @@ static int metricgroup__add_metric(const char *pmu, const char *metric_name, con * Iterate over all metrics seeing if metric matches either the * name or group. When it does add the metric to the list. */ - ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback, + ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback, &data); if (ret) goto out; @@ -1740,7 +1740,7 @@ bool metricgroup__has_metric(const char *pmu, const char *metric) if (!table) return false; - return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, &data) + return pmu_metrics_table__for_each_metric(table, metricgroup__has_metric_callback, &data) ? true : false; } @@ -1770,7 +1770,7 @@ unsigned int metricgroups__topdown_max_level(void) if (!table) return false; - pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback, + pmu_metrics_table__for_each_metric(table, metricgroup__topdown_max_level_callback, &max_level); return max_level; } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c9ec0cafb69d..65608a3cba81 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -13,13 +13,12 @@ #include <subcmd/parse-options.h> #include "parse-events.h" #include "string2.h" -#include "strlist.h" -#include "bpf-loader.h" +#include "strbuf.h" #include "debug.h" #include <api/fs/tracing_path.h> #include <perf/cpumap.h> -#include "parse-events-bison.h" -#include "parse-events-flex.h" +#include <util/parse-events-bison.h> +#include <util/parse-events-flex.h> #include "pmu.h" #include "pmus.h" #include "asm/bug.h" @@ -35,7 +34,6 @@ #ifdef PARSER_DEBUG extern int parse_events_debug; #endif -int parse_events_parse(void *parse_state, void *scanner); static int get_config_terms(struct list_head *head_config, struct list_head *head_terms __maybe_unused); @@ -155,7 +153,7 @@ const char *event_type(int type) return "unknown"; } -static char *get_config_str(struct list_head *head_terms, int type_term) +static char *get_config_str(struct list_head *head_terms, enum parse_events__term_type type_term) { struct parse_events_term *term; @@ -195,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu) struct parse_events_term *term; list_for_each_entry(term, config_terms, list) { - struct perf_pmu_alias *alias; - bool matched = false; + u64 num; if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW) continue; - list_for_each_entry(alias, &pmu->aliases, list) { - if (!strcmp(alias->name, term->val.str)) { - free(term->config); - term->config = term->val.str; - term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; - term->type_term = PARSE_EVENTS__TERM_TYPE_USER; - term->val.num = 1; - term->no_value = true; - matched = true; - break; - } - } - if (!matched) { - u64 num; - - free(term->config); - term->config = strdup("config"); - errno = 0; - num = strtoull(term->val.str + 1, NULL, 16); - assert(errno == 0); - free(term->val.str); + if (perf_pmu__have_event(pmu, term->val.str)) { + zfree(&term->config); + term->config = term->val.str; term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; - term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG; - term->val.num = num; - term->no_value = false; + term->type_term = PARSE_EVENTS__TERM_TYPE_USER; + term->val.num = 1; + term->no_value = true; + continue; } + + zfree(&term->config); + term->config = strdup("config"); + errno = 0; + num = strtoull(term->val.str + 1, NULL, 16); + assert(errno == 0); + free(term->val.str); + term->type_val = PARSE_EVENTS__TERM_TYPE_NUM; + term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG; + term->val.num = num; + term->no_value = false; } } @@ -271,7 +262,7 @@ __add_event(struct list_head *list, int *idx, evsel->core.is_pmu_core = pmu ? pmu->is_core : false; evsel->auto_merge_stats = auto_merge_stats; evsel->pmu = pmu; - evsel->pmu_name = pmu && pmu->name ? strdup(pmu->name) : NULL; + evsel->pmu_name = pmu ? strdup(pmu->name) : NULL; if (name) evsel->name = strdup(name); @@ -446,9 +437,6 @@ bool parse_events__filter_pmu(const struct parse_events_state *parse_state, if (parse_state->pmu_filter == NULL) return false; - if (pmu->name == NULL) - return true; - return strcmp(parse_state->pmu_filter, pmu->name) != 0; } @@ -499,7 +487,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name, #ifdef HAVE_LIBTRACEEVENT static void tracepoint_error(struct parse_events_error *e, int err, - const char *sys, const char *name) + const char *sys, const char *name, int column) { const char *str; char help[BUFSIZ]; @@ -526,18 +514,19 @@ static void tracepoint_error(struct parse_events_error *e, int err, } tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name); - parse_events_error__handle(e, 0, strdup(str), strdup(help)); + parse_events_error__handle(e, column, strdup(str), strdup(help)); } static int add_tracepoint(struct list_head *list, int *idx, const char *sys_name, const char *evt_name, struct parse_events_error *err, - struct list_head *head_config) + struct list_head *head_config, void *loc_) { + YYLTYPE *loc = loc_; struct evsel *evsel = evsel__newtp_idx(sys_name, evt_name, (*idx)++); if (IS_ERR(evsel)) { - tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name); + tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name, loc->first_column); return PTR_ERR(evsel); } @@ -556,7 +545,7 @@ static int add_tracepoint(struct list_head *list, int *idx, static int add_tracepoint_multi_event(struct list_head *list, int *idx, const char *sys_name, const char *evt_name, struct parse_events_error *err, - struct list_head *head_config) + struct list_head *head_config, YYLTYPE *loc) { char *evt_path; struct dirent *evt_ent; @@ -565,13 +554,13 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, evt_path = get_events_file(sys_name); if (!evt_path) { - tracepoint_error(err, errno, sys_name, evt_name); + tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); return -1; } evt_dir = opendir(evt_path); if (!evt_dir) { put_events_file(evt_path); - tracepoint_error(err, errno, sys_name, evt_name); + tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); return -1; } @@ -588,11 +577,11 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, found++; ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name, - err, head_config); + err, head_config, loc); } if (!found) { - tracepoint_error(err, ENOENT, sys_name, evt_name); + tracepoint_error(err, ENOENT, sys_name, evt_name, loc->first_column); ret = -1; } @@ -604,19 +593,19 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, static int add_tracepoint_event(struct list_head *list, int *idx, const char *sys_name, const char *evt_name, struct parse_events_error *err, - struct list_head *head_config) + struct list_head *head_config, YYLTYPE *loc) { return strpbrk(evt_name, "*?") ? - add_tracepoint_multi_event(list, idx, sys_name, evt_name, - err, head_config) : - add_tracepoint(list, idx, sys_name, evt_name, - err, head_config); + add_tracepoint_multi_event(list, idx, sys_name, evt_name, + err, head_config, loc) : + add_tracepoint(list, idx, sys_name, evt_name, + err, head_config, loc); } static int add_tracepoint_multi_sys(struct list_head *list, int *idx, const char *sys_name, const char *evt_name, struct parse_events_error *err, - struct list_head *head_config) + struct list_head *head_config, YYLTYPE *loc) { struct dirent *events_ent; DIR *events_dir; @@ -624,7 +613,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, events_dir = tracing_events__opendir(); if (!events_dir) { - tracepoint_error(err, errno, sys_name, evt_name); + tracepoint_error(err, errno, sys_name, evt_name, loc->first_column); return -1; } @@ -640,7 +629,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, continue; ret = add_tracepoint_event(list, idx, events_ent->d_name, - evt_name, err, head_config); + evt_name, err, head_config, loc); } closedir(events_dir); @@ -648,264 +637,6 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, } #endif /* HAVE_LIBTRACEEVENT */ -#ifdef HAVE_LIBBPF_SUPPORT -struct __add_bpf_event_param { - struct parse_events_state *parse_state; - struct list_head *list; - struct list_head *head_config; -}; - -static int add_bpf_event(const char *group, const char *event, int fd, struct bpf_object *obj, - void *_param) -{ - LIST_HEAD(new_evsels); - struct __add_bpf_event_param *param = _param; - struct parse_events_state *parse_state = param->parse_state; - struct list_head *list = param->list; - struct evsel *pos; - int err; - /* - * Check if we should add the event, i.e. if it is a TP but starts with a '!', - * then don't add the tracepoint, this will be used for something else, like - * adding to a BPF_MAP_TYPE_PROG_ARRAY. - * - * See tools/perf/examples/bpf/augmented_raw_syscalls.c - */ - if (group[0] == '!') - return 0; - - pr_debug("add bpf event %s:%s and attach bpf program %d\n", - group, event, fd); - - err = parse_events_add_tracepoint(&new_evsels, &parse_state->idx, group, - event, parse_state->error, - param->head_config); - if (err) { - struct evsel *evsel, *tmp; - - pr_debug("Failed to add BPF event %s:%s\n", - group, event); - list_for_each_entry_safe(evsel, tmp, &new_evsels, core.node) { - list_del_init(&evsel->core.node); - evsel__delete(evsel); - } - return err; - } - pr_debug("adding %s:%s\n", group, event); - - list_for_each_entry(pos, &new_evsels, core.node) { - pr_debug("adding %s:%s to %p\n", - group, event, pos); - pos->bpf_fd = fd; - pos->bpf_obj = obj; - } - list_splice(&new_evsels, list); - return 0; -} - -int parse_events_load_bpf_obj(struct parse_events_state *parse_state, - struct list_head *list, - struct bpf_object *obj, - struct list_head *head_config) -{ - int err; - char errbuf[BUFSIZ]; - struct __add_bpf_event_param param = {parse_state, list, head_config}; - static bool registered_unprobe_atexit = false; - - if (IS_ERR(obj) || !obj) { - snprintf(errbuf, sizeof(errbuf), - "Internal error: load bpf obj with NULL"); - err = -EINVAL; - goto errout; - } - - /* - * Register atexit handler before calling bpf__probe() so - * bpf__probe() don't need to unprobe probe points its already - * created when failure. - */ - if (!registered_unprobe_atexit) { - atexit(bpf__clear); - registered_unprobe_atexit = true; - } - - err = bpf__probe(obj); - if (err) { - bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf)); - goto errout; - } - - err = bpf__load(obj); - if (err) { - bpf__strerror_load(obj, err, errbuf, sizeof(errbuf)); - goto errout; - } - - err = bpf__foreach_event(obj, add_bpf_event, ¶m); - if (err) { - snprintf(errbuf, sizeof(errbuf), - "Attach events in BPF object failed"); - goto errout; - } - - return 0; -errout: - parse_events_error__handle(parse_state->error, 0, - strdup(errbuf), strdup("(add -v to see detail)")); - return err; -} - -static int -parse_events_config_bpf(struct parse_events_state *parse_state, - struct bpf_object *obj, - struct list_head *head_config) -{ - struct parse_events_term *term; - int error_pos; - - if (!head_config || list_empty(head_config)) - return 0; - - list_for_each_entry(term, head_config, list) { - int err; - - if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) { - parse_events_error__handle(parse_state->error, term->err_term, - strdup("Invalid config term for BPF object"), - NULL); - return -EINVAL; - } - - err = bpf__config_obj(obj, term, parse_state->evlist, &error_pos); - if (err) { - char errbuf[BUFSIZ]; - int idx; - - bpf__strerror_config_obj(obj, term, parse_state->evlist, - &error_pos, err, errbuf, - sizeof(errbuf)); - - if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE) - idx = term->err_val; - else - idx = term->err_term + error_pos; - - parse_events_error__handle(parse_state->error, idx, - strdup(errbuf), - strdup( -"Hint:\tValid config terms:\n" -" \tmap:[<arraymap>].value<indices>=[value]\n" -" \tmap:[<eventmap>].event<indices>=[event]\n" -"\n" -" \twhere <indices> is something like [0,3...5] or [all]\n" -" \t(add -v to see detail)")); - return err; - } - } - return 0; -} - -/* - * Split config terms: - * perf record -e bpf.c/call-graph=fp,map:array.value[0]=1/ ... - * 'call-graph=fp' is 'evt config', should be applied to each - * events in bpf.c. - * 'map:array.value[0]=1' is 'obj config', should be processed - * with parse_events_config_bpf. - * - * Move object config terms from the first list to obj_head_config. - */ -static void -split_bpf_config_terms(struct list_head *evt_head_config, - struct list_head *obj_head_config) -{ - struct parse_events_term *term, *temp; - - /* - * Currently, all possible user config term - * belong to bpf object. parse_events__is_hardcoded_term() - * happens to be a good flag. - * - * See parse_events_config_bpf() and - * config_term_tracepoint(). - */ - list_for_each_entry_safe(term, temp, evt_head_config, list) - if (!parse_events__is_hardcoded_term(term)) - list_move_tail(&term->list, obj_head_config); -} - -int parse_events_load_bpf(struct parse_events_state *parse_state, - struct list_head *list, - char *bpf_file_name, - bool source, - struct list_head *head_config) -{ - int err; - struct bpf_object *obj; - LIST_HEAD(obj_head_config); - - if (head_config) - split_bpf_config_terms(head_config, &obj_head_config); - - obj = bpf__prepare_load(bpf_file_name, source); - if (IS_ERR(obj)) { - char errbuf[BUFSIZ]; - - err = PTR_ERR(obj); - - if (err == -ENOTSUP) - snprintf(errbuf, sizeof(errbuf), - "BPF support is not compiled"); - else - bpf__strerror_prepare_load(bpf_file_name, - source, - -err, errbuf, - sizeof(errbuf)); - - parse_events_error__handle(parse_state->error, 0, - strdup(errbuf), strdup("(add -v to see detail)")); - return err; - } - - err = parse_events_load_bpf_obj(parse_state, list, obj, head_config); - if (err) - return err; - err = parse_events_config_bpf(parse_state, obj, &obj_head_config); - - /* - * Caller doesn't know anything about obj_head_config, - * so combine them together again before returning. - */ - if (head_config) - list_splice_tail(&obj_head_config, head_config); - return err; -} -#else // HAVE_LIBBPF_SUPPORT -int parse_events_load_bpf_obj(struct parse_events_state *parse_state, - struct list_head *list __maybe_unused, - struct bpf_object *obj __maybe_unused, - struct list_head *head_config __maybe_unused) -{ - parse_events_error__handle(parse_state->error, 0, - strdup("BPF support is not compiled"), - strdup("Make sure libbpf-devel is available at build time.")); - return -ENOTSUP; -} - -int parse_events_load_bpf(struct parse_events_state *parse_state, - struct list_head *list __maybe_unused, - char *bpf_file_name __maybe_unused, - bool source __maybe_unused, - struct list_head *head_config __maybe_unused) -{ - parse_events_error__handle(parse_state->error, 0, - strdup("BPF support is not compiled"), - strdup("Make sure libbpf-devel is available at build time.")); - return -ENOTSUP; -} -#endif // HAVE_LIBBPF_SUPPORT - static int parse_breakpoint_type(const char *type, struct perf_event_attr *attr) { @@ -991,7 +722,7 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state, static int check_type_val(struct parse_events_term *term, struct parse_events_error *err, - int type) + enum parse_events__term_val_type type) { if (type == term->type_val) return 0; @@ -1006,42 +737,49 @@ static int check_type_val(struct parse_events_term *term, return -EINVAL; } -/* - * Update according to parse-events.l - */ -static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { - [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>", - [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config", - [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1", - [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2", - [PARSE_EVENTS__TERM_TYPE_CONFIG3] = "config3", - [PARSE_EVENTS__TERM_TYPE_NAME] = "name", - [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period", - [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq", - [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type", - [PARSE_EVENTS__TERM_TYPE_TIME] = "time", - [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph", - [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size", - [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", - [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", - [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", - [PARSE_EVENTS__TERM_TYPE_MAX_EVENTS] = "nr", - [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite", - [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite", - [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config", - [PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore", - [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output", - [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size", - [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", - [PARSE_EVENTS__TERM_TYPE_RAW] = "raw", - [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache", - [PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware", -}; - static bool config_term_shrinked; +static const char *config_term_name(enum parse_events__term_type term_type) +{ + /* + * Update according to parse-events.l + */ + static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { + [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>", + [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config", + [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1", + [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2", + [PARSE_EVENTS__TERM_TYPE_CONFIG3] = "config3", + [PARSE_EVENTS__TERM_TYPE_NAME] = "name", + [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period", + [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq", + [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type", + [PARSE_EVENTS__TERM_TYPE_TIME] = "time", + [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph", + [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size", + [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", + [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", + [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", + [PARSE_EVENTS__TERM_TYPE_MAX_EVENTS] = "nr", + [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite", + [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite", + [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config", + [PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore", + [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output", + [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size", + [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", + [PARSE_EVENTS__TERM_TYPE_RAW] = "raw", + [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache", + [PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware", + }; + if ((unsigned int)term_type >= __PARSE_EVENTS__TERM_TYPE_NR) + return "unknown term"; + + return config_term_names[term_type]; +} + static bool -config_term_avail(int term_type, struct parse_events_error *err) +config_term_avail(enum parse_events__term_type term_type, struct parse_events_error *err) { char *err_str; @@ -1063,13 +801,31 @@ config_term_avail(int term_type, struct parse_events_error *err) case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: case PARSE_EVENTS__TERM_TYPE_PERCORE: return true; + case PARSE_EVENTS__TERM_TYPE_USER: + case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ: + case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + case PARSE_EVENTS__TERM_TYPE_TIME: + case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: + case PARSE_EVENTS__TERM_TYPE_STACKSIZE: + case PARSE_EVENTS__TERM_TYPE_NOINHERIT: + case PARSE_EVENTS__TERM_TYPE_INHERIT: + case PARSE_EVENTS__TERM_TYPE_MAX_STACK: + case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: + case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE: + case PARSE_EVENTS__TERM_TYPE_OVERWRITE: + case PARSE_EVENTS__TERM_TYPE_DRV_CFG: + case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT: + case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: + case PARSE_EVENTS__TERM_TYPE_RAW: + case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: + case PARSE_EVENTS__TERM_TYPE_HARDWARE: default: if (!err) return false; /* term_type is validated so indexing is safe */ if (asprintf(&err_str, "'%s' is not usable in 'perf stat'", - config_term_names[term_type]) >= 0) + config_term_name(term_type)) >= 0) parse_events_error__handle(err, -1, err_str, NULL); return false; } @@ -1187,10 +943,14 @@ do { \ return -EINVAL; } break; + case PARSE_EVENTS__TERM_TYPE_DRV_CFG: + case PARSE_EVENTS__TERM_TYPE_USER: + case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: + case PARSE_EVENTS__TERM_TYPE_HARDWARE: default: parse_events_error__handle(err, term->err_term, - strdup("unknown term"), - parse_events_formats_error_string(NULL)); + strdup(config_term_name(term->type_term)), + parse_events_formats_error_string(NULL)); return -EINVAL; } @@ -1276,10 +1036,26 @@ static int config_term_tracepoint(struct perf_event_attr *attr, case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT: case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: return config_term_common(attr, term, err); + case PARSE_EVENTS__TERM_TYPE_USER: + case PARSE_EVENTS__TERM_TYPE_CONFIG: + case PARSE_EVENTS__TERM_TYPE_CONFIG1: + case PARSE_EVENTS__TERM_TYPE_CONFIG2: + case PARSE_EVENTS__TERM_TYPE_CONFIG3: + case PARSE_EVENTS__TERM_TYPE_NAME: + case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: + case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ: + case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + case PARSE_EVENTS__TERM_TYPE_TIME: + case PARSE_EVENTS__TERM_TYPE_DRV_CFG: + case PARSE_EVENTS__TERM_TYPE_PERCORE: + case PARSE_EVENTS__TERM_TYPE_METRIC_ID: + case PARSE_EVENTS__TERM_TYPE_RAW: + case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: + case PARSE_EVENTS__TERM_TYPE_HARDWARE: default: if (err) { parse_events_error__handle(err, term->err_term, - strdup("unknown term"), + strdup(config_term_name(term->type_term)), strdup("valid terms: call-graph,stack-size\n")); } return -EINVAL; @@ -1397,6 +1173,16 @@ do { \ ADD_CONFIG_TERM_VAL(AUX_SAMPLE_SIZE, aux_sample_size, term->val.num, term->weak); break; + case PARSE_EVENTS__TERM_TYPE_USER: + case PARSE_EVENTS__TERM_TYPE_CONFIG: + case PARSE_EVENTS__TERM_TYPE_CONFIG1: + case PARSE_EVENTS__TERM_TYPE_CONFIG2: + case PARSE_EVENTS__TERM_TYPE_CONFIG3: + case PARSE_EVENTS__TERM_TYPE_NAME: + case PARSE_EVENTS__TERM_TYPE_METRIC_ID: + case PARSE_EVENTS__TERM_TYPE_RAW: + case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: + case PARSE_EVENTS__TERM_TYPE_HARDWARE: default: break; } @@ -1418,14 +1204,38 @@ static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config, list_for_each_entry(term, head_config, list) { switch (term->type_term) { case PARSE_EVENTS__TERM_TYPE_USER: - type = perf_pmu__format_type(&pmu->format, term->config); + type = perf_pmu__format_type(pmu, term->config); if (type != PERF_PMU_FORMAT_VALUE_CONFIG) continue; - bits |= perf_pmu__format_bits(&pmu->format, term->config); + bits |= perf_pmu__format_bits(pmu, term->config); break; case PARSE_EVENTS__TERM_TYPE_CONFIG: bits = ~(u64)0; break; + case PARSE_EVENTS__TERM_TYPE_CONFIG1: + case PARSE_EVENTS__TERM_TYPE_CONFIG2: + case PARSE_EVENTS__TERM_TYPE_CONFIG3: + case PARSE_EVENTS__TERM_TYPE_NAME: + case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: + case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ: + case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + case PARSE_EVENTS__TERM_TYPE_TIME: + case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: + case PARSE_EVENTS__TERM_TYPE_STACKSIZE: + case PARSE_EVENTS__TERM_TYPE_NOINHERIT: + case PARSE_EVENTS__TERM_TYPE_INHERIT: + case PARSE_EVENTS__TERM_TYPE_MAX_STACK: + case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: + case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE: + case PARSE_EVENTS__TERM_TYPE_OVERWRITE: + case PARSE_EVENTS__TERM_TYPE_DRV_CFG: + case PARSE_EVENTS__TERM_TYPE_PERCORE: + case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT: + case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: + case PARSE_EVENTS__TERM_TYPE_METRIC_ID: + case PARSE_EVENTS__TERM_TYPE_RAW: + case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE: + case PARSE_EVENTS__TERM_TYPE_HARDWARE: default: break; } @@ -1441,8 +1251,9 @@ static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config, int parse_events_add_tracepoint(struct list_head *list, int *idx, const char *sys, const char *event, struct parse_events_error *err, - struct list_head *head_config) + struct list_head *head_config, void *loc_) { + YYLTYPE *loc = loc_; #ifdef HAVE_LIBTRACEEVENT if (head_config) { struct perf_event_attr attr; @@ -1454,17 +1265,17 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx, if (strpbrk(sys, "*?")) return add_tracepoint_multi_sys(list, idx, sys, event, - err, head_config); + err, head_config, loc); else return add_tracepoint_event(list, idx, sys, event, - err, head_config); + err, head_config, loc); #else (void)list; (void)idx; (void)sys; (void)event; (void)head_config; - parse_events_error__handle(err, 0, strdup("unsupported tracepoint"), + parse_events_error__handle(err, loc->first_column, strdup("unsupported tracepoint"), strdup("libtraceevent is necessary for tracepoint support")); return -1; #endif @@ -1557,41 +1368,44 @@ static bool config_term_percore(struct list_head *config_terms) } int parse_events_add_pmu(struct parse_events_state *parse_state, - struct list_head *list, char *name, + struct list_head *list, const char *name, struct list_head *head_config, - bool auto_merge_stats) + bool auto_merge_stats, void *loc_) { struct perf_event_attr attr; struct perf_pmu_info info; struct perf_pmu *pmu; struct evsel *evsel; struct parse_events_error *err = parse_state->error; + YYLTYPE *loc = loc_; LIST_HEAD(config_terms); pmu = parse_state->fake_pmu ?: perf_pmus__find(name); - if (verbose > 1 && !(pmu && pmu->selectable)) { - fprintf(stderr, "Attempting to add event pmu '%s' with '", - name); - if (head_config) { - struct parse_events_term *term; - - list_for_each_entry(term, head_config, list) { - fprintf(stderr, "%s,", term->config); - } - } - fprintf(stderr, "' that may result in non-fatal errors\n"); - } - if (!pmu) { char *err_str; if (asprintf(&err_str, "Cannot find PMU `%s'. Missing kernel support?", name) >= 0) - parse_events_error__handle(err, 0, err_str, NULL); + parse_events_error__handle(err, loc->first_column, err_str, NULL); return -EINVAL; } + + if (verbose > 1) { + struct strbuf sb; + + strbuf_init(&sb, /*hint=*/ 0); + if (pmu->selectable && !head_config) { + strbuf_addf(&sb, "%s//", name); + } else { + strbuf_addf(&sb, "%s/", name); + parse_events_term__to_strbuf(head_config, &sb); + strbuf_addch(&sb, '/'); + } + fprintf(stderr, "Attempt to add: %s\n", sb.buf); + strbuf_release(&sb); + } if (head_config) fix_raw(head_config, pmu); @@ -1612,20 +1426,16 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, return evsel ? 0 : -ENOMEM; } - if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, head_config, &info)) + if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, head_config, &info, err)) return -EINVAL; if (verbose > 1) { - fprintf(stderr, "After aliases, add event pmu '%s' with '", - name); - if (head_config) { - struct parse_events_term *term; + struct strbuf sb; - list_for_each_entry(term, head_config, list) { - fprintf(stderr, "%s,", term->config); - } - } - fprintf(stderr, "' that may result in non-fatal errors\n"); + strbuf_init(&sb, /*hint=*/ 0); + parse_events_term__to_strbuf(head_config, &sb); + fprintf(stderr, "..after resolving event: %s/%s/\n", name, sb.buf); + strbuf_release(&sb); } /* @@ -1675,14 +1485,15 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, int parse_events_multi_pmu_add(struct parse_events_state *parse_state, char *str, struct list_head *head, - struct list_head **listp) + struct list_head **listp, void *loc_) { struct parse_events_term *term; struct list_head *list = NULL; struct list_head *orig_head = NULL; struct perf_pmu *pmu = NULL; + YYLTYPE *loc = loc_; int ok = 0; - char *config; + const char *config; *listp = NULL; @@ -1699,9 +1510,9 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - config, 1, false, NULL, - NULL) < 0) { - free(config); + config, /*num=*/1, /*novalue=*/true, + loc, /*loc_val=*/NULL) < 0) { + zfree(&config); goto out_err; } list_add_tail(&term->list, head); @@ -1714,33 +1525,38 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, INIT_LIST_HEAD(list); while ((pmu = perf_pmus__scan(pmu)) != NULL) { - struct perf_pmu_alias *alias; bool auto_merge_stats; if (parse_events__filter_pmu(parse_state, pmu)) continue; - auto_merge_stats = perf_pmu__auto_merge_stats(pmu); + if (!perf_pmu__have_event(pmu, str)) + continue; - list_for_each_entry(alias, &pmu->aliases, list) { - if (!strcasecmp(alias->name, str)) { - parse_events_copy_term_list(head, &orig_head); - if (!parse_events_add_pmu(parse_state, list, - pmu->name, orig_head, - auto_merge_stats)) { - pr_debug("%s -> %s/%s/\n", str, - pmu->name, alias->str); - ok++; - } - parse_events_terms__delete(orig_head); - } + auto_merge_stats = perf_pmu__auto_merge_stats(pmu); + parse_events_copy_term_list(head, &orig_head); + if (!parse_events_add_pmu(parse_state, list, pmu->name, + orig_head, auto_merge_stats, loc)) { + struct strbuf sb; + + strbuf_init(&sb, /*hint=*/ 0); + parse_events_term__to_strbuf(orig_head, &sb); + pr_debug("%s -> %s/%s/\n", str, pmu->name, sb.buf); + strbuf_release(&sb); + ok++; } + parse_events_terms__delete(orig_head); } if (parse_state->fake_pmu) { if (!parse_events_add_pmu(parse_state, list, str, head, - /*auto_merge_stats=*/true)) { - pr_debug("%s -> %s/%s/\n", str, "fake_pmu", str); + /*auto_merge_stats=*/true, loc)) { + struct strbuf sb; + + strbuf_init(&sb, /*hint=*/ 0); + parse_events_term__to_strbuf(head, &sb); + pr_debug("%s -> %s/%s/\n", str, "fake_pmu", sb.buf); + strbuf_release(&sb); ok++; } } @@ -1972,14 +1788,18 @@ int parse_events_name(struct list_head *list, const char *name) struct evsel *evsel; __evlist__for_each_entry(list, evsel) { - if (!evsel->name) + if (!evsel->name) { evsel->name = strdup(name); + if (!evsel->name) + return -ENOMEM; + } } return 0; } static int parse_events__scanner(const char *str, + FILE *input, struct parse_events_state *parse_state) { YY_BUFFER_STATE buffer; @@ -1990,7 +1810,10 @@ static int parse_events__scanner(const char *str, if (ret) return ret; - buffer = parse_events__scan_string(str, scanner); + if (str) + buffer = parse_events__scan_string(str, scanner); + else + parse_events_set_in(input, scanner); #ifdef PARSER_DEBUG parse_events_debug = 1; @@ -1998,8 +1821,10 @@ static int parse_events__scanner(const char *str, #endif ret = parse_events_parse(parse_state, scanner); - parse_events__flush_buffer(buffer, scanner); - parse_events__delete_buffer(buffer, scanner); + if (str) { + parse_events__flush_buffer(buffer, scanner); + parse_events__delete_buffer(buffer, scanner); + } parse_events_lex_destroy(scanner); return ret; } @@ -2007,7 +1832,7 @@ static int parse_events__scanner(const char *str, /* * parse event config string, return a list of event terms. */ -int parse_events_terms(struct list_head *terms, const char *str) +int parse_events_terms(struct list_head *terms, const char *str, FILE *input) { struct parse_events_state parse_state = { .terms = NULL, @@ -2015,7 +1840,7 @@ int parse_events_terms(struct list_head *terms, const char *str) }; int ret; - ret = parse_events__scanner(str, &parse_state); + ret = parse_events__scanner(str, input, &parse_state); if (!ret) { list_splice(parse_state.terms, terms); @@ -2259,7 +2084,6 @@ int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filte .list = LIST_HEAD_INIT(parse_state.list), .idx = evlist->core.nr_entries, .error = err, - .evlist = evlist, .stoken = PE_START_EVENTS, .fake_pmu = fake_pmu, .pmu_filter = pmu_filter, @@ -2267,7 +2091,7 @@ int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filte }; int ret, ret2; - ret = parse_events__scanner(str, &parse_state); + ret = parse_events__scanner(str, /*input=*/ NULL, &parse_state); if (!ret && list_empty(&parse_state.list)) { WARN_ONCE(true, "WARNING: event parser found nothing\n"); @@ -2348,7 +2172,7 @@ void parse_events_error__handle(struct parse_events_error *err, int idx, break; default: pr_debug("Multiple errors dropping message: %s (%s)\n", - err->str, err->help); + err->str, err->help ?: "<no help>"); free(err->str); err->str = str; free(err->help); @@ -2641,7 +2465,8 @@ static int new_term(struct parse_events_term **_term, } int parse_events_term__num(struct parse_events_term **term, - int type_term, char *config, u64 num, + enum parse_events__term_type type_term, + const char *config, u64 num, bool no_value, void *loc_term_, void *loc_val_) { @@ -2651,17 +2476,18 @@ int parse_events_term__num(struct parse_events_term **term, struct parse_events_term temp = { .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = type_term, - .config = config ? : strdup(config_term_names[type_term]), + .config = config ? : strdup(config_term_name(type_term)), .no_value = no_value, .err_term = loc_term ? loc_term->first_column : 0, .err_val = loc_val ? loc_val->first_column : 0, }; - return new_term(term, &temp, NULL, num); + return new_term(term, &temp, /*str=*/NULL, num); } int parse_events_term__str(struct parse_events_term **term, - int type_term, char *config, char *str, + enum parse_events__term_type type_term, + char *config, char *str, void *loc_term_, void *loc_val_) { YYLTYPE *loc_term = loc_term_; @@ -2675,15 +2501,16 @@ int parse_events_term__str(struct parse_events_term **term, .err_val = loc_val ? loc_val->first_column : 0, }; - return new_term(term, &temp, str, 0); + return new_term(term, &temp, str, /*num=*/0); } int parse_events_term__term(struct parse_events_term **term, - int term_lhs, int term_rhs, + enum parse_events__term_type term_lhs, + enum parse_events__term_type term_rhs, void *loc_term, void *loc_val) { return parse_events_term__str(term, term_lhs, NULL, - strdup(config_term_names[term_rhs]), + strdup(config_term_name(term_rhs)), loc_term, loc_val); } @@ -2691,33 +2518,25 @@ int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term) { char *str; - struct parse_events_term temp = { - .type_val = term->type_val, - .type_term = term->type_term, - .config = NULL, - .err_term = term->err_term, - .err_val = term->err_val, - }; + struct parse_events_term temp = *term; + temp.used = false; if (term->config) { temp.config = strdup(term->config); if (!temp.config) return -ENOMEM; } if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) - return new_term(new, &temp, NULL, term->val.num); + return new_term(new, &temp, /*str=*/NULL, term->val.num); str = strdup(term->val.str); if (!str) return -ENOMEM; - return new_term(new, &temp, str, 0); + return new_term(new, &temp, str, /*num=*/0); } void parse_events_term__delete(struct parse_events_term *term) { - if (term->array.nr_ranges) - zfree(&term->array.ranges); - if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) zfree(&term->val.str); @@ -2768,9 +2587,47 @@ void parse_events_terms__delete(struct list_head *terms) free(terms); } -void parse_events__clear_array(struct parse_events_array *a) +int parse_events_term__to_strbuf(struct list_head *term_list, struct strbuf *sb) { - zfree(&a->ranges); + struct parse_events_term *term; + bool first = true; + + if (!term_list) + return 0; + + list_for_each_entry(term, term_list, list) { + int ret; + + if (!first) { + ret = strbuf_addch(sb, ','); + if (ret < 0) + return ret; + } + first = false; + + if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) + if (term->no_value) { + assert(term->val.num == 1); + ret = strbuf_addf(sb, "%s", term->config); + } else + ret = strbuf_addf(sb, "%s=%#"PRIx64, term->config, term->val.num); + else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { + if (term->config) { + ret = strbuf_addf(sb, "%s=", term->config); + if (ret < 0) + return ret; + } else if ((unsigned int)term->type_term < __PARSE_EVENTS__TERM_TYPE_NR) { + ret = strbuf_addf(sb, "%s=", config_term_name(term->type_term)); + if (ret < 0) + return ret; + } + assert(!term->no_value); + ret = strbuf_addf(sb, "%s", term->val.str); + } + if (ret < 0) + return ret; + } + return 0; } void parse_events_evlist_error(struct parse_events_state *parse_state, @@ -2789,7 +2646,7 @@ static void config_terms_list(char *buf, size_t buf_sz) buf[0] = '\0'; for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) { - const char *name = config_term_names[i]; + const char *name = config_term_name(i); if (!config_term_avail(i, NULL)) continue; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b0eb95f93e9c..594e5d2dc67f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -9,6 +9,7 @@ #include <stdbool.h> #include <linux/types.h> #include <linux/perf_event.h> +#include <stdio.h> #include <string.h> struct evsel; @@ -17,6 +18,7 @@ struct parse_events_error; struct option; struct perf_pmu; +struct strbuf; const char *event_type(int type); @@ -42,16 +44,16 @@ static inline int parse_events(struct evlist *evlist, const char *str, int parse_event(struct evlist *evlist, const char *str); -int parse_events_terms(struct list_head *terms, const char *str); +int parse_events_terms(struct list_head *terms, const char *str, FILE *input); int parse_filter(const struct option *opt, const char *str, int unset); int exclude_perf(const struct option *opt, const char *arg, int unset); -enum { +enum parse_events__term_val_type { PARSE_EVENTS__TERM_TYPE_NUM, PARSE_EVENTS__TERM_TYPE_STR, }; -enum { +enum parse_events__term_type { PARSE_EVENTS__TERM_TYPE_USER, PARSE_EVENTS__TERM_TYPE_CONFIG, PARSE_EVENTS__TERM_TYPE_CONFIG1, @@ -78,36 +80,54 @@ enum { PARSE_EVENTS__TERM_TYPE_RAW, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, PARSE_EVENTS__TERM_TYPE_HARDWARE, - __PARSE_EVENTS__TERM_TYPE_NR, -}; - -struct parse_events_array { - size_t nr_ranges; - struct { - unsigned int start; - size_t length; - } *ranges; +#define __PARSE_EVENTS__TERM_TYPE_NR (PARSE_EVENTS__TERM_TYPE_HARDWARE + 1) }; struct parse_events_term { - char *config; - struct parse_events_array array; + /** @list: The term list the term is a part of. */ + struct list_head list; + /** + * @config: The left-hand side of a term assignment, so the term + * "event=8" would have the config be "event" + */ + const char *config; + /** + * @val: The right-hand side of a term assignment that can either be a + * string or a number depending on type_val. + */ union { char *str; u64 num; } val; - int type_val; - int type_term; - struct list_head list; - bool used; - bool no_value; - - /* error string indexes for within parsed string */ + /** @type_val: The union variable in val to be used for the term. */ + enum parse_events__term_val_type type_val; + /** + * @type_term: A predefined term type or PARSE_EVENTS__TERM_TYPE_USER + * when not inbuilt. + */ + enum parse_events__term_type type_term; + /** + * @err_term: The column index of the term from parsing, used during + * error output. + */ int err_term; + /** + * @err_val: The column index of the val from parsing, used during error + * output. + */ int err_val; - - /* Coming from implicit alias */ + /** @used: Was the term used during parameterized-eval. */ + bool used; + /** + * @weak: A term from the sysfs or json encoding of an event that + * shouldn't override terms coming from the command line. + */ bool weak; + /** + * @no_value: Is there no value. If a numeric term has no value then the + * value is assumed to be 1. An event name also has no value. + */ + bool no_value; }; struct parse_events_error { @@ -121,17 +141,23 @@ struct parse_events_error { }; struct parse_events_state { + /* The list parsed events are placed on. */ struct list_head list; + /* The updated index used by entries as they are added. */ int idx; + /* Error information. */ struct parse_events_error *error; - struct evlist *evlist; + /* Holds returned terms for term parsing. */ struct list_head *terms; + /* Start token. */ int stoken; + /* Special fake PMU marker for testing. */ struct perf_pmu *fake_pmu; /* If non-null, when wildcard matching only match the given PMU. */ const char *pmu_filter; /* Should PE_LEGACY_NAME tokens be generated for config terms? */ bool match_legacy_cache_terms; + /* Were multiple PMUs scanned to find events? */ bool wild_card_pmus; }; @@ -140,39 +166,31 @@ bool parse_events__filter_pmu(const struct parse_events_state *parse_state, void parse_events__shrink_config_terms(void); int parse_events__is_hardcoded_term(struct parse_events_term *term); int parse_events_term__num(struct parse_events_term **term, - int type_term, char *config, u64 num, + enum parse_events__term_type type_term, + const char *config, u64 num, bool novalue, void *loc_term, void *loc_val); int parse_events_term__str(struct parse_events_term **term, - int type_term, char *config, char *str, + enum parse_events__term_type type_term, + char *config, char *str, void *loc_term, void *loc_val); int parse_events_term__term(struct parse_events_term **term, - int term_lhs, int term_rhs, + enum parse_events__term_type term_lhs, + enum parse_events__term_type term_rhs, void *loc_term, void *loc_val); int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term); void parse_events_term__delete(struct parse_events_term *term); void parse_events_terms__delete(struct list_head *terms); void parse_events_terms__purge(struct list_head *terms); -void parse_events__clear_array(struct parse_events_array *a); +int parse_events_term__to_strbuf(struct list_head *term_list, struct strbuf *sb); int parse_events__modifier_event(struct list_head *list, char *str, bool add); int parse_events__modifier_group(struct list_head *list, char *event_mod); int parse_events_name(struct list_head *list, const char *name); int parse_events_add_tracepoint(struct list_head *list, int *idx, const char *sys, const char *event, struct parse_events_error *error, - struct list_head *head_config); -int parse_events_load_bpf(struct parse_events_state *parse_state, - struct list_head *list, - char *bpf_file_name, - bool source, - struct list_head *head_config); -/* Provide this function for perf test */ -struct bpf_object; -int parse_events_load_bpf_obj(struct parse_events_state *parse_state, - struct list_head *list, - struct bpf_object *obj, - struct list_head *head_config); + struct list_head *head_config, void *loc); int parse_events_add_numeric(struct parse_events_state *parse_state, struct list_head *list, u32 type, u64 config, @@ -190,9 +208,9 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state, u64 addr, char *type, u64 len, struct list_head *head_config); int parse_events_add_pmu(struct parse_events_state *parse_state, - struct list_head *list, char *name, + struct list_head *list, const char *name, struct list_head *head_config, - bool auto_merge_stats); + bool auto_merge_stats, void *loc); struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, const char *name, const char *metric_id, @@ -201,7 +219,7 @@ struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, int parse_events_multi_pmu_add(struct parse_events_state *parse_state, char *str, struct list_head *head_config, - struct list_head **listp); + struct list_head **listp, void *loc); int parse_events_copy_term_list(struct list_head *old, struct list_head **new); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 99335ec586ae..4ef4b6f171a0 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -68,31 +68,6 @@ static int lc_str(yyscan_t scanner, const struct parse_events_state *state) return str(scanner, state->match_legacy_cache_terms ? PE_LEGACY_CACHE : PE_NAME); } -static bool isbpf_suffix(char *text) -{ - int len = strlen(text); - - if (len < 2) - return false; - if ((text[len - 1] == 'c' || text[len - 1] == 'o') && - text[len - 2] == '.') - return true; - if (len > 4 && !strcmp(text + len - 4, ".obj")) - return true; - return false; -} - -static bool isbpf(yyscan_t scanner) -{ - char *text = parse_events_get_text(scanner); - struct stat st; - - if (!isbpf_suffix(text)) - return false; - - return stat(text, &st) == 0; -} - /* * This function is called when the parser gets two kind of input: * @@ -141,7 +116,7 @@ static int tool(yyscan_t scanner, enum perf_tool_event event) return PE_VALUE_SYM_TOOL; } -static int term(yyscan_t scanner, int type) +static int term(yyscan_t scanner, enum parse_events__term_type type) { YYSTYPE *yylval = parse_events_get_lval(scanner); @@ -175,13 +150,10 @@ do { \ %x mem %s config %x event -%x array group [^,{}/]*[{][^}]*[}][^,{}/]* event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* event [^,{}/]+ -bpf_object [^,{}]+\.(o|bpf)[a-zA-Z0-9._]* -bpf_source [^,{}]+\.c[a-zA-Z0-9._]* num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ @@ -234,8 +206,6 @@ non_digit [^0-9] } {event_pmu} | -{bpf_object} | -{bpf_source} | {event} { BEGIN(INITIAL); REWIND(1); @@ -251,14 +221,6 @@ non_digit [^0-9] } } -<array>{ -"]" { BEGIN(config); return ']'; } -{num_dec} { return value(yyscanner, 10); } -{num_hex} { return value(yyscanner, 16); } -, { return ','; } -"\.\.\." { return PE_ARRAY_RANGE; } -} - <config>{ /* * Please update config_term_names when new static term is added. @@ -302,8 +264,6 @@ r0x{num_raw_hex} { return str(yyscanner, PE_RAW); } {lc_type}-{lc_op_result} { return lc_str(yyscanner, _parse_state); } {lc_type}-{lc_op_result}-{lc_op_result} { return lc_str(yyscanner, _parse_state); } {name_minus} { return str(yyscanner, PE_NAME); } -\[all\] { return PE_ARRAY_ALL; } -"[" { BEGIN(array); return '['; } @{drv_cfg_term} { return drv_str(yyscanner, PE_DRV_CFG_TERM); } } @@ -374,8 +334,6 @@ r{num_raw_hex} { return str(yyscanner, PE_RAW); } {num_hex} { return value(yyscanner, 16); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } -{bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); } -{bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); } {name} { return str(yyscanner, PE_NAME); } {name_tag} { return str(yyscanner, PE_NAME); } "/" { BEGIN(config); return '/'; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 9f28d4b5502f..21bfe7e0d944 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -20,12 +20,14 @@ #include "parse-events.h" #include "parse-events-bison.h" +int parse_events_lex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void *yyscanner); void parse_events_error(YYLTYPE *loc, void *parse_state, void *scanner, char const *msg); -#define ABORT_ON(val) \ +#define PE_ABORT(val) \ do { \ - if (val) \ - YYABORT; \ + if (val == -ENOMEM) \ + YYNOMEM; \ + YYABORT; \ } while (0) static struct list_head* alloc_list(void) @@ -58,13 +60,10 @@ static void free_list_evsel(struct list_head* list_evsel) %token PE_VALUE_SYM_TOOL %token PE_EVENT_NAME %token PE_RAW PE_NAME -%token PE_BPF_OBJECT PE_BPF_SOURCE %token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH %token PE_LEGACY_CACHE -%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP +%token PE_PREFIX_MEM %token PE_ERROR -%token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE -%token PE_ARRAY_ALL PE_ARRAY_RANGE %token PE_DRV_CFG_TERM %token PE_TERM_HW %type <num> PE_VALUE @@ -75,13 +74,10 @@ static void free_list_evsel(struct list_head* list_evsel) %type <num> value_sym %type <str> PE_RAW %type <str> PE_NAME -%type <str> PE_BPF_OBJECT -%type <str> PE_BPF_SOURCE %type <str> PE_LEGACY_CACHE %type <str> PE_MODIFIER_EVENT %type <str> PE_MODIFIER_BP %type <str> PE_EVENT_NAME -%type <str> PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE %type <str> PE_DRV_CFG_TERM %type <str> name_or_raw name_or_legacy %destructor { free ($$); } <str> @@ -98,7 +94,6 @@ static void free_list_evsel(struct list_head* list_evsel) %type <list_evsel> event_legacy_tracepoint %type <list_evsel> event_legacy_numeric %type <list_evsel> event_legacy_raw -%type <list_evsel> event_bpf_file %type <list_evsel> event_def %type <list_evsel> event_mod %type <list_evsel> event_name @@ -109,11 +104,6 @@ static void free_list_evsel(struct list_head* list_evsel) %type <list_evsel> groups %destructor { free_list_evsel ($$); } <list_evsel> %type <tracepoint_name> tracepoint_name -%destructor { free ($$.sys); free ($$.event); } <tracepoint_name> -%type <array> array -%type <array> array_term -%type <array> array_terms -%destructor { free ($$.ranges); } <array> %type <hardware_term> PE_TERM_HW %destructor { free ($$.str); } <hardware_term> @@ -128,7 +118,6 @@ static void free_list_evsel(struct list_head* list_evsel) char *sys; char *event; } tracepoint_name; - struct parse_events_array array; struct hardware_term { char *str; u64 num; @@ -265,7 +254,7 @@ PE_EVENT_NAME event_def free($1); if (err) { free_list_evsel($2); - YYABORT; + YYNOMEM; } $$ = $2; } @@ -278,47 +267,47 @@ event_def: event_pmu | event_legacy_mem sep_dc | event_legacy_tracepoint sep_dc | event_legacy_numeric sep_dc | - event_legacy_raw sep_dc | - event_bpf_file + event_legacy_raw sep_dc event_pmu: PE_NAME opt_pmu_config { struct parse_events_state *parse_state = _parse_state; - struct parse_events_error *error = parse_state->error; struct list_head *list = NULL, *orig_terms = NULL, *terms= NULL; char *pattern = NULL; -#define CLEANUP_YYABORT \ +#define CLEANUP \ do { \ parse_events_terms__delete($2); \ parse_events_terms__delete(orig_terms); \ free(list); \ free($1); \ free(pattern); \ - YYABORT; \ } while(0) - if (parse_events_copy_term_list($2, &orig_terms)) - CLEANUP_YYABORT; - - if (error) - error->idx = @1.first_column; + if (parse_events_copy_term_list($2, &orig_terms)) { + CLEANUP; + YYNOMEM; + } list = alloc_list(); - if (!list) - CLEANUP_YYABORT; + if (!list) { + CLEANUP; + YYNOMEM; + } /* Attempt to add to list assuming $1 is a PMU name. */ - if (parse_events_add_pmu(parse_state, list, $1, $2, /*auto_merge_stats=*/false)) { + if (parse_events_add_pmu(parse_state, list, $1, $2, /*auto_merge_stats=*/false, &@1)) { struct perf_pmu *pmu = NULL; int ok = 0; /* Failure to add, try wildcard expansion of $1 as a PMU name. */ - if (asprintf(&pattern, "%s*", $1) < 0) - CLEANUP_YYABORT; + if (asprintf(&pattern, "%s*", $1) < 0) { + CLEANUP; + YYNOMEM; + } while ((pmu = perf_pmus__scan(pmu)) != NULL) { - char *name = pmu->name; + const char *name = pmu->name; if (parse_events__filter_pmu(parse_state, pmu)) continue; @@ -330,10 +319,12 @@ PE_NAME opt_pmu_config !perf_pmu__match(pattern, pmu->alias_name, $1)) { bool auto_merge_stats = perf_pmu__auto_merge_stats(pmu); - if (parse_events_copy_term_list(orig_terms, &terms)) - CLEANUP_YYABORT; + if (parse_events_copy_term_list(orig_terms, &terms)) { + CLEANUP; + YYNOMEM; + } if (!parse_events_add_pmu(parse_state, list, pmu->name, terms, - auto_merge_stats)) { + auto_merge_stats, &@1)) { ok++; parse_state->wild_card_pmus = true; } @@ -344,30 +335,26 @@ PE_NAME opt_pmu_config if (!ok) { /* Failure to add, assume $1 is an event name. */ zfree(&list); - ok = !parse_events_multi_pmu_add(parse_state, $1, $2, &list); + ok = !parse_events_multi_pmu_add(parse_state, $1, $2, &list, &@1); $2 = NULL; } - if (!ok) - CLEANUP_YYABORT; + if (!ok) { + struct parse_events_error *error = parse_state->error; + char *help; + + if (asprintf(&help, "Unable to find PMU or event on a PMU of '%s'", $1) < 0) + help = NULL; + parse_events_error__handle(error, @1.first_column, + strdup("Bad event or PMU"), + help); + CLEANUP; + YYABORT; + } } - parse_events_terms__delete($2); - parse_events_terms__delete(orig_terms); - free(pattern); - free($1); - $$ = list; -#undef CLEANUP_YYABORT -} -| -PE_KERNEL_PMU_EVENT sep_dc -{ - struct list_head *list; - int err; - - err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list); - free($1); - if (err < 0) - YYABORT; $$ = list; + list = NULL; + CLEANUP; +#undef CLEANUP } | PE_NAME sep_dc @@ -375,61 +362,19 @@ PE_NAME sep_dc struct list_head *list; int err; - err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list); - free($1); - if (err < 0) - YYABORT; - $$ = list; -} -| -PE_KERNEL_PMU_EVENT opt_pmu_config -{ - struct list_head *list; - int err; - - /* frees $2 */ - err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list); - free($1); - if (err < 0) - YYABORT; - $$ = list; -} -| -PE_PMU_EVENT_FAKE sep_dc -{ - struct list_head *list; - int err; - - list = alloc_list(); - if (!list) - YYABORT; - - err = parse_events_add_pmu(_parse_state, list, $1, /*head_config=*/NULL, - /*auto_merge_stats=*/false); - free($1); + err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list, &@1); if (err < 0) { - free(list); - YYABORT; - } - $$ = list; -} -| -PE_PMU_EVENT_FAKE opt_pmu_config -{ - struct list_head *list; - int err; - - list = alloc_list(); - if (!list) - YYABORT; + struct parse_events_state *parse_state = _parse_state; + struct parse_events_error *error = parse_state->error; + char *help; - err = parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false); - free($1); - parse_events_terms__delete($2); - if (err < 0) { - free(list); - YYABORT; + if (asprintf(&help, "Unable to find event on a PMU of '%s'", $1) < 0) + help = NULL; + parse_events_error__handle(error, @1.first_column, strdup("Bad event name"), help); + free($1); + PE_ABORT(err); } + free($1); $$ = list; } @@ -448,12 +393,13 @@ value_sym '/' event_config '/' bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE); list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; err = parse_events_add_numeric(_parse_state, list, type, config, $3, wildcard); parse_events_terms__delete($3); if (err) { free_list_evsel(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -464,21 +410,28 @@ value_sym sep_slash_slash_dc int type = $1 >> 16; int config = $1 & 255; bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE); + int err; list = alloc_list(); - ABORT_ON(!list); - ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config, - /*head_config=*/NULL, wildcard)); + if (!list) + YYNOMEM; + err = parse_events_add_numeric(_parse_state, list, type, config, /*head_config=*/NULL, wildcard); + if (err) + PE_ABORT(err); $$ = list; } | PE_VALUE_SYM_TOOL sep_slash_slash_dc { struct list_head *list; + int err; list = alloc_list(); - ABORT_ON(!list); - ABORT_ON(parse_events_add_tool(_parse_state, list, $1)); + if (!list) + YYNOMEM; + err = parse_events_add_tool(_parse_state, list, $1); + if (err) + YYNOMEM; $$ = list; } @@ -490,14 +443,16 @@ PE_LEGACY_CACHE opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; + err = parse_events_add_cache(list, &parse_state->idx, $1, parse_state, $2); parse_events_terms__delete($2); free($1); if (err) { free_list_evsel(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -509,14 +464,16 @@ PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; + err = parse_events_add_breakpoint(_parse_state, list, $2, $6, $4, $7); parse_events_terms__delete($7); free($6); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -527,13 +484,15 @@ PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; + err = parse_events_add_breakpoint(_parse_state, list, $2, NULL, $4, $5); parse_events_terms__delete($5); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -544,14 +503,16 @@ PE_PREFIX_MEM PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; + err = parse_events_add_breakpoint(_parse_state, list, $2, $4, 0, $5); parse_events_terms__delete($5); free($4); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -562,13 +523,14 @@ PE_PREFIX_MEM PE_VALUE opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; err = parse_events_add_breakpoint(_parse_state, list, $2, NULL, 0, $3); parse_events_terms__delete($3); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -582,19 +544,20 @@ tracepoint_name opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; if (error) error->idx = @1.first_column; err = parse_events_add_tracepoint(list, &parse_state->idx, $1.sys, $1.event, - error, $2); + error, $2, &@1); parse_events_terms__delete($2); free($1.sys); free($1.event); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -614,13 +577,14 @@ PE_VALUE ':' PE_VALUE opt_event_config int err; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; err = parse_events_add_numeric(_parse_state, list, (u32)$1, $3, $4, /*wildcard=*/false); parse_events_terms__delete($4); if (err) { free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -633,52 +597,20 @@ PE_RAW opt_event_config u64 num; list = alloc_list(); - ABORT_ON(!list); + if (!list) + YYNOMEM; errno = 0; num = strtoull($1 + 1, NULL, 16); - ABORT_ON(errno); + /* Given the lexer will only give [a-fA-F0-9]+ a failure here should be impossible. */ + if (errno) + YYABORT; free($1); err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2, /*wildcard=*/false); parse_events_terms__delete($2); if (err) { free(list); - YYABORT; - } - $$ = list; -} - -event_bpf_file: -PE_BPF_OBJECT opt_event_config -{ - struct parse_events_state *parse_state = _parse_state; - struct list_head *list; - int err; - - list = alloc_list(); - ABORT_ON(!list); - err = parse_events_load_bpf(parse_state, list, $1, false, $2); - parse_events_terms__delete($2); - free($1); - if (err) { - free(list); - YYABORT; - } - $$ = list; -} -| -PE_BPF_SOURCE opt_event_config -{ - struct list_head *list; - int err; - - list = alloc_list(); - ABORT_ON(!list); - err = parse_events_load_bpf(_parse_state, list, $1, true, $2); - parse_events_terms__delete($2); - if (err) { - free(list); - YYABORT; + PE_ABORT(err); } $$ = list; } @@ -738,7 +670,8 @@ event_term struct list_head *head = malloc(sizeof(*head)); struct parse_events_term *term = $1; - ABORT_ON(!head); + if (!head) + YYNOMEM; INIT_LIST_HEAD(head); list_add_tail(&term->list, head); $$ = head; @@ -752,11 +685,12 @@ event_term: PE_RAW { struct parse_events_term *term; + int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW, + strdup("raw"), $1, &@1, &@1); - if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW, - strdup("raw"), $1, &@1, &@1)) { + if (err) { free($1); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -764,12 +698,12 @@ PE_RAW name_or_raw '=' name_or_legacy { struct parse_events_term *term; + int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, $1, $3, &@1, &@3); - if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $3, &@1, &@3)) { + if (err) { free($1); free($3); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -777,11 +711,12 @@ name_or_raw '=' name_or_legacy name_or_raw '=' PE_VALUE { struct parse_events_term *term; + int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3, /*novalue=*/false, &@1, &@3); - if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $3, false, &@1, &@3)) { + if (err) { free($1); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -789,12 +724,13 @@ name_or_raw '=' PE_VALUE name_or_raw '=' PE_TERM_HW { struct parse_events_term *term; + int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3.str, &@1, &@3); - if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $3.str, &@1, &@3)) { + if (err) { free($1); free($3.str); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -802,11 +738,12 @@ name_or_raw '=' PE_TERM_HW PE_LEGACY_CACHE { struct parse_events_term *term; + int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, + $1, /*num=*/1, /*novalue=*/true, &@1, /*loc_val=*/NULL); - if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE, - $1, 1, true, &@1, NULL)) { + if (err) { free($1); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -814,11 +751,12 @@ PE_LEGACY_CACHE PE_NAME { struct parse_events_term *term; + int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, /*num=*/1, /*novalue=*/true, &@1, /*loc_val=*/NULL); - if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, 1, true, &@1, NULL)) { + if (err) { free($1); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -826,11 +764,13 @@ PE_NAME PE_TERM_HW { struct parse_events_term *term; + int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_HARDWARE, + $1.str, $1.num & 255, /*novalue=*/false, + &@1, /*loc_val=*/NULL); - if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_HARDWARE, - $1.str, $1.num & 255, false, &@1, NULL)) { + if (err) { free($1.str); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -838,10 +778,12 @@ PE_TERM_HW PE_TERM '=' name_or_legacy { struct parse_events_term *term; + int err = parse_events_term__str(&term, (enum parse_events__term_type)$1, + /*config=*/NULL, $3, &@1, &@3); - if (parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3)) { + if (err) { free($3); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -849,10 +791,12 @@ PE_TERM '=' name_or_legacy PE_TERM '=' PE_TERM_HW { struct parse_events_term *term; + int err = parse_events_term__str(&term, (enum parse_events__term_type)$1, + /*config=*/NULL, $3.str, &@1, &@3); - if (parse_events_term__str(&term, (int)$1, NULL, $3.str, &@1, &@3)) { + if (err) { free($3.str); - YYABORT; + PE_ABORT(err); } $$ = term; } @@ -860,53 +804,39 @@ PE_TERM '=' PE_TERM_HW PE_TERM '=' PE_TERM { struct parse_events_term *term; + int err = parse_events_term__term(&term, + (enum parse_events__term_type)$1, + (enum parse_events__term_type)$3, + &@1, &@3); + + if (err) + PE_ABORT(err); - ABORT_ON(parse_events_term__term(&term, (int)$1, (int)$3, &@1, &@3)); $$ = term; } | PE_TERM '=' PE_VALUE { struct parse_events_term *term; + int err = parse_events_term__num(&term, (enum parse_events__term_type)$1, + /*config=*/NULL, $3, /*novalue=*/false, &@1, &@3); + + if (err) + PE_ABORT(err); - ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, false, &@1, &@3)); $$ = term; } | PE_TERM { struct parse_events_term *term; + int err = parse_events_term__num(&term, (enum parse_events__term_type)$1, + /*config=*/NULL, /*num=*/1, /*novalue=*/true, + &@1, /*loc_val=*/NULL); - ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, true, &@1, NULL)); - $$ = term; -} -| -name_or_raw array '=' name_or_legacy -{ - struct parse_events_term *term; + if (err) + PE_ABORT(err); - if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $4, &@1, &@4)) { - free($1); - free($4); - free($2.ranges); - YYABORT; - } - term->array = $2; - $$ = term; -} -| -name_or_raw array '=' PE_VALUE -{ - struct parse_events_term *term; - - if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $4, false, &@1, &@4)) { - free($1); - free($2.ranges); - YYABORT; - } - term->array = $2; $$ = term; } | @@ -914,73 +844,19 @@ PE_DRV_CFG_TERM { struct parse_events_term *term; char *config = strdup($1); + int err; - ABORT_ON(!config); - if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG, - config, $1, &@1, NULL)) { + if (!config) + YYNOMEM; + err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG, config, $1, &@1, NULL); + if (err) { free($1); free(config); - YYABORT; + PE_ABORT(err); } $$ = term; } -array: -'[' array_terms ']' -{ - $$ = $2; -} -| -PE_ARRAY_ALL -{ - $$.nr_ranges = 0; - $$.ranges = NULL; -} - -array_terms: -array_terms ',' array_term -{ - struct parse_events_array new_array; - - new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges; - new_array.ranges = realloc($1.ranges, - sizeof(new_array.ranges[0]) * - new_array.nr_ranges); - ABORT_ON(!new_array.ranges); - memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges, - $3.nr_ranges * sizeof(new_array.ranges[0])); - free($3.ranges); - $$ = new_array; -} -| -array_term - -array_term: -PE_VALUE -{ - struct parse_events_array array; - - array.nr_ranges = 1; - array.ranges = malloc(sizeof(array.ranges[0])); - ABORT_ON(!array.ranges); - array.ranges[0].start = $1; - array.ranges[0].length = 1; - $$ = array; -} -| -PE_VALUE PE_ARRAY_RANGE PE_VALUE -{ - struct parse_events_array array; - - ABORT_ON($3 < $1); - array.nr_ranges = 1; - array.ranges = malloc(sizeof(array.ranges[0])); - ABORT_ON(!array.ranges); - array.ranges[0].start = $1; - array.ranges[0].length = $3 - $1 + 1; - $$ = array; -} - sep_dc: ':' | sep_slash_slash_dc: '/' '/' | ':' | diff --git a/tools/perf/util/perf-regs-arch/Build b/tools/perf/util/perf-regs-arch/Build new file mode 100644 index 000000000000..d9d596d330a7 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/Build @@ -0,0 +1,9 @@ +perf-y += perf_regs_aarch64.o +perf-y += perf_regs_arm.o +perf-y += perf_regs_csky.o +perf-y += perf_regs_loongarch.o +perf-y += perf_regs_mips.o +perf-y += perf_regs_powerpc.o +perf-y += perf_regs_riscv.o +perf-y += perf_regs_s390.o +perf-y += perf_regs_x86.o diff --git a/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c new file mode 100644 index 000000000000..696566c54768 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/arm64/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_arm64(int id) +{ + switch (id) { + case PERF_REG_ARM64_X0: + return "x0"; + case PERF_REG_ARM64_X1: + return "x1"; + case PERF_REG_ARM64_X2: + return "x2"; + case PERF_REG_ARM64_X3: + return "x3"; + case PERF_REG_ARM64_X4: + return "x4"; + case PERF_REG_ARM64_X5: + return "x5"; + case PERF_REG_ARM64_X6: + return "x6"; + case PERF_REG_ARM64_X7: + return "x7"; + case PERF_REG_ARM64_X8: + return "x8"; + case PERF_REG_ARM64_X9: + return "x9"; + case PERF_REG_ARM64_X10: + return "x10"; + case PERF_REG_ARM64_X11: + return "x11"; + case PERF_REG_ARM64_X12: + return "x12"; + case PERF_REG_ARM64_X13: + return "x13"; + case PERF_REG_ARM64_X14: + return "x14"; + case PERF_REG_ARM64_X15: + return "x15"; + case PERF_REG_ARM64_X16: + return "x16"; + case PERF_REG_ARM64_X17: + return "x17"; + case PERF_REG_ARM64_X18: + return "x18"; + case PERF_REG_ARM64_X19: + return "x19"; + case PERF_REG_ARM64_X20: + return "x20"; + case PERF_REG_ARM64_X21: + return "x21"; + case PERF_REG_ARM64_X22: + return "x22"; + case PERF_REG_ARM64_X23: + return "x23"; + case PERF_REG_ARM64_X24: + return "x24"; + case PERF_REG_ARM64_X25: + return "x25"; + case PERF_REG_ARM64_X26: + return "x26"; + case PERF_REG_ARM64_X27: + return "x27"; + case PERF_REG_ARM64_X28: + return "x28"; + case PERF_REG_ARM64_X29: + return "x29"; + case PERF_REG_ARM64_SP: + return "sp"; + case PERF_REG_ARM64_LR: + return "lr"; + case PERF_REG_ARM64_PC: + return "pc"; + case PERF_REG_ARM64_VG: + return "vg"; + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_arm64(void) +{ + return PERF_REG_ARM64_PC; +} + +uint64_t __perf_reg_sp_arm64(void) +{ + return PERF_REG_ARM64_SP; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_arm.c b/tools/perf/util/perf-regs-arch/perf_regs_arm.c new file mode 100644 index 000000000000..700fd07cd2aa --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_arm.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/arm/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_arm(int id) +{ + switch (id) { + case PERF_REG_ARM_R0: + return "r0"; + case PERF_REG_ARM_R1: + return "r1"; + case PERF_REG_ARM_R2: + return "r2"; + case PERF_REG_ARM_R3: + return "r3"; + case PERF_REG_ARM_R4: + return "r4"; + case PERF_REG_ARM_R5: + return "r5"; + case PERF_REG_ARM_R6: + return "r6"; + case PERF_REG_ARM_R7: + return "r7"; + case PERF_REG_ARM_R8: + return "r8"; + case PERF_REG_ARM_R9: + return "r9"; + case PERF_REG_ARM_R10: + return "r10"; + case PERF_REG_ARM_FP: + return "fp"; + case PERF_REG_ARM_IP: + return "ip"; + case PERF_REG_ARM_SP: + return "sp"; + case PERF_REG_ARM_LR: + return "lr"; + case PERF_REG_ARM_PC: + return "pc"; + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_arm(void) +{ + return PERF_REG_ARM_PC; +} + +uint64_t __perf_reg_sp_arm(void) +{ + return PERF_REG_ARM_SP; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_csky.c b/tools/perf/util/perf-regs-arch/perf_regs_csky.c new file mode 100644 index 000000000000..a2841094e096 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_csky.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../arch/csky/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_csky(int id) +{ + switch (id) { + case PERF_REG_CSKY_A0: + return "a0"; + case PERF_REG_CSKY_A1: + return "a1"; + case PERF_REG_CSKY_A2: + return "a2"; + case PERF_REG_CSKY_A3: + return "a3"; + case PERF_REG_CSKY_REGS0: + return "regs0"; + case PERF_REG_CSKY_REGS1: + return "regs1"; + case PERF_REG_CSKY_REGS2: + return "regs2"; + case PERF_REG_CSKY_REGS3: + return "regs3"; + case PERF_REG_CSKY_REGS4: + return "regs4"; + case PERF_REG_CSKY_REGS5: + return "regs5"; + case PERF_REG_CSKY_REGS6: + return "regs6"; + case PERF_REG_CSKY_REGS7: + return "regs7"; + case PERF_REG_CSKY_REGS8: + return "regs8"; + case PERF_REG_CSKY_REGS9: + return "regs9"; + case PERF_REG_CSKY_SP: + return "sp"; + case PERF_REG_CSKY_LR: + return "lr"; + case PERF_REG_CSKY_PC: + return "pc"; +#if defined(__CSKYABIV2__) + case PERF_REG_CSKY_EXREGS0: + return "exregs0"; + case PERF_REG_CSKY_EXREGS1: + return "exregs1"; + case PERF_REG_CSKY_EXREGS2: + return "exregs2"; + case PERF_REG_CSKY_EXREGS3: + return "exregs3"; + case PERF_REG_CSKY_EXREGS4: + return "exregs4"; + case PERF_REG_CSKY_EXREGS5: + return "exregs5"; + case PERF_REG_CSKY_EXREGS6: + return "exregs6"; + case PERF_REG_CSKY_EXREGS7: + return "exregs7"; + case PERF_REG_CSKY_EXREGS8: + return "exregs8"; + case PERF_REG_CSKY_EXREGS9: + return "exregs9"; + case PERF_REG_CSKY_EXREGS10: + return "exregs10"; + case PERF_REG_CSKY_EXREGS11: + return "exregs11"; + case PERF_REG_CSKY_EXREGS12: + return "exregs12"; + case PERF_REG_CSKY_EXREGS13: + return "exregs13"; + case PERF_REG_CSKY_EXREGS14: + return "exregs14"; + case PERF_REG_CSKY_TLS: + return "tls"; + case PERF_REG_CSKY_HI: + return "hi"; + case PERF_REG_CSKY_LO: + return "lo"; +#endif + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_csky(void) +{ + return PERF_REG_CSKY_PC; +} + +uint64_t __perf_reg_sp_csky(void) +{ + return PERF_REG_CSKY_SP; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c new file mode 100644 index 000000000000..a9ba0f934123 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_loongarch(int id) +{ + switch (id) { + case PERF_REG_LOONGARCH_PC: + return "PC"; + case PERF_REG_LOONGARCH_R1: + return "%r1"; + case PERF_REG_LOONGARCH_R2: + return "%r2"; + case PERF_REG_LOONGARCH_R3: + return "%r3"; + case PERF_REG_LOONGARCH_R4: + return "%r4"; + case PERF_REG_LOONGARCH_R5: + return "%r5"; + case PERF_REG_LOONGARCH_R6: + return "%r6"; + case PERF_REG_LOONGARCH_R7: + return "%r7"; + case PERF_REG_LOONGARCH_R8: + return "%r8"; + case PERF_REG_LOONGARCH_R9: + return "%r9"; + case PERF_REG_LOONGARCH_R10: + return "%r10"; + case PERF_REG_LOONGARCH_R11: + return "%r11"; + case PERF_REG_LOONGARCH_R12: + return "%r12"; + case PERF_REG_LOONGARCH_R13: + return "%r13"; + case PERF_REG_LOONGARCH_R14: + return "%r14"; + case PERF_REG_LOONGARCH_R15: + return "%r15"; + case PERF_REG_LOONGARCH_R16: + return "%r16"; + case PERF_REG_LOONGARCH_R17: + return "%r17"; + case PERF_REG_LOONGARCH_R18: + return "%r18"; + case PERF_REG_LOONGARCH_R19: + return "%r19"; + case PERF_REG_LOONGARCH_R20: + return "%r20"; + case PERF_REG_LOONGARCH_R21: + return "%r21"; + case PERF_REG_LOONGARCH_R22: + return "%r22"; + case PERF_REG_LOONGARCH_R23: + return "%r23"; + case PERF_REG_LOONGARCH_R24: + return "%r24"; + case PERF_REG_LOONGARCH_R25: + return "%r25"; + case PERF_REG_LOONGARCH_R26: + return "%r26"; + case PERF_REG_LOONGARCH_R27: + return "%r27"; + case PERF_REG_LOONGARCH_R28: + return "%r28"; + case PERF_REG_LOONGARCH_R29: + return "%r29"; + case PERF_REG_LOONGARCH_R30: + return "%r30"; + case PERF_REG_LOONGARCH_R31: + return "%r31"; + default: + break; + } + return NULL; +} + +uint64_t __perf_reg_ip_loongarch(void) +{ + return PERF_REG_LOONGARCH_PC; +} + +uint64_t __perf_reg_sp_loongarch(void) +{ + return PERF_REG_LOONGARCH_R3; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_mips.c b/tools/perf/util/perf-regs-arch/perf_regs_mips.c new file mode 100644 index 000000000000..5a45830cfbf5 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_mips.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/mips/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_mips(int id) +{ + switch (id) { + case PERF_REG_MIPS_PC: + return "PC"; + case PERF_REG_MIPS_R1: + return "$1"; + case PERF_REG_MIPS_R2: + return "$2"; + case PERF_REG_MIPS_R3: + return "$3"; + case PERF_REG_MIPS_R4: + return "$4"; + case PERF_REG_MIPS_R5: + return "$5"; + case PERF_REG_MIPS_R6: + return "$6"; + case PERF_REG_MIPS_R7: + return "$7"; + case PERF_REG_MIPS_R8: + return "$8"; + case PERF_REG_MIPS_R9: + return "$9"; + case PERF_REG_MIPS_R10: + return "$10"; + case PERF_REG_MIPS_R11: + return "$11"; + case PERF_REG_MIPS_R12: + return "$12"; + case PERF_REG_MIPS_R13: + return "$13"; + case PERF_REG_MIPS_R14: + return "$14"; + case PERF_REG_MIPS_R15: + return "$15"; + case PERF_REG_MIPS_R16: + return "$16"; + case PERF_REG_MIPS_R17: + return "$17"; + case PERF_REG_MIPS_R18: + return "$18"; + case PERF_REG_MIPS_R19: + return "$19"; + case PERF_REG_MIPS_R20: + return "$20"; + case PERF_REG_MIPS_R21: + return "$21"; + case PERF_REG_MIPS_R22: + return "$22"; + case PERF_REG_MIPS_R23: + return "$23"; + case PERF_REG_MIPS_R24: + return "$24"; + case PERF_REG_MIPS_R25: + return "$25"; + case PERF_REG_MIPS_R28: + return "$28"; + case PERF_REG_MIPS_R29: + return "$29"; + case PERF_REG_MIPS_R30: + return "$30"; + case PERF_REG_MIPS_R31: + return "$31"; + default: + break; + } + return NULL; +} + +uint64_t __perf_reg_ip_mips(void) +{ + return PERF_REG_MIPS_PC; +} + +uint64_t __perf_reg_sp_mips(void) +{ + return PERF_REG_MIPS_R29; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c new file mode 100644 index 000000000000..1f0d682db74a --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_powerpc(int id) +{ + switch (id) { + case PERF_REG_POWERPC_R0: + return "r0"; + case PERF_REG_POWERPC_R1: + return "r1"; + case PERF_REG_POWERPC_R2: + return "r2"; + case PERF_REG_POWERPC_R3: + return "r3"; + case PERF_REG_POWERPC_R4: + return "r4"; + case PERF_REG_POWERPC_R5: + return "r5"; + case PERF_REG_POWERPC_R6: + return "r6"; + case PERF_REG_POWERPC_R7: + return "r7"; + case PERF_REG_POWERPC_R8: + return "r8"; + case PERF_REG_POWERPC_R9: + return "r9"; + case PERF_REG_POWERPC_R10: + return "r10"; + case PERF_REG_POWERPC_R11: + return "r11"; + case PERF_REG_POWERPC_R12: + return "r12"; + case PERF_REG_POWERPC_R13: + return "r13"; + case PERF_REG_POWERPC_R14: + return "r14"; + case PERF_REG_POWERPC_R15: + return "r15"; + case PERF_REG_POWERPC_R16: + return "r16"; + case PERF_REG_POWERPC_R17: + return "r17"; + case PERF_REG_POWERPC_R18: + return "r18"; + case PERF_REG_POWERPC_R19: + return "r19"; + case PERF_REG_POWERPC_R20: + return "r20"; + case PERF_REG_POWERPC_R21: + return "r21"; + case PERF_REG_POWERPC_R22: + return "r22"; + case PERF_REG_POWERPC_R23: + return "r23"; + case PERF_REG_POWERPC_R24: + return "r24"; + case PERF_REG_POWERPC_R25: + return "r25"; + case PERF_REG_POWERPC_R26: + return "r26"; + case PERF_REG_POWERPC_R27: + return "r27"; + case PERF_REG_POWERPC_R28: + return "r28"; + case PERF_REG_POWERPC_R29: + return "r29"; + case PERF_REG_POWERPC_R30: + return "r30"; + case PERF_REG_POWERPC_R31: + return "r31"; + case PERF_REG_POWERPC_NIP: + return "nip"; + case PERF_REG_POWERPC_MSR: + return "msr"; + case PERF_REG_POWERPC_ORIG_R3: + return "orig_r3"; + case PERF_REG_POWERPC_CTR: + return "ctr"; + case PERF_REG_POWERPC_LINK: + return "link"; + case PERF_REG_POWERPC_XER: + return "xer"; + case PERF_REG_POWERPC_CCR: + return "ccr"; + case PERF_REG_POWERPC_SOFTE: + return "softe"; + case PERF_REG_POWERPC_TRAP: + return "trap"; + case PERF_REG_POWERPC_DAR: + return "dar"; + case PERF_REG_POWERPC_DSISR: + return "dsisr"; + case PERF_REG_POWERPC_SIER: + return "sier"; + case PERF_REG_POWERPC_MMCRA: + return "mmcra"; + case PERF_REG_POWERPC_MMCR0: + return "mmcr0"; + case PERF_REG_POWERPC_MMCR1: + return "mmcr1"; + case PERF_REG_POWERPC_MMCR2: + return "mmcr2"; + case PERF_REG_POWERPC_MMCR3: + return "mmcr3"; + case PERF_REG_POWERPC_SIER2: + return "sier2"; + case PERF_REG_POWERPC_SIER3: + return "sier3"; + case PERF_REG_POWERPC_PMC1: + return "pmc1"; + case PERF_REG_POWERPC_PMC2: + return "pmc2"; + case PERF_REG_POWERPC_PMC3: + return "pmc3"; + case PERF_REG_POWERPC_PMC4: + return "pmc4"; + case PERF_REG_POWERPC_PMC5: + return "pmc5"; + case PERF_REG_POWERPC_PMC6: + return "pmc6"; + case PERF_REG_POWERPC_SDAR: + return "sdar"; + case PERF_REG_POWERPC_SIAR: + return "siar"; + default: + break; + } + return NULL; +} + +uint64_t __perf_reg_ip_powerpc(void) +{ + return PERF_REG_POWERPC_NIP; +} + +uint64_t __perf_reg_sp_powerpc(void) +{ + return PERF_REG_POWERPC_R1; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c new file mode 100644 index 000000000000..e432630be4c5 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/riscv/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_riscv(int id) +{ + switch (id) { + case PERF_REG_RISCV_PC: + return "pc"; + case PERF_REG_RISCV_RA: + return "ra"; + case PERF_REG_RISCV_SP: + return "sp"; + case PERF_REG_RISCV_GP: + return "gp"; + case PERF_REG_RISCV_TP: + return "tp"; + case PERF_REG_RISCV_T0: + return "t0"; + case PERF_REG_RISCV_T1: + return "t1"; + case PERF_REG_RISCV_T2: + return "t2"; + case PERF_REG_RISCV_S0: + return "s0"; + case PERF_REG_RISCV_S1: + return "s1"; + case PERF_REG_RISCV_A0: + return "a0"; + case PERF_REG_RISCV_A1: + return "a1"; + case PERF_REG_RISCV_A2: + return "a2"; + case PERF_REG_RISCV_A3: + return "a3"; + case PERF_REG_RISCV_A4: + return "a4"; + case PERF_REG_RISCV_A5: + return "a5"; + case PERF_REG_RISCV_A6: + return "a6"; + case PERF_REG_RISCV_A7: + return "a7"; + case PERF_REG_RISCV_S2: + return "s2"; + case PERF_REG_RISCV_S3: + return "s3"; + case PERF_REG_RISCV_S4: + return "s4"; + case PERF_REG_RISCV_S5: + return "s5"; + case PERF_REG_RISCV_S6: + return "s6"; + case PERF_REG_RISCV_S7: + return "s7"; + case PERF_REG_RISCV_S8: + return "s8"; + case PERF_REG_RISCV_S9: + return "s9"; + case PERF_REG_RISCV_S10: + return "s10"; + case PERF_REG_RISCV_S11: + return "s11"; + case PERF_REG_RISCV_T3: + return "t3"; + case PERF_REG_RISCV_T4: + return "t4"; + case PERF_REG_RISCV_T5: + return "t5"; + case PERF_REG_RISCV_T6: + return "t6"; + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_riscv(void) +{ + return PERF_REG_RISCV_PC; +} + +uint64_t __perf_reg_sp_riscv(void) +{ + return PERF_REG_RISCV_SP; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_s390.c b/tools/perf/util/perf-regs-arch/perf_regs_s390.c new file mode 100644 index 000000000000..1c7a46db778c --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_s390.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/s390/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_s390(int id) +{ + switch (id) { + case PERF_REG_S390_R0: + return "R0"; + case PERF_REG_S390_R1: + return "R1"; + case PERF_REG_S390_R2: + return "R2"; + case PERF_REG_S390_R3: + return "R3"; + case PERF_REG_S390_R4: + return "R4"; + case PERF_REG_S390_R5: + return "R5"; + case PERF_REG_S390_R6: + return "R6"; + case PERF_REG_S390_R7: + return "R7"; + case PERF_REG_S390_R8: + return "R8"; + case PERF_REG_S390_R9: + return "R9"; + case PERF_REG_S390_R10: + return "R10"; + case PERF_REG_S390_R11: + return "R11"; + case PERF_REG_S390_R12: + return "R12"; + case PERF_REG_S390_R13: + return "R13"; + case PERF_REG_S390_R14: + return "R14"; + case PERF_REG_S390_R15: + return "R15"; + case PERF_REG_S390_FP0: + return "FP0"; + case PERF_REG_S390_FP1: + return "FP1"; + case PERF_REG_S390_FP2: + return "FP2"; + case PERF_REG_S390_FP3: + return "FP3"; + case PERF_REG_S390_FP4: + return "FP4"; + case PERF_REG_S390_FP5: + return "FP5"; + case PERF_REG_S390_FP6: + return "FP6"; + case PERF_REG_S390_FP7: + return "FP7"; + case PERF_REG_S390_FP8: + return "FP8"; + case PERF_REG_S390_FP9: + return "FP9"; + case PERF_REG_S390_FP10: + return "FP10"; + case PERF_REG_S390_FP11: + return "FP11"; + case PERF_REG_S390_FP12: + return "FP12"; + case PERF_REG_S390_FP13: + return "FP13"; + case PERF_REG_S390_FP14: + return "FP14"; + case PERF_REG_S390_FP15: + return "FP15"; + case PERF_REG_S390_MASK: + return "MASK"; + case PERF_REG_S390_PC: + return "PC"; + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_s390(void) +{ + return PERF_REG_S390_PC; +} + +uint64_t __perf_reg_sp_s390(void) +{ + return PERF_REG_S390_R15; +} + +#endif diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c new file mode 100644 index 000000000000..873c620f0634 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifdef HAVE_PERF_REGS_SUPPORT + +#include "../perf_regs.h" +#include "../../../arch/x86/include/uapi/asm/perf_regs.h" + +const char *__perf_reg_name_x86(int id) +{ + switch (id) { + case PERF_REG_X86_AX: + return "AX"; + case PERF_REG_X86_BX: + return "BX"; + case PERF_REG_X86_CX: + return "CX"; + case PERF_REG_X86_DX: + return "DX"; + case PERF_REG_X86_SI: + return "SI"; + case PERF_REG_X86_DI: + return "DI"; + case PERF_REG_X86_BP: + return "BP"; + case PERF_REG_X86_SP: + return "SP"; + case PERF_REG_X86_IP: + return "IP"; + case PERF_REG_X86_FLAGS: + return "FLAGS"; + case PERF_REG_X86_CS: + return "CS"; + case PERF_REG_X86_SS: + return "SS"; + case PERF_REG_X86_DS: + return "DS"; + case PERF_REG_X86_ES: + return "ES"; + case PERF_REG_X86_FS: + return "FS"; + case PERF_REG_X86_GS: + return "GS"; + case PERF_REG_X86_R8: + return "R8"; + case PERF_REG_X86_R9: + return "R9"; + case PERF_REG_X86_R10: + return "R10"; + case PERF_REG_X86_R11: + return "R11"; + case PERF_REG_X86_R12: + return "R12"; + case PERF_REG_X86_R13: + return "R13"; + case PERF_REG_X86_R14: + return "R14"; + case PERF_REG_X86_R15: + return "R15"; + +#define XMM(x) \ + case PERF_REG_X86_XMM ## x: \ + case PERF_REG_X86_XMM ## x + 1: \ + return "XMM" #x; + XMM(0) + XMM(1) + XMM(2) + XMM(3) + XMM(4) + XMM(5) + XMM(6) + XMM(7) + XMM(8) + XMM(9) + XMM(10) + XMM(11) + XMM(12) + XMM(13) + XMM(14) + XMM(15) +#undef XMM + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_x86(void) +{ + return PERF_REG_X86_IP; +} + +uint64_t __perf_reg_sp_x86(void) +{ + return PERF_REG_X86_SP; +} + +#endif diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index 9bdbaa37f813..e2275856b570 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -3,6 +3,7 @@ #include <string.h> #include "perf_regs.h" #include "util/sample.h" +#include "debug.h" int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused, char **new_op __maybe_unused) @@ -12,732 +13,16 @@ int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused, uint64_t __weak arch__intr_reg_mask(void) { - return PERF_REGS_MASK; + return 0; } uint64_t __weak arch__user_reg_mask(void) { - return PERF_REGS_MASK; + return 0; } #ifdef HAVE_PERF_REGS_SUPPORT -#define perf_event_arm_regs perf_event_arm64_regs -#include "../../arch/arm64/include/uapi/asm/perf_regs.h" -#undef perf_event_arm_regs - -#include "../../arch/arm/include/uapi/asm/perf_regs.h" -#include "../../arch/csky/include/uapi/asm/perf_regs.h" -#include "../../arch/loongarch/include/uapi/asm/perf_regs.h" -#include "../../arch/mips/include/uapi/asm/perf_regs.h" -#include "../../arch/powerpc/include/uapi/asm/perf_regs.h" -#include "../../arch/riscv/include/uapi/asm/perf_regs.h" -#include "../../arch/s390/include/uapi/asm/perf_regs.h" -#include "../../arch/x86/include/uapi/asm/perf_regs.h" - -static const char *__perf_reg_name_arm64(int id) -{ - switch (id) { - case PERF_REG_ARM64_X0: - return "x0"; - case PERF_REG_ARM64_X1: - return "x1"; - case PERF_REG_ARM64_X2: - return "x2"; - case PERF_REG_ARM64_X3: - return "x3"; - case PERF_REG_ARM64_X4: - return "x4"; - case PERF_REG_ARM64_X5: - return "x5"; - case PERF_REG_ARM64_X6: - return "x6"; - case PERF_REG_ARM64_X7: - return "x7"; - case PERF_REG_ARM64_X8: - return "x8"; - case PERF_REG_ARM64_X9: - return "x9"; - case PERF_REG_ARM64_X10: - return "x10"; - case PERF_REG_ARM64_X11: - return "x11"; - case PERF_REG_ARM64_X12: - return "x12"; - case PERF_REG_ARM64_X13: - return "x13"; - case PERF_REG_ARM64_X14: - return "x14"; - case PERF_REG_ARM64_X15: - return "x15"; - case PERF_REG_ARM64_X16: - return "x16"; - case PERF_REG_ARM64_X17: - return "x17"; - case PERF_REG_ARM64_X18: - return "x18"; - case PERF_REG_ARM64_X19: - return "x19"; - case PERF_REG_ARM64_X20: - return "x20"; - case PERF_REG_ARM64_X21: - return "x21"; - case PERF_REG_ARM64_X22: - return "x22"; - case PERF_REG_ARM64_X23: - return "x23"; - case PERF_REG_ARM64_X24: - return "x24"; - case PERF_REG_ARM64_X25: - return "x25"; - case PERF_REG_ARM64_X26: - return "x26"; - case PERF_REG_ARM64_X27: - return "x27"; - case PERF_REG_ARM64_X28: - return "x28"; - case PERF_REG_ARM64_X29: - return "x29"; - case PERF_REG_ARM64_SP: - return "sp"; - case PERF_REG_ARM64_LR: - return "lr"; - case PERF_REG_ARM64_PC: - return "pc"; - case PERF_REG_ARM64_VG: - return "vg"; - default: - return NULL; - } - - return NULL; -} - -static const char *__perf_reg_name_arm(int id) -{ - switch (id) { - case PERF_REG_ARM_R0: - return "r0"; - case PERF_REG_ARM_R1: - return "r1"; - case PERF_REG_ARM_R2: - return "r2"; - case PERF_REG_ARM_R3: - return "r3"; - case PERF_REG_ARM_R4: - return "r4"; - case PERF_REG_ARM_R5: - return "r5"; - case PERF_REG_ARM_R6: - return "r6"; - case PERF_REG_ARM_R7: - return "r7"; - case PERF_REG_ARM_R8: - return "r8"; - case PERF_REG_ARM_R9: - return "r9"; - case PERF_REG_ARM_R10: - return "r10"; - case PERF_REG_ARM_FP: - return "fp"; - case PERF_REG_ARM_IP: - return "ip"; - case PERF_REG_ARM_SP: - return "sp"; - case PERF_REG_ARM_LR: - return "lr"; - case PERF_REG_ARM_PC: - return "pc"; - default: - return NULL; - } - - return NULL; -} - -static const char *__perf_reg_name_csky(int id) -{ - switch (id) { - case PERF_REG_CSKY_A0: - return "a0"; - case PERF_REG_CSKY_A1: - return "a1"; - case PERF_REG_CSKY_A2: - return "a2"; - case PERF_REG_CSKY_A3: - return "a3"; - case PERF_REG_CSKY_REGS0: - return "regs0"; - case PERF_REG_CSKY_REGS1: - return "regs1"; - case PERF_REG_CSKY_REGS2: - return "regs2"; - case PERF_REG_CSKY_REGS3: - return "regs3"; - case PERF_REG_CSKY_REGS4: - return "regs4"; - case PERF_REG_CSKY_REGS5: - return "regs5"; - case PERF_REG_CSKY_REGS6: - return "regs6"; - case PERF_REG_CSKY_REGS7: - return "regs7"; - case PERF_REG_CSKY_REGS8: - return "regs8"; - case PERF_REG_CSKY_REGS9: - return "regs9"; - case PERF_REG_CSKY_SP: - return "sp"; - case PERF_REG_CSKY_LR: - return "lr"; - case PERF_REG_CSKY_PC: - return "pc"; -#if defined(__CSKYABIV2__) - case PERF_REG_CSKY_EXREGS0: - return "exregs0"; - case PERF_REG_CSKY_EXREGS1: - return "exregs1"; - case PERF_REG_CSKY_EXREGS2: - return "exregs2"; - case PERF_REG_CSKY_EXREGS3: - return "exregs3"; - case PERF_REG_CSKY_EXREGS4: - return "exregs4"; - case PERF_REG_CSKY_EXREGS5: - return "exregs5"; - case PERF_REG_CSKY_EXREGS6: - return "exregs6"; - case PERF_REG_CSKY_EXREGS7: - return "exregs7"; - case PERF_REG_CSKY_EXREGS8: - return "exregs8"; - case PERF_REG_CSKY_EXREGS9: - return "exregs9"; - case PERF_REG_CSKY_EXREGS10: - return "exregs10"; - case PERF_REG_CSKY_EXREGS11: - return "exregs11"; - case PERF_REG_CSKY_EXREGS12: - return "exregs12"; - case PERF_REG_CSKY_EXREGS13: - return "exregs13"; - case PERF_REG_CSKY_EXREGS14: - return "exregs14"; - case PERF_REG_CSKY_TLS: - return "tls"; - case PERF_REG_CSKY_HI: - return "hi"; - case PERF_REG_CSKY_LO: - return "lo"; -#endif - default: - return NULL; - } - - return NULL; -} - -static inline const char *__perf_reg_name_loongarch(int id) -{ - switch (id) { - case PERF_REG_LOONGARCH_PC: - return "PC"; - case PERF_REG_LOONGARCH_R1: - return "%r1"; - case PERF_REG_LOONGARCH_R2: - return "%r2"; - case PERF_REG_LOONGARCH_R3: - return "%r3"; - case PERF_REG_LOONGARCH_R4: - return "%r4"; - case PERF_REG_LOONGARCH_R5: - return "%r5"; - case PERF_REG_LOONGARCH_R6: - return "%r6"; - case PERF_REG_LOONGARCH_R7: - return "%r7"; - case PERF_REG_LOONGARCH_R8: - return "%r8"; - case PERF_REG_LOONGARCH_R9: - return "%r9"; - case PERF_REG_LOONGARCH_R10: - return "%r10"; - case PERF_REG_LOONGARCH_R11: - return "%r11"; - case PERF_REG_LOONGARCH_R12: - return "%r12"; - case PERF_REG_LOONGARCH_R13: - return "%r13"; - case PERF_REG_LOONGARCH_R14: - return "%r14"; - case PERF_REG_LOONGARCH_R15: - return "%r15"; - case PERF_REG_LOONGARCH_R16: - return "%r16"; - case PERF_REG_LOONGARCH_R17: - return "%r17"; - case PERF_REG_LOONGARCH_R18: - return "%r18"; - case PERF_REG_LOONGARCH_R19: - return "%r19"; - case PERF_REG_LOONGARCH_R20: - return "%r20"; - case PERF_REG_LOONGARCH_R21: - return "%r21"; - case PERF_REG_LOONGARCH_R22: - return "%r22"; - case PERF_REG_LOONGARCH_R23: - return "%r23"; - case PERF_REG_LOONGARCH_R24: - return "%r24"; - case PERF_REG_LOONGARCH_R25: - return "%r25"; - case PERF_REG_LOONGARCH_R26: - return "%r26"; - case PERF_REG_LOONGARCH_R27: - return "%r27"; - case PERF_REG_LOONGARCH_R28: - return "%r28"; - case PERF_REG_LOONGARCH_R29: - return "%r29"; - case PERF_REG_LOONGARCH_R30: - return "%r30"; - case PERF_REG_LOONGARCH_R31: - return "%r31"; - default: - break; - } - return NULL; -} - -static const char *__perf_reg_name_mips(int id) -{ - switch (id) { - case PERF_REG_MIPS_PC: - return "PC"; - case PERF_REG_MIPS_R1: - return "$1"; - case PERF_REG_MIPS_R2: - return "$2"; - case PERF_REG_MIPS_R3: - return "$3"; - case PERF_REG_MIPS_R4: - return "$4"; - case PERF_REG_MIPS_R5: - return "$5"; - case PERF_REG_MIPS_R6: - return "$6"; - case PERF_REG_MIPS_R7: - return "$7"; - case PERF_REG_MIPS_R8: - return "$8"; - case PERF_REG_MIPS_R9: - return "$9"; - case PERF_REG_MIPS_R10: - return "$10"; - case PERF_REG_MIPS_R11: - return "$11"; - case PERF_REG_MIPS_R12: - return "$12"; - case PERF_REG_MIPS_R13: - return "$13"; - case PERF_REG_MIPS_R14: - return "$14"; - case PERF_REG_MIPS_R15: - return "$15"; - case PERF_REG_MIPS_R16: - return "$16"; - case PERF_REG_MIPS_R17: - return "$17"; - case PERF_REG_MIPS_R18: - return "$18"; - case PERF_REG_MIPS_R19: - return "$19"; - case PERF_REG_MIPS_R20: - return "$20"; - case PERF_REG_MIPS_R21: - return "$21"; - case PERF_REG_MIPS_R22: - return "$22"; - case PERF_REG_MIPS_R23: - return "$23"; - case PERF_REG_MIPS_R24: - return "$24"; - case PERF_REG_MIPS_R25: - return "$25"; - case PERF_REG_MIPS_R28: - return "$28"; - case PERF_REG_MIPS_R29: - return "$29"; - case PERF_REG_MIPS_R30: - return "$30"; - case PERF_REG_MIPS_R31: - return "$31"; - default: - break; - } - return NULL; -} - -static const char *__perf_reg_name_powerpc(int id) -{ - switch (id) { - case PERF_REG_POWERPC_R0: - return "r0"; - case PERF_REG_POWERPC_R1: - return "r1"; - case PERF_REG_POWERPC_R2: - return "r2"; - case PERF_REG_POWERPC_R3: - return "r3"; - case PERF_REG_POWERPC_R4: - return "r4"; - case PERF_REG_POWERPC_R5: - return "r5"; - case PERF_REG_POWERPC_R6: - return "r6"; - case PERF_REG_POWERPC_R7: - return "r7"; - case PERF_REG_POWERPC_R8: - return "r8"; - case PERF_REG_POWERPC_R9: - return "r9"; - case PERF_REG_POWERPC_R10: - return "r10"; - case PERF_REG_POWERPC_R11: - return "r11"; - case PERF_REG_POWERPC_R12: - return "r12"; - case PERF_REG_POWERPC_R13: - return "r13"; - case PERF_REG_POWERPC_R14: - return "r14"; - case PERF_REG_POWERPC_R15: - return "r15"; - case PERF_REG_POWERPC_R16: - return "r16"; - case PERF_REG_POWERPC_R17: - return "r17"; - case PERF_REG_POWERPC_R18: - return "r18"; - case PERF_REG_POWERPC_R19: - return "r19"; - case PERF_REG_POWERPC_R20: - return "r20"; - case PERF_REG_POWERPC_R21: - return "r21"; - case PERF_REG_POWERPC_R22: - return "r22"; - case PERF_REG_POWERPC_R23: - return "r23"; - case PERF_REG_POWERPC_R24: - return "r24"; - case PERF_REG_POWERPC_R25: - return "r25"; - case PERF_REG_POWERPC_R26: - return "r26"; - case PERF_REG_POWERPC_R27: - return "r27"; - case PERF_REG_POWERPC_R28: - return "r28"; - case PERF_REG_POWERPC_R29: - return "r29"; - case PERF_REG_POWERPC_R30: - return "r30"; - case PERF_REG_POWERPC_R31: - return "r31"; - case PERF_REG_POWERPC_NIP: - return "nip"; - case PERF_REG_POWERPC_MSR: - return "msr"; - case PERF_REG_POWERPC_ORIG_R3: - return "orig_r3"; - case PERF_REG_POWERPC_CTR: - return "ctr"; - case PERF_REG_POWERPC_LINK: - return "link"; - case PERF_REG_POWERPC_XER: - return "xer"; - case PERF_REG_POWERPC_CCR: - return "ccr"; - case PERF_REG_POWERPC_SOFTE: - return "softe"; - case PERF_REG_POWERPC_TRAP: - return "trap"; - case PERF_REG_POWERPC_DAR: - return "dar"; - case PERF_REG_POWERPC_DSISR: - return "dsisr"; - case PERF_REG_POWERPC_SIER: - return "sier"; - case PERF_REG_POWERPC_MMCRA: - return "mmcra"; - case PERF_REG_POWERPC_MMCR0: - return "mmcr0"; - case PERF_REG_POWERPC_MMCR1: - return "mmcr1"; - case PERF_REG_POWERPC_MMCR2: - return "mmcr2"; - case PERF_REG_POWERPC_MMCR3: - return "mmcr3"; - case PERF_REG_POWERPC_SIER2: - return "sier2"; - case PERF_REG_POWERPC_SIER3: - return "sier3"; - case PERF_REG_POWERPC_PMC1: - return "pmc1"; - case PERF_REG_POWERPC_PMC2: - return "pmc2"; - case PERF_REG_POWERPC_PMC3: - return "pmc3"; - case PERF_REG_POWERPC_PMC4: - return "pmc4"; - case PERF_REG_POWERPC_PMC5: - return "pmc5"; - case PERF_REG_POWERPC_PMC6: - return "pmc6"; - case PERF_REG_POWERPC_SDAR: - return "sdar"; - case PERF_REG_POWERPC_SIAR: - return "siar"; - default: - break; - } - return NULL; -} - -static const char *__perf_reg_name_riscv(int id) -{ - switch (id) { - case PERF_REG_RISCV_PC: - return "pc"; - case PERF_REG_RISCV_RA: - return "ra"; - case PERF_REG_RISCV_SP: - return "sp"; - case PERF_REG_RISCV_GP: - return "gp"; - case PERF_REG_RISCV_TP: - return "tp"; - case PERF_REG_RISCV_T0: - return "t0"; - case PERF_REG_RISCV_T1: - return "t1"; - case PERF_REG_RISCV_T2: - return "t2"; - case PERF_REG_RISCV_S0: - return "s0"; - case PERF_REG_RISCV_S1: - return "s1"; - case PERF_REG_RISCV_A0: - return "a0"; - case PERF_REG_RISCV_A1: - return "a1"; - case PERF_REG_RISCV_A2: - return "a2"; - case PERF_REG_RISCV_A3: - return "a3"; - case PERF_REG_RISCV_A4: - return "a4"; - case PERF_REG_RISCV_A5: - return "a5"; - case PERF_REG_RISCV_A6: - return "a6"; - case PERF_REG_RISCV_A7: - return "a7"; - case PERF_REG_RISCV_S2: - return "s2"; - case PERF_REG_RISCV_S3: - return "s3"; - case PERF_REG_RISCV_S4: - return "s4"; - case PERF_REG_RISCV_S5: - return "s5"; - case PERF_REG_RISCV_S6: - return "s6"; - case PERF_REG_RISCV_S7: - return "s7"; - case PERF_REG_RISCV_S8: - return "s8"; - case PERF_REG_RISCV_S9: - return "s9"; - case PERF_REG_RISCV_S10: - return "s10"; - case PERF_REG_RISCV_S11: - return "s11"; - case PERF_REG_RISCV_T3: - return "t3"; - case PERF_REG_RISCV_T4: - return "t4"; - case PERF_REG_RISCV_T5: - return "t5"; - case PERF_REG_RISCV_T6: - return "t6"; - default: - return NULL; - } - - return NULL; -} - -static const char *__perf_reg_name_s390(int id) -{ - switch (id) { - case PERF_REG_S390_R0: - return "R0"; - case PERF_REG_S390_R1: - return "R1"; - case PERF_REG_S390_R2: - return "R2"; - case PERF_REG_S390_R3: - return "R3"; - case PERF_REG_S390_R4: - return "R4"; - case PERF_REG_S390_R5: - return "R5"; - case PERF_REG_S390_R6: - return "R6"; - case PERF_REG_S390_R7: - return "R7"; - case PERF_REG_S390_R8: - return "R8"; - case PERF_REG_S390_R9: - return "R9"; - case PERF_REG_S390_R10: - return "R10"; - case PERF_REG_S390_R11: - return "R11"; - case PERF_REG_S390_R12: - return "R12"; - case PERF_REG_S390_R13: - return "R13"; - case PERF_REG_S390_R14: - return "R14"; - case PERF_REG_S390_R15: - return "R15"; - case PERF_REG_S390_FP0: - return "FP0"; - case PERF_REG_S390_FP1: - return "FP1"; - case PERF_REG_S390_FP2: - return "FP2"; - case PERF_REG_S390_FP3: - return "FP3"; - case PERF_REG_S390_FP4: - return "FP4"; - case PERF_REG_S390_FP5: - return "FP5"; - case PERF_REG_S390_FP6: - return "FP6"; - case PERF_REG_S390_FP7: - return "FP7"; - case PERF_REG_S390_FP8: - return "FP8"; - case PERF_REG_S390_FP9: - return "FP9"; - case PERF_REG_S390_FP10: - return "FP10"; - case PERF_REG_S390_FP11: - return "FP11"; - case PERF_REG_S390_FP12: - return "FP12"; - case PERF_REG_S390_FP13: - return "FP13"; - case PERF_REG_S390_FP14: - return "FP14"; - case PERF_REG_S390_FP15: - return "FP15"; - case PERF_REG_S390_MASK: - return "MASK"; - case PERF_REG_S390_PC: - return "PC"; - default: - return NULL; - } - - return NULL; -} - -static const char *__perf_reg_name_x86(int id) -{ - switch (id) { - case PERF_REG_X86_AX: - return "AX"; - case PERF_REG_X86_BX: - return "BX"; - case PERF_REG_X86_CX: - return "CX"; - case PERF_REG_X86_DX: - return "DX"; - case PERF_REG_X86_SI: - return "SI"; - case PERF_REG_X86_DI: - return "DI"; - case PERF_REG_X86_BP: - return "BP"; - case PERF_REG_X86_SP: - return "SP"; - case PERF_REG_X86_IP: - return "IP"; - case PERF_REG_X86_FLAGS: - return "FLAGS"; - case PERF_REG_X86_CS: - return "CS"; - case PERF_REG_X86_SS: - return "SS"; - case PERF_REG_X86_DS: - return "DS"; - case PERF_REG_X86_ES: - return "ES"; - case PERF_REG_X86_FS: - return "FS"; - case PERF_REG_X86_GS: - return "GS"; - case PERF_REG_X86_R8: - return "R8"; - case PERF_REG_X86_R9: - return "R9"; - case PERF_REG_X86_R10: - return "R10"; - case PERF_REG_X86_R11: - return "R11"; - case PERF_REG_X86_R12: - return "R12"; - case PERF_REG_X86_R13: - return "R13"; - case PERF_REG_X86_R14: - return "R14"; - case PERF_REG_X86_R15: - return "R15"; - -#define XMM(x) \ - case PERF_REG_X86_XMM ## x: \ - case PERF_REG_X86_XMM ## x + 1: \ - return "XMM" #x; - XMM(0) - XMM(1) - XMM(2) - XMM(3) - XMM(4) - XMM(5) - XMM(6) - XMM(7) - XMM(8) - XMM(9) - XMM(10) - XMM(11) - XMM(12) - XMM(13) - XMM(14) - XMM(15) -#undef XMM - default: - return NULL; - } - - return NULL; -} - const char *perf_reg_name(int id, const char *arch) { const char *reg_name = NULL; @@ -790,4 +75,55 @@ out: *valp = regs->cache_regs[id]; return 0; } + +uint64_t perf_arch_reg_ip(const char *arch) +{ + if (!strcmp(arch, "arm")) + return __perf_reg_ip_arm(); + else if (!strcmp(arch, "arm64")) + return __perf_reg_ip_arm64(); + else if (!strcmp(arch, "csky")) + return __perf_reg_ip_csky(); + else if (!strcmp(arch, "loongarch")) + return __perf_reg_ip_loongarch(); + else if (!strcmp(arch, "mips")) + return __perf_reg_ip_mips(); + else if (!strcmp(arch, "powerpc")) + return __perf_reg_ip_powerpc(); + else if (!strcmp(arch, "riscv")) + return __perf_reg_ip_riscv(); + else if (!strcmp(arch, "s390")) + return __perf_reg_ip_s390(); + else if (!strcmp(arch, "x86")) + return __perf_reg_ip_x86(); + + pr_err("Fail to find IP register for arch %s, returns 0\n", arch); + return 0; +} + +uint64_t perf_arch_reg_sp(const char *arch) +{ + if (!strcmp(arch, "arm")) + return __perf_reg_sp_arm(); + else if (!strcmp(arch, "arm64")) + return __perf_reg_sp_arm64(); + else if (!strcmp(arch, "csky")) + return __perf_reg_sp_csky(); + else if (!strcmp(arch, "loongarch")) + return __perf_reg_sp_loongarch(); + else if (!strcmp(arch, "mips")) + return __perf_reg_sp_mips(); + else if (!strcmp(arch, "powerpc")) + return __perf_reg_sp_powerpc(); + else if (!strcmp(arch, "riscv")) + return __perf_reg_sp_riscv(); + else if (!strcmp(arch, "s390")) + return __perf_reg_sp_s390(); + else if (!strcmp(arch, "x86")) + return __perf_reg_sp_x86(); + + pr_err("Fail to find SP register for arch %s, returns 0\n", arch); + return 0; +} + #endif diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index ce1127af05e4..ecd2a5362042 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -30,18 +30,49 @@ uint64_t arch__user_reg_mask(void); #ifdef HAVE_PERF_REGS_SUPPORT extern const struct sample_reg sample_reg_masks[]; -#include <perf_regs.h> - -#define DWARF_MINIMAL_REGS ((1ULL << PERF_REG_IP) | (1ULL << PERF_REG_SP)) - const char *perf_reg_name(int id, const char *arch); int perf_reg_value(u64 *valp, struct regs_dump *regs, int id); +uint64_t perf_arch_reg_ip(const char *arch); +uint64_t perf_arch_reg_sp(const char *arch); +const char *__perf_reg_name_arm64(int id); +uint64_t __perf_reg_ip_arm64(void); +uint64_t __perf_reg_sp_arm64(void); +const char *__perf_reg_name_arm(int id); +uint64_t __perf_reg_ip_arm(void); +uint64_t __perf_reg_sp_arm(void); +const char *__perf_reg_name_csky(int id); +uint64_t __perf_reg_ip_csky(void); +uint64_t __perf_reg_sp_csky(void); +const char *__perf_reg_name_loongarch(int id); +uint64_t __perf_reg_ip_loongarch(void); +uint64_t __perf_reg_sp_loongarch(void); +const char *__perf_reg_name_mips(int id); +uint64_t __perf_reg_ip_mips(void); +uint64_t __perf_reg_sp_mips(void); +const char *__perf_reg_name_powerpc(int id); +uint64_t __perf_reg_ip_powerpc(void); +uint64_t __perf_reg_sp_powerpc(void); +const char *__perf_reg_name_riscv(int id); +uint64_t __perf_reg_ip_riscv(void); +uint64_t __perf_reg_sp_riscv(void); +const char *__perf_reg_name_s390(int id); +uint64_t __perf_reg_ip_s390(void); +uint64_t __perf_reg_sp_s390(void); +const char *__perf_reg_name_x86(int id); +uint64_t __perf_reg_ip_x86(void); +uint64_t __perf_reg_sp_x86(void); + +static inline uint64_t DWARF_MINIMAL_REGS(const char *arch) +{ + return (1ULL << perf_arch_reg_ip(arch)) | (1ULL << perf_arch_reg_sp(arch)); +} #else -#define PERF_REGS_MASK 0 -#define PERF_REGS_MAX 0 -#define DWARF_MINIMAL_REGS PERF_REGS_MASK +static inline uint64_t DWARF_MINIMAL_REGS(const char *arch __maybe_unused) +{ + return 0; +} static inline const char *perf_reg_name(int id __maybe_unused, const char *arch __maybe_unused) { @@ -54,5 +85,16 @@ static inline int perf_reg_value(u64 *valp __maybe_unused, { return 0; } + +static inline uint64_t perf_arch_reg_ip(const char *arch __maybe_unused) +{ + return 0; +} + +static inline uint64_t perf_arch_reg_sp(const char *arch __maybe_unused) +{ + return 0; +} + #endif /* HAVE_PERF_REGS_SUPPORT */ #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 28380e7aa8d0..8de6f39abd1b 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -19,8 +19,8 @@ #include "evsel.h" #include "pmu.h" #include "pmus.h" -#include "pmu-bison.h" -#include "pmu-flex.h" +#include <util/pmu-bison.h> +#include <util/pmu-flex.h> #include "parse-events.h" #include "print-events.h" #include "header.h" @@ -29,7 +29,63 @@ #include "fncache.h" #include "util/evsel_config.h" -struct perf_pmu perf_pmu__fake; +struct perf_pmu perf_pmu__fake = { + .name = "fake", +}; + +#define UNIT_MAX_LEN 31 /* max length for event unit name */ + +/** + * struct perf_pmu_alias - An event either read from sysfs or builtin in + * pmu-events.c, created by parsing the pmu-events json files. + */ +struct perf_pmu_alias { + /** @name: Name of the event like "mem-loads". */ + char *name; + /** @desc: Optional short description of the event. */ + char *desc; + /** @long_desc: Optional long description. */ + char *long_desc; + /** + * @topic: Optional topic such as cache or pipeline, particularly for + * json events. + */ + char *topic; + /** @terms: Owned list of the original parsed parameters. */ + struct list_head terms; + /** @list: List element of struct perf_pmu aliases. */ + struct list_head list; + /** + * @pmu_name: The name copied from the json struct pmu_event. This can + * differ from the PMU name as it won't have suffixes. + */ + char *pmu_name; + /** @unit: Units for the event, such as bytes or cache lines. */ + char unit[UNIT_MAX_LEN+1]; + /** @scale: Value to scale read counter values by. */ + double scale; + /** + * @per_pkg: Does the file + * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or + * equivalent json value exist and have the value 1. + */ + bool per_pkg; + /** + * @snapshot: Does the file + * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot + * exist and have the value 1. + */ + bool snapshot; + /** + * @deprecated: Is the event hidden and so not shown in perf list by + * default. + */ + bool deprecated; + /** @from_sysfs: Was the alias from sysfs or a json event? */ + bool from_sysfs; + /** @info_loaded: Have the scale, unit and other values been read from disk? */ + bool info_loaded; +}; /** * struct perf_pmu_format - Values from a format file read from @@ -40,6 +96,10 @@ struct perf_pmu perf_pmu__fake; * value=PERF_PMU_FORMAT_VALUE_CONFIG and bits 0 to 7 will be set. */ struct perf_pmu_format { + /** @list: Element on list within struct perf_pmu. */ + struct list_head list; + /** @bits: Which config bits are set by this format value. */ + DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); /** @name: The modifier/file name. */ char *name; /** @@ -47,18 +107,81 @@ struct perf_pmu_format { * are from PERF_PMU_FORMAT_VALUE_CONFIG to * PERF_PMU_FORMAT_VALUE_CONFIG_END. */ - int value; - /** @bits: Which config bits are set by this format value. */ - DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); - /** @list: Element on list within struct perf_pmu. */ - struct list_head list; + u16 value; + /** @loaded: Has the contents been loaded/parsed. */ + bool loaded; }; +static int pmu_aliases_parse(struct perf_pmu *pmu); + +static struct perf_pmu_format *perf_pmu__new_format(struct list_head *list, char *name) +{ + struct perf_pmu_format *format; + + format = zalloc(sizeof(*format)); + if (!format) + return NULL; + + format->name = strdup(name); + if (!format->name) { + free(format); + return NULL; + } + list_add_tail(&format->list, list); + return format; +} + +/* Called at the end of parsing a format. */ +void perf_pmu_format__set_value(void *vformat, int config, unsigned long *bits) +{ + struct perf_pmu_format *format = vformat; + + format->value = config; + memcpy(format->bits, bits, sizeof(format->bits)); +} + +static void __perf_pmu_format__load(struct perf_pmu_format *format, FILE *file) +{ + void *scanner; + int ret; + + ret = perf_pmu_lex_init(&scanner); + if (ret) + return; + + perf_pmu_set_in(file, scanner); + ret = perf_pmu_parse(format, scanner); + perf_pmu_lex_destroy(scanner); + format->loaded = true; +} + +static void perf_pmu_format__load(struct perf_pmu *pmu, struct perf_pmu_format *format) +{ + char path[PATH_MAX]; + FILE *file = NULL; + + if (format->loaded) + return; + + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, "format")) + return; + + assert(strlen(path) + strlen(format->name) + 2 < sizeof(path)); + strcat(path, "/"); + strcat(path, format->name); + + file = fopen(path, "r"); + if (!file) + return; + __perf_pmu_format__load(format, file); + fclose(file); +} + /* * Parse & process all the sysfs attributes located under * the directory specified in 'dir' parameter. */ -int perf_pmu__format_parse(int dirfd, struct list_head *head) +int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load) { struct dirent *evt_ent; DIR *format_dir; @@ -68,37 +191,35 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head) if (!format_dir) return -EINVAL; - while (!ret && (evt_ent = readdir(format_dir))) { + while ((evt_ent = readdir(format_dir)) != NULL) { + struct perf_pmu_format *format; char *name = evt_ent->d_name; - int fd; - void *scanner; - FILE *file; if (!strcmp(name, ".") || !strcmp(name, "..")) continue; - - ret = -EINVAL; - fd = openat(dirfd, name, O_RDONLY); - if (fd < 0) - break; - - file = fdopen(fd, "r"); - if (!file) { - close(fd); + format = perf_pmu__new_format(&pmu->format, name); + if (!format) { + ret = -ENOMEM; break; } - ret = perf_pmu_lex_init(&scanner); - if (ret) { + if (eager_load) { + FILE *file; + int fd = openat(dirfd, name, O_RDONLY); + + if (fd < 0) { + ret = -errno; + break; + } + file = fdopen(fd, "r"); + if (!file) { + close(fd); + break; + } + __perf_pmu_format__load(format, file); fclose(file); - break; } - - perf_pmu_set_in(file, scanner); - ret = perf_pmu_parse(head, name, scanner); - perf_pmu_lex_destroy(scanner); - fclose(file); } closedir(format_dir); @@ -110,7 +231,7 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head) * located at: * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. */ -static int pmu_format(int dirfd, const char *name, struct list_head *format) +static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name) { int fd; @@ -119,7 +240,7 @@ static int pmu_format(int dirfd, const char *name, struct list_head *format) return 0; /* it'll close the fd */ - if (perf_pmu__format_parse(fd, format)) + if (perf_pmu__format_parse(pmu, fd, /*eager_load=*/false)) return -1; return 0; @@ -162,17 +283,21 @@ out: return ret; } -static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, int dirfd, char *name) +static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *alias) { struct stat st; ssize_t sret; + size_t len; char scale[128]; int fd, ret = -1; char path[PATH_MAX]; - scnprintf(path, PATH_MAX, "%s.scale", name); + len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + if (!len) + return 0; + scnprintf(path + len, sizeof(path) - len, "%s/%s.scale", pmu->name, alias->name); - fd = openat(dirfd, path, O_RDONLY); + fd = open(path, O_RDONLY); if (fd == -1) return -1; @@ -194,15 +319,20 @@ error: return ret; } -static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, int dirfd, char *name) +static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *alias) { char path[PATH_MAX]; + size_t len; ssize_t sret; int fd; - scnprintf(path, PATH_MAX, "%s.unit", name); - fd = openat(dirfd, path, O_RDONLY); + len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + if (!len) + return 0; + scnprintf(path + len, sizeof(path) - len, "%s/%s.unit", pmu->name, alias->name); + + fd = open(path, O_RDONLY); if (fd == -1) return -1; @@ -225,14 +355,18 @@ error: } static int -perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name) +perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias) { char path[PATH_MAX]; + size_t len; int fd; - scnprintf(path, PATH_MAX, "%s.per-pkg", name); + len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + if (!len) + return 0; + scnprintf(path + len, sizeof(path) - len, "%s/%s.per-pkg", pmu->name, alias->name); - fd = openat(dirfd, path, O_RDONLY); + fd = open(path, O_RDONLY); if (fd == -1) return -1; @@ -242,15 +376,18 @@ perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name) return 0; } -static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, - int dirfd, char *name) +static int perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias) { char path[PATH_MAX]; + size_t len; int fd; - scnprintf(path, PATH_MAX, "%s.snapshot", name); + len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + if (!len) + return 0; + scnprintf(path + len, sizeof(path) - len, "%s/%s.snapshot", pmu->name, alias->name); - fd = openat(dirfd, path, O_RDONLY); + fd = open(path, O_RDONLY); if (fd == -1) return -1; @@ -259,46 +396,13 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, return 0; } -static void perf_pmu_assign_str(char *name, const char *field, char **old_str, - char **new_str) -{ - if (!*old_str) - goto set_new; - - if (*new_str) { /* Have new string, check with old */ - if (strcasecmp(*old_str, *new_str)) - pr_debug("alias %s differs in field '%s'\n", - name, field); - zfree(old_str); - } else /* Nothing new --> keep old string */ - return; -set_new: - *old_str = *new_str; - *new_str = NULL; -} - -static void perf_pmu_update_alias(struct perf_pmu_alias *old, - struct perf_pmu_alias *newalias) -{ - perf_pmu_assign_str(old->name, "desc", &old->desc, &newalias->desc); - perf_pmu_assign_str(old->name, "long_desc", &old->long_desc, - &newalias->long_desc); - perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic); - perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str); - old->scale = newalias->scale; - old->per_pkg = newalias->per_pkg; - old->snapshot = newalias->snapshot; - memcpy(old->unit, newalias->unit, sizeof(old->unit)); -} - /* Delete an alias entry. */ -void perf_pmu_free_alias(struct perf_pmu_alias *newalias) +static void perf_pmu_free_alias(struct perf_pmu_alias *newalias) { zfree(&newalias->name); zfree(&newalias->desc); zfree(&newalias->long_desc); zfree(&newalias->topic); - zfree(&newalias->str); zfree(&newalias->pmu_name); parse_events_terms__purge(&newalias->terms); free(newalias); @@ -314,38 +418,99 @@ static void perf_pmu__del_aliases(struct perf_pmu *pmu) } } -/* Merge an alias, search in alias list. If this name is already - * present merge both of them to combine all information. - */ -static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias, - struct list_head *alist) +static struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu, + const char *name, + bool load) { - struct perf_pmu_alias *a; + struct perf_pmu_alias *alias; - list_for_each_entry(a, alist, list) { - if (!strcasecmp(newalias->name, a->name)) { - if (newalias->pmu_name && a->pmu_name && - !strcasecmp(newalias->pmu_name, a->pmu_name)) { - continue; - } - perf_pmu_update_alias(a, newalias); - perf_pmu_free_alias(newalias); - return true; - } + if (load && !pmu->sysfs_aliases_loaded) + pmu_aliases_parse(pmu); + + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcasecmp(alias->name, name)) + return alias; } - return false; + return NULL; } -static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, - char *desc, char *val, const struct pmu_event *pe) +static bool assign_str(const char *name, const char *field, char **old_str, + const char *new_str) +{ + if (!*old_str && new_str) { + *old_str = strdup(new_str); + return true; + } + + if (!new_str || !strcasecmp(*old_str, new_str)) + return false; /* Nothing to update. */ + + pr_debug("alias %s differs in field '%s' ('%s' != '%s')\n", + name, field, *old_str, new_str); + zfree(old_str); + *old_str = strdup(new_str); + return true; +} + +static void read_alias_info(struct perf_pmu *pmu, struct perf_pmu_alias *alias) +{ + if (!alias->from_sysfs || alias->info_loaded) + return; + + /* + * load unit name and scale if available + */ + perf_pmu__parse_unit(pmu, alias); + perf_pmu__parse_scale(pmu, alias); + perf_pmu__parse_per_pkg(pmu, alias); + perf_pmu__parse_snapshot(pmu, alias); +} + +struct update_alias_data { + struct perf_pmu *pmu; + struct perf_pmu_alias *alias; +}; + +static int update_alias(const struct pmu_event *pe, + const struct pmu_events_table *table __maybe_unused, + void *vdata) +{ + struct update_alias_data *data = vdata; + int ret = 0; + + read_alias_info(data->pmu, data->alias); + assign_str(pe->name, "desc", &data->alias->desc, pe->desc); + assign_str(pe->name, "long_desc", &data->alias->long_desc, pe->long_desc); + assign_str(pe->name, "topic", &data->alias->topic, pe->topic); + data->alias->per_pkg = pe->perpkg; + if (pe->event) { + parse_events_terms__purge(&data->alias->terms); + ret = parse_events_terms(&data->alias->terms, pe->event, /*input=*/NULL); + } + if (!ret && pe->unit) { + char *unit; + + ret = perf_pmu__convert_scale(pe->unit, &unit, &data->alias->scale); + if (!ret) + snprintf(data->alias->unit, sizeof(data->alias->unit), "%s", unit); + } + return ret; +} + +static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name, + const char *desc, const char *val, FILE *val_fd, + const struct pmu_event *pe) { - struct parse_events_term *term; struct perf_pmu_alias *alias; int ret; - char newval[256]; const char *long_desc = NULL, *topic = NULL, *unit = NULL, *pmu_name = NULL; bool deprecated = false, perpkg = false; + if (perf_pmu__find_alias(pmu, name, /*load=*/ false)) { + /* Alias was already created/loaded. */ + return 0; + } + if (pe) { long_desc = pe->long_desc; topic = pe->topic; @@ -355,7 +520,7 @@ static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, pmu_name = pe->pmu; } - alias = malloc(sizeof(*alias)); + alias = zalloc(sizeof(*alias)); if (!alias) return -ENOMEM; @@ -366,80 +531,49 @@ static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, alias->snapshot = false; alias->deprecated = deprecated; - ret = parse_events_terms(&alias->terms, val); + ret = parse_events_terms(&alias->terms, val, val_fd); if (ret) { pr_err("Cannot parse alias %s: %d\n", val, ret); free(alias); return ret; } - /* Scan event and remove leading zeroes, spaces, newlines, some - * platforms have terms specified as - * event=0x0091 (read from files ../<PMU>/events/<FILE> - * and terms specified as event=0x91 (read from JSON files). - * - * Rebuild string to make alias->str member comparable. - */ - memset(newval, 0, sizeof(newval)); - ret = 0; - list_for_each_entry(term, &alias->terms, list) { - if (ret) - ret += scnprintf(newval + ret, sizeof(newval) - ret, - ","); - if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) - ret += scnprintf(newval + ret, sizeof(newval) - ret, - "%s=%#x", term->config, term->val.num); - else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) - ret += scnprintf(newval + ret, sizeof(newval) - ret, - "%s=%s", term->config, term->val.str); - } - alias->name = strdup(name); - if (dirfd >= 0) { - /* - * load unit name and scale if available - */ - perf_pmu__parse_unit(alias, dirfd, name); - perf_pmu__parse_scale(alias, dirfd, name); - perf_pmu__parse_per_pkg(alias, dirfd, name); - perf_pmu__parse_snapshot(alias, dirfd, name); - } - alias->desc = desc ? strdup(desc) : NULL; alias->long_desc = long_desc ? strdup(long_desc) : desc ? strdup(desc) : NULL; alias->topic = topic ? strdup(topic) : NULL; + alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL; if (unit) { - if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0) + if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0) { + perf_pmu_free_alias(alias); return -1; + } snprintf(alias->unit, sizeof(alias->unit), "%s", unit); } - alias->str = strdup(newval); - alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL; - - if (!perf_pmu_merge_alias(alias, list)) - list_add_tail(&alias->list, list); + if (!pe) { + /* Update an event from sysfs with json data. */ + struct update_alias_data data = { + .pmu = pmu, + .alias = alias, + }; + + alias->from_sysfs = true; + if (pmu->events_table) { + if (pmu_events_table__find_event(pmu->events_table, pmu, name, + update_alias, &data) == 0) + pmu->loaded_json_aliases++; + } + } + if (!pe) + pmu->sysfs_aliases++; + else + pmu->loaded_json_aliases++; + list_add_tail(&alias->list, &pmu->aliases); return 0; } -static int perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, FILE *file) -{ - char buf[256]; - int ret; - - ret = fread(buf, 1, sizeof(buf), file); - if (ret == 0) - return -EINVAL; - - buf[ret] = 0; - - /* Remove trailing newline from sysfs file */ - strim(buf); - - return __perf_pmu__new_alias(list, dirfd, name, NULL, buf, NULL); -} - static inline bool pmu_alias_info_file(char *name) { size_t len; @@ -458,18 +592,33 @@ static inline bool pmu_alias_info_file(char *name) } /* - * Process all the sysfs attributes located under the directory - * specified in 'dir' parameter. + * Reading the pmu event aliases definition, which should be located at: + * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. */ -static int pmu_aliases_parse(int dirfd, struct list_head *head) +static int pmu_aliases_parse(struct perf_pmu *pmu) { + char path[PATH_MAX]; struct dirent *evt_ent; DIR *event_dir; - int fd; + size_t len; + int fd, dir_fd; - event_dir = fdopendir(dirfd); - if (!event_dir) + len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); + if (!len) + return 0; + scnprintf(path + len, sizeof(path) - len, "%s/events", pmu->name); + + dir_fd = open(path, O_DIRECTORY); + if (dir_fd == -1) { + pmu->sysfs_aliases_loaded = true; + return 0; + } + + event_dir = fdopendir(dir_fd); + if (!event_dir){ + close (dir_fd); return -EINVAL; + } while ((evt_ent = readdir(event_dir))) { char *name = evt_ent->d_name; @@ -484,7 +633,7 @@ static int pmu_aliases_parse(int dirfd, struct list_head *head) if (pmu_alias_info_file(name)) continue; - fd = openat(dirfd, name, O_RDONLY); + fd = openat(dir_fd, name, O_RDONLY); if (fd == -1) { pr_debug("Cannot open %s\n", name); continue; @@ -495,31 +644,15 @@ static int pmu_aliases_parse(int dirfd, struct list_head *head) continue; } - if (perf_pmu__new_alias(head, dirfd, name, file) < 0) + if (perf_pmu__new_alias(pmu, name, /*desc=*/ NULL, + /*val=*/ NULL, file, /*pe=*/ NULL) < 0) pr_debug("Cannot set up %s\n", name); fclose(file); } closedir(event_dir); - return 0; -} - -/* - * Reading the pmu event aliases definition, which should be located at: - * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. - */ -static int pmu_aliases(int dirfd, const char *name, struct list_head *head) -{ - int fd; - - fd = perf_pmu__pathname_fd(dirfd, name, "events", O_DIRECTORY); - if (fd < 0) - return 0; - - /* it'll close the fd */ - if (pmu_aliases_parse(fd, head)) - return -1; - + close (dir_fd); + pmu->sysfs_aliases_loaded = true; return 0; } @@ -741,28 +874,13 @@ out: return res; } -struct pmu_add_cpu_aliases_map_data { - /* List being added to. */ - struct list_head *head; - /* If a pmu_event lacks a given PMU the default used. */ - char *default_pmu_name; - /* The PMU that we're searching for events for. */ - struct perf_pmu *pmu; -}; - static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe, const struct pmu_events_table *table __maybe_unused, void *vdata) { - struct pmu_add_cpu_aliases_map_data *data = vdata; - const char *pname = pe->pmu ?: data->default_pmu_name; + struct perf_pmu *pmu = vdata; - if (!strcmp(pname, data->pmu->name) || - (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->pmu->name))) { - /* need type casts to override 'const' */ - __perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc, - (char *)pe->event, pe); - } + perf_pmu__new_alias(pmu, pe->name, pe->desc, pe->event, /*val_fd=*/ NULL, pe); return 0; } @@ -770,68 +888,51 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe, * From the pmu_events_table, find the events that correspond to the given * PMU and add them to the list 'head'. */ -void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu, - const struct pmu_events_table *table) +void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, const struct pmu_events_table *table) { - struct pmu_add_cpu_aliases_map_data data = { - .head = head, - .default_pmu_name = perf_pmus__default_pmu_name(), - .pmu = pmu, - }; - - pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data); - free(data.default_pmu_name); + pmu_events_table__for_each_event(table, pmu, pmu_add_cpu_aliases_map_callback, pmu); } -static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu) +static void pmu_add_cpu_aliases(struct perf_pmu *pmu) { - const struct pmu_events_table *table; + if (!pmu->events_table) + return; - table = perf_pmu__find_events_table(pmu); - if (!table) + if (pmu->cpu_aliases_added) return; - pmu_add_cpu_aliases_table(head, pmu, table); + pmu_add_cpu_aliases_table(pmu, pmu->events_table); + pmu->cpu_aliases_added = true; } -struct pmu_sys_event_iter_data { - struct list_head *head; - struct perf_pmu *pmu; -}; - static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe, const struct pmu_events_table *table __maybe_unused, - void *data) + void *vdata) { - struct pmu_sys_event_iter_data *idata = data; - struct perf_pmu *pmu = idata->pmu; + struct perf_pmu *pmu = vdata; if (!pe->compat || !pe->pmu) return 0; if (!strcmp(pmu->id, pe->compat) && pmu_uncore_alias_match(pe->pmu, pmu->name)) { - __perf_pmu__new_alias(idata->head, -1, - (char *)pe->name, - (char *)pe->desc, - (char *)pe->event, - pe); + perf_pmu__new_alias(pmu, + pe->name, + pe->desc, + pe->event, + /*val_fd=*/ NULL, + pe); } return 0; } -void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu) +void pmu_add_sys_aliases(struct perf_pmu *pmu) { - struct pmu_sys_event_iter_data idata = { - .head = head, - .pmu = pmu, - }; - if (!pmu->id) return; - pmu_for_each_sys_event(pmu_add_sys_aliases_iter_fn, &idata); + pmu_for_each_sys_event(pmu_add_sys_aliases_iter_fn, pmu); } struct perf_event_attr * __weak @@ -840,13 +941,13 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) return NULL; } -char * __weak +const char * __weak pmu_find_real_name(const char *name) { - return (char *)name; + return name; } -char * __weak +const char * __weak pmu_find_alias_name(const char *name __maybe_unused) { return NULL; @@ -863,40 +964,41 @@ static int pmu_max_precise(int dirfd, struct perf_pmu *pmu) struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name) { struct perf_pmu *pmu; - LIST_HEAD(format); - LIST_HEAD(aliases); __u32 type; - char *name = pmu_find_real_name(lookup_name); - char *alias_name; - - /* - * The pmu data we store & need consists of the pmu - * type value and format definitions. Load both right - * now. - */ - if (pmu_format(dirfd, name, &format)) - return NULL; - - /* - * Check the aliases first to avoid unnecessary work. - */ - if (pmu_aliases(dirfd, name, &aliases)) - return NULL; + const char *name = pmu_find_real_name(lookup_name); + const char *alias_name; pmu = zalloc(sizeof(*pmu)); if (!pmu) return NULL; - pmu->is_core = is_pmu_core(name); - pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core); pmu->name = strdup(name); if (!pmu->name) goto err; - /* Read type, and ensure that type value is successfully assigned (return 1) */ + /* + * Read type early to fail fast if a lookup name isn't a PMU. Ensure + * that type value is successfully assigned (return 1). + */ if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &type) != 1) goto err; + INIT_LIST_HEAD(&pmu->format); + INIT_LIST_HEAD(&pmu->aliases); + INIT_LIST_HEAD(&pmu->caps); + + /* + * The pmu data we store & need consists of the pmu + * type value and format definitions. Load both right + * now. + */ + if (pmu_format(pmu, dirfd, name)) { + free(pmu); + return NULL; + } + pmu->is_core = is_pmu_core(name); + pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core); + alias_name = pmu_find_alias_name(name); if (alias_name) { pmu->alias_name = strdup(alias_name); @@ -909,14 +1011,8 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char if (pmu->is_uncore) pmu->id = pmu_id(name); pmu->max_precise = pmu_max_precise(dirfd, pmu); - pmu_add_cpu_aliases(&aliases, pmu); - pmu_add_sys_aliases(&aliases, pmu); - - INIT_LIST_HEAD(&pmu->format); - INIT_LIST_HEAD(&pmu->aliases); - INIT_LIST_HEAD(&pmu->caps); - list_splice(&format, &pmu->format); - list_splice(&aliases, &pmu->aliases); + pmu->events_table = perf_pmu__find_events_table(pmu); + pmu_add_sys_aliases(pmu); list_add_tail(&pmu->list, pmus); pmu->default_config = perf_pmu__get_default_config(pmu); @@ -966,13 +1062,15 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) if (pmu == &perf_pmu__fake) return; - list_for_each_entry(format, &pmu->format, list) + list_for_each_entry(format, &pmu->format, list) { + perf_pmu_format__load(pmu, format); if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) { pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'" "which is not supported by this version of perf!\n", pmu->name, format->name, format->value); return; } + } } bool evsel__is_aux_event(const struct evsel *evsel) @@ -1000,7 +1098,7 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel, if (term) user_bits = term->val.cfg_chg; - bits = perf_pmu__format_bits(&pmu->format, config_name); + bits = perf_pmu__format_bits(pmu, config_name); /* Do nothing if the user changed the value */ if (bits & user_bits) @@ -1023,9 +1121,9 @@ pmu_find_format(struct list_head *formats, const char *name) return NULL; } -__u64 perf_pmu__format_bits(struct list_head *formats, const char *name) +__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name) { - struct perf_pmu_format *format = pmu_find_format(formats, name); + struct perf_pmu_format *format = pmu_find_format(&pmu->format, name); __u64 bits = 0; int fbit; @@ -1038,13 +1136,14 @@ __u64 perf_pmu__format_bits(struct list_head *formats, const char *name) return bits; } -int perf_pmu__format_type(struct list_head *formats, const char *name) +int perf_pmu__format_type(struct perf_pmu *pmu, const char *name) { - struct perf_pmu_format *format = pmu_find_format(formats, name); + struct perf_pmu_format *format = pmu_find_format(&pmu->format, name); if (!format) return -1; + perf_pmu_format__load(pmu, format); return format->value; } @@ -1135,8 +1234,7 @@ error: * Setup one of config[12] attr members based on the * user input data - term parameter. */ -static int pmu_config_term(const char *pmu_name, - struct list_head *formats, +static int pmu_config_term(struct perf_pmu *pmu, struct perf_event_attr *attr, struct parse_events_term *term, struct list_head *head_terms, @@ -1160,15 +1258,15 @@ static int pmu_config_term(const char *pmu_name, if (parse_events__is_hardcoded_term(term)) return 0; - format = pmu_find_format(formats, term->config); + format = pmu_find_format(&pmu->format, term->config); if (!format) { - char *pmu_term = pmu_formats_string(formats); + char *pmu_term = pmu_formats_string(&pmu->format); char *unknown_term; char *help_msg; if (asprintf(&unknown_term, "unknown term '%s' for pmu '%s'", - term->config, pmu_name) < 0) + term->config, pmu->name) < 0) unknown_term = NULL; help_msg = parse_events_formats_error_string(pmu_term); if (err) { @@ -1182,7 +1280,7 @@ static int pmu_config_term(const char *pmu_name, free(pmu_term); return -EINVAL; } - + perf_pmu_format__load(pmu, format); switch (format->value) { case PERF_PMU_FORMAT_VALUE_CONFIG: vp = &attr->config; @@ -1259,7 +1357,7 @@ static int pmu_config_term(const char *pmu_name, return 0; } -int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats, +int perf_pmu__config_terms(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms, bool zero, struct parse_events_error *err) @@ -1267,8 +1365,7 @@ int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats, struct parse_events_term *term; list_for_each_entry(term, head_terms, list) { - if (pmu_config_term(pmu_name, formats, attr, term, head_terms, - zero, err)) + if (pmu_config_term(pmu, attr, term, head_terms, zero, err)) return -EINVAL; } @@ -1286,25 +1383,25 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, { bool zero = !!pmu->default_config; - return perf_pmu__config_terms(pmu->name, &pmu->format, attr, - head_terms, zero, err); + return perf_pmu__config_terms(pmu, attr, head_terms, zero, err); } static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, struct parse_events_term *term) { struct perf_pmu_alias *alias; - char *name; + const char *name; if (parse_events__is_hardcoded_term(term)) return NULL; if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { - if (term->val.num != 1) + if (!term->no_value) return NULL; if (pmu_find_format(&pmu->format, term->config)) return NULL; name = term->config; + } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { if (strcasecmp(term->config, "event")) return NULL; @@ -1313,26 +1410,51 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, return NULL; } - list_for_each_entry(alias, &pmu->aliases, list) { - if (!strcasecmp(alias->name, name)) - return alias; + alias = perf_pmu__find_alias(pmu, name, /*load=*/ true); + if (alias || pmu->cpu_aliases_added) + return alias; + + /* Alias doesn't exist, try to get it from the json events. */ + if (pmu->events_table && + pmu_events_table__find_event(pmu->events_table, pmu, name, + pmu_add_cpu_aliases_map_callback, + pmu) == 0) { + alias = perf_pmu__find_alias(pmu, name, /*load=*/ false); } - return NULL; + return alias; } -static int check_info_data(struct perf_pmu_alias *alias, - struct perf_pmu_info *info) +static int check_info_data(struct perf_pmu *pmu, + struct perf_pmu_alias *alias, + struct perf_pmu_info *info, + struct parse_events_error *err, + int column) { + read_alias_info(pmu, alias); /* * Only one term in event definition can * define unit, scale and snapshot, fail * if there's more than one. */ - if ((info->unit && alias->unit[0]) || - (info->scale && alias->scale) || - (info->snapshot && alias->snapshot)) + if (info->unit && alias->unit[0]) { + parse_events_error__handle(err, column, + strdup("Attempt to set event's unit twice"), + NULL); return -EINVAL; + } + if (info->scale && alias->scale) { + parse_events_error__handle(err, column, + strdup("Attempt to set event's scale twice"), + NULL); + return -EINVAL; + } + if (info->snapshot && alias->snapshot) { + parse_events_error__handle(err, column, + strdup("Attempt to set event snapshot twice"), + NULL); + return -EINVAL; + } if (alias->unit[0]) info->unit = alias->unit; @@ -1351,7 +1473,7 @@ static int check_info_data(struct perf_pmu_alias *alias, * defined for the alias */ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, - struct perf_pmu_info *info) + struct perf_pmu_info *info, struct parse_events_error *err) { struct parse_events_term *term, *h; struct perf_pmu_alias *alias; @@ -1372,10 +1494,14 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, if (!alias) continue; ret = pmu_alias_terms(alias, &term->list); - if (ret) + if (ret) { + parse_events_error__handle(err, term->err_term, + strdup("Failure to duplicate terms"), + NULL); return ret; + } - ret = check_info_data(alias, info); + ret = check_info_data(pmu, alias, info, err, term->err_term); if (ret) return ret; @@ -1400,36 +1526,36 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, return 0; } -int perf_pmu__new_format(struct list_head *list, char *name, - int config, unsigned long *bits) -{ - struct perf_pmu_format *format; +struct find_event_args { + const char *event; + void *state; + pmu_event_callback cb; +}; - format = zalloc(sizeof(*format)); - if (!format) - return -ENOMEM; +static int find_event_callback(void *state, struct pmu_event_info *info) +{ + struct find_event_args *args = state; - format->name = strdup(name); - format->value = config; - memcpy(format->bits, bits, sizeof(format->bits)); + if (!strcmp(args->event, info->name)) + return args->cb(args->state, info); - list_add_tail(&format->list, list); return 0; } -void perf_pmu__set_format(unsigned long *bits, long from, long to) +int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb) { - long b; - - if (!to) - to = from; + struct find_event_args args = { + .event = event, + .state = state, + .cb = cb, + }; - memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); - for (b = from; b <= to; b++) - __set_bit(b, bits); + /* Sub-optimal, but function is only used by tests. */ + return perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ false, + &args, find_event_callback); } -void perf_pmu__del_formats(struct list_head *formats) +static void perf_pmu__del_formats(struct list_head *formats) { struct perf_pmu_format *fmt, *tmp; @@ -1466,15 +1592,145 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu) return !pmu->is_core || perf_pmus__num_core_pmus() == 1; } -bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name) +bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name) { - struct perf_pmu_alias *alias; + if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL) + return true; + if (pmu->cpu_aliases_added || !pmu->events_table) + return false; + return pmu_events_table__find_event(pmu->events_table, pmu, name, NULL, NULL) == 0; +} - list_for_each_entry(alias, &pmu->aliases, list) { - if (!strcmp(alias->name, name)) - return true; +size_t perf_pmu__num_events(struct perf_pmu *pmu) +{ + size_t nr; + + if (!pmu->sysfs_aliases_loaded) + pmu_aliases_parse(pmu); + + nr = pmu->sysfs_aliases; + + if (pmu->cpu_aliases_added) + nr += pmu->loaded_json_aliases; + else if (pmu->events_table) + nr += pmu_events_table__num_events(pmu->events_table, pmu) - pmu->loaded_json_aliases; + + return pmu->selectable ? nr + 1 : nr; +} + +static int sub_non_neg(int a, int b) +{ + if (b > a) + return 0; + return a - b; +} + +static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, + const struct perf_pmu_alias *alias, bool skip_duplicate_pmus) +{ + struct parse_events_term *term; + int pmu_name_len = skip_duplicate_pmus + ? pmu_name_len_no_suffix(pmu->name, /*num=*/NULL) + : (int)strlen(pmu->name); + int used = snprintf(buf, len, "%.*s/%s", pmu_name_len, pmu->name, alias->name); + + list_for_each_entry(term, &alias->terms, list) { + if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) + used += snprintf(buf + used, sub_non_neg(len, used), + ",%s=%s", term->config, + term->val.str); } - return false; + + if (sub_non_neg(len, used) > 0) { + buf[used] = '/'; + used++; + } + if (sub_non_neg(len, used) > 0) { + buf[used] = '\0'; + used++; + } else + buf[len - 1] = '\0'; + + return buf; +} + +int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, + void *state, pmu_event_callback cb) +{ + char buf[1024]; + struct perf_pmu_alias *event; + struct pmu_event_info info = { + .pmu = pmu, + }; + int ret = 0; + struct strbuf sb; + + strbuf_init(&sb, /*hint=*/ 0); + pmu_add_cpu_aliases(pmu); + list_for_each_entry(event, &pmu->aliases, list) { + size_t buf_used; + + info.pmu_name = event->pmu_name ?: pmu->name; + info.alias = NULL; + if (event->desc) { + info.name = event->name; + buf_used = 0; + } else { + info.name = format_alias(buf, sizeof(buf), pmu, event, + skip_duplicate_pmus); + if (pmu->is_core) { + info.alias = info.name; + info.name = event->name; + } + buf_used = strlen(buf) + 1; + } + info.scale_unit = NULL; + if (strlen(event->unit) || event->scale != 1.0) { + info.scale_unit = buf + buf_used; + buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, + "%G%s", event->scale, event->unit) + 1; + } + info.desc = event->desc; + info.long_desc = event->long_desc; + info.encoding_desc = buf + buf_used; + parse_events_term__to_strbuf(&event->terms, &sb); + buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, + "%s/%s/", info.pmu_name, sb.buf) + 1; + info.topic = event->topic; + info.str = sb.buf; + info.deprecated = event->deprecated; + ret = cb(state, &info); + if (ret) + goto out; + strbuf_setlen(&sb, /*len=*/ 0); + } + if (pmu->selectable) { + info.name = buf; + snprintf(buf, sizeof(buf), "%s//", pmu->name); + info.alias = NULL; + info.scale_unit = NULL; + info.desc = NULL; + info.long_desc = NULL; + info.encoding_desc = NULL; + info.topic = NULL; + info.pmu_name = pmu->name; + info.deprecated = false; + ret = cb(state, &info); + } +out: + strbuf_release(&sb); + return ret; +} + +bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name) +{ + return !strcmp(pmu->name, pmu_name) || + (pmu->is_uncore && pmu_uncore_alias_match(pmu_name, pmu->name)) || + /* + * jevents and tests use default_core as a marker for any core + * PMU as the PMU name varies across architectures. + */ + (pmu->is_core && !strcmp(pmu_name, "default_core")); } bool perf_pmu__is_software(const struct perf_pmu *pmu) @@ -1710,7 +1966,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, name ?: "N/A", buf, config_name, config); } -int perf_pmu__match(char *pattern, char *name, char *tok) +int perf_pmu__match(const char *pattern, const char *name, const char *tok) { if (!name) return -1; @@ -1756,17 +2012,19 @@ int perf_pmu__event_source_devices_fd(void) * then pathname will be filled with * "/sys/bus/event_source/devices/cs_etm/format" * - * Return 0 if the sysfs mountpoint couldn't be found or if no - * characters were written. + * Return 0 if the sysfs mountpoint couldn't be found, if no characters were + * written or if the buffer size is exceeded. */ int perf_pmu__pathname_scnprintf(char *buf, size_t size, const char *pmu_name, const char *filename) { - char base_path[PATH_MAX]; + size_t len; - if (!perf_pmu__event_source_devices_scnprintf(base_path, sizeof(base_path))) + len = perf_pmu__event_source_devices_scnprintf(buf, size); + if (!len || (len + strlen(pmu_name) + strlen(filename) + 1) >= size) return 0; - return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename); + + return scnprintf(buf + len, size - len, "%s/%s", pmu_name, filename); } int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags) @@ -1788,5 +2046,23 @@ void perf_pmu__delete(struct perf_pmu *pmu) zfree(&pmu->default_config); zfree(&pmu->name); zfree(&pmu->alias_name); + zfree(&pmu->id); free(pmu); } + +struct perf_pmu *pmu__find_core_pmu(void) +{ + struct perf_pmu *pmu = NULL; + + while ((pmu = perf_pmus__scan_core(pmu))) { + /* + * The cpumap should cover all CPUs. Otherwise, some CPUs may + * not support some events or have different event IDs. + */ + if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu) + return NULL; + + return pmu; + } + return NULL; +} diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b414cecbad2..6a4e170c61d6 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -39,7 +39,7 @@ struct perf_pmu_caps { */ struct perf_pmu { /** @name: The name of the PMU such as "cpu". */ - char *name; + const char *name; /** * @alias_name: Optional alternate name for the PMU determined in * architecture specific code. @@ -49,7 +49,7 @@ struct perf_pmu { * @id: Optional PMU identifier read from * <sysfs>/bus/event_source/devices/<name>/identifier. */ - char *id; + const char *id; /** * @type: Perf event attributed type value, read from * <sysfs>/bus/event_source/devices/<name>/type. @@ -114,6 +114,21 @@ struct perf_pmu { * from json events in pmu-events.c. */ struct list_head aliases; + /** + * @events_table: The events table for json events in pmu-events.c. + */ + const struct pmu_events_table *events_table; + /** @sysfs_aliases: Number of sysfs aliases loaded. */ + uint32_t sysfs_aliases; + /** @sysfs_aliases: Number of json event aliases loaded. */ + uint32_t loaded_json_aliases; + /** @sysfs_aliases_loaded: Are sysfs aliases loaded from disk? */ + bool sysfs_aliases_loaded; + /** + * @cpu_aliases_added: Have all json events table entries for the PMU + * been added? + */ + bool cpu_aliases_added; /** @caps_initialized: Has the list caps been initialized? */ bool caps_initialized; /** @nr_caps: The length of the list caps. */ @@ -158,88 +173,49 @@ struct perf_pmu_info { bool snapshot; }; -#define UNIT_MAX_LEN 31 /* max length for event unit name */ - -/** - * struct perf_pmu_alias - An event either read from sysfs or builtin in - * pmu-events.c, created by parsing the pmu-events json files. - */ -struct perf_pmu_alias { - /** @name: Name of the event like "mem-loads". */ - char *name; - /** @desc: Optional short description of the event. */ - char *desc; - /** @long_desc: Optional long description. */ - char *long_desc; - /** - * @topic: Optional topic such as cache or pipeline, particularly for - * json events. - */ - char *topic; - /** - * @str: Comma separated parameter list like - * "event=0xcd,umask=0x1,ldlat=0x3". - */ - char *str; - /** @terms: Owned list of the original parsed parameters. */ - struct list_head terms; - /** @list: List element of struct perf_pmu aliases. */ - struct list_head list; - /** @unit: Units for the event, such as bytes or cache lines. */ - char unit[UNIT_MAX_LEN+1]; - /** @scale: Value to scale read counter values by. */ - double scale; - /** - * @per_pkg: Does the file - * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or - * equivalent json value exist and have the value 1. - */ - bool per_pkg; - /** - * @snapshot: Does the file - * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot - * exist and have the value 1. - */ - bool snapshot; - /** - * @deprecated: Is the event hidden and so not shown in perf list by - * default. - */ +struct pmu_event_info { + const struct perf_pmu *pmu; + const char *name; + const char* alias; + const char *scale_unit; + const char *desc; + const char *long_desc; + const char *encoding_desc; + const char *topic; + const char *pmu_name; + const char *str; bool deprecated; - /** - * @pmu_name: The name copied from the json struct pmu_event. This can - * differ from the PMU name as it won't have suffixes. - */ - char *pmu_name; }; -void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu); +typedef int (*pmu_event_callback)(void *state, struct pmu_event_info *info); + +void pmu_add_sys_aliases(struct perf_pmu *pmu); int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms, struct parse_events_error *error); -int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats, +int perf_pmu__config_terms(struct perf_pmu *pmu, struct perf_event_attr *attr, struct list_head *head_terms, bool zero, struct parse_events_error *error); -__u64 perf_pmu__format_bits(struct list_head *formats, const char *name); -int perf_pmu__format_type(struct list_head *formats, const char *name); +__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name); +int perf_pmu__format_type(struct perf_pmu *pmu, const char *name); int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, - struct perf_pmu_info *info); -struct list_head *perf_pmu__alias(struct perf_pmu *pmu, - struct list_head *head_terms); -void perf_pmu_error(struct list_head *list, char *name, void *scanner, char const *msg); + struct perf_pmu_info *info, struct parse_events_error *err); +int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb); -int perf_pmu__new_format(struct list_head *list, char *name, - int config, unsigned long *bits); -void perf_pmu__set_format(unsigned long *bits, long from, long to); -int perf_pmu__format_parse(int dirfd, struct list_head *head); -void perf_pmu__del_formats(struct list_head *formats); +int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load); +void perf_pmu_format__set_value(void *format, int config, unsigned long *bits); bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name); bool is_pmu_core(const char *name); bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu); -bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name); +bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name); +size_t perf_pmu__num_events(struct perf_pmu *pmu); +int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, + void *state, pmu_event_callback cb); +bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name); + /** * perf_pmu_is_software - is the PMU a software PMU as in it uses the * perf_sw_context in the kernel? @@ -258,13 +234,12 @@ bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name); int perf_pmu__test(void); struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); -void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu, +void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, const struct pmu_events_table *table); char *perf_pmu__getcpuid(struct perf_pmu *pmu); const struct pmu_events_table *pmu_events_table__find(void); const struct pmu_metrics_table *pmu_metrics_table__find(void); -void perf_pmu_free_alias(struct perf_pmu_alias *alias); int perf_pmu__convert_scale(const char *scale, char **end, double *sval); @@ -275,10 +250,10 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, const char *config_name); void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu); -int perf_pmu__match(char *pattern, char *name, char *tok); +int perf_pmu__match(const char *pattern, const char *name, const char *tok); -char *pmu_find_real_name(const char *name); -char *pmu_find_alias_name(const char *name); +const char *pmu_find_real_name(const char *name); +const char *pmu_find_alias_name(const char *name); double perf_pmu__cpu_slots_per_cycle(void); int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size); int perf_pmu__pathname_scnprintf(char *buf, size_t size, @@ -289,5 +264,6 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name); struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus); void perf_pmu__delete(struct perf_pmu *pmu); +struct perf_pmu *pmu__find_core_pmu(void); #endif /* __PMU_H */ diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y index dff4e892ac4d..600c8c158c8e 100644 --- a/tools/perf/util/pmu.y +++ b/tools/perf/util/pmu.y @@ -1,6 +1,5 @@ %define api.pure full -%parse-param {struct list_head *format} -%parse-param {char *name} +%parse-param {void *format} %parse-param {void *scanner} %lex-param {void* scanner} @@ -11,6 +10,9 @@ #include <linux/bitmap.h> #include <string.h> #include "pmu.h" +#include "pmu-bison.h" + +int perf_pmu_lex(YYSTYPE * yylval_param , void *yyscanner); #define ABORT_ON(val) \ do { \ @@ -18,6 +20,20 @@ do { \ YYABORT; \ } while (0) +static void perf_pmu_error(void *format, void *scanner, const char *msg); + +static void perf_pmu__set_format(unsigned long *bits, long from, long to) +{ + long b; + + if (!to) + to = from; + + memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); + for (b = from; b <= to; b++) + __set_bit(b, bits); +} + %} %token PP_CONFIG @@ -42,16 +58,12 @@ format_term format_term: PP_CONFIG ':' bits { - ABORT_ON(perf_pmu__new_format(format, name, - PERF_PMU_FORMAT_VALUE_CONFIG, - $3)); + perf_pmu_format__set_value(format, PERF_PMU_FORMAT_VALUE_CONFIG, $3); } | PP_CONFIG PP_VALUE ':' bits { - ABORT_ON(perf_pmu__new_format(format, name, - $2, - $4)); + perf_pmu_format__set_value(format, $2, $4); } bits: @@ -78,9 +90,8 @@ PP_VALUE %% -void perf_pmu_error(struct list_head *list __maybe_unused, - char *name __maybe_unused, - void *scanner __maybe_unused, - char const *msg __maybe_unused) +static void perf_pmu_error(void *format __maybe_unused, + void *scanner __maybe_unused, + const char *msg __maybe_unused) { } diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index c58ba9fb6a36..6631367c756f 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/list.h> +#include <linux/list_sort.h> +#include <linux/string.h> #include <linux/zalloc.h> #include <subcmd/pager.h> #include <sys/types.h> +#include <ctype.h> #include <dirent.h> #include <pthread.h> #include <string.h> @@ -33,6 +36,31 @@ static LIST_HEAD(other_pmus); static bool read_sysfs_core_pmus; static bool read_sysfs_all_pmus; +int pmu_name_len_no_suffix(const char *str, unsigned long *num) +{ + int orig_len, len; + + orig_len = len = strlen(str); + + /* Non-uncore PMUs have their full length, for example, i915. */ + if (!strstarts(str, "uncore_")) + return len; + + /* + * Count trailing digits and '_', if '_{num}' suffix isn't present use + * the full length. + */ + while (len > 0 && isdigit(str[len - 1])) + len--; + + if (len > 0 && len != orig_len && str[len - 1] == '_') { + if (num) + *num = strtoul(&str[len], NULL, 10); + return len - 1; + } + return orig_len; +} + void perf_pmus__destroy(void) { struct perf_pmu *pmu, *tmp; @@ -122,6 +150,25 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name) return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name); } +static int pmus_cmp(void *priv __maybe_unused, + const struct list_head *lhs, const struct list_head *rhs) +{ + unsigned long lhs_num = 0, rhs_num = 0; + struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list); + struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list); + const char *lhs_pmu_name = lhs_pmu->name ?: ""; + const char *rhs_pmu_name = rhs_pmu->name ?: ""; + int lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name, &lhs_num); + int rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name, &rhs_num); + int ret = strncmp(lhs_pmu_name, rhs_pmu_name, + lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len); + + if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0) + return ret; + + return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0); +} + /* Add all pmus in sysfs to pmu list: */ static void pmu_read_sysfs(bool core_only) { @@ -156,6 +203,8 @@ static void pmu_read_sysfs(bool core_only) if (!perf_pmu__create_placeholder_core_pmu(&core_pmus)) pr_err("Failure to set up any core PMUs\n"); } + list_sort(NULL, &core_pmus, pmus_cmp); + list_sort(NULL, &other_pmus, pmus_cmp); if (!list_empty(&core_pmus)) { read_sysfs_core_pmus = true; if (!core_only) @@ -227,6 +276,43 @@ struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu) return NULL; } +static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu) +{ + bool use_core_pmus = !pmu || pmu->is_core; + int last_pmu_name_len = 0; + const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : ""; + + if (!pmu) { + pmu_read_sysfs(/*core_only=*/false); + pmu = list_prepare_entry(pmu, &core_pmus, list); + } else + last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", NULL); + + if (use_core_pmus) { + list_for_each_entry_continue(pmu, &core_pmus, list) { + int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); + + if (last_pmu_name_len == pmu_name_len && + !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) + continue; + + return pmu; + } + pmu = NULL; + pmu = list_prepare_entry(pmu, &other_pmus, list); + } + list_for_each_entry_continue(pmu, &other_pmus, list) { + int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL); + + if (last_pmu_name_len == pmu_name_len && + !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len)) + continue; + + return pmu; + } + return NULL; +} + const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str) { struct perf_pmu *pmu = NULL; @@ -258,219 +344,153 @@ int __weak perf_pmus__num_mem_pmus(void) struct sevent { /** PMU for event. */ const struct perf_pmu *pmu; - /** - * Optional event for name, desc, etc. If not present then this is a - * selectable PMU and the event name is shown as "//". - */ - const struct perf_pmu_alias *event; - /** Is the PMU for the CPU? */ - bool is_cpu; + const char *name; + const char* alias; + const char *scale_unit; + const char *desc; + const char *long_desc; + const char *encoding_desc; + const char *topic; + const char *pmu_name; + bool deprecated; }; static int cmp_sevent(const void *a, const void *b) { const struct sevent *as = a; const struct sevent *bs = b; - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *a_desc = NULL, *a_topic = ""; - const char *b_name = "//", *b_desc = NULL, *b_topic = ""; + bool a_iscpu, b_iscpu; int ret; - if (as->event) { - a_name = as->event->name; - a_desc = as->event->desc; - a_topic = as->event->topic ?: ""; - a_pmu_name = as->event->pmu_name; - } - if (bs->event) { - b_name = bs->event->name; - b_desc = bs->event->desc; - b_topic = bs->event->topic ?: ""; - b_pmu_name = bs->event->pmu_name; - } /* Put extra events last. */ - if (!!a_desc != !!b_desc) - return !!a_desc - !!b_desc; + if (!!as->desc != !!bs->desc) + return !!as->desc - !!bs->desc; /* Order by topics. */ - ret = strcmp(a_topic, b_topic); + ret = strcmp(as->topic ?: "", bs->topic ?: ""); if (ret) return ret; /* Order CPU core events to be first */ - if (as->is_cpu != bs->is_cpu) - return as->is_cpu ? -1 : 1; + a_iscpu = as->pmu ? as->pmu->is_core : true; + b_iscpu = bs->pmu ? bs->pmu->is_core : true; + if (a_iscpu != b_iscpu) + return a_iscpu ? -1 : 1; /* Order by PMU name. */ if (as->pmu != bs->pmu) { - a_pmu_name = a_pmu_name ?: (as->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: ""); - ret = strcmp(a_pmu_name, b_pmu_name); + ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: ""); if (ret) return ret; } /* Order by event name. */ - return strcmp(a_name, b_name); + return strcmp(as->name, bs->name); } -static bool pmu_alias_is_duplicate(struct sevent *alias_a, - struct sevent *alias_b) +static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *b) { - const char *a_pmu_name = NULL, *b_pmu_name = NULL; - const char *a_name = "//", *b_name = "//"; - - - if (alias_a->event) { - a_name = alias_a->event->name; - a_pmu_name = alias_a->event->pmu_name; - } - if (alias_b->event) { - b_name = alias_b->event->name; - b_pmu_name = alias_b->event->pmu_name; - } - /* Different names -> never duplicates */ - if (strcmp(a_name, b_name)) + if (strcmp(a->name ?: "//", b->name ?: "//")) return false; /* Don't remove duplicates for different PMUs */ - a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: ""); - b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: ""); - return strcmp(a_pmu_name, b_pmu_name) == 0; + return strcmp(a->pmu_name, b->pmu_name) == 0; } -static int sub_non_neg(int a, int b) -{ - if (b > a) - return 0; - return a - b; -} +struct events_callback_state { + struct sevent *aliases; + size_t aliases_len; + size_t index; +}; -static char *format_alias(char *buf, int len, const struct perf_pmu *pmu, - const struct perf_pmu_alias *alias) +static int perf_pmus__print_pmu_events__callback(void *vstate, + struct pmu_event_info *info) { - struct parse_events_term *term; - int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); - - list_for_each_entry(term, &alias->terms, list) { - if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) - used += snprintf(buf + used, sub_non_neg(len, used), - ",%s=%s", term->config, - term->val.str); - } + struct events_callback_state *state = vstate; + struct sevent *s; - if (sub_non_neg(len, used) > 0) { - buf[used] = '/'; - used++; + if (state->index >= state->aliases_len) { + pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name); + return 1; } - if (sub_non_neg(len, used) > 0) { - buf[used] = '\0'; - used++; - } else - buf[len - 1] = '\0'; - - return buf; + s = &state->aliases[state->index]; + s->pmu = info->pmu; +#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL + COPY_STR(name); + COPY_STR(alias); + COPY_STR(scale_unit); + COPY_STR(desc); + COPY_STR(long_desc); + COPY_STR(encoding_desc); + COPY_STR(topic); + COPY_STR(pmu_name); +#undef COPY_STR + s->deprecated = info->deprecated; + state->index++; + return 0; } void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state) { struct perf_pmu *pmu; - struct perf_pmu_alias *event; - char buf[1024]; int printed = 0; - int len, j; + int len; struct sevent *aliases; + struct events_callback_state state; + bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state); + struct perf_pmu *(*scan_fn)(struct perf_pmu *); + + if (skip_duplicate_pmus) + scan_fn = perf_pmus__scan_skip_duplicates; + else + scan_fn = perf_pmus__scan; pmu = NULL; len = 0; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - list_for_each_entry(event, &pmu->aliases, list) - len++; - if (pmu->selectable) - len++; - } + while ((pmu = scan_fn(pmu)) != NULL) + len += perf_pmu__num_events(pmu); + aliases = zalloc(sizeof(struct sevent) * len); if (!aliases) { pr_err("FATAL: not enough memory to print PMU events\n"); return; } pmu = NULL; - j = 0; - while ((pmu = perf_pmus__scan(pmu)) != NULL) { - bool is_cpu = pmu->is_core; - - list_for_each_entry(event, &pmu->aliases, list) { - aliases[j].event = event; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } - if (pmu->selectable) { - aliases[j].event = NULL; - aliases[j].pmu = pmu; - aliases[j].is_cpu = is_cpu; - j++; - } + state = (struct events_callback_state) { + .aliases = aliases, + .aliases_len = len, + .index = 0, + }; + while ((pmu = scan_fn(pmu)) != NULL) { + perf_pmu__for_each_event(pmu, skip_duplicate_pmus, &state, + perf_pmus__print_pmu_events__callback); } - len = j; qsort(aliases, len, sizeof(struct sevent), cmp_sevent); - for (j = 0; j < len; j++) { - const char *name, *alias = NULL, *scale_unit = NULL, - *desc = NULL, *long_desc = NULL, - *encoding_desc = NULL, *topic = NULL, - *pmu_name = NULL; - bool deprecated = false; - size_t buf_used; - + for (int j = 0; j < len; j++) { /* Skip duplicates */ if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1])) continue; - if (!aliases[j].event) { - /* A selectable event. */ - pmu_name = aliases[j].pmu->name; - buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1; - name = buf; - } else { - if (aliases[j].event->desc) { - name = aliases[j].event->name; - buf_used = 0; - } else { - name = format_alias(buf, sizeof(buf), aliases[j].pmu, - aliases[j].event); - if (aliases[j].is_cpu) { - alias = name; - name = aliases[j].event->name; - } - buf_used = strlen(buf) + 1; - } - pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: ""); - if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) { - scale_unit = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%G%s", aliases[j].event->scale, - aliases[j].event->unit) + 1; - } - desc = aliases[j].event->desc; - long_desc = aliases[j].event->long_desc; - topic = aliases[j].event->topic; - encoding_desc = buf + buf_used; - buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, - "%s/%s/", pmu_name, aliases[j].event->str) + 1; - deprecated = aliases[j].event->deprecated; - } print_cb->print_event(print_state, - pmu_name, - topic, - name, - alias, - scale_unit, - deprecated, + aliases[j].pmu_name, + aliases[j].topic, + aliases[j].name, + aliases[j].alias, + aliases[j].scale_unit, + aliases[j].deprecated, "Kernel PMU event", - desc, - long_desc, - encoding_desc); + aliases[j].desc, + aliases[j].long_desc, + aliases[j].encoding_desc); + zfree(&aliases[j].name); + zfree(&aliases[j].alias); + zfree(&aliases[j].scale_unit); + zfree(&aliases[j].desc); + zfree(&aliases[j].long_desc); + zfree(&aliases[j].encoding_desc); + zfree(&aliases[j].topic); + zfree(&aliases[j].pmu_name); } if (printed && pager_in_use()) printf("\n"); diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index a21464432d0f..4c67153ac257 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -5,6 +5,8 @@ struct perf_pmu; struct print_callbacks; +int pmu_name_len_no_suffix(const char *str, unsigned long *num); + void perf_pmus__destroy(void); struct perf_pmu *perf_pmus__find(const char *name); diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h index d7fab411e75c..bf4290bef0cd 100644 --- a/tools/perf/util/print-events.h +++ b/tools/perf/util/print-events.h @@ -26,6 +26,7 @@ struct print_callbacks { const char *expr, const char *threshold, const char *unit); + bool (*skip_duplicate_pmus)(void *print_state); }; /** Print all events, the default when no options are specified. */ diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 16822a8a540f..1a5b7fa459b2 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -53,6 +53,8 @@ bool probe_event_dry_run; /* Dry run flag */ struct probe_conf probe_conf = { .magic_num = DEFAULT_PROBE_MAGIC_NUM }; +static char *synthesize_perf_probe_point(struct perf_probe_point *pp); + #define semantic_error(msg ...) pr_err("Semantic error :" msg) int e_snprintf(char *str, size_t size, const char *format, ...) @@ -961,8 +963,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, debuginfo__delete(dinfo); if (ntevs == 0) { /* No error but failed to find probe point. */ - pr_warning("Probe point '%s' not found.\n", - synthesize_perf_probe_point(&pev->point)); + char *probe_point = synthesize_perf_probe_point(&pev->point); + pr_warning("Probe point '%s' not found.\n", probe_point); + free(probe_point); return -ENODEV; } else if (ntevs < 0) { /* Error path : ntevs < 0 */ @@ -2009,7 +2012,7 @@ out: } /* Compose only probe point (not argument) */ -char *synthesize_perf_probe_point(struct perf_probe_point *pp) +static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { struct strbuf buf; char *tmp, *ret = NULL; @@ -2062,14 +2065,18 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev) goto out; tmp = synthesize_perf_probe_point(&pev->point); - if (!tmp || strbuf_addstr(&buf, tmp) < 0) + if (!tmp || strbuf_addstr(&buf, tmp) < 0) { + free(tmp); goto out; + } free(tmp); for (i = 0; i < pev->nargs; i++) { tmp = synthesize_perf_probe_arg(pev->args + i); - if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0) + if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0) { + free(tmp); goto out; + } free(tmp); } @@ -2800,13 +2807,18 @@ static void warn_uprobe_event_compat(struct probe_trace_event *tev) if (!tev->uprobes || tev->nargs == 0 || !buf) goto out; - for (i = 0; i < tev->nargs; i++) - if (strglobmatch(tev->args[i].value, "[$@+-]*")) { - pr_warning("Please upgrade your kernel to at least " - "3.14 to have access to feature %s\n", + for (i = 0; i < tev->nargs; i++) { + if (strchr(tev->args[i].value, '@')) { + pr_warning("%s accesses a variable by symbol name, but that is not supported for user application probe.\n", tev->args[i].value); break; } + if (strglobmatch(tev->args[i].value, "[$+-]*")) { + pr_warning("Please upgrade your kernel to at least 3.14 to have access to feature %s\n", + tev->args[i].value); + break; + } + } out: free(buf); } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 8ad5b1579f1d..7e3b6c3d1f74 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -137,7 +137,6 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev); char *synthesize_perf_probe_command(struct perf_probe_event *pev); char *synthesize_probe_trace_command(struct probe_trace_event *tev); char *synthesize_perf_probe_arg(struct perf_probe_arg *pa); -char *synthesize_perf_probe_point(struct perf_probe_point *pp); int perf_probe_event__copy(struct perf_probe_event *dst, struct perf_probe_event *src); diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index d4c9b4cd35ef..26e1c8d973ea 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -40,3 +40,12 @@ util/rwsem.c util/hashmap.c util/perf_regs.c util/fncache.c +util/perf-regs-arch/perf_regs_aarch64.c +util/perf-regs-arch/perf_regs_arm.c +util/perf-regs-arch/perf_regs_csky.c +util/perf-regs-arch/perf_regs_loongarch.c +util/perf-regs-arch/perf_regs_mips.c +util/perf-regs-arch/perf_regs_powerpc.c +util/perf-regs-arch/perf_regs_riscv.c +util/perf-regs-arch/perf_regs_s390.c +util/perf-regs-arch/perf_regs_x86.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4eed8ec23994..c29f5f0bb552 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -113,6 +113,11 @@ bool evsel__is_aux_event(const struct evsel *evsel __maybe_unused) return false; } +bool perf_pmus__supports_extended_type(void) +{ + return false; +} + /* * Add this one here not to drag util/metricgroup.c */ diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c index c10b891dbad6..115b16edb451 100644 --- a/tools/perf/util/s390-sample-raw.c +++ b/tools/perf/util/s390-sample-raw.c @@ -27,7 +27,7 @@ #include "color.h" #include "sample-raw.h" #include "s390-cpumcf-kernel.h" -#include "pmu-events/pmu-events.h" +#include "util/pmu.h" #include "util/sample.h" static size_t ctrset_size(struct cf_ctrset_entry *set) @@ -132,56 +132,58 @@ static int get_counterset_start(int setnr) struct get_counter_name_data { int wanted; - const char *result; + char *result; }; -static int get_counter_name_callback(const struct pmu_event *evp, - const struct pmu_events_table *table __maybe_unused, - void *vdata) +static int get_counter_name_callback(void *vdata, struct pmu_event_info *info) { struct get_counter_name_data *data = vdata; int rc, event_nr; + const char *event_str; - if (evp->name == NULL || evp->event == NULL) + if (info->str == NULL) return 0; - rc = sscanf(evp->event, "event=%x", &event_nr); + + event_str = strstr(info->str, "event="); + if (!event_str) + return 0; + + rc = sscanf(event_str, "event=%x", &event_nr); if (rc == 1 && event_nr == data->wanted) { - data->result = evp->name; + data->result = strdup(info->name); return 1; /* Terminate the search. */ } return 0; } -/* Scan the PMU table and extract the logical name of a counter from the - * PMU events table. Input is the counter set and counter number with in the - * set. Construct the event number and use this as key. If they match return - * the name of this counter. +/* Scan the PMU and extract the logical name of a counter from the event. Input + * is the counter set and counter number with in the set. Construct the event + * number and use this as key. If they match return the name of this counter. * If no match is found a NULL pointer is returned. */ -static const char *get_counter_name(int set, int nr, const struct pmu_events_table *table) +static char *get_counter_name(int set, int nr, struct perf_pmu *pmu) { struct get_counter_name_data data = { .wanted = get_counterset_start(set) + nr, .result = NULL, }; - if (!table) + if (!pmu) return NULL; - pmu_events_table_for_each_event(table, get_counter_name_callback, &data); + perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ true, + &data, get_counter_name_callback); return data.result; } -static void s390_cpumcfdg_dump(struct perf_sample *sample) +static void s390_cpumcfdg_dump(struct perf_pmu *pmu, struct perf_sample *sample) { size_t i, len = sample->raw_size, offset = 0; unsigned char *buf = sample->raw_data; const char *color = PERF_COLOR_BLUE; struct cf_ctrset_entry *cep, ce; - const struct pmu_events_table *table; u64 *p; - table = pmu_events_table__find(); while (offset < len) { cep = (struct cf_ctrset_entry *)(buf + offset); @@ -199,11 +201,12 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample) color_fprintf(stdout, color, " [%#08zx] Counterset:%d" " Counters:%d\n", offset, ce.set, ce.ctr); for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) { - const char *ev_name = get_counter_name(ce.set, i, table); + char *ev_name = get_counter_name(ce.set, i, pmu); color_fprintf(stdout, color, "\tCounter:%03d %s Value:%#018lx\n", i, ev_name ?: "<unknown>", be64_to_cpu(*p)); + free(ev_name); } offset += ctrset_size(&ce); } @@ -216,14 +219,14 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample) */ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample) { - struct evsel *ev_bc000; + struct evsel *evsel; if (event->header.type != PERF_RECORD_SAMPLE) return; - ev_bc000 = evlist__event2evsel(evlist, event); - if (ev_bc000 == NULL || - ev_bc000->core.attr.config != PERF_EVENT_CPUM_CF_DIAG) + evsel = evlist__event2evsel(evlist, event); + if (evsel == NULL || + evsel->core.attr.config != PERF_EVENT_CPUM_CF_DIAG) return; /* Display raw data on screen */ @@ -231,5 +234,5 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, str pr_err("Invalid counter set data encountered\n"); return; } - s390_cpumcfdg_dump(sample); + s390_cpumcfdg_dump(evsel->pmu, sample); } diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build index c220fec97032..586b94e90f4e 100644 --- a/tools/perf/util/scripting-engines/Build +++ b/tools/perf/util/scripting-engines/Build @@ -5,4 +5,5 @@ perf-$(CONFIG_LIBPYTHON) += trace-event-python.o CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum -CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated-declarations -Wno-switch-enum +# -Wno-declaration-after-statement: The python headers have mixed code with declarations (decls after asserts, for instance) +CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated-declarations -Wno-switch-enum -Wno-declaration-after-statement diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 00d18c74c090..1e9aa8ed15b6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -833,8 +833,8 @@ static void perf_event__hdr_attr_swap(union perf_event *event, perf_event__attr_swap(&event->attr.attr); size = event->header.size; - size -= (void *)&event->attr.id - (void *)event; - mem_bswap_64(event->attr.id, size); + size -= perf_record_header_attr_id(event) - (void *)event; + mem_bswap_64(perf_record_header_attr_id(event), size); } static void perf_event__event_update_swap(union perf_event *event, diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 869738fc06c3..79d5e2955f85 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -66,6 +66,9 @@ if cc_is_clang: else: cflags += ['-Wno-cast-function-type' ] +# The python headers have mixed code with declarations (decls after asserts, for instance) +cflags += [ "-Wno-declaration-after-statement" ] + src_perf = getenv('srctree') + '/tools/perf' build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index d45d5dcb0e2b..afe6db8e7bf4 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -578,7 +578,7 @@ static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused if (!valid_only_metric(unit)) return; unit = fixunit(tbuf, os->evsel, unit); - snprintf(buf, sizeof buf, fmt, val); + snprintf(buf, sizeof(buf), fmt ?: "", val); ends = vals = skip_spaces(buf); while (isdigit(*ends) || *ends == '.') ends++; @@ -600,7 +600,7 @@ static void print_metric_only_json(struct perf_stat_config *config __maybe_unuse if (!valid_only_metric(unit)) return; unit = fixunit(tbuf, os->evsel, unit); - snprintf(buf, sizeof(buf), fmt, val); + snprintf(buf, sizeof(buf), fmt ?: "", val); ends = vals = skip_spaces(buf); while (isdigit(*ends) || *ends == '.') ends++; diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 967e583392c7..ec3506042217 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -729,7 +729,7 @@ size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp) size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) { - struct perf_stat_config sc; + struct perf_stat_config sc = {}; size_t ret; perf_event__read_stat_config(&sc, &event->stat_config); diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 5c62d3118c41..0e4dc31c6c9c 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -331,7 +331,7 @@ static char *cpu_model(void) file = fopen("/proc/cpuinfo", "r"); if (file) { while (fgets(buf, 255, file)) { - if (strstr(buf, "model name")) { + if (strcasestr(buf, "model name")) { strlcpy(cpu_m, &buf[13], 255); break; } diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 8bd466d1c2bd..95e99c332d7e 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1440,6 +1440,8 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map, curr_dso->kernel = dso->kernel; curr_dso->long_name = dso->long_name; curr_dso->long_name_len = dso->long_name_len; + curr_dso->binary_type = dso->binary_type; + curr_dso->adjust_symbols = dso->adjust_symbols; curr_map = map__new2(start, curr_dso); dso__put(curr_dso); if (curr_map == NULL) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index f849f9ef68e6..3f36675b7c8f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2204,15 +2204,20 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type)) return -1; + /* + * dso__load_sym() may copy 'dso' which will result in the copies having + * an incorrect long name unless we set it here first. + */ + dso__set_long_name(dso, vmlinux, vmlinux_allocated); + if (dso->kernel == DSO_SPACE__KERNEL_GUEST) + dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; + else + dso->binary_type = DSO_BINARY_TYPE__VMLINUX; + err = dso__load_sym(dso, map, &ss, &ss, 0); symsrc__destroy(&ss); if (err > 0) { - if (dso->kernel == DSO_SPACE__KERNEL_GUEST) - dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; - else - dso->binary_type = DSO_BINARY_TYPE__VMLINUX; - dso__set_long_name(dso, vmlinux, vmlinux_allocated); dso__set_loaded(dso); pr_debug("Using %s for symbols\n", symfs_vmlinux); } diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 45714a2785fd..a0579c7d7b9e 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2145,7 +2145,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr * return -ENOMEM; ev->attr.attr = *attr; - memcpy(ev->attr.id, id, ids * sizeof(u64)); + memcpy(perf_record_header_attr_id(ev), id, ids * sizeof(u64)); ev->attr.header.type = PERF_RECORD_HEADER_ATTR; ev->attr.header.size = (u16)size; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0b166404c5c3..fe5e6991ae4b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -80,6 +80,15 @@ err_thread: return NULL; } +static void (*thread__priv_destructor)(void *priv); + +void thread__set_priv_destructor(void (*destructor)(void *priv)) +{ + assert(thread__priv_destructor == NULL); + + thread__priv_destructor = destructor; +} + void thread__delete(struct thread *thread) { struct namespaces *namespaces, *tmp_namespaces; @@ -112,6 +121,10 @@ void thread__delete(struct thread *thread) exit_rwsem(thread__namespaces_lock(thread)); exit_rwsem(thread__comm_lock(thread)); thread__free_stitch_list(thread); + + if (thread__priv_destructor) + thread__priv_destructor(thread__priv(thread)); + RC_CHK_FREE(thread); } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9068a21ce0fa..e79225a0ea46 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -71,6 +71,8 @@ struct thread *thread__new(pid_t pid, pid_t tid); int thread__init_maps(struct thread *thread, struct machine *machine); void thread__delete(struct thread *thread); +void thread__set_priv_destructor(void (*destructor)(void *priv)); + struct thread *thread__get(struct thread *thread); void thread__put(struct thread *thread); diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 2a96df4c8d42..8554db3fc0d7 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -17,6 +17,7 @@ #include "event.h" #include "perf_regs.h" #include "callchain.h" +#include "util/env.h" static char *debuginfo_path; @@ -170,12 +171,14 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word * void *arg) { struct unwind_info *ui = arg; + const char *arch = perf_env__arch(ui->machine->env); struct stack_dump *stack = &ui->sample->user_stack; u64 start, end; int offset; int ret; - ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); + ret = perf_reg_value(&start, &ui->sample->user_regs, + perf_arch_reg_sp(arch)); if (ret) return false; @@ -253,6 +256,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, .max_stack = max_stack, .best_effort = best_effort }; + const char *arch = perf_env__arch(ui_buf.machine->env); Dwarf_Word ip; int err = -EINVAL, i; @@ -269,7 +273,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, if (!ui->dwfl) goto out; - err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); + err = perf_reg_value(&ip, &data->user_regs, perf_arch_reg_ip(arch)); if (err) goto out; diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index ebfde537b99b..c0641882fd2f 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -553,6 +553,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as, int __write, void *arg) { struct unwind_info *ui = arg; + const char *arch = perf_env__arch(ui->machine->env); struct stack_dump *stack = &ui->sample->user_stack; u64 start, end; int offset; @@ -565,7 +566,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as, } ret = perf_reg_value(&start, &ui->sample->user_regs, - LIBUNWIND__ARCH_REG_SP); + perf_arch_reg_sp(arch)); if (ret) return ret; @@ -714,6 +715,7 @@ static void _unwind__finish_access(struct maps *maps) static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { + const char *arch = perf_env__arch(ui->machine->env); u64 val; unw_word_t ips[max_stack]; unw_addr_space_t addr_space; @@ -721,7 +723,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, int ret, i = 0; ret = perf_reg_value(&val, &ui->sample->user_regs, - LIBUNWIND__ARCH_REG_IP); + perf_arch_reg_ip(arch)); if (ret) return ret; diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index b2a03fa5289b..9f7164c6d9aa 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -42,14 +42,6 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, #define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum) #endif -#ifndef LIBUNWIND__ARCH_REG_SP -#define LIBUNWIND__ARCH_REG_SP PERF_REG_SP -#endif - -#ifndef LIBUNWIND__ARCH_REG_IP -#define LIBUNWIND__ARCH_REG_IP PERF_REG_IP -#endif - int LIBUNWIND__ARCH_REG_ID(int regnum); int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized); void unwind__flush_access(struct maps *maps); diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index dc531805a570..b53753dee02f 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -57,7 +57,7 @@ LIB_MIN= 1 PACKAGE = cpupower PACKAGE_BUGREPORT = linux-pm@vger.kernel.org -LANGUAGES = de fr it cs pt +LANGUAGES = de fr it cs pt ka # Directory definitions. These are default and most probably diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index a73346e854b8..5fcc2a92957e 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -5,6 +5,7 @@ */ #include <linux/isst_if.h> +#include <sys/utsname.h> #include "isst.h" @@ -15,7 +16,7 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.16"; +static const char *version_str = "v1.17"; static const int supported_api_ver = 2; static struct isst_if_platform_info isst_platform_info; @@ -473,11 +474,44 @@ static unsigned int is_cpu_online(int cpu) return online; } +static int get_kernel_version(int *major, int *minor) +{ + struct utsname buf; + int ret; + + ret = uname(&buf); + if (ret) + return ret; + + ret = sscanf(buf.release, "%d.%d", major, minor); + if (ret != 2) + return ret; + + return 0; +} + +#define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER 6 +#define CPU0_HOTPLUG_DEPRECATE_MINOR_VER 5 + void set_cpu_online_offline(int cpu, int state) { char buffer[128]; int fd, ret; + if (!cpu) { + int major, minor; + + ret = get_kernel_version(&major, &minor); + if (!ret) { + if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER && + minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) { + debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor); + debug_printf("Use cgroups to isolate CPU 0\n"); + return; + } + } + } + snprintf(buffer, sizeof(buffer), "/sys/devices/system/cpu/cpu%d/online", cpu); @@ -778,6 +812,7 @@ static void create_cpu_map(void) map.cpu_map[0].logical_cpu); } else { update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]); + punit_id = cpu_map[i].punit_id; } } cpu_map[i].initialized = 1; @@ -2621,10 +2656,11 @@ static struct process_cmd_struct isst_cmds[] = { */ void parse_cpu_command(char *optarg) { - unsigned int start, end; + unsigned int start, end, invalid_count; char *next; next = optarg; + invalid_count = 0; while (next && *next) { if (*next == '-') /* no negative cpu numbers */ @@ -2634,6 +2670,8 @@ void parse_cpu_command(char *optarg) if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) target_cpus[max_target_cpus++] = start; + else + invalid_count = 1; if (*next == '\0') break; @@ -2660,6 +2698,8 @@ void parse_cpu_command(char *optarg) while (++start <= end) { if (max_target_cpus < MAX_CPUS_IN_ONE_REQ) target_cpus[max_target_cpus++] = start; + else + invalid_count = 1; } if (*next == ',') @@ -2668,6 +2708,13 @@ void parse_cpu_command(char *optarg) goto error; } + if (invalid_count) { + isst_ctdp_display_information_start(outf); + isst_display_error_info_message(1, "Too many CPUs in one request: max is", 1, MAX_CPUS_IN_ONE_REQ - 1); + isst_ctdp_display_information_end(outf); + exit(-1); + } + #ifdef DEBUG { int i; diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 0403d42ab1ba..14c9b037859a 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -442,7 +442,7 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level } if (ctdp_level->mem_freq) { - snprintf(header, sizeof(header), "mem-frequency(MHz)"); + snprintf(header, sizeof(header), "max-mem-frequency(MHz)"); snprintf(value, sizeof(value), "%d", ctdp_level->mem_freq); format_and_print(outf, level + 2, header, value); diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 54fc21575d56..8def22dec4a2 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -79,7 +79,7 @@ #define DISP_FREQ_MULTIPLIER 100 -#define MAX_PACKAGE_COUNT 8 +#define MAX_PACKAGE_COUNT 32 #define MAX_DIE_PER_PACKAGE 2 #define MAX_PUNIT_PER_DIE 8 diff --git a/tools/scripts/utilities.mak b/tools/scripts/utilities.mak index 172e47273b5d..d69d0345cc23 100644 --- a/tools/scripts/utilities.mak +++ b/tools/scripts/utilities.mak @@ -177,3 +177,23 @@ $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) endef _ge_attempt = $(or $(get-executable),$(call _gea_err,$(2))) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) + +# version-ge3 +# +# Usage $(call version-ge3,2.6.4,$(FLEX_VERSION)) +# +# To compare if a 3 component version is greater or equal to another, first use +# was to check the flex version to see if we can use compiler warnings as +# errors for one of the cases flex generates code C compilers complains about. + +version-ge3 = $(shell echo "$(1).$(2)" | awk -F'.' '{ printf("%d\n", (10000000 * $$1 + 10000 * $$2 + $$3) >= (10000000 * $$4 + 10000 * $$5 + $$6)) }') + +# version-lt3 +# +# Usage $(call version-lt3,2.6.2,$(FLEX_VERSION)) +# +# To compare if a 3 component version is less thjan another, first use was to +# check the flex version to see if we can use compiler warnings as errors for +# one of the cases flex generates code C compilers complains about. + +version-lt3 = $(shell echo "$(1).$(2)" | awk -F'.' '{ printf("%d\n", (10000000 * $$1 + 10000 * $$2 + $$3) < (10000000 * $$4 + 10000 * $$5 + $$6)) }') diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config index 873f3e06ccad..3bf506d4a63c 100644 --- a/tools/testing/kunit/configs/all_tests.config +++ b/tools/testing/kunit/configs/all_tests.config @@ -37,3 +37,8 @@ CONFIG_REGMAP_BUILD=y CONFIG_SECURITY=y CONFIG_SECURITY_APPARMOR=y + +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_TOPOLOGY_BUILD=y diff --git a/tools/testing/memblock/internal.h b/tools/testing/memblock/internal.h index fdb7f5db7308..f6c6e5474c3a 100644 --- a/tools/testing/memblock/internal.h +++ b/tools/testing/memblock/internal.h @@ -20,4 +20,8 @@ void memblock_free_pages(struct page *page, unsigned long pfn, { } +static inline void accept_memory(phys_addr_t start, phys_addr_t end) +{ +} + #endif diff --git a/tools/testing/memblock/mmzone.c b/tools/testing/memblock/mmzone.c index 7b0909e8b759..d3d58851864e 100644 --- a/tools/testing/memblock/mmzone.c +++ b/tools/testing/memblock/mmzone.c @@ -11,7 +11,7 @@ struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) return NULL; } -void reserve_bootmem_region(phys_addr_t start, phys_addr_t end) +void reserve_bootmem_region(phys_addr_t start, phys_addr_t end, int nid) { } diff --git a/tools/testing/memblock/tests/basic_api.c b/tools/testing/memblock/tests/basic_api.c index 411647094cc3..57bf2688edfd 100644 --- a/tools/testing/memblock/tests/basic_api.c +++ b/tools/testing/memblock/tests/basic_api.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include "basic_api.h" #include <string.h> #include <linux/memblock.h> -#include "basic_api.h" #define EXPECTED_MEMBLOCK_REGIONS 128 #define FUNC_ADD "memblock_add" diff --git a/tools/testing/memblock/tests/common.h b/tools/testing/memblock/tests/common.h index 4f23302ee677..b5ec59aa62d7 100644 --- a/tools/testing/memblock/tests/common.h +++ b/tools/testing/memblock/tests/common.h @@ -5,6 +5,7 @@ #include <stdlib.h> #include <assert.h> #include <linux/types.h> +#include <linux/seq_file.h> #include <linux/memblock.h> #include <linux/sizes.h> #include <linux/printk.h> diff --git a/tools/testing/radix-tree/maple.c b/tools/testing/radix-tree/maple.c index 75ea2081a317..e5da1cad70ba 100644 --- a/tools/testing/radix-tree/maple.c +++ b/tools/testing/radix-tree/maple.c @@ -45,6 +45,13 @@ struct rcu_test_struct2 { unsigned long last[RCU_RANGE_COUNT]; }; +struct rcu_test_struct3 { + struct maple_tree *mt; + unsigned long index; + unsigned long last; + bool stop; +}; + struct rcu_reader_struct { unsigned int id; int mod; @@ -34954,6 +34961,70 @@ void run_check_rcu(struct maple_tree *mt, struct rcu_test_struct *vals) MT_BUG_ON(mt, !vals->seen_entry2); } +static void *rcu_slot_store_reader(void *ptr) +{ + struct rcu_test_struct3 *test = ptr; + MA_STATE(mas, test->mt, test->index, test->index); + + rcu_register_thread(); + + rcu_read_lock(); + while (!test->stop) { + mas_walk(&mas); + /* The length of growth to both sides must be equal. */ + RCU_MT_BUG_ON(test, (test->index - mas.index) != + (mas.last - test->last)); + } + rcu_read_unlock(); + + rcu_unregister_thread(); + return NULL; +} + +static noinline void run_check_rcu_slot_store(struct maple_tree *mt) +{ + pthread_t readers[20]; + int range_cnt = 200, i, limit = 10000; + unsigned long len = ULONG_MAX / range_cnt, start, end; + struct rcu_test_struct3 test = {.stop = false, .mt = mt}; + + start = range_cnt / 2 * len; + end = start + len - 1; + test.index = start; + test.last = end; + + for (i = 0; i < range_cnt; i++) { + mtree_store_range(mt, i * len, i * len + len - 1, + xa_mk_value(i * 100), GFP_KERNEL); + } + + mt_set_in_rcu(mt); + MT_BUG_ON(mt, !mt_in_rcu(mt)); + + for (i = 0; i < ARRAY_SIZE(readers); i++) { + if (pthread_create(&readers[i], NULL, rcu_slot_store_reader, + &test)) { + perror("creating reader thread"); + exit(1); + } + } + + usleep(5); + + while (limit--) { + /* Step by step, expand the most middle range to both sides. */ + mtree_store_range(mt, --start, ++end, xa_mk_value(100), + GFP_KERNEL); + } + + test.stop = true; + + while (i--) + pthread_join(readers[i], NULL); + + mt_validate(mt); +} + static noinline void run_check_rcu_slowread(struct maple_tree *mt, struct rcu_test_struct *vals) { @@ -35206,6 +35277,10 @@ static noinline void __init check_rcu_threaded(struct maple_tree *mt) run_check_rcu(mt, &vals); mtree_destroy(mt); + /* Check expanding range in RCU mode */ + mt_init_flags(mt, MT_FLAGS_ALLOC_RANGE); + run_check_rcu_slot_store(mt); + mtree_destroy(mt); /* Forward writer for rcu stress */ mt_init_flags(mt, MT_FLAGS_ALLOC_RANGE); @@ -35383,7 +35458,9 @@ static noinline void __init check_prealloc(struct maple_tree *mt) for (i = 0; i <= max; i++) mtree_test_store_range(mt, i * 10, i * 10 + 5, &i); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + /* Spanning store */ + mas_set_range(&mas, 470, 500); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated == 0); @@ -35392,105 +35469,108 @@ static noinline void __init check_prealloc(struct maple_tree *mt) allocated = mas_allocated(&mas); MT_BUG_ON(mt, allocated != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); mas_destroy(&mas); allocated = mas_allocated(&mas); MT_BUG_ON(mt, allocated != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); mn = mas_pop_node(&mas); MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1); mn->parent = ma_parent_ptr(mn); ma_free_rcu(mn); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); mas_destroy(&mas); allocated = mas_allocated(&mas); MT_BUG_ON(mt, allocated != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); mn = mas_pop_node(&mas); MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); mas_destroy(&mas); allocated = mas_allocated(&mas); MT_BUG_ON(mt, allocated != 0); mn->parent = ma_parent_ptr(mn); ma_free_rcu(mn); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); mn = mas_pop_node(&mas); MT_BUG_ON(mt, mas_allocated(&mas) != allocated - 1); mas_push_node(&mas, mn); MT_BUG_ON(mt, mas_allocated(&mas) != allocated); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); mas_destroy(&mas); allocated = mas_allocated(&mas); MT_BUG_ON(mt, allocated != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); mas_store_prealloc(&mas, ptr); MT_BUG_ON(mt, mas_allocated(&mas) != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + /* Slot store does not need allocations */ + mas_set_range(&mas, 6, 9); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); - height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); - MT_BUG_ON(mt, allocated != 1 + height * 3); + MT_BUG_ON(mt, allocated != 0); mas_store_prealloc(&mas, ptr); MT_BUG_ON(mt, mas_allocated(&mas) != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + + mas_set_range(&mas, 6, 10); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); - MT_BUG_ON(mt, allocated != 1 + height * 3); + MT_BUG_ON(mt, allocated != 1); mas_store_prealloc(&mas, ptr); + MT_BUG_ON(mt, mas_allocated(&mas) != 0); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + /* Split */ + mas_set_range(&mas, 54, 54); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); - MT_BUG_ON(mt, allocated == 0); - MT_BUG_ON(mt, allocated != 1 + height * 3); + MT_BUG_ON(mt, allocated != 1 + height * 2); mas_store_prealloc(&mas, ptr); MT_BUG_ON(mt, mas_allocated(&mas) != 0); mt_set_non_kernel(1); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL & GFP_NOWAIT) == 0); + /* Spanning store */ + mas_set_range(&mas, 1, 100); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL & GFP_NOWAIT) == 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated != 0); mas_destroy(&mas); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL) != 0); + /* Spanning store */ + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL) != 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated == 0); MT_BUG_ON(mt, allocated != 1 + height * 3); mas_store_prealloc(&mas, ptr); MT_BUG_ON(mt, mas_allocated(&mas) != 0); + mas_set_range(&mas, 0, 200); mt_set_non_kernel(1); - MT_BUG_ON(mt, mas_preallocate(&mas, GFP_KERNEL & GFP_NOWAIT) == 0); + MT_BUG_ON(mt, mas_preallocate(&mas, ptr, GFP_KERNEL & GFP_NOWAIT) == 0); allocated = mas_allocated(&mas); height = mas_mt_height(&mas); MT_BUG_ON(mt, allocated != 0); diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c index e00520cc6349..cffaf2245d4f 100644 --- a/tools/testing/radix-tree/multiorder.c +++ b/tools/testing/radix-tree/multiorder.c @@ -159,7 +159,7 @@ void multiorder_tagged_iteration(struct xarray *xa) item_kill_tree(xa); } -bool stop_iteration = false; +bool stop_iteration; static void *creator_func(void *ptr) { @@ -201,6 +201,7 @@ static void multiorder_iteration_race(struct xarray *xa) pthread_t worker_thread[num_threads]; int i; + stop_iteration = false; pthread_create(&worker_thread[0], NULL, &creator_func, xa); for (i = 1; i < num_threads; i++) pthread_create(&worker_thread[i], NULL, &iterator_func, xa); @@ -211,6 +212,61 @@ static void multiorder_iteration_race(struct xarray *xa) item_kill_tree(xa); } +static void *load_creator(void *ptr) +{ + /* 'order' is set up to ensure we have sibling entries */ + unsigned int order; + struct radix_tree_root *tree = ptr; + int i; + + rcu_register_thread(); + item_insert_order(tree, 3 << RADIX_TREE_MAP_SHIFT, 0); + item_insert_order(tree, 2 << RADIX_TREE_MAP_SHIFT, 0); + for (i = 0; i < 10000; i++) { + for (order = 1; order < RADIX_TREE_MAP_SHIFT; order++) { + unsigned long index = (3 << RADIX_TREE_MAP_SHIFT) - + (1 << order); + item_insert_order(tree, index, order); + item_delete_rcu(tree, index); + } + } + rcu_unregister_thread(); + + stop_iteration = true; + return NULL; +} + +static void *load_worker(void *ptr) +{ + unsigned long index = (3 << RADIX_TREE_MAP_SHIFT) - 1; + + rcu_register_thread(); + while (!stop_iteration) { + struct item *item = xa_load(ptr, index); + assert(!xa_is_internal(item)); + } + rcu_unregister_thread(); + + return NULL; +} + +static void load_race(struct xarray *xa) +{ + const int num_threads = sysconf(_SC_NPROCESSORS_ONLN) * 4; + pthread_t worker_thread[num_threads]; + int i; + + stop_iteration = false; + pthread_create(&worker_thread[0], NULL, &load_creator, xa); + for (i = 1; i < num_threads; i++) + pthread_create(&worker_thread[i], NULL, &load_worker, xa); + + for (i = 0; i < num_threads; i++) + pthread_join(worker_thread[i], NULL); + + item_kill_tree(xa); +} + static DEFINE_XARRAY(array); void multiorder_checks(void) @@ -218,12 +274,20 @@ void multiorder_checks(void) multiorder_iteration(&array); multiorder_tagged_iteration(&array); multiorder_iteration_race(&array); + load_race(&array); radix_tree_cpu_dead(0); } -int __weak main(void) +int __weak main(int argc, char **argv) { + int opt; + + while ((opt = getopt(argc, argv, "ls:v")) != -1) { + if (opt == 'v') + test_verbose++; + } + rcu_register_thread(); radix_tree_init(); multiorder_checks(); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 42806add0114..1a21d6beebc6 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -92,7 +92,7 @@ endif TARGETS += tmpfs TARGETS += tpm2 TARGETS += tty -TARGETS += uevents +TARGETS += uevent TARGETS += user TARGETS += user_events TARGETS += vDSO diff --git a/tools/testing/selftests/alsa/conf.c b/tools/testing/selftests/alsa/conf.c index d7aafe5a1993..2f1685a3eae1 100644 --- a/tools/testing/selftests/alsa/conf.c +++ b/tools/testing/selftests/alsa/conf.c @@ -431,7 +431,6 @@ long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def) { snd_config_t *cfg; - long l; int ret; if (!root) diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c index c95d63e553f4..21e482b23f50 100644 --- a/tools/testing/selftests/alsa/mixer-test.c +++ b/tools/testing/selftests/alsa/mixer-test.c @@ -188,7 +188,7 @@ static int wait_for_event(struct ctl_data *ctl, int timeout) { unsigned short revents; snd_ctl_event_t *event; - int count, err; + int err; unsigned int mask = 0; unsigned int ev_id; @@ -430,7 +430,6 @@ static bool strend(const char *haystack, const char *needle) static void test_ctl_name(struct ctl_data *ctl) { bool name_ok = true; - bool check; ksft_print_msg("%d.%d %s\n", ctl->card->card, ctl->elem, ctl->name); @@ -863,7 +862,6 @@ static bool test_ctl_write_invalid_value(struct ctl_data *ctl, snd_ctl_elem_value_t *val) { int err; - long val_read; /* Ideally this will fail... */ err = snd_ctl_elem_write(ctl->card->handle, val); @@ -883,8 +881,7 @@ static bool test_ctl_write_invalid_value(struct ctl_data *ctl, static bool test_ctl_write_invalid_boolean(struct ctl_data *ctl) { - int err, i; - long val_read; + int i; bool fail = false; snd_ctl_elem_value_t *val; snd_ctl_elem_value_alloca(&val); @@ -994,8 +991,7 @@ static bool test_ctl_write_invalid_integer64(struct ctl_data *ctl) static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) { - int err, i; - unsigned int val_read; + int i; bool fail = false; snd_ctl_elem_value_t *val; snd_ctl_elem_value_alloca(&val); @@ -1027,7 +1023,6 @@ static bool test_ctl_write_invalid_enumerated(struct ctl_data *ctl) static void test_ctl_write_invalid(struct ctl_data *ctl) { bool pass; - int err; /* If the control is turned off let's be polite */ if (snd_ctl_elem_info_is_inactive(ctl->info)) { diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c index b7eef32addb4..c0a39818c5a4 100644 --- a/tools/testing/selftests/alsa/pcm-test.c +++ b/tools/testing/selftests/alsa/pcm-test.c @@ -257,7 +257,9 @@ static void find_pcms(void) static void test_pcm_time(struct pcm_data *data, enum test_class class, const char *test_name, snd_config_t *pcm_cfg) { - char name[64], key[128], msg[256]; + char name[64], msg[256]; + const int duration_s = 2, margin_ms = 100; + const int duration_ms = duration_s * 1000; const char *cs; int i, err; snd_pcm_t *handle = NULL; @@ -442,7 +444,7 @@ __format: skip = false; timestamp_now(&tstamp); - for (i = 0; i < 4; i++) { + for (i = 0; i < duration_s; i++) { if (data->stream == SND_PCM_STREAM_PLAYBACK) { frames = snd_pcm_writei(handle, samples, rate); if (frames < 0) { @@ -472,8 +474,8 @@ __format: snd_pcm_drain(handle); ms = timestamp_diff_ms(&tstamp); - if (ms < 3900 || ms > 4100) { - snprintf(msg, sizeof(msg), "time mismatch: expected 4000ms got %lld", ms); + if (ms < duration_ms - margin_ms || ms > duration_ms + margin_ms) { + snprintf(msg, sizeof(msg), "time mismatch: expected %dms got %lld", duration_ms, ms); goto __close; } @@ -565,7 +567,7 @@ int main(void) { struct card_data *card; struct pcm_data *pcm; - snd_config_t *global_config, *cfg, *pcm_cfg; + snd_config_t *global_config, *cfg; int num_pcm_tests = 0, num_tests, num_std_pcm_tests; int ret; void *thread_ret; diff --git a/tools/testing/selftests/alsa/test-pcmtest-driver.c b/tools/testing/selftests/alsa/test-pcmtest-driver.c index 357adc722cba..a52ecd43dbe3 100644 --- a/tools/testing/selftests/alsa/test-pcmtest-driver.c +++ b/tools/testing/selftests/alsa/test-pcmtest-driver.c @@ -313,7 +313,6 @@ TEST_F(pcmtest, ni_playback) { */ TEST_F(pcmtest, reset_ioctl) { snd_pcm_t *handle; - unsigned char *it; int test_res; struct pcmtest_test_params *params = &self->params; diff --git a/tools/testing/selftests/bpf/DENYLIST.aarch64 b/tools/testing/selftests/bpf/DENYLIST.aarch64 index 7f768d335698..1e827c24761c 100644 --- a/tools/testing/selftests/bpf/DENYLIST.aarch64 +++ b/tools/testing/selftests/bpf/DENYLIST.aarch64 @@ -1,14 +1,9 @@ bpf_cookie/multi_kprobe_attach_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3 bpf_cookie/multi_kprobe_link_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3 +exceptions # JIT does not support calling kfunc bpf_throw: -524 fexit_sleep # The test never returns. The remaining tests cannot start. -kprobe_multi_bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95 -kprobe_multi_test/attach_api_addrs # bpf_program__attach_kprobe_multi_opts unexpected error: -95 -kprobe_multi_test/attach_api_pattern # bpf_program__attach_kprobe_multi_opts unexpected error: -95 -kprobe_multi_test/attach_api_syms # bpf_program__attach_kprobe_multi_opts unexpected error: -95 -kprobe_multi_test/bench_attach # bpf_program__attach_kprobe_multi_opts unexpected error: -95 -kprobe_multi_test/link_api_addrs # link_fd unexpected link_fd: actual -95 < expected 0 -kprobe_multi_test/link_api_syms # link_fd unexpected link_fd: actual -95 < expected 0 -kprobe_multi_test/skel_api # libbpf: failed to load BPF skeleton 'kprobe_multi': -3 +kprobe_multi_bench_attach # needs CONFIG_FPROBE +kprobe_multi_test # needs CONFIG_FPROBE module_attach # prog 'kprobe_multi': failed to auto-attach: -95 fentry_test/fentry_many_args # fentry_many_args:FAIL:fentry_many_args_attach unexpected error: -524 fexit_test/fexit_many_args # fexit_many_args:FAIL:fexit_many_args_attach unexpected error: -524 diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x index 5061d9e24c16..ce6f291665cf 100644 --- a/tools/testing/selftests/bpf/DENYLIST.s390x +++ b/tools/testing/selftests/bpf/DENYLIST.s390x @@ -6,6 +6,7 @@ bpf_loop # attaches to __x64_sys_nanosleep cgrp_local_storage # prog_attach unexpected error: -524 (trampoline) dynptr/test_dynptr_skb_data dynptr/test_skb_readonly +exceptions # JIT does not support calling kfunc bpf_throw (exceptions) fexit_sleep # fexit_skel_load fexit skeleton failed (trampoline) get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace) iters/testmod_seq* # s390x doesn't support kfuncs in modules yet diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index edef49fcd23e..caede9b574cb 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -50,14 +50,17 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_cgroup_storage \ test_tcpnotify_user test_sysctl \ test_progs-no_alu32 +TEST_INST_SUBDIRS := no_alu32 # Also test bpf-gcc, if present ifneq ($(BPF_GCC),) TEST_GEN_PROGS += test_progs-bpf_gcc +TEST_INST_SUBDIRS += bpf_gcc endif ifneq ($(CLANG_CPUV4),) TEST_GEN_PROGS += test_progs-cpuv4 +TEST_INST_SUBDIRS += cpuv4 endif TEST_GEN_FILES = test_lwt_ip_encap.bpf.o test_tc_edt.bpf.o @@ -714,3 +717,12 @@ EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \ # Delete partially updated (corrupted) files on error .DELETE_ON_ERROR: + +DEFAULT_INSTALL_RULE := $(INSTALL_RULE) +override define INSTALL_RULE + $(DEFAULT_INSTALL_RULE) + @for DIR in $(TEST_INST_SUBDIRS); do \ + mkdir -p $(INSTALL_PATH)/$$DIR; \ + rsync -a $(OUTPUT)/$$DIR/*.bpf.o $(INSTALL_PATH)/$$DIR;\ + done +endef diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index 209811b1993a..9aa29564bd74 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -131,4 +131,323 @@ extern int bpf_rbtree_add_impl(struct bpf_rb_root *root, struct bpf_rb_node *nod */ extern struct bpf_rb_node *bpf_rbtree_first(struct bpf_rb_root *root) __ksym; +/* Description + * Allocates a percpu object of the type represented by 'local_type_id' in + * program BTF. User may use the bpf_core_type_id_local macro to pass the + * type ID of a struct in program BTF. + * + * The 'local_type_id' parameter must be a known constant. + * The 'meta' parameter is rewritten by the verifier, no need for BPF + * program to set it. + * Returns + * A pointer to a percpu object of the type corresponding to the passed in + * 'local_type_id', or NULL on failure. + */ +extern void *bpf_percpu_obj_new_impl(__u64 local_type_id, void *meta) __ksym; + +/* Convenience macro to wrap over bpf_percpu_obj_new_impl */ +#define bpf_percpu_obj_new(type) ((type __percpu_kptr *)bpf_percpu_obj_new_impl(bpf_core_type_id_local(type), NULL)) + +/* Description + * Free an allocated percpu object. All fields of the object that require + * destruction will be destructed before the storage is freed. + * + * The 'meta' parameter is rewritten by the verifier, no need for BPF + * program to set it. + * Returns + * Void. + */ +extern void bpf_percpu_obj_drop_impl(void *kptr, void *meta) __ksym; + +/* Convenience macro to wrap over bpf_obj_drop_impl */ +#define bpf_percpu_obj_drop(kptr) bpf_percpu_obj_drop_impl(kptr, NULL) + +/* Description + * Throw a BPF exception from the program, immediately terminating its + * execution and unwinding the stack. The supplied 'cookie' parameter + * will be the return value of the program when an exception is thrown, + * and the default exception callback is used. Otherwise, if an exception + * callback is set using the '__exception_cb(callback)' declaration tag + * on the main program, the 'cookie' parameter will be the callback's only + * input argument. + * + * Thus, in case of default exception callback, 'cookie' is subjected to + * constraints on the program's return value (as with R0 on exit). + * Otherwise, the return value of the marked exception callback will be + * subjected to the same checks. + * + * Note that throwing an exception with lingering resources (locks, + * references, etc.) will lead to a verification error. + * + * Note that callbacks *cannot* call this helper. + * Returns + * Never. + * Throws + * An exception with the specified 'cookie' value. + */ +extern void bpf_throw(u64 cookie) __ksym; + +/* This macro must be used to mark the exception callback corresponding to the + * main program. For example: + * + * int exception_cb(u64 cookie) { + * return cookie; + * } + * + * SEC("tc") + * __exception_cb(exception_cb) + * int main_prog(struct __sk_buff *ctx) { + * ... + * return TC_ACT_OK; + * } + * + * Here, exception callback for the main program will be 'exception_cb'. Note + * that this attribute can only be used once, and multiple exception callbacks + * specified for the main program will lead to verification error. + */ +#define __exception_cb(name) __attribute__((btf_decl_tag("exception_callback:" #name))) + +#define __bpf_assert_signed(x) _Generic((x), \ + unsigned long: 0, \ + unsigned long long: 0, \ + signed long: 1, \ + signed long long: 1 \ +) + +#define __bpf_assert_check(LHS, op, RHS) \ + _Static_assert(sizeof(&(LHS)), "1st argument must be an lvalue expression"); \ + _Static_assert(sizeof(LHS) == 8, "Only 8-byte integers are supported\n"); \ + _Static_assert(__builtin_constant_p(__bpf_assert_signed(LHS)), "internal static assert"); \ + _Static_assert(__builtin_constant_p((RHS)), "2nd argument must be a constant expression") + +#define __bpf_assert(LHS, op, cons, RHS, VAL) \ + ({ \ + (void)bpf_throw; \ + asm volatile ("if %[lhs] " op " %[rhs] goto +2; r1 = %[value]; call bpf_throw" \ + : : [lhs] "r"(LHS), [rhs] cons(RHS), [value] "ri"(VAL) : ); \ + }) + +#define __bpf_assert_op_sign(LHS, op, cons, RHS, VAL, supp_sign) \ + ({ \ + __bpf_assert_check(LHS, op, RHS); \ + if (__bpf_assert_signed(LHS) && !(supp_sign)) \ + __bpf_assert(LHS, "s" #op, cons, RHS, VAL); \ + else \ + __bpf_assert(LHS, #op, cons, RHS, VAL); \ + }) + +#define __bpf_assert_op(LHS, op, RHS, VAL, supp_sign) \ + ({ \ + if (sizeof(typeof(RHS)) == 8) { \ + const typeof(RHS) rhs_var = (RHS); \ + __bpf_assert_op_sign(LHS, op, "r", rhs_var, VAL, supp_sign); \ + } else { \ + __bpf_assert_op_sign(LHS, op, "i", RHS, VAL, supp_sign); \ + } \ + }) + +/* Description + * Assert that a conditional expression is true. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert(cond) if (!(cond)) bpf_throw(0); + +/* Description + * Assert that a conditional expression is true. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_with(cond, value) if (!(cond)) bpf_throw(value); + +/* Description + * Assert that LHS is equal to RHS. This statement updates the known value + * of LHS during verification. Note that RHS must be a constant value, and + * must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_eq(LHS, RHS) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, ==, RHS, 0, true); \ + }) + +/* Description + * Assert that LHS is equal to RHS. This statement updates the known value + * of LHS during verification. Note that RHS must be a constant value, and + * must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_eq_with(LHS, RHS, value) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, ==, RHS, value, true); \ + }) + +/* Description + * Assert that LHS is less than RHS. This statement updates the known + * bounds of LHS during verification. Note that RHS must be a constant + * value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_lt(LHS, RHS) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, <, RHS, 0, false); \ + }) + +/* Description + * Assert that LHS is less than RHS. This statement updates the known + * bounds of LHS during verification. Note that RHS must be a constant + * value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_lt_with(LHS, RHS, value) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, <, RHS, value, false); \ + }) + +/* Description + * Assert that LHS is greater than RHS. This statement updates the known + * bounds of LHS during verification. Note that RHS must be a constant + * value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_gt(LHS, RHS) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >, RHS, 0, false); \ + }) + +/* Description + * Assert that LHS is greater than RHS. This statement updates the known + * bounds of LHS during verification. Note that RHS must be a constant + * value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_gt_with(LHS, RHS, value) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >, RHS, value, false); \ + }) + +/* Description + * Assert that LHS is less than or equal to RHS. This statement updates the + * known bounds of LHS during verification. Note that RHS must be a + * constant value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_le(LHS, RHS) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, <=, RHS, 0, false); \ + }) + +/* Description + * Assert that LHS is less than or equal to RHS. This statement updates the + * known bounds of LHS during verification. Note that RHS must be a + * constant value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_le_with(LHS, RHS, value) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, <=, RHS, value, false); \ + }) + +/* Description + * Assert that LHS is greater than or equal to RHS. This statement updates + * the known bounds of LHS during verification. Note that RHS must be a + * constant value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_ge(LHS, RHS) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >=, RHS, 0, false); \ + }) + +/* Description + * Assert that LHS is greater than or equal to RHS. This statement updates + * the known bounds of LHS during verification. Note that RHS must be a + * constant value, and must fit within the data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_ge_with(LHS, RHS, value) \ + ({ \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >=, RHS, value, false); \ + }) + +/* Description + * Assert that LHS is in the range [BEG, END] (inclusive of both). This + * statement updates the known bounds of LHS during verification. Note + * that both BEG and END must be constant values, and must fit within the + * data type of LHS. + * Returns + * Void. + * Throws + * An exception with the value zero when the assertion fails. + */ +#define bpf_assert_range(LHS, BEG, END) \ + ({ \ + _Static_assert(BEG <= END, "BEG must be <= END"); \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >=, BEG, 0, false); \ + __bpf_assert_op(LHS, <=, END, 0, false); \ + }) + +/* Description + * Assert that LHS is in the range [BEG, END] (inclusive of both). This + * statement updates the known bounds of LHS during verification. Note + * that both BEG and END must be constant values, and must fit within the + * data type of LHS. + * Returns + * Void. + * Throws + * An exception with the specified value when the assertion fails. + */ +#define bpf_assert_range_with(LHS, BEG, END, value) \ + ({ \ + _Static_assert(BEG <= END, "BEG must be <= END"); \ + barrier_var(LHS); \ + __bpf_assert_op(LHS, >=, BEG, value, false); \ + __bpf_assert_op(LHS, <=, END, value, false); \ + }) + #endif diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config index 1c7584e8dd9e..e41eb33b2704 100644 --- a/tools/testing/selftests/bpf/config +++ b/tools/testing/selftests/bpf/config @@ -4,6 +4,7 @@ CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y CONFIG_BPF=y CONFIG_BPF_EVENTS=y CONFIG_BPF_JIT=y +CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_BPF_LIRC_MODE2=y CONFIG_BPF_LSM=y CONFIG_BPF_STREAM_PARSER=y diff --git a/tools/testing/selftests/bpf/config.x86_64 b/tools/testing/selftests/bpf/config.x86_64 index b650b2e617b8..2e70a6048278 100644 --- a/tools/testing/selftests/bpf/config.x86_64 +++ b/tools/testing/selftests/bpf/config.x86_64 @@ -20,7 +20,6 @@ CONFIG_BLK_DEV_THROTTLING=y CONFIG_BONDING=y CONFIG_BOOTTIME_TRACING=y CONFIG_BPF_JIT_ALWAYS_ON=y -CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_BPF_PRELOAD=y CONFIG_BPF_PRELOAD_UMD=y CONFIG_BPFILTER=y diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_obj_pinning.c b/tools/testing/selftests/bpf/prog_tests/bpf_obj_pinning.c index 31f1e815f671..ee0458a5ce78 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_obj_pinning.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_obj_pinning.c @@ -8,6 +8,7 @@ #include <linux/unistd.h> #include <linux/mount.h> #include <sys/syscall.h> +#include "bpf/libbpf_internal.h" static inline int sys_fsopen(const char *fsname, unsigned flags) { @@ -155,7 +156,7 @@ static void validate_pin(int map_fd, const char *map_name, int src_value, ASSERT_OK(err, "obj_pin"); /* cleanup */ - if (pin_opts.path_fd >= 0) + if (path_kind == PATH_FD_REL && pin_opts.path_fd >= 0) close(pin_opts.path_fd); if (old_cwd[0]) ASSERT_OK(chdir(old_cwd), "restore_cwd"); @@ -220,7 +221,7 @@ static void validate_get(int map_fd, const char *map_name, int src_value, goto cleanup; /* cleanup */ - if (get_opts.path_fd >= 0) + if (path_kind == PATH_FD_REL && get_opts.path_fd >= 0) close(get_opts.path_fd); if (old_cwd[0]) ASSERT_OK(chdir(old_cwd), "restore_cwd"); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c index a53c254c6058..4aabeaa525d4 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c @@ -185,6 +185,8 @@ static void test_cubic(void) do_test("bpf_cubic", NULL); + ASSERT_EQ(cubic_skel->bss->bpf_cubic_acked_called, 1, "pkts_acked called"); + bpf_link__destroy(link); bpf_cubic__destroy(cubic_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 4e0cdb593318..92d51f377fe5 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -7296,7 +7296,7 @@ static struct btf_dedup_test dedup_tests[] = { BTF_FUNC_PROTO_ENC(0, 2), /* [3] */ BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1), BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(3), 1), - BTF_FUNC_ENC(NAME_NTH(4), 2), /* [4] */ + BTF_FUNC_ENC(NAME_NTH(4), 3), /* [4] */ /* tag -> t */ BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [5] */ BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [6] */ @@ -7317,7 +7317,7 @@ static struct btf_dedup_test dedup_tests[] = { BTF_FUNC_PROTO_ENC(0, 2), /* [3] */ BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1), BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(3), 1), - BTF_FUNC_ENC(NAME_NTH(4), 2), /* [4] */ + BTF_FUNC_ENC(NAME_NTH(4), 3), /* [4] */ BTF_DECL_TAG_ENC(NAME_NTH(5), 2, -1), /* [5] */ BTF_DECL_TAG_ENC(NAME_NTH(5), 4, -1), /* [6] */ BTF_DECL_TAG_ENC(NAME_NTH(5), 4, 1), /* [7] */ diff --git a/tools/testing/selftests/bpf/prog_tests/d_path.c b/tools/testing/selftests/bpf/prog_tests/d_path.c index 911345c526e6..ccc768592e66 100644 --- a/tools/testing/selftests/bpf/prog_tests/d_path.c +++ b/tools/testing/selftests/bpf/prog_tests/d_path.c @@ -12,6 +12,17 @@ #include "test_d_path_check_rdonly_mem.skel.h" #include "test_d_path_check_types.skel.h" +/* sys_close_range is not around for long time, so let's + * make sure we can call it on systems with older glibc + */ +#ifndef __NR_close_range +#ifdef __alpha__ +#define __NR_close_range 546 +#else +#define __NR_close_range 436 +#endif +#endif + static int duration; static struct { @@ -90,7 +101,11 @@ static int trigger_fstat_events(pid_t pid) fstat(indicatorfd, &fileStat); out_close: - /* triggers filp_close */ + /* sys_close no longer triggers filp_close, but we can + * call sys_close_range instead which still does + */ +#define close(fd) syscall(__NR_close_range, fd, fd, 0) + close(pipefd[0]); close(pipefd[1]); close(sockfd); @@ -98,6 +113,8 @@ out_close: close(devfd); close(localfd); close(indicatorfd); + +#undef close return ret; } diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index 3b77d8a422db..261228eb68e8 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -24,6 +24,7 @@ void test_empty_skb(void) int *ifindex; int err; int ret; + int lwt_egress_ret; /* expected retval at lwt/egress */ bool success_on_tc; } tests[] = { /* Empty packets are always rejected. */ @@ -57,6 +58,7 @@ void test_empty_skb(void) .data_size_in = sizeof(eth_hlen), .ifindex = &veth_ifindex, .ret = -ERANGE, + .lwt_egress_ret = -ERANGE, .success_on_tc = true, }, { @@ -70,6 +72,7 @@ void test_empty_skb(void) .data_size_in = sizeof(eth_hlen), .ifindex = &ipip_ifindex, .ret = -ERANGE, + .lwt_egress_ret = -ERANGE, }, /* ETH_HLEN+1-sized packet should be redirected. */ @@ -79,6 +82,7 @@ void test_empty_skb(void) .data_in = eth_hlen_pp, .data_size_in = sizeof(eth_hlen_pp), .ifindex = &veth_ifindex, + .lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */ }, { .msg = "ipip ETH_HLEN+1 packet ingress", @@ -108,8 +112,12 @@ void test_empty_skb(void) for (i = 0; i < ARRAY_SIZE(tests); i++) { bpf_object__for_each_program(prog, bpf_obj->obj) { - char buf[128]; + bool at_egress = strstr(bpf_program__name(prog), "egress") != NULL; bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2); + int expected_ret; + char buf[128]; + + expected_ret = at_egress && !at_tc ? tests[i].lwt_egress_ret : tests[i].ret; tattr.data_in = tests[i].data_in; tattr.data_size_in = tests[i].data_size_in; @@ -128,7 +136,7 @@ void test_empty_skb(void) if (at_tc && tests[i].success_on_tc) ASSERT_GE(bpf_obj->bss->ret, 0, buf); else - ASSERT_EQ(bpf_obj->bss->ret, tests[i].ret, buf); + ASSERT_EQ(bpf_obj->bss->ret, expected_ret, buf); } } diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c new file mode 100644 index 000000000000..516f4a13013c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <network_helpers.h> + +#include "exceptions.skel.h" +#include "exceptions_ext.skel.h" +#include "exceptions_fail.skel.h" +#include "exceptions_assert.skel.h" + +static char log_buf[1024 * 1024]; + +static void test_exceptions_failure(void) +{ + RUN_TESTS(exceptions_fail); +} + +static void test_exceptions_success(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, ropts, + .data_in = &pkt_v4, + .data_size_in = sizeof(pkt_v4), + .repeat = 1, + ); + struct exceptions_ext *eskel = NULL; + struct exceptions *skel; + int ret; + + skel = exceptions__open(); + if (!ASSERT_OK_PTR(skel, "exceptions__open")) + return; + + ret = exceptions__load(skel); + if (!ASSERT_OK(ret, "exceptions__load")) + goto done; + + if (!ASSERT_OK(bpf_map_update_elem(bpf_map__fd(skel->maps.jmp_table), &(int){0}, + &(int){bpf_program__fd(skel->progs.exception_tail_call_target)}, BPF_ANY), + "bpf_map_update_elem jmp_table")) + goto done; + +#define RUN_SUCCESS(_prog, return_val) \ + if (!test__start_subtest(#_prog)) goto _prog##_##return_val; \ + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs._prog), &ropts); \ + ASSERT_OK(ret, #_prog " prog run ret"); \ + ASSERT_EQ(ropts.retval, return_val, #_prog " prog run retval"); \ + _prog##_##return_val: + + RUN_SUCCESS(exception_throw_always_1, 64); + RUN_SUCCESS(exception_throw_always_2, 32); + RUN_SUCCESS(exception_throw_unwind_1, 16); + RUN_SUCCESS(exception_throw_unwind_2, 32); + RUN_SUCCESS(exception_throw_default, 0); + RUN_SUCCESS(exception_throw_default_value, 5); + RUN_SUCCESS(exception_tail_call, 24); + RUN_SUCCESS(exception_ext, 0); + RUN_SUCCESS(exception_ext_mod_cb_runtime, 35); + RUN_SUCCESS(exception_throw_subprog, 1); + RUN_SUCCESS(exception_assert_nz_gfunc, 1); + RUN_SUCCESS(exception_assert_zero_gfunc, 1); + RUN_SUCCESS(exception_assert_neg_gfunc, 1); + RUN_SUCCESS(exception_assert_pos_gfunc, 1); + RUN_SUCCESS(exception_assert_negeq_gfunc, 1); + RUN_SUCCESS(exception_assert_poseq_gfunc, 1); + RUN_SUCCESS(exception_assert_nz_gfunc_with, 1); + RUN_SUCCESS(exception_assert_zero_gfunc_with, 1); + RUN_SUCCESS(exception_assert_neg_gfunc_with, 1); + RUN_SUCCESS(exception_assert_pos_gfunc_with, 1); + RUN_SUCCESS(exception_assert_negeq_gfunc_with, 1); + RUN_SUCCESS(exception_assert_poseq_gfunc_with, 1); + RUN_SUCCESS(exception_bad_assert_nz_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_zero_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_neg_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_pos_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_negeq_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_poseq_gfunc, 0); + RUN_SUCCESS(exception_bad_assert_nz_gfunc_with, 100); + RUN_SUCCESS(exception_bad_assert_zero_gfunc_with, 105); + RUN_SUCCESS(exception_bad_assert_neg_gfunc_with, 200); + RUN_SUCCESS(exception_bad_assert_pos_gfunc_with, 0); + RUN_SUCCESS(exception_bad_assert_negeq_gfunc_with, 101); + RUN_SUCCESS(exception_bad_assert_poseq_gfunc_with, 99); + RUN_SUCCESS(exception_assert_range, 1); + RUN_SUCCESS(exception_assert_range_with, 1); + RUN_SUCCESS(exception_bad_assert_range, 0); + RUN_SUCCESS(exception_bad_assert_range_with, 10); + +#define RUN_EXT(load_ret, attach_err, expr, msg, after_link) \ + { \ + LIBBPF_OPTS(bpf_object_open_opts, o, .kernel_log_buf = log_buf, \ + .kernel_log_size = sizeof(log_buf), \ + .kernel_log_level = 2); \ + exceptions_ext__destroy(eskel); \ + eskel = exceptions_ext__open_opts(&o); \ + struct bpf_program *prog = NULL; \ + struct bpf_link *link = NULL; \ + if (!ASSERT_OK_PTR(eskel, "exceptions_ext__open")) \ + goto done; \ + (expr); \ + ASSERT_OK_PTR(bpf_program__name(prog), bpf_program__name(prog)); \ + if (!ASSERT_EQ(exceptions_ext__load(eskel), load_ret, \ + "exceptions_ext__load")) { \ + printf("%s\n", log_buf); \ + goto done; \ + } \ + if (load_ret != 0) { \ + if (!ASSERT_OK_PTR(strstr(log_buf, msg), "strstr")) { \ + printf("%s\n", log_buf); \ + goto done; \ + } \ + } \ + if (!load_ret && attach_err) { \ + if (!ASSERT_ERR_PTR(link = bpf_program__attach(prog), "attach err")) \ + goto done; \ + } else if (!load_ret) { \ + if (!ASSERT_OK_PTR(link = bpf_program__attach(prog), "attach ok")) \ + goto done; \ + (void)(after_link); \ + bpf_link__destroy(link); \ + } \ + } + + if (test__start_subtest("non-throwing fentry -> exception_cb")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.pfentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod"), "set_attach_target")) + goto done; + }), "FENTRY/FEXIT programs cannot attach to exception callback", 0); + + if (test__start_subtest("throwing fentry -> exception_cb")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.throwing_fentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod"), "set_attach_target")) + goto done; + }), "FENTRY/FEXIT programs cannot attach to exception callback", 0); + + if (test__start_subtest("non-throwing fexit -> exception_cb")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.pfexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod"), "set_attach_target")) + goto done; + }), "FENTRY/FEXIT programs cannot attach to exception callback", 0); + + if (test__start_subtest("throwing fexit -> exception_cb")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.throwing_fexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod"), "set_attach_target")) + goto done; + }), "FENTRY/FEXIT programs cannot attach to exception callback", 0); + + if (test__start_subtest("throwing extension (with custom cb) -> exception_cb")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.throwing_exception_cb_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod"), "set_attach_target")) + goto done; + }), "Extension programs cannot attach to exception callback", 0); + + if (test__start_subtest("throwing extension -> global func in exception_cb")) + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_exception_cb_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext_mod_cb_runtime), + "exception_cb_mod_global"), "set_attach_target")) + goto done; + }), "", ({ RUN_SUCCESS(exception_ext_mod_cb_runtime, 131); })); + + if (test__start_subtest("throwing extension (with custom cb) -> global func in exception_cb")) + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_ext), + "exception_ext_global"), "set_attach_target")) + goto done; + }), "", ({ RUN_SUCCESS(exception_ext, 128); })); + + if (test__start_subtest("non-throwing fentry -> non-throwing subprog")) + /* non-throwing fentry -> non-throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.pfentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing fentry -> non-throwing subprog")) + /* throwing fentry -> non-throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_fentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("non-throwing fentry -> throwing subprog")) + /* non-throwing fentry -> throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.pfentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing fentry -> throwing subprog")) + /* throwing fentry -> throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_fentry; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("non-throwing fexit -> non-throwing subprog")) + /* non-throwing fexit -> non-throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.pfexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing fexit -> non-throwing subprog")) + /* throwing fexit -> non-throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_fexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("non-throwing fexit -> throwing subprog")) + /* non-throwing fexit -> throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.pfexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing fexit -> throwing subprog")) + /* throwing fexit -> throwing subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_fexit; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + /* fmod_ret not allowed for subprog - Check so we remember to handle its + * throwing specification compatibility with target when supported. + */ + if (test__start_subtest("non-throwing fmod_ret -> non-throwing subprog")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.pfmod_ret; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "can't modify return codes of BPF program", 0); + + /* fmod_ret not allowed for subprog - Check so we remember to handle its + * throwing specification compatibility with target when supported. + */ + if (test__start_subtest("non-throwing fmod_ret -> non-throwing global subprog")) + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.pfmod_ret; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "global_subprog"), "set_attach_target")) + goto done; + }), "can't modify return codes of BPF program", 0); + + if (test__start_subtest("non-throwing extension -> non-throwing subprog")) + /* non-throwing extension -> non-throwing subprog : BAD (!global) */ + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "subprog"), "set_attach_target")) + goto done; + }), "subprog() is not a global function", 0); + + if (test__start_subtest("non-throwing extension -> throwing subprog")) + /* non-throwing extension -> throwing subprog : BAD (!global) */ + RUN_EXT(-EINVAL, true, ({ + prog = eskel->progs.extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_subprog"), "set_attach_target")) + goto done; + }), "throwing_subprog() is not a global function", 0); + + if (test__start_subtest("non-throwing extension -> non-throwing subprog")) + /* non-throwing extension -> non-throwing global subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "global_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("non-throwing extension -> throwing global subprog")) + /* non-throwing extension -> throwing global subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_global_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing extension -> throwing global subprog")) + /* throwing extension -> throwing global subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "throwing_global_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing extension -> non-throwing global subprog")) + /* throwing extension -> non-throwing global subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "global_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("non-throwing extension -> main subprog")) + /* non-throwing extension -> main subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "exception_throw_subprog"), "set_attach_target")) + goto done; + }), "", 0); + + if (test__start_subtest("throwing extension -> main subprog")) + /* throwing extension -> main subprog : OK */ + RUN_EXT(0, false, ({ + prog = eskel->progs.throwing_extension; + bpf_program__set_autoload(prog, true); + if (!ASSERT_OK(bpf_program__set_attach_target(prog, + bpf_program__fd(skel->progs.exception_throw_subprog), + "exception_throw_subprog"), "set_attach_target")) + goto done; + }), "", 0); + +done: + exceptions_ext__destroy(eskel); + exceptions__destroy(skel); +} + +static void test_exceptions_assertions(void) +{ + RUN_TESTS(exceptions_assert); +} + +void test_exceptions(void) +{ + test_exceptions_success(); + test_exceptions_failure(); + test_exceptions_assertions(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c index 9d768e083714..97142a4db374 100644 --- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c +++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c @@ -308,7 +308,7 @@ void test_fill_link_info(void) return; /* load kallsyms to compare the addr */ - if (!ASSERT_OK(load_kallsyms_refresh(), "load_kallsyms_refresh")) + if (!ASSERT_OK(load_kallsyms(), "load_kallsyms")) goto cleanup; kprobe_addr = ksym_get_addr(KPROBE_FUNC); diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c index 179fe300534f..4041cfa670eb 100644 --- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c @@ -3,6 +3,7 @@ #include "kprobe_multi.skel.h" #include "trace_helpers.h" #include "kprobe_multi_empty.skel.h" +#include "kprobe_multi_override.skel.h" #include "bpf/libbpf_internal.h" #include "bpf/hashmap.h" @@ -453,6 +454,40 @@ cleanup: } } +static void test_attach_override(void) +{ + struct kprobe_multi_override *skel = NULL; + struct bpf_link *link = NULL; + + skel = kprobe_multi_override__open_and_load(); + if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load")) + goto cleanup; + + /* The test_override calls bpf_override_return so it should fail + * to attach to bpf_fentry_test1 function, which is not on error + * injection list. + */ + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_override, + "bpf_fentry_test1", NULL); + if (!ASSERT_ERR_PTR(link, "override_attached_bpf_fentry_test1")) { + bpf_link__destroy(link); + goto cleanup; + } + + /* The should_fail_bio function is on error injection list, + * attach should succeed. + */ + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_override, + "should_fail_bio", NULL); + if (!ASSERT_OK_PTR(link, "override_attached_should_fail_bio")) + goto cleanup; + + bpf_link__destroy(link); + +cleanup: + kprobe_multi_override__destroy(skel); +} + void serial_test_kprobe_multi_bench_attach(void) { if (test__start_subtest("kernel")) @@ -480,4 +515,6 @@ void test_kprobe_multi_test(void) test_attach_api_syms(); if (test__start_subtest("attach_api_fails")) test_attach_api_fails(); + if (test__start_subtest("attach_override")) + test_attach_override(); } diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c index 1fbe7e4ac00a..9d03528f05db 100644 --- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_testmod_test.c @@ -4,6 +4,8 @@ #include "trace_helpers.h" #include "bpf/libbpf_internal.h" +static struct ksyms *ksyms; + static void kprobe_multi_testmod_check(struct kprobe_multi *skel) { ASSERT_EQ(skel->bss->kprobe_testmod_test1_result, 1, "kprobe_test1_result"); @@ -50,12 +52,12 @@ static void test_testmod_attach_api_addrs(void) LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); unsigned long long addrs[3]; - addrs[0] = ksym_get_addr("bpf_testmod_fentry_test1"); - ASSERT_NEQ(addrs[0], 0, "ksym_get_addr"); - addrs[1] = ksym_get_addr("bpf_testmod_fentry_test2"); - ASSERT_NEQ(addrs[1], 0, "ksym_get_addr"); - addrs[2] = ksym_get_addr("bpf_testmod_fentry_test3"); - ASSERT_NEQ(addrs[2], 0, "ksym_get_addr"); + addrs[0] = ksym_get_addr_local(ksyms, "bpf_testmod_fentry_test1"); + ASSERT_NEQ(addrs[0], 0, "ksym_get_addr_local"); + addrs[1] = ksym_get_addr_local(ksyms, "bpf_testmod_fentry_test2"); + ASSERT_NEQ(addrs[1], 0, "ksym_get_addr_local"); + addrs[2] = ksym_get_addr_local(ksyms, "bpf_testmod_fentry_test3"); + ASSERT_NEQ(addrs[2], 0, "ksym_get_addr_local"); opts.addrs = (const unsigned long *) addrs; opts.cnt = ARRAY_SIZE(addrs); @@ -79,11 +81,15 @@ static void test_testmod_attach_api_syms(void) void serial_test_kprobe_multi_testmod_test(void) { - if (!ASSERT_OK(load_kallsyms_refresh(), "load_kallsyms_refresh")) + ksyms = load_kallsyms_local(); + if (!ASSERT_OK_PTR(ksyms, "load_kallsyms_local")) return; if (test__start_subtest("testmod_attach_api_syms")) test_testmod_attach_api_syms(); + if (test__start_subtest("testmod_attach_api_addrs")) test_testmod_attach_api_addrs(); + + free_kallsyms_local(ksyms); } diff --git a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c index efb8bd43653c..c440ea3311ed 100644 --- a/tools/testing/selftests/bpf/prog_tests/libbpf_str.c +++ b/tools/testing/selftests/bpf/prog_tests/libbpf_str.c @@ -142,10 +142,14 @@ static void test_libbpf_bpf_map_type_str(void) /* Special case for map_type_name BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED * where it and BPF_MAP_TYPE_CGROUP_STORAGE have the same enum value * (map_type). For this enum value, libbpf_bpf_map_type_str() picks - * BPF_MAP_TYPE_CGROUP_STORAGE. + * BPF_MAP_TYPE_CGROUP_STORAGE. The same for + * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED and + * BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE. */ if (strcmp(map_type_name, "BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED") == 0) continue; + if (strcmp(map_type_name, "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED") == 0) + continue; ASSERT_STREQ(buf, map_type_name, "exp_str_value"); } diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 18cf7b17463d..db3bf6bbe01a 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -65,8 +65,8 @@ static struct { { "map_compat_raw_tp", "tracing progs cannot use bpf_{list_head,rb_root} yet" }, { "map_compat_raw_tp_w", "tracing progs cannot use bpf_{list_head,rb_root} yet" }, { "obj_type_id_oor", "local type ID argument must be in range [0, U32_MAX]" }, - { "obj_new_no_composite", "bpf_obj_new type ID argument must be of a struct" }, - { "obj_new_no_struct", "bpf_obj_new type ID argument must be of a struct" }, + { "obj_new_no_composite", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, + { "obj_new_no_struct", "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct" }, { "obj_drop_non_zero_off", "R1 must have zero offset when passed to release func" }, { "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" }, { "obj_new_acq", "Unreleased reference id=" }, diff --git a/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c b/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c index c7636e18b1eb..aa9f67eb1c95 100644 --- a/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c +++ b/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c @@ -61,6 +61,11 @@ void test_module_fentry_shadow(void) int link_fd[2] = {}; __s32 btf_id[2] = {}; + if (!env.has_testmod) { + test__skip(); + return; + } + LIBBPF_OPTS(bpf_prog_load_opts, load_opts, .expected_attach_type = BPF_TRACE_FENTRY, ); diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c new file mode 100644 index 000000000000..9541e9b3a034 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include "percpu_alloc_array.skel.h" +#include "percpu_alloc_cgrp_local_storage.skel.h" +#include "percpu_alloc_fail.skel.h" + +static void test_array(void) +{ + struct percpu_alloc_array *skel; + int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + skel = percpu_alloc_array__open(); + if (!ASSERT_OK_PTR(skel, "percpu_alloc_array__open")) + return; + + bpf_program__set_autoload(skel->progs.test_array_map_1, true); + bpf_program__set_autoload(skel->progs.test_array_map_2, true); + bpf_program__set_autoload(skel->progs.test_array_map_3, true); + bpf_program__set_autoload(skel->progs.test_array_map_4, true); + + skel->rodata->nr_cpus = libbpf_num_possible_cpus(); + + err = percpu_alloc_array__load(skel); + if (!ASSERT_OK(err, "percpu_alloc_array__load")) + goto out; + + err = percpu_alloc_array__attach(skel); + if (!ASSERT_OK(err, "percpu_alloc_array__attach")) + goto out; + + prog_fd = bpf_program__fd(skel->progs.test_array_map_1); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run array_map 1-4"); + ASSERT_EQ(topts.retval, 0, "test_run array_map 1-4"); + ASSERT_EQ(skel->bss->cpu0_field_d, 2, "cpu0_field_d"); + ASSERT_EQ(skel->bss->sum_field_c, 1, "sum_field_c"); +out: + percpu_alloc_array__destroy(skel); +} + +static void test_array_sleepable(void) +{ + struct percpu_alloc_array *skel; + int err, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + skel = percpu_alloc_array__open(); + if (!ASSERT_OK_PTR(skel, "percpu_alloc__open")) + return; + + bpf_program__set_autoload(skel->progs.test_array_map_10, true); + + skel->rodata->nr_cpus = libbpf_num_possible_cpus(); + + err = percpu_alloc_array__load(skel); + if (!ASSERT_OK(err, "percpu_alloc_array__load")) + goto out; + + err = percpu_alloc_array__attach(skel); + if (!ASSERT_OK(err, "percpu_alloc_array__attach")) + goto out; + + prog_fd = bpf_program__fd(skel->progs.test_array_map_10); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run array_map_10"); + ASSERT_EQ(topts.retval, 0, "test_run array_map_10"); + ASSERT_EQ(skel->bss->cpu0_field_d, 2, "cpu0_field_d"); + ASSERT_EQ(skel->bss->sum_field_c, 1, "sum_field_c"); +out: + percpu_alloc_array__destroy(skel); +} + +static void test_cgrp_local_storage(void) +{ + struct percpu_alloc_cgrp_local_storage *skel; + int err, cgroup_fd, prog_fd; + LIBBPF_OPTS(bpf_test_run_opts, topts); + + cgroup_fd = test__join_cgroup("/percpu_alloc"); + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /percpu_alloc")) + return; + + skel = percpu_alloc_cgrp_local_storage__open(); + if (!ASSERT_OK_PTR(skel, "percpu_alloc_cgrp_local_storage__open")) + goto close_fd; + + skel->rodata->nr_cpus = libbpf_num_possible_cpus(); + + err = percpu_alloc_cgrp_local_storage__load(skel); + if (!ASSERT_OK(err, "percpu_alloc_cgrp_local_storage__load")) + goto destroy_skel; + + err = percpu_alloc_cgrp_local_storage__attach(skel); + if (!ASSERT_OK(err, "percpu_alloc_cgrp_local_storage__attach")) + goto destroy_skel; + + prog_fd = bpf_program__fd(skel->progs.test_cgrp_local_storage_1); + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "test_run cgrp_local_storage 1-3"); + ASSERT_EQ(topts.retval, 0, "test_run cgrp_local_storage 1-3"); + ASSERT_EQ(skel->bss->cpu0_field_d, 2, "cpu0_field_d"); + ASSERT_EQ(skel->bss->sum_field_c, 1, "sum_field_c"); + +destroy_skel: + percpu_alloc_cgrp_local_storage__destroy(skel); +close_fd: + close(cgroup_fd); +} + +static void test_failure(void) { + RUN_TESTS(percpu_alloc_fail); +} + +void test_percpu_alloc(void) +{ + if (test__start_subtest("array")) + test_array(); + if (test__start_subtest("array_sleepable")) + test_array_sleepable(); + if (test__start_subtest("cgrp_local_storage")) + test_cgrp_local_storage(); + if (test__start_subtest("failure_tests")) + test_failure(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/preempted_bpf_ma_op.c b/tools/testing/selftests/bpf/prog_tests/preempted_bpf_ma_op.c new file mode 100644 index 000000000000..3a2ec3923fca --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/preempted_bpf_ma_op.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023. Huawei Technologies Co., Ltd */ +#define _GNU_SOURCE +#include <sched.h> +#include <pthread.h> +#include <stdbool.h> +#include <test_progs.h> + +#include "preempted_bpf_ma_op.skel.h" + +#define ALLOC_THREAD_NR 4 +#define ALLOC_LOOP_NR 512 + +struct alloc_ctx { + /* output */ + int run_err; + /* input */ + int fd; + bool *nomem_err; +}; + +static void *run_alloc_prog(void *data) +{ + struct alloc_ctx *ctx = data; + cpu_set_t cpu_set; + int i; + + CPU_ZERO(&cpu_set); + CPU_SET(0, &cpu_set); + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set); + + for (i = 0; i < ALLOC_LOOP_NR && !*ctx->nomem_err; i++) { + LIBBPF_OPTS(bpf_test_run_opts, topts); + int err; + + err = bpf_prog_test_run_opts(ctx->fd, &topts); + ctx->run_err |= err | topts.retval; + } + + return NULL; +} + +void test_preempted_bpf_ma_op(void) +{ + struct alloc_ctx ctx[ALLOC_THREAD_NR]; + struct preempted_bpf_ma_op *skel; + pthread_t tid[ALLOC_THREAD_NR]; + int i, err; + + skel = preempted_bpf_ma_op__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + err = preempted_bpf_ma_op__attach(skel); + if (!ASSERT_OK(err, "attach")) + goto out; + + for (i = 0; i < ARRAY_SIZE(ctx); i++) { + struct bpf_program *prog; + char name[8]; + + snprintf(name, sizeof(name), "test%d", i); + prog = bpf_object__find_program_by_name(skel->obj, name); + if (!ASSERT_OK_PTR(prog, "no test prog")) + goto out; + + ctx[i].run_err = 0; + ctx[i].fd = bpf_program__fd(prog); + ctx[i].nomem_err = &skel->bss->nomem_err; + } + + memset(tid, 0, sizeof(tid)); + for (i = 0; i < ARRAY_SIZE(tid); i++) { + err = pthread_create(&tid[i], NULL, run_alloc_prog, &ctx[i]); + if (!ASSERT_OK(err, "pthread_create")) + break; + } + + for (i = 0; i < ARRAY_SIZE(tid); i++) { + if (!tid[i]) + break; + pthread_join(tid[i], NULL); + ASSERT_EQ(ctx[i].run_err, 0, "run prog err"); + } + + ASSERT_FALSE(skel->bss->nomem_err, "ENOMEM"); +out: + preempted_bpf_ma_op__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/sk_storage_omem_uncharge.c b/tools/testing/selftests/bpf/prog_tests/sk_storage_omem_uncharge.c new file mode 100644 index 000000000000..f35852d245e3 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/sk_storage_omem_uncharge.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Facebook */ +#include <test_progs.h> +#include <bpf/libbpf.h> +#include <sys/types.h> +#include <sys/socket.h> +#include "sk_storage_omem_uncharge.skel.h" + +void test_sk_storage_omem_uncharge(void) +{ + struct sk_storage_omem_uncharge *skel; + int sk_fd = -1, map_fd, err, value; + socklen_t optlen; + + skel = sk_storage_omem_uncharge__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel open_and_load")) + return; + map_fd = bpf_map__fd(skel->maps.sk_storage); + + /* A standalone socket not binding to addr:port, + * so nentns is not needed. + */ + sk_fd = socket(AF_INET6, SOCK_STREAM, 0); + if (!ASSERT_GE(sk_fd, 0, "socket")) + goto done; + + optlen = sizeof(skel->bss->cookie); + err = getsockopt(sk_fd, SOL_SOCKET, SO_COOKIE, &skel->bss->cookie, &optlen); + if (!ASSERT_OK(err, "getsockopt(SO_COOKIE)")) + goto done; + + value = 0; + err = bpf_map_update_elem(map_fd, &sk_fd, &value, 0); + if (!ASSERT_OK(err, "bpf_map_update_elem(value=0)")) + goto done; + + value = 0xdeadbeef; + err = bpf_map_update_elem(map_fd, &sk_fd, &value, 0); + if (!ASSERT_OK(err, "bpf_map_update_elem(value=0xdeadbeef)")) + goto done; + + err = sk_storage_omem_uncharge__attach(skel); + if (!ASSERT_OK(err, "attach")) + goto done; + + close(sk_fd); + sk_fd = -1; + + ASSERT_EQ(skel->bss->cookie_found, 2, "cookie_found"); + ASSERT_EQ(skel->bss->omem, 0, "omem"); + +done: + sk_storage_omem_uncharge__destroy(skel); + if (sk_fd != -1) + close(sk_fd); +} diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index 064cc5e8d9ad..dda7060e86a0 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -475,6 +475,55 @@ out: test_sockmap_drop_prog__destroy(drop); } +static void test_sockmap_skb_verdict_peek(void) +{ + int err, map, verdict, s, c1, p1, zero = 0, sent, recvd, avail; + struct test_sockmap_pass_prog *pass; + char snd[256] = "0123456789"; + char rcv[256] = "0"; + + pass = test_sockmap_pass_prog__open_and_load(); + if (!ASSERT_OK_PTR(pass, "open_and_load")) + return; + verdict = bpf_program__fd(pass->progs.prog_skb_verdict); + map = bpf_map__fd(pass->maps.sock_map_rx); + + err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0); + if (!ASSERT_OK(err, "bpf_prog_attach")) + goto out; + + s = socket_loopback(AF_INET, SOCK_STREAM); + if (!ASSERT_GT(s, -1, "socket_loopback(s)")) + goto out; + + err = create_pair(s, AF_INET, SOCK_STREAM, &c1, &p1); + if (!ASSERT_OK(err, "create_pairs(s)")) + goto out; + + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST); + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)")) + goto out_close; + + sent = xsend(p1, snd, sizeof(snd), 0); + ASSERT_EQ(sent, sizeof(snd), "xsend(p1)"); + recvd = recv(c1, rcv, sizeof(rcv), MSG_PEEK); + ASSERT_EQ(recvd, sizeof(rcv), "recv(c1)"); + err = ioctl(c1, FIONREAD, &avail); + ASSERT_OK(err, "ioctl(FIONREAD) error"); + ASSERT_EQ(avail, sizeof(snd), "after peek ioctl(FIONREAD)"); + recvd = recv(c1, rcv, sizeof(rcv), 0); + ASSERT_EQ(recvd, sizeof(rcv), "recv(p0)"); + err = ioctl(c1, FIONREAD, &avail); + ASSERT_OK(err, "ioctl(FIONREAD) error"); + ASSERT_EQ(avail, 0, "after read ioctl(FIONREAD)"); + +out_close: + close(c1); + close(p1); +out: + test_sockmap_pass_prog__destroy(pass); +} + void test_sockmap_basic(void) { if (test__start_subtest("sockmap create_update_free")) @@ -515,4 +564,6 @@ void test_sockmap_basic(void) test_sockmap_skb_verdict_fionread(true); if (test__start_subtest("sockmap skb_verdict fionread on drop")) test_sockmap_skb_verdict_fionread(false); + if (test__start_subtest("sockmap skb_verdict msg_f_peek")) + test_sockmap_skb_verdict_peek(); } diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h b/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h index d12665490a90..36d829a65aa4 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_helpers.h @@ -179,6 +179,32 @@ __ret; \ }) +static inline int poll_connect(int fd, unsigned int timeout_sec) +{ + struct timeval timeout = { .tv_sec = timeout_sec }; + fd_set wfds; + int r, eval; + socklen_t esize = sizeof(eval); + + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + r = select(fd + 1, NULL, &wfds, NULL, &timeout); + if (r == 0) + errno = ETIME; + if (r != 1) + return -1; + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &eval, &esize) < 0) + return -1; + if (eval != 0) { + errno = eval; + return -1; + } + + return 0; +} + static inline int poll_read(int fd, unsigned int timeout_sec) { struct timeval timeout = { .tv_sec = timeout_sec }; diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index 5674a9d0cacf..8df8cbb447f1 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -1452,11 +1452,18 @@ static int vsock_socketpair_connectible(int sotype, int *v0, int *v1) if (p < 0) goto close_cli; + if (poll_connect(c, IO_TIMEOUT_SEC) < 0) { + FAIL_ERRNO("poll_connect"); + goto close_acc; + } + *v0 = p; *v1 = c; return 0; +close_acc: + close(p); close_cli: close(c); close_srv: diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c index 58fe2c586ed7..fc6b2954e8f5 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -218,12 +218,14 @@ out: bpf_object__close(obj); } -static void test_tailcall_count(const char *which) +static void test_tailcall_count(const char *which, bool test_fentry, + bool test_fexit) { + struct bpf_object *obj = NULL, *fentry_obj = NULL, *fexit_obj = NULL; + struct bpf_link *fentry_link = NULL, *fexit_link = NULL; int err, map_fd, prog_fd, main_fd, data_fd, i, val; struct bpf_map *prog_array, *data_map; struct bpf_program *prog; - struct bpf_object *obj; char buff[128] = {}; LIBBPF_OPTS(bpf_test_run_opts, topts, .data_in = buff, @@ -265,23 +267,105 @@ static void test_tailcall_count(const char *which) if (CHECK_FAIL(err)) goto out; + if (test_fentry) { + fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.bpf.o", + NULL); + if (!ASSERT_OK_PTR(fentry_obj, "open fentry_obj file")) + goto out; + + prog = bpf_object__find_program_by_name(fentry_obj, "fentry"); + if (!ASSERT_OK_PTR(prog, "find fentry prog")) + goto out; + + err = bpf_program__set_attach_target(prog, prog_fd, + "subprog_tail"); + if (!ASSERT_OK(err, "set_attach_target subprog_tail")) + goto out; + + err = bpf_object__load(fentry_obj); + if (!ASSERT_OK(err, "load fentry_obj")) + goto out; + + fentry_link = bpf_program__attach_trace(prog); + if (!ASSERT_OK_PTR(fentry_link, "attach_trace")) + goto out; + } + + if (test_fexit) { + fexit_obj = bpf_object__open_file("tailcall_bpf2bpf_fexit.bpf.o", + NULL); + if (!ASSERT_OK_PTR(fexit_obj, "open fexit_obj file")) + goto out; + + prog = bpf_object__find_program_by_name(fexit_obj, "fexit"); + if (!ASSERT_OK_PTR(prog, "find fexit prog")) + goto out; + + err = bpf_program__set_attach_target(prog, prog_fd, + "subprog_tail"); + if (!ASSERT_OK(err, "set_attach_target subprog_tail")) + goto out; + + err = bpf_object__load(fexit_obj); + if (!ASSERT_OK(err, "load fexit_obj")) + goto out; + + fexit_link = bpf_program__attach_trace(prog); + if (!ASSERT_OK_PTR(fexit_link, "attach_trace")) + goto out; + } + err = bpf_prog_test_run_opts(main_fd, &topts); ASSERT_OK(err, "tailcall"); ASSERT_EQ(topts.retval, 1, "tailcall retval"); data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) - return; + goto out; data_fd = bpf_map__fd(data_map); - if (CHECK_FAIL(map_fd < 0)) - return; + if (CHECK_FAIL(data_fd < 0)) + goto out; i = 0; err = bpf_map_lookup_elem(data_fd, &i, &val); ASSERT_OK(err, "tailcall count"); ASSERT_EQ(val, 33, "tailcall count"); + if (test_fentry) { + data_map = bpf_object__find_map_by_name(fentry_obj, ".bss"); + if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), + "find tailcall_bpf2bpf_fentry.bss map")) + goto out; + + data_fd = bpf_map__fd(data_map); + if (!ASSERT_FALSE(data_fd < 0, + "find tailcall_bpf2bpf_fentry.bss map fd")) + goto out; + + i = 0; + err = bpf_map_lookup_elem(data_fd, &i, &val); + ASSERT_OK(err, "fentry count"); + ASSERT_EQ(val, 33, "fentry count"); + } + + if (test_fexit) { + data_map = bpf_object__find_map_by_name(fexit_obj, ".bss"); + if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), + "find tailcall_bpf2bpf_fexit.bss map")) + goto out; + + data_fd = bpf_map__fd(data_map); + if (!ASSERT_FALSE(data_fd < 0, + "find tailcall_bpf2bpf_fexit.bss map fd")) + goto out; + + i = 0; + err = bpf_map_lookup_elem(data_fd, &i, &val); + ASSERT_OK(err, "fexit count"); + ASSERT_EQ(val, 33, "fexit count"); + } + i = 0; err = bpf_map_delete_elem(map_fd, &i); if (CHECK_FAIL(err)) @@ -291,6 +375,10 @@ static void test_tailcall_count(const char *which) ASSERT_OK(err, "tailcall"); ASSERT_OK(topts.retval, "tailcall retval"); out: + bpf_link__destroy(fentry_link); + bpf_link__destroy(fexit_link); + bpf_object__close(fentry_obj); + bpf_object__close(fexit_obj); bpf_object__close(obj); } @@ -299,7 +387,7 @@ out: */ static void test_tailcall_3(void) { - test_tailcall_count("tailcall3.bpf.o"); + test_tailcall_count("tailcall3.bpf.o", false, false); } /* test_tailcall_6 checks that the count value of the tail call limit @@ -307,7 +395,7 @@ static void test_tailcall_3(void) */ static void test_tailcall_6(void) { - test_tailcall_count("tailcall6.bpf.o"); + test_tailcall_count("tailcall6.bpf.o", false, false); } /* test_tailcall_4 checks that the kernel properly selects indirect jump @@ -352,11 +440,11 @@ static void test_tailcall_4(void) data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) - return; + goto out; data_fd = bpf_map__fd(data_map); - if (CHECK_FAIL(map_fd < 0)) - return; + if (CHECK_FAIL(data_fd < 0)) + goto out; for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); @@ -442,11 +530,11 @@ static void test_tailcall_5(void) data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) - return; + goto out; data_fd = bpf_map__fd(data_map); - if (CHECK_FAIL(map_fd < 0)) - return; + if (CHECK_FAIL(data_fd < 0)) + goto out; for (i = 0; i < bpf_map__max_entries(prog_array); i++) { snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); @@ -631,11 +719,11 @@ static void test_tailcall_bpf2bpf_2(void) data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) - return; + goto out; data_fd = bpf_map__fd(data_map); - if (CHECK_FAIL(map_fd < 0)) - return; + if (CHECK_FAIL(data_fd < 0)) + goto out; i = 0; err = bpf_map_lookup_elem(data_fd, &i, &val); @@ -805,11 +893,11 @@ static void test_tailcall_bpf2bpf_4(bool noise) data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) - return; + goto out; data_fd = bpf_map__fd(data_map); - if (CHECK_FAIL(map_fd < 0)) - return; + if (CHECK_FAIL(data_fd < 0)) + goto out; i = 0; val.noise = noise; @@ -872,7 +960,7 @@ static void test_tailcall_bpf2bpf_6(void) ASSERT_EQ(topts.retval, 0, "tailcall retval"); data_fd = bpf_map__fd(obj->maps.bss); - if (!ASSERT_GE(map_fd, 0, "bss map fd")) + if (!ASSERT_GE(data_fd, 0, "bss map fd")) goto out; i = 0; @@ -884,6 +972,139 @@ out: tailcall_bpf2bpf6__destroy(obj); } +/* test_tailcall_bpf2bpf_fentry checks that the count value of the tail call + * limit enforcement matches with expectations when tailcall is preceded with + * bpf2bpf call, and the bpf2bpf call is traced by fentry. + */ +static void test_tailcall_bpf2bpf_fentry(void) +{ + test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, false); +} + +/* test_tailcall_bpf2bpf_fexit checks that the count value of the tail call + * limit enforcement matches with expectations when tailcall is preceded with + * bpf2bpf call, and the bpf2bpf call is traced by fexit. + */ +static void test_tailcall_bpf2bpf_fexit(void) +{ + test_tailcall_count("tailcall_bpf2bpf2.bpf.o", false, true); +} + +/* test_tailcall_bpf2bpf_fentry_fexit checks that the count value of the tail + * call limit enforcement matches with expectations when tailcall is preceded + * with bpf2bpf call, and the bpf2bpf call is traced by both fentry and fexit. + */ +static void test_tailcall_bpf2bpf_fentry_fexit(void) +{ + test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, true); +} + +/* test_tailcall_bpf2bpf_fentry_entry checks that the count value of the tail + * call limit enforcement matches with expectations when tailcall is preceded + * with bpf2bpf call, and the bpf2bpf caller is traced by fentry. + */ +static void test_tailcall_bpf2bpf_fentry_entry(void) +{ + struct bpf_object *tgt_obj = NULL, *fentry_obj = NULL; + int err, map_fd, prog_fd, data_fd, i, val; + struct bpf_map *prog_array, *data_map; + struct bpf_link *fentry_link = NULL; + struct bpf_program *prog; + char buff[128] = {}; + + LIBBPF_OPTS(bpf_test_run_opts, topts, + .data_in = buff, + .data_size_in = sizeof(buff), + .repeat = 1, + ); + + err = bpf_prog_test_load("tailcall_bpf2bpf2.bpf.o", + BPF_PROG_TYPE_SCHED_CLS, + &tgt_obj, &prog_fd); + if (!ASSERT_OK(err, "load tgt_obj")) + return; + + prog_array = bpf_object__find_map_by_name(tgt_obj, "jmp_table"); + if (!ASSERT_OK_PTR(prog_array, "find jmp_table map")) + goto out; + + map_fd = bpf_map__fd(prog_array); + if (!ASSERT_FALSE(map_fd < 0, "find jmp_table map fd")) + goto out; + + prog = bpf_object__find_program_by_name(tgt_obj, "classifier_0"); + if (!ASSERT_OK_PTR(prog, "find classifier_0 prog")) + goto out; + + prog_fd = bpf_program__fd(prog); + if (!ASSERT_FALSE(prog_fd < 0, "find classifier_0 prog fd")) + goto out; + + i = 0; + err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); + if (!ASSERT_OK(err, "update jmp_table")) + goto out; + + fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.bpf.o", + NULL); + if (!ASSERT_OK_PTR(fentry_obj, "open fentry_obj file")) + goto out; + + prog = bpf_object__find_program_by_name(fentry_obj, "fentry"); + if (!ASSERT_OK_PTR(prog, "find fentry prog")) + goto out; + + err = bpf_program__set_attach_target(prog, prog_fd, "classifier_0"); + if (!ASSERT_OK(err, "set_attach_target classifier_0")) + goto out; + + err = bpf_object__load(fentry_obj); + if (!ASSERT_OK(err, "load fentry_obj")) + goto out; + + fentry_link = bpf_program__attach_trace(prog); + if (!ASSERT_OK_PTR(fentry_link, "attach_trace")) + goto out; + + err = bpf_prog_test_run_opts(prog_fd, &topts); + ASSERT_OK(err, "tailcall"); + ASSERT_EQ(topts.retval, 1, "tailcall retval"); + + data_map = bpf_object__find_map_by_name(tgt_obj, "tailcall.bss"); + if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), + "find tailcall.bss map")) + goto out; + + data_fd = bpf_map__fd(data_map); + if (!ASSERT_FALSE(data_fd < 0, "find tailcall.bss map fd")) + goto out; + + i = 0; + err = bpf_map_lookup_elem(data_fd, &i, &val); + ASSERT_OK(err, "tailcall count"); + ASSERT_EQ(val, 34, "tailcall count"); + + data_map = bpf_object__find_map_by_name(fentry_obj, ".bss"); + if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), + "find tailcall_bpf2bpf_fentry.bss map")) + goto out; + + data_fd = bpf_map__fd(data_map); + if (!ASSERT_FALSE(data_fd < 0, + "find tailcall_bpf2bpf_fentry.bss map fd")) + goto out; + + i = 0; + err = bpf_map_lookup_elem(data_fd, &i, &val); + ASSERT_OK(err, "fentry count"); + ASSERT_EQ(val, 1, "fentry count"); + +out: + bpf_link__destroy(fentry_link); + bpf_object__close(fentry_obj); + bpf_object__close(tgt_obj); +} + void test_tailcalls(void) { if (test__start_subtest("tailcall_1")) @@ -910,4 +1131,12 @@ void test_tailcalls(void) test_tailcall_bpf2bpf_4(true); if (test__start_subtest("tailcall_bpf2bpf_6")) test_tailcall_bpf2bpf_6(); + if (test__start_subtest("tailcall_bpf2bpf_fentry")) + test_tailcall_bpf2bpf_fentry(); + if (test__start_subtest("tailcall_bpf2bpf_fexit")) + test_tailcall_bpf2bpf_fexit(); + if (test__start_subtest("tailcall_bpf2bpf_fentry_fexit")) + test_tailcall_bpf2bpf_fentry_fexit(); + if (test__start_subtest("tailcall_bpf2bpf_fentry_entry")) + test_tailcall_bpf2bpf_fentry_entry(); } diff --git a/tools/testing/selftests/bpf/prog_tests/tc_opts.c b/tools/testing/selftests/bpf/prog_tests/tc_opts.c index 7a2ecd4eca5d..99af79ea21a9 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_opts.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_opts.c @@ -2378,3 +2378,87 @@ void serial_test_tc_opts_chain_mixed(void) test_tc_chain_mixed(BPF_TCX_INGRESS); test_tc_chain_mixed(BPF_TCX_EGRESS); } + +static int generate_dummy_prog(void) +{ + const struct bpf_insn prog_insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + const size_t prog_insn_cnt = sizeof(prog_insns) / sizeof(struct bpf_insn); + LIBBPF_OPTS(bpf_prog_load_opts, opts); + const size_t log_buf_sz = 256; + char *log_buf; + int fd = -1; + + log_buf = malloc(log_buf_sz); + if (!ASSERT_OK_PTR(log_buf, "log_buf_alloc")) + return fd; + opts.log_buf = log_buf; + opts.log_size = log_buf_sz; + + log_buf[0] = '\0'; + opts.log_level = 0; + fd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, "tcx_prog", "GPL", + prog_insns, prog_insn_cnt, &opts); + ASSERT_STREQ(log_buf, "", "log_0"); + ASSERT_GE(fd, 0, "prog_fd"); + free(log_buf); + return fd; +} + +static void test_tc_opts_max_target(int target, int flags, bool relative) +{ + int err, ifindex, i, prog_fd, last_fd = -1; + LIBBPF_OPTS(bpf_prog_attach_opts, opta); + const int max_progs = 63; + + ASSERT_OK(system("ip link add dev tcx_opts1 type veth peer name tcx_opts2"), "add veth"); + ifindex = if_nametoindex("tcx_opts1"); + ASSERT_NEQ(ifindex, 0, "non_zero_ifindex"); + + assert_mprog_count_ifindex(ifindex, target, 0); + + for (i = 0; i < max_progs; i++) { + prog_fd = generate_dummy_prog(); + if (!ASSERT_GE(prog_fd, 0, "dummy_prog")) + goto cleanup; + err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta); + if (!ASSERT_EQ(err, 0, "prog_attach")) + goto cleanup; + assert_mprog_count_ifindex(ifindex, target, i + 1); + if (i == max_progs - 1 && relative) + last_fd = prog_fd; + else + close(prog_fd); + } + + prog_fd = generate_dummy_prog(); + if (!ASSERT_GE(prog_fd, 0, "dummy_prog")) + goto cleanup; + opta.flags = flags; + if (last_fd > 0) + opta.relative_fd = last_fd; + err = bpf_prog_attach_opts(prog_fd, ifindex, target, &opta); + ASSERT_EQ(err, -ERANGE, "prog_64_attach"); + assert_mprog_count_ifindex(ifindex, target, max_progs); + close(prog_fd); +cleanup: + if (last_fd > 0) + close(last_fd); + ASSERT_OK(system("ip link del dev tcx_opts1"), "del veth"); + ASSERT_EQ(if_nametoindex("tcx_opts1"), 0, "dev1_removed"); + ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed"); +} + +void serial_test_tc_opts_max(void) +{ + test_tc_opts_max_target(BPF_TCX_INGRESS, 0, false); + test_tc_opts_max_target(BPF_TCX_EGRESS, 0, false); + + test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_BEFORE, false); + test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_BEFORE, true); + + test_tc_opts_max_target(BPF_TCX_INGRESS, BPF_F_AFTER, true); + test_tc_opts_max_target(BPF_TCX_EGRESS, BPF_F_AFTER, false); +} diff --git a/tools/testing/selftests/bpf/prog_tests/test_bpf_ma.c b/tools/testing/selftests/bpf/prog_tests/test_bpf_ma.c new file mode 100644 index 000000000000..0cca4e8ae38e --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/test_bpf_ma.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023. Huawei Technologies Co., Ltd */ +#define _GNU_SOURCE +#include <sched.h> +#include <pthread.h> +#include <stdbool.h> +#include <bpf/btf.h> +#include <test_progs.h> + +#include "test_bpf_ma.skel.h" + +void test_test_bpf_ma(void) +{ + struct test_bpf_ma *skel; + struct btf *btf; + int i, err; + + skel = test_bpf_ma__open(); + if (!ASSERT_OK_PTR(skel, "open")) + return; + + btf = bpf_object__btf(skel->obj); + if (!ASSERT_OK_PTR(btf, "btf")) + goto out; + + for (i = 0; i < ARRAY_SIZE(skel->rodata->data_sizes); i++) { + char name[32]; + int id; + + snprintf(name, sizeof(name), "bin_data_%u", skel->rodata->data_sizes[i]); + id = btf__find_by_name_kind(btf, name, BTF_KIND_STRUCT); + if (!ASSERT_GT(id, 0, "bin_data")) + goto out; + skel->rodata->data_btf_ids[i] = id; + } + + err = test_bpf_ma__load(skel); + if (!ASSERT_OK(err, "load")) + goto out; + + err = test_bpf_ma__attach(skel); + if (!ASSERT_OK(err, "attach")) + goto out; + + skel->bss->pid = getpid(); + usleep(1); + ASSERT_OK(skel->bss->err, "test error"); +out: + test_bpf_ma__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_dev_bound_only.c b/tools/testing/selftests/bpf/prog_tests/xdp_dev_bound_only.c new file mode 100644 index 000000000000..7dd18c6d06c6 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/xdp_dev_bound_only.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <net/if.h> +#include <test_progs.h> +#include <network_helpers.h> + +#define LOCAL_NETNS "xdp_dev_bound_only_netns" + +static int load_dummy_prog(char *name, __u32 ifindex, __u32 flags) +{ + struct bpf_insn insns[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN() }; + LIBBPF_OPTS(bpf_prog_load_opts, opts); + + opts.prog_flags = flags; + opts.prog_ifindex = ifindex; + return bpf_prog_load(BPF_PROG_TYPE_XDP, name, "GPL", insns, ARRAY_SIZE(insns), &opts); +} + +/* A test case for bpf_offload_netdev->offload handling bug: + * - create a veth device (does not support offload); + * - create a device bound XDP program with BPF_F_XDP_DEV_BOUND_ONLY flag + * (such programs are not offloaded); + * - create a device bound XDP program without flags (such programs are offloaded). + * This might lead to 'BUG: kernel NULL pointer dereference'. + */ +void test_xdp_dev_bound_only_offdev(void) +{ + struct nstoken *tok = NULL; + __u32 ifindex; + int fd1 = -1; + int fd2 = -1; + + SYS(out, "ip netns add " LOCAL_NETNS); + tok = open_netns(LOCAL_NETNS); + if (!ASSERT_OK_PTR(tok, "open_netns")) + goto out; + SYS(out, "ip link add eth42 type veth"); + ifindex = if_nametoindex("eth42"); + if (!ASSERT_NEQ(ifindex, 0, "if_nametoindex")) { + perror("if_nametoindex"); + goto out; + } + fd1 = load_dummy_prog("dummy1", ifindex, BPF_F_XDP_DEV_BOUND_ONLY); + if (!ASSERT_GE(fd1, 0, "load_dummy_prog #1")) { + perror("load_dummy_prog #1"); + goto out; + } + /* Program with ifindex is considered offloaded, however veth + * does not support offload => error should be reported. + */ + fd2 = load_dummy_prog("dummy2", ifindex, 0); + ASSERT_EQ(fd2, -EINVAL, "load_dummy_prog #2 (offloaded)"); + +out: + close(fd1); + close(fd2); + close_netns(tok); + /* eth42 was added inside netns, removing the netns will + * also remove eth42 veth pair. + */ + SYS_NOFAIL("ip netns del " LOCAL_NETNS); +} diff --git a/tools/testing/selftests/bpf/progs/bpf_cubic.c b/tools/testing/selftests/bpf/progs/bpf_cubic.c index d9660e7200e2..c997e3e3d3fb 100644 --- a/tools/testing/selftests/bpf/progs/bpf_cubic.c +++ b/tools/testing/selftests/bpf/progs/bpf_cubic.c @@ -490,6 +490,8 @@ static __always_inline void hystart_update(struct sock *sk, __u32 delay) } } +int bpf_cubic_acked_called = 0; + void BPF_STRUCT_OPS(bpf_cubic_acked, struct sock *sk, const struct ack_sample *sample) { @@ -497,6 +499,7 @@ void BPF_STRUCT_OPS(bpf_cubic_acked, struct sock *sk, struct bictcp *ca = inet_csk_ca(sk); __u32 delay; + bpf_cubic_acked_called = 1; /* Some calls are for duplicates without timetamps */ if (sample->rtt_us < 0) return; diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h index cfed4df490f3..0b793a102791 100644 --- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h +++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h @@ -88,6 +88,7 @@ #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr #define sk_flags __sk_common.skc_flags #define sk_reuse __sk_common.skc_reuse +#define sk_cookie __sk_common.skc_cookie #define s6_addr32 in6_u.u6_addr32 diff --git a/tools/testing/selftests/bpf/progs/exceptions.c b/tools/testing/selftests/bpf/progs/exceptions.c new file mode 100644 index 000000000000..2811ee842b01 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/exceptions.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> +#include <bpf/bpf_endian.h> +#include "bpf_misc.h" +#include "bpf_experimental.h" + +#ifndef ETH_P_IP +#define ETH_P_IP 0x0800 +#endif + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 4); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} jmp_table SEC(".maps"); + +static __noinline int static_func(u64 i) +{ + bpf_throw(32); + return i; +} + +__noinline int global2static_simple(u64 i) +{ + static_func(i + 2); + return i - 1; +} + +__noinline int global2static(u64 i) +{ + if (i == ETH_P_IP) + bpf_throw(16); + return static_func(i); +} + +static __noinline int static2global(u64 i) +{ + return global2static(i) + i; +} + +SEC("tc") +int exception_throw_always_1(struct __sk_buff *ctx) +{ + bpf_throw(64); + return 0; +} + +/* In this case, the global func will never be seen executing after call to + * static subprog, hence verifier will DCE the remaining instructions. Ensure we + * are resilient to that. + */ +SEC("tc") +int exception_throw_always_2(struct __sk_buff *ctx) +{ + return global2static_simple(ctx->protocol); +} + +SEC("tc") +int exception_throw_unwind_1(struct __sk_buff *ctx) +{ + return static2global(bpf_ntohs(ctx->protocol)); +} + +SEC("tc") +int exception_throw_unwind_2(struct __sk_buff *ctx) +{ + return static2global(bpf_ntohs(ctx->protocol) - 1); +} + +SEC("tc") +int exception_throw_default(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 1; +} + +SEC("tc") +int exception_throw_default_value(struct __sk_buff *ctx) +{ + bpf_throw(5); + return 1; +} + +SEC("tc") +int exception_tail_call_target(struct __sk_buff *ctx) +{ + bpf_throw(16); + return 0; +} + +static __noinline +int exception_tail_call_subprog(struct __sk_buff *ctx) +{ + volatile int ret = 10; + + bpf_tail_call_static(ctx, &jmp_table, 0); + return ret; +} + +SEC("tc") +int exception_tail_call(struct __sk_buff *ctx) { + volatile int ret = 0; + + ret = exception_tail_call_subprog(ctx); + return ret + 8; +} + +__noinline int exception_ext_global(struct __sk_buff *ctx) +{ + volatile int ret = 0; + + return ret; +} + +static __noinline int exception_ext_static(struct __sk_buff *ctx) +{ + return exception_ext_global(ctx); +} + +SEC("tc") +int exception_ext(struct __sk_buff *ctx) +{ + return exception_ext_static(ctx); +} + +__noinline int exception_cb_mod_global(u64 cookie) +{ + volatile int ret = 0; + + return ret; +} + +/* Example of how the exception callback supplied during verification can still + * introduce extensions by calling to dummy global functions, and alter runtime + * behavior. + * + * Right now we don't allow freplace attachment to exception callback itself, + * but if the need arises this restriction is technically feasible to relax in + * the future. + */ +__noinline int exception_cb_mod(u64 cookie) +{ + return exception_cb_mod_global(cookie) + cookie + 10; +} + +SEC("tc") +__exception_cb(exception_cb_mod) +int exception_ext_mod_cb_runtime(struct __sk_buff *ctx) +{ + bpf_throw(25); + return 0; +} + +__noinline static int subprog(struct __sk_buff *ctx) +{ + return bpf_ktime_get_ns(); +} + +__noinline static int throwing_subprog(struct __sk_buff *ctx) +{ + if (ctx->tstamp) + bpf_throw(0); + return bpf_ktime_get_ns(); +} + +__noinline int global_subprog(struct __sk_buff *ctx) +{ + return bpf_ktime_get_ns(); +} + +__noinline int throwing_global_subprog(struct __sk_buff *ctx) +{ + if (ctx->tstamp) + bpf_throw(0); + return bpf_ktime_get_ns(); +} + +SEC("tc") +int exception_throw_subprog(struct __sk_buff *ctx) +{ + switch (ctx->protocol) { + case 1: + return subprog(ctx); + case 2: + return global_subprog(ctx); + case 3: + return throwing_subprog(ctx); + case 4: + return throwing_global_subprog(ctx); + default: + break; + } + bpf_throw(1); + return 0; +} + +__noinline int assert_nz_gfunc(u64 c) +{ + volatile u64 cookie = c; + + bpf_assert(cookie != 0); + return 0; +} + +__noinline int assert_zero_gfunc(u64 c) +{ + volatile u64 cookie = c; + + bpf_assert_eq(cookie, 0); + return 0; +} + +__noinline int assert_neg_gfunc(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_lt(cookie, 0); + return 0; +} + +__noinline int assert_pos_gfunc(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_gt(cookie, 0); + return 0; +} + +__noinline int assert_negeq_gfunc(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_le(cookie, -1); + return 0; +} + +__noinline int assert_poseq_gfunc(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_ge(cookie, 1); + return 0; +} + +__noinline int assert_nz_gfunc_with(u64 c) +{ + volatile u64 cookie = c; + + bpf_assert_with(cookie != 0, cookie + 100); + return 0; +} + +__noinline int assert_zero_gfunc_with(u64 c) +{ + volatile u64 cookie = c; + + bpf_assert_eq_with(cookie, 0, cookie + 100); + return 0; +} + +__noinline int assert_neg_gfunc_with(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_lt_with(cookie, 0, cookie + 100); + return 0; +} + +__noinline int assert_pos_gfunc_with(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_gt_with(cookie, 0, cookie + 100); + return 0; +} + +__noinline int assert_negeq_gfunc_with(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_le_with(cookie, -1, cookie + 100); + return 0; +} + +__noinline int assert_poseq_gfunc_with(s64 c) +{ + volatile s64 cookie = c; + + bpf_assert_ge_with(cookie, 1, cookie + 100); + return 0; +} + +#define check_assert(name, cookie, tag) \ +SEC("tc") \ +int exception##tag##name(struct __sk_buff *ctx) \ +{ \ + return name(cookie) + 1; \ +} + +check_assert(assert_nz_gfunc, 5, _); +check_assert(assert_zero_gfunc, 0, _); +check_assert(assert_neg_gfunc, -100, _); +check_assert(assert_pos_gfunc, 100, _); +check_assert(assert_negeq_gfunc, -1, _); +check_assert(assert_poseq_gfunc, 1, _); + +check_assert(assert_nz_gfunc_with, 5, _); +check_assert(assert_zero_gfunc_with, 0, _); +check_assert(assert_neg_gfunc_with, -100, _); +check_assert(assert_pos_gfunc_with, 100, _); +check_assert(assert_negeq_gfunc_with, -1, _); +check_assert(assert_poseq_gfunc_with, 1, _); + +check_assert(assert_nz_gfunc, 0, _bad_); +check_assert(assert_zero_gfunc, 5, _bad_); +check_assert(assert_neg_gfunc, 100, _bad_); +check_assert(assert_pos_gfunc, -100, _bad_); +check_assert(assert_negeq_gfunc, 1, _bad_); +check_assert(assert_poseq_gfunc, -1, _bad_); + +check_assert(assert_nz_gfunc_with, 0, _bad_); +check_assert(assert_zero_gfunc_with, 5, _bad_); +check_assert(assert_neg_gfunc_with, 100, _bad_); +check_assert(assert_pos_gfunc_with, -100, _bad_); +check_assert(assert_negeq_gfunc_with, 1, _bad_); +check_assert(assert_poseq_gfunc_with, -1, _bad_); + +SEC("tc") +int exception_assert_range(struct __sk_buff *ctx) +{ + u64 time = bpf_ktime_get_ns(); + + bpf_assert_range(time, 0, ~0ULL); + return 1; +} + +SEC("tc") +int exception_assert_range_with(struct __sk_buff *ctx) +{ + u64 time = bpf_ktime_get_ns(); + + bpf_assert_range_with(time, 0, ~0ULL, 10); + return 1; +} + +SEC("tc") +int exception_bad_assert_range(struct __sk_buff *ctx) +{ + u64 time = bpf_ktime_get_ns(); + + bpf_assert_range(time, -100, 100); + return 1; +} + +SEC("tc") +int exception_bad_assert_range_with(struct __sk_buff *ctx) +{ + u64 time = bpf_ktime_get_ns(); + + bpf_assert_range_with(time, -1000, 1000, 10); + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/exceptions_assert.c b/tools/testing/selftests/bpf/progs/exceptions_assert.c new file mode 100644 index 000000000000..fa35832e6748 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/exceptions_assert.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <limits.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> +#include <bpf/bpf_endian.h> +#include "bpf_misc.h" +#include "bpf_experimental.h" + +#define check_assert(type, op, name, value) \ + SEC("?tc") \ + __log_level(2) __failure \ + int check_assert_##op##_##name(void *ctx) \ + { \ + type num = bpf_ktime_get_ns(); \ + bpf_assert_##op(num, value); \ + return *(u64 *)num; \ + } + +__msg(": R0_w=-2147483648 R10=fp0") +check_assert(s64, eq, int_min, INT_MIN); +__msg(": R0_w=2147483647 R10=fp0") +check_assert(s64, eq, int_max, INT_MAX); +__msg(": R0_w=0 R10=fp0") +check_assert(s64, eq, zero, 0); +__msg(": R0_w=-9223372036854775808 R1_w=-9223372036854775808 R10=fp0") +check_assert(s64, eq, llong_min, LLONG_MIN); +__msg(": R0_w=9223372036854775807 R1_w=9223372036854775807 R10=fp0") +check_assert(s64, eq, llong_max, LLONG_MAX); + +__msg(": R0_w=scalar(smax=2147483646) R10=fp0") +check_assert(s64, lt, pos, INT_MAX); +__msg(": R0_w=scalar(umin=9223372036854775808,var_off=(0x8000000000000000; 0x7fffffffffffffff))") +check_assert(s64, lt, zero, 0); +__msg(": R0_w=scalar(umin=9223372036854775808,umax=18446744071562067967,var_off=(0x8000000000000000; 0x7fffffffffffffff))") +check_assert(s64, lt, neg, INT_MIN); + +__msg(": R0_w=scalar(smax=2147483647) R10=fp0") +check_assert(s64, le, pos, INT_MAX); +__msg(": R0_w=scalar(smax=0) R10=fp0") +check_assert(s64, le, zero, 0); +__msg(": R0_w=scalar(umin=9223372036854775808,umax=18446744071562067968,var_off=(0x8000000000000000; 0x7fffffffffffffff))") +check_assert(s64, le, neg, INT_MIN); + +__msg(": R0_w=scalar(umin=2147483648,umax=9223372036854775807,var_off=(0x0; 0x7fffffffffffffff))") +check_assert(s64, gt, pos, INT_MAX); +__msg(": R0_w=scalar(umin=1,umax=9223372036854775807,var_off=(0x0; 0x7fffffffffffffff))") +check_assert(s64, gt, zero, 0); +__msg(": R0_w=scalar(smin=-2147483647) R10=fp0") +check_assert(s64, gt, neg, INT_MIN); + +__msg(": R0_w=scalar(umin=2147483647,umax=9223372036854775807,var_off=(0x0; 0x7fffffffffffffff))") +check_assert(s64, ge, pos, INT_MAX); +__msg(": R0_w=scalar(umax=9223372036854775807,var_off=(0x0; 0x7fffffffffffffff)) R10=fp0") +check_assert(s64, ge, zero, 0); +__msg(": R0_w=scalar(smin=-2147483648) R10=fp0") +check_assert(s64, ge, neg, INT_MIN); + +SEC("?tc") +__log_level(2) __failure +__msg(": R0=0 R1=ctx(off=0,imm=0) R2=scalar(smin=-2147483646,smax=2147483645) R10=fp0") +int check_assert_range_s64(struct __sk_buff *ctx) +{ + struct bpf_sock *sk = ctx->sk; + s64 num; + + _Static_assert(_Generic((sk->rx_queue_mapping), s32: 1, default: 0), "type match"); + if (!sk) + return 0; + num = sk->rx_queue_mapping; + bpf_assert_range(num, INT_MIN + 2, INT_MAX - 2); + return *((u8 *)ctx + num); +} + +SEC("?tc") +__log_level(2) __failure +__msg(": R1=ctx(off=0,imm=0) R2=scalar(umin=4096,umax=8192,var_off=(0x0; 0x3fff))") +int check_assert_range_u64(struct __sk_buff *ctx) +{ + u64 num = ctx->len; + + bpf_assert_range(num, 4096, 8192); + return *((u8 *)ctx + num); +} + +SEC("?tc") +__log_level(2) __failure +__msg(": R0=0 R1=ctx(off=0,imm=0) R2=4096 R10=fp0") +int check_assert_single_range_s64(struct __sk_buff *ctx) +{ + struct bpf_sock *sk = ctx->sk; + s64 num; + + _Static_assert(_Generic((sk->rx_queue_mapping), s32: 1, default: 0), "type match"); + if (!sk) + return 0; + num = sk->rx_queue_mapping; + + bpf_assert_range(num, 4096, 4096); + return *((u8 *)ctx + num); +} + +SEC("?tc") +__log_level(2) __failure +__msg(": R1=ctx(off=0,imm=0) R2=4096 R10=fp0") +int check_assert_single_range_u64(struct __sk_buff *ctx) +{ + u64 num = ctx->len; + + bpf_assert_range(num, 4096, 4096); + return *((u8 *)ctx + num); +} + +SEC("?tc") +__log_level(2) __failure +__msg(": R1=pkt(off=64,r=64,imm=0) R2=pkt_end(off=0,imm=0) R6=pkt(off=0,r=64,imm=0) R10=fp0") +int check_assert_generic(struct __sk_buff *ctx) +{ + u8 *data_end = (void *)(long)ctx->data_end; + u8 *data = (void *)(long)ctx->data; + + bpf_assert(data + 64 <= data_end); + return data[128]; +} + +SEC("?fentry/bpf_check") +__failure __msg("At program exit the register R0 has value (0x40; 0x0)") +int check_assert_with_return(void *ctx) +{ + bpf_assert_with(!ctx, 64); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/exceptions_ext.c b/tools/testing/selftests/bpf/progs/exceptions_ext.c new file mode 100644 index 000000000000..743c05185d9b --- /dev/null +++ b/tools/testing/selftests/bpf/progs/exceptions_ext.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include "bpf_experimental.h" + +SEC("?fentry") +int pfentry(void *ctx) +{ + return 0; +} + +SEC("?fentry") +int throwing_fentry(void *ctx) +{ + bpf_throw(0); + return 0; +} + +__noinline int exception_cb(u64 cookie) +{ + return cookie + 64; +} + +SEC("?freplace") +int extension(struct __sk_buff *ctx) +{ + return 0; +} + +SEC("?freplace") +__exception_cb(exception_cb) +int throwing_exception_cb_extension(u64 cookie) +{ + bpf_throw(32); + return 0; +} + +SEC("?freplace") +__exception_cb(exception_cb) +int throwing_extension(struct __sk_buff *ctx) +{ + bpf_throw(64); + return 0; +} + +SEC("?fexit") +int pfexit(void *ctx) +{ + return 0; +} + +SEC("?fexit") +int throwing_fexit(void *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?fmod_ret") +int pfmod_ret(void *ctx) +{ + return 0; +} + +SEC("?fmod_ret") +int throwing_fmod_ret(void *ctx) +{ + bpf_throw(0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c new file mode 100644 index 000000000000..4c39e920dac2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> + +#include "bpf_misc.h" +#include "bpf_experimental.h" + +extern void bpf_rcu_read_lock(void) __ksym; + +#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) + +struct foo { + struct bpf_rb_node node; +}; + +struct hmap_elem { + struct bpf_timer timer; +}; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 64); + __type(key, int); + __type(value, struct hmap_elem); +} hmap SEC(".maps"); + +private(A) struct bpf_spin_lock lock; +private(A) struct bpf_rb_root rbtree __contains(foo, node); + +__noinline void *exception_cb_bad_ret_type(u64 cookie) +{ + return NULL; +} + +__noinline int exception_cb_bad_arg_0(void) +{ + return 0; +} + +__noinline int exception_cb_bad_arg_2(int a, int b) +{ + return 0; +} + +__noinline int exception_cb_ok_arg_small(int a) +{ + return 0; +} + +SEC("?tc") +__exception_cb(exception_cb_bad_ret_type) +__failure __msg("Global function exception_cb_bad_ret_type() doesn't return scalar.") +int reject_exception_cb_type_1(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__exception_cb(exception_cb_bad_arg_0) +__failure __msg("exception cb only supports single integer argument") +int reject_exception_cb_type_2(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__exception_cb(exception_cb_bad_arg_2) +__failure __msg("exception cb only supports single integer argument") +int reject_exception_cb_type_3(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__exception_cb(exception_cb_ok_arg_small) +__success +int reject_exception_cb_type_4(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 0; +} + +__noinline +static int timer_cb(void *map, int *key, struct bpf_timer *timer) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__failure __msg("cannot be called from callback subprog") +int reject_async_callback_throw(struct __sk_buff *ctx) +{ + struct hmap_elem *elem; + + elem = bpf_map_lookup_elem(&hmap, &(int){0}); + if (!elem) + return 0; + return bpf_timer_set_callback(&elem->timer, timer_cb); +} + +__noinline static int subprog_lock(struct __sk_buff *ctx) +{ + volatile int ret = 0; + + bpf_spin_lock(&lock); + if (ctx->len) + bpf_throw(0); + return ret; +} + +SEC("?tc") +__failure __msg("function calls are not allowed while holding a lock") +int reject_with_lock(void *ctx) +{ + bpf_spin_lock(&lock); + bpf_throw(0); + return 0; +} + +SEC("?tc") +__failure __msg("function calls are not allowed while holding a lock") +int reject_subprog_with_lock(void *ctx) +{ + return subprog_lock(ctx); +} + +SEC("?tc") +__failure __msg("bpf_rcu_read_unlock is missing") +int reject_with_rcu_read_lock(void *ctx) +{ + bpf_rcu_read_lock(); + bpf_throw(0); + return 0; +} + +__noinline static int throwing_subprog(struct __sk_buff *ctx) +{ + if (ctx->len) + bpf_throw(0); + return 0; +} + +SEC("?tc") +__failure __msg("bpf_rcu_read_unlock is missing") +int reject_subprog_with_rcu_read_lock(void *ctx) +{ + bpf_rcu_read_lock(); + return throwing_subprog(ctx); +} + +static bool rbless(struct bpf_rb_node *n1, const struct bpf_rb_node *n2) +{ + bpf_throw(0); + return true; +} + +SEC("?tc") +__failure __msg("function calls are not allowed while holding a lock") +int reject_with_rbtree_add_throw(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_spin_lock(&lock); + bpf_rbtree_add(&rbtree, &f->node, rbless); + return 0; +} + +SEC("?tc") +__failure __msg("Unreleased reference") +int reject_with_reference(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_throw(0); + return 0; +} + +__noinline static int subprog_ref(struct __sk_buff *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_throw(0); + return 0; +} + +__noinline static int subprog_cb_ref(u32 i, void *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__failure __msg("Unreleased reference") +int reject_with_cb_reference(void *ctx) +{ + struct foo *f; + + f = bpf_obj_new(typeof(*f)); + if (!f) + return 0; + bpf_loop(5, subprog_cb_ref, NULL, 0); + return 0; +} + +SEC("?tc") +__failure __msg("cannot be called from callback") +int reject_with_cb(void *ctx) +{ + bpf_loop(5, subprog_cb_ref, NULL, 0); + return 0; +} + +SEC("?tc") +__failure __msg("Unreleased reference") +int reject_with_subprog_reference(void *ctx) +{ + return subprog_ref(ctx) + 1; +} + +__noinline int throwing_exception_cb(u64 c) +{ + bpf_throw(0); + return c; +} + +__noinline int exception_cb1(u64 c) +{ + return c; +} + +__noinline int exception_cb2(u64 c) +{ + return c; +} + +static __noinline int static_func(struct __sk_buff *ctx) +{ + return exception_cb1(ctx->tstamp); +} + +__noinline int global_func(struct __sk_buff *ctx) +{ + return exception_cb1(ctx->tstamp); +} + +SEC("?tc") +__exception_cb(throwing_exception_cb) +__failure __msg("cannot be called from callback subprog") +int reject_throwing_exception_cb(struct __sk_buff *ctx) +{ + return 0; +} + +SEC("?tc") +__exception_cb(exception_cb1) +__failure __msg("cannot call exception cb directly") +int reject_exception_cb_call_global_func(struct __sk_buff *ctx) +{ + return global_func(ctx); +} + +SEC("?tc") +__exception_cb(exception_cb1) +__failure __msg("cannot call exception cb directly") +int reject_exception_cb_call_static_func(struct __sk_buff *ctx) +{ + return static_func(ctx); +} + +SEC("?tc") +__exception_cb(exception_cb1) +__exception_cb(exception_cb2) +__failure __msg("multiple exception callback tags for main subprog") +int reject_multiple_exception_cb(struct __sk_buff *ctx) +{ + bpf_throw(0); + return 16; +} + +__noinline int exception_cb_bad_ret(u64 c) +{ + return c; +} + +SEC("?fentry/bpf_check") +__exception_cb(exception_cb_bad_ret) +__failure __msg("At program exit the register R0 has unknown scalar value should") +int reject_set_exception_cb_bad_ret1(void *ctx) +{ + return 0; +} + +SEC("?fentry/bpf_check") +__failure __msg("At program exit the register R0 has value (0x40; 0x0) should") +int reject_set_exception_cb_bad_ret2(void *ctx) +{ + bpf_throw(64); + return 0; +} + +__noinline static int loop_cb1(u32 index, int *ctx) +{ + bpf_throw(0); + return 0; +} + +__noinline static int loop_cb2(u32 index, int *ctx) +{ + bpf_throw(0); + return 0; +} + +SEC("?tc") +__failure __msg("cannot be called from callback") +int reject_exception_throw_cb(struct __sk_buff *ctx) +{ + bpf_loop(5, loop_cb1, NULL, 0); + return 0; +} + +SEC("?tc") +__failure __msg("cannot be called from callback") +int reject_exception_throw_cb_diff(struct __sk_buff *ctx) +{ + if (ctx->protocol) + bpf_loop(5, loop_cb1, NULL, 0); + else + bpf_loop(5, loop_cb2, NULL, 0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/get_branch_snapshot.c b/tools/testing/selftests/bpf/progs/get_branch_snapshot.c index a1b139888048..511ac634eef0 100644 --- a/tools/testing/selftests/bpf/progs/get_branch_snapshot.c +++ b/tools/testing/selftests/bpf/progs/get_branch_snapshot.c @@ -15,7 +15,7 @@ long total_entries = 0; #define ENTRY_CNT 32 struct perf_branch_entry entries[ENTRY_CNT] = {}; -static inline bool in_range(__u64 val) +static inline bool gbs_in_range(__u64 val) { return (val >= address_low) && (val < address_high); } @@ -31,7 +31,7 @@ int BPF_PROG(test1, int n, int ret) for (i = 0; i < ENTRY_CNT; i++) { if (i >= total_entries) break; - if (in_range(entries[i].from) && in_range(entries[i].to)) + if (gbs_in_range(entries[i].from) && gbs_in_range(entries[i].to)) test1_hits++; else if (!test1_hits) wasted_entries++; diff --git a/tools/perf/examples/bpf/empty.c b/tools/testing/selftests/bpf/progs/kprobe_multi_override.c index 3e296c0c53d7..28f8487c9059 100644 --- a/tools/perf/examples/bpf/empty.c +++ b/tools/testing/selftests/bpf/progs/kprobe_multi_override.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/bpf.h> #include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> -struct syscall_enter_args; +char _license[] SEC("license") = "GPL"; -SEC("raw_syscalls:sys_enter") -int sys_enter(struct syscall_enter_args *args) +SEC("kprobe.multi") +int test_override(struct pt_regs *ctx) { + bpf_override_return(ctx, 123); return 0; } -char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/percpu_alloc_array.c b/tools/testing/selftests/bpf/progs/percpu_alloc_array.c new file mode 100644 index 000000000000..bbc45346e006 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/percpu_alloc_array.c @@ -0,0 +1,183 @@ +#include "bpf_experimental.h" + +struct val_t { + long b, c, d; +}; + +struct elem { + long sum; + struct val_t __percpu_kptr *pc; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct elem); +} array SEC(".maps"); + +void bpf_rcu_read_lock(void) __ksym; +void bpf_rcu_read_unlock(void) __ksym; + +const volatile int nr_cpus; + +/* Initialize the percpu object */ +SEC("?fentry/bpf_fentry_test1") +int BPF_PROG(test_array_map_1) +{ + struct val_t __percpu_kptr *p; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = bpf_percpu_obj_new(struct val_t); + if (!p) + return 0; + + p = bpf_kptr_xchg(&e->pc, p); + if (p) + bpf_percpu_obj_drop(p); + + return 0; +} + +/* Update percpu data */ +SEC("?fentry/bpf_fentry_test2") +int BPF_PROG(test_array_map_2) +{ + struct val_t __percpu_kptr *p; + struct val_t *v; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = e->pc; + if (!p) + return 0; + + v = bpf_per_cpu_ptr(p, 0); + if (!v) + return 0; + v->c = 1; + v->d = 2; + + return 0; +} + +int cpu0_field_d, sum_field_c; + +/* Summarize percpu data */ +SEC("?fentry/bpf_fentry_test3") +int BPF_PROG(test_array_map_3) +{ + struct val_t __percpu_kptr *p; + int i, index = 0; + struct val_t *v; + struct elem *e; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = e->pc; + if (!p) + return 0; + + bpf_for(i, 0, nr_cpus) { + v = bpf_per_cpu_ptr(p, i); + if (v) { + if (i == 0) + cpu0_field_d = v->d; + sum_field_c += v->c; + } + } + + return 0; +} + +/* Explicitly free allocated percpu data */ +SEC("?fentry/bpf_fentry_test4") +int BPF_PROG(test_array_map_4) +{ + struct val_t __percpu_kptr *p; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + /* delete */ + p = bpf_kptr_xchg(&e->pc, NULL); + if (p) { + bpf_percpu_obj_drop(p); + } + + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +int BPF_PROG(test_array_map_10) +{ + struct val_t __percpu_kptr *p, *p1; + int i, index = 0; + struct val_t *v; + struct elem *e; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + bpf_rcu_read_lock(); + p = e->pc; + if (!p) { + p = bpf_percpu_obj_new(struct val_t); + if (!p) + goto out; + + p1 = bpf_kptr_xchg(&e->pc, p); + if (p1) { + /* race condition */ + bpf_percpu_obj_drop(p1); + } + } + + v = bpf_this_cpu_ptr(p); + v->c = 3; + v = bpf_this_cpu_ptr(p); + v->c = 0; + + v = bpf_per_cpu_ptr(p, 0); + if (!v) + goto out; + v->c = 1; + v->d = 2; + + /* delete */ + p1 = bpf_kptr_xchg(&e->pc, NULL); + if (!p1) + goto out; + + bpf_for(i, 0, nr_cpus) { + v = bpf_per_cpu_ptr(p, i); + if (v) { + if (i == 0) + cpu0_field_d = v->d; + sum_field_c += v->c; + } + } + + /* finally release p */ + bpf_percpu_obj_drop(p1); +out: + bpf_rcu_read_unlock(); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c b/tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c new file mode 100644 index 000000000000..1c36a241852c --- /dev/null +++ b/tools/testing/selftests/bpf/progs/percpu_alloc_cgrp_local_storage.c @@ -0,0 +1,105 @@ +#include "bpf_experimental.h" + +struct val_t { + long b, c, d; +}; + +struct elem { + long sum; + struct val_t __percpu_kptr *pc; +}; + +struct { + __uint(type, BPF_MAP_TYPE_CGRP_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct elem); +} cgrp SEC(".maps"); + +const volatile int nr_cpus; + +/* Initialize the percpu object */ +SEC("fentry/bpf_fentry_test1") +int BPF_PROG(test_cgrp_local_storage_1) +{ + struct task_struct *task; + struct val_t __percpu_kptr *p; + struct elem *e; + + task = bpf_get_current_task_btf(); + e = bpf_cgrp_storage_get(&cgrp, task->cgroups->dfl_cgrp, 0, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!e) + return 0; + + p = bpf_percpu_obj_new(struct val_t); + if (!p) + return 0; + + p = bpf_kptr_xchg(&e->pc, p); + if (p) + bpf_percpu_obj_drop(p); + + return 0; +} + +/* Percpu data collection */ +SEC("fentry/bpf_fentry_test2") +int BPF_PROG(test_cgrp_local_storage_2) +{ + struct task_struct *task; + struct val_t __percpu_kptr *p; + struct val_t *v; + struct elem *e; + + task = bpf_get_current_task_btf(); + e = bpf_cgrp_storage_get(&cgrp, task->cgroups->dfl_cgrp, 0, 0); + if (!e) + return 0; + + p = e->pc; + if (!p) + return 0; + + v = bpf_per_cpu_ptr(p, 0); + if (!v) + return 0; + v->c = 1; + v->d = 2; + return 0; +} + +int cpu0_field_d, sum_field_c; + +/* Summarize percpu data collection */ +SEC("fentry/bpf_fentry_test3") +int BPF_PROG(test_cgrp_local_storage_3) +{ + struct task_struct *task; + struct val_t __percpu_kptr *p; + struct val_t *v; + struct elem *e; + int i; + + task = bpf_get_current_task_btf(); + e = bpf_cgrp_storage_get(&cgrp, task->cgroups->dfl_cgrp, 0, 0); + if (!e) + return 0; + + p = e->pc; + if (!p) + return 0; + + bpf_for(i, 0, nr_cpus) { + v = bpf_per_cpu_ptr(p, i); + if (v) { + if (i == 0) + cpu0_field_d = v->d; + sum_field_c += v->c; + } + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c b/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c new file mode 100644 index 000000000000..1a891d30f1fe --- /dev/null +++ b/tools/testing/selftests/bpf/progs/percpu_alloc_fail.c @@ -0,0 +1,164 @@ +#include "bpf_experimental.h" +#include "bpf_misc.h" + +struct val_t { + long b, c, d; +}; + +struct val2_t { + long b; +}; + +struct val_with_ptr_t { + char *p; +}; + +struct val_with_rb_root_t { + struct bpf_spin_lock lock; +}; + +struct elem { + long sum; + struct val_t __percpu_kptr *pc; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, struct elem); +} array SEC(".maps"); + +long ret; + +SEC("?fentry/bpf_fentry_test1") +__failure __msg("store to referenced kptr disallowed") +int BPF_PROG(test_array_map_1) +{ + struct val_t __percpu_kptr *p; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = bpf_percpu_obj_new(struct val_t); + if (!p) + return 0; + + p = bpf_kptr_xchg(&e->pc, p); + if (p) + bpf_percpu_obj_drop(p); + + e->pc = (struct val_t __percpu_kptr *)ret; + return 0; +} + +SEC("?fentry/bpf_fentry_test1") +__failure __msg("invalid kptr access, R2 type=percpu_ptr_val2_t expected=ptr_val_t") +int BPF_PROG(test_array_map_2) +{ + struct val2_t __percpu_kptr *p2; + struct val_t __percpu_kptr *p; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p2 = bpf_percpu_obj_new(struct val2_t); + if (!p2) + return 0; + + p = bpf_kptr_xchg(&e->pc, p2); + if (p) + bpf_percpu_obj_drop(p); + + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("R1 type=scalar expected=percpu_ptr_, percpu_rcu_ptr_, percpu_trusted_ptr_") +int BPF_PROG(test_array_map_3) +{ + struct val_t __percpu_kptr *p, *p1; + struct val_t *v; + struct elem *e; + int index = 0; + + e = bpf_map_lookup_elem(&array, &index); + if (!e) + return 0; + + p = bpf_percpu_obj_new(struct val_t); + if (!p) + return 0; + + p1 = bpf_kptr_xchg(&e->pc, p); + if (p1) + bpf_percpu_obj_drop(p1); + + v = bpf_this_cpu_ptr(p); + ret = v->b; + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("arg#0 expected for bpf_percpu_obj_drop_impl()") +int BPF_PROG(test_array_map_4) +{ + struct val_t __percpu_kptr *p; + + p = bpf_percpu_obj_new(struct val_t); + if (!p) + return 0; + + bpf_obj_drop(p); + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("arg#0 expected for bpf_obj_drop_impl()") +int BPF_PROG(test_array_map_5) +{ + struct val_t *p; + + p = bpf_obj_new(struct val_t); + if (!p) + return 0; + + bpf_percpu_obj_drop(p); + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("bpf_percpu_obj_new type ID argument must be of a struct of scalars") +int BPF_PROG(test_array_map_6) +{ + struct val_with_ptr_t __percpu_kptr *p; + + p = bpf_percpu_obj_new(struct val_with_ptr_t); + if (!p) + return 0; + + bpf_percpu_obj_drop(p); + return 0; +} + +SEC("?fentry.s/bpf_fentry_test1") +__failure __msg("bpf_percpu_obj_new type ID argument must not contain special fields") +int BPF_PROG(test_array_map_7) +{ + struct val_with_rb_root_t __percpu_kptr *p; + + p = bpf_percpu_obj_new(struct val_with_rb_root_t); + if (!p) + return 0; + + bpf_percpu_obj_drop(p); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/preempted_bpf_ma_op.c b/tools/testing/selftests/bpf/progs/preempted_bpf_ma_op.c new file mode 100644 index 000000000000..55907ef961bf --- /dev/null +++ b/tools/testing/selftests/bpf/progs/preempted_bpf_ma_op.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023. Huawei Technologies Co., Ltd */ +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> + +#include "bpf_experimental.h" + +struct bin_data { + char data[256]; + struct bpf_spin_lock lock; +}; + +struct map_value { + struct bin_data __kptr * data; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct map_value); + __uint(max_entries, 2048); +} array SEC(".maps"); + +char _license[] SEC("license") = "GPL"; + +bool nomem_err = false; + +static int del_array(unsigned int i, int *from) +{ + struct map_value *value; + struct bin_data *old; + + value = bpf_map_lookup_elem(&array, from); + if (!value) + return 1; + + old = bpf_kptr_xchg(&value->data, NULL); + if (old) + bpf_obj_drop(old); + + (*from)++; + return 0; +} + +static int add_array(unsigned int i, int *from) +{ + struct bin_data *old, *new; + struct map_value *value; + + value = bpf_map_lookup_elem(&array, from); + if (!value) + return 1; + + new = bpf_obj_new(typeof(*new)); + if (!new) { + nomem_err = true; + return 1; + } + + old = bpf_kptr_xchg(&value->data, new); + if (old) + bpf_obj_drop(old); + + (*from)++; + return 0; +} + +static void del_then_add_array(int from) +{ + int i; + + i = from; + bpf_loop(512, del_array, &i, 0); + + i = from; + bpf_loop(512, add_array, &i, 0); +} + +SEC("fentry/bpf_fentry_test1") +int BPF_PROG2(test0, int, a) +{ + del_then_add_array(0); + return 0; +} + +SEC("fentry/bpf_fentry_test2") +int BPF_PROG2(test1, int, a, u64, b) +{ + del_then_add_array(512); + return 0; +} + +SEC("fentry/bpf_fentry_test3") +int BPF_PROG2(test2, char, a, int, b, u64, c) +{ + del_then_add_array(1024); + return 0; +} + +SEC("fentry/bpf_fentry_test4") +int BPF_PROG2(test3, void *, a, char, b, int, c, u64, d) +{ + del_then_add_array(1536); + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/sk_storage_omem_uncharge.c b/tools/testing/selftests/bpf/progs/sk_storage_omem_uncharge.c new file mode 100644 index 000000000000..3e745793b27a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/sk_storage_omem_uncharge.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Facebook */ +#include "vmlinux.h" +#include "bpf_tracing_net.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_core_read.h> + +void *local_storage_ptr = NULL; +void *sk_ptr = NULL; +int cookie_found = 0; +__u64 cookie = 0; +__u32 omem = 0; + +void *bpf_rdonly_cast(void *, __u32) __ksym; + +struct { + __uint(type, BPF_MAP_TYPE_SK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, int); +} sk_storage SEC(".maps"); + +SEC("fexit/bpf_local_storage_destroy") +int BPF_PROG(bpf_local_storage_destroy, struct bpf_local_storage *local_storage) +{ + struct sock *sk; + + if (local_storage_ptr != local_storage) + return 0; + + sk = bpf_rdonly_cast(sk_ptr, bpf_core_type_id_kernel(struct sock)); + if (sk->sk_cookie.counter != cookie) + return 0; + + cookie_found++; + omem = sk->sk_omem_alloc.counter; + local_storage_ptr = NULL; + + return 0; +} + +SEC("fentry/inet6_sock_destruct") +int BPF_PROG(inet6_sock_destruct, struct sock *sk) +{ + int *value; + + if (!cookie || sk->sk_cookie.counter != cookie) + return 0; + + value = bpf_sk_storage_get(&sk_storage, sk, 0, 0); + if (value && *value == 0xdeadbeef) { + cookie_found++; + sk_ptr = sk; + local_storage_ptr = sk->sk_bpf_storage; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fentry.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fentry.c new file mode 100644 index 000000000000..8436c6729167 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fentry.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright Leon Hwang */ + +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +int count = 0; + +SEC("fentry/subprog_tail") +int BPF_PROG(fentry, struct sk_buff *skb) +{ + count++; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fexit.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fexit.c new file mode 100644 index 000000000000..fe16412c6e6e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_fexit.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright Leon Hwang */ + +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_tracing.h> + +int count = 0; + +SEC("fexit/subprog_tail") +int BPF_PROG(fexit, struct sk_buff *skb) +{ + count++; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_bpf_ma.c b/tools/testing/selftests/bpf/progs/test_bpf_ma.c new file mode 100644 index 000000000000..ecde41ae0fc8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_bpf_ma.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023. Huawei Technologies Co., Ltd */ +#include <vmlinux.h> +#include <bpf/bpf_tracing.h> +#include <bpf/bpf_helpers.h> + +#include "bpf_experimental.h" +#include "bpf_misc.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +struct generic_map_value { + void *data; +}; + +char _license[] SEC("license") = "GPL"; + +const unsigned int data_sizes[] = {8, 16, 32, 64, 96, 128, 192, 256, 512, 1024, 2048, 4096}; +const volatile unsigned int data_btf_ids[ARRAY_SIZE(data_sizes)] = {}; + +int err = 0; +int pid = 0; + +#define DEFINE_ARRAY_WITH_KPTR(_size) \ + struct bin_data_##_size { \ + char data[_size - sizeof(void *)]; \ + }; \ + struct map_value_##_size { \ + struct bin_data_##_size __kptr * data; \ + /* To emit BTF info for bin_data_xx */ \ + struct bin_data_##_size not_used; \ + }; \ + struct { \ + __uint(type, BPF_MAP_TYPE_ARRAY); \ + __type(key, int); \ + __type(value, struct map_value_##_size); \ + __uint(max_entries, 128); \ + } array_##_size SEC(".maps"); + +static __always_inline void batch_alloc_free(struct bpf_map *map, unsigned int batch, + unsigned int idx) +{ + struct generic_map_value *value; + unsigned int i, key; + void *old, *new; + + for (i = 0; i < batch; i++) { + key = i; + value = bpf_map_lookup_elem(map, &key); + if (!value) { + err = 1; + return; + } + new = bpf_obj_new_impl(data_btf_ids[idx], NULL); + if (!new) { + err = 2; + return; + } + old = bpf_kptr_xchg(&value->data, new); + if (old) { + bpf_obj_drop(old); + err = 3; + return; + } + } + for (i = 0; i < batch; i++) { + key = i; + value = bpf_map_lookup_elem(map, &key); + if (!value) { + err = 4; + return; + } + old = bpf_kptr_xchg(&value->data, NULL); + if (!old) { + err = 5; + return; + } + bpf_obj_drop(old); + } +} + +#define CALL_BATCH_ALLOC_FREE(size, batch, idx) \ + batch_alloc_free((struct bpf_map *)(&array_##size), batch, idx) + +DEFINE_ARRAY_WITH_KPTR(8); +DEFINE_ARRAY_WITH_KPTR(16); +DEFINE_ARRAY_WITH_KPTR(32); +DEFINE_ARRAY_WITH_KPTR(64); +DEFINE_ARRAY_WITH_KPTR(96); +DEFINE_ARRAY_WITH_KPTR(128); +DEFINE_ARRAY_WITH_KPTR(192); +DEFINE_ARRAY_WITH_KPTR(256); +DEFINE_ARRAY_WITH_KPTR(512); +DEFINE_ARRAY_WITH_KPTR(1024); +DEFINE_ARRAY_WITH_KPTR(2048); +DEFINE_ARRAY_WITH_KPTR(4096); + +SEC("fentry/" SYS_PREFIX "sys_nanosleep") +int test_bpf_mem_alloc_free(void *ctx) +{ + if ((u32)bpf_get_current_pid_tgid() != pid) + return 0; + + /* Alloc 128 8-bytes objects in batch to trigger refilling, + * then free 128 8-bytes objects in batch to trigger freeing. + */ + CALL_BATCH_ALLOC_FREE(8, 128, 0); + CALL_BATCH_ALLOC_FREE(16, 128, 1); + CALL_BATCH_ALLOC_FREE(32, 128, 2); + CALL_BATCH_ALLOC_FREE(64, 128, 3); + CALL_BATCH_ALLOC_FREE(96, 128, 4); + CALL_BATCH_ALLOC_FREE(128, 128, 5); + CALL_BATCH_ALLOC_FREE(192, 128, 6); + CALL_BATCH_ALLOC_FREE(256, 128, 7); + CALL_BATCH_ALLOC_FREE(512, 64, 8); + CALL_BATCH_ALLOC_FREE(1024, 32, 9); + CALL_BATCH_ALLOC_FREE(2048, 16, 10); + CALL_BATCH_ALLOC_FREE(4096, 8, 11); + + return 0; +} diff --git a/tools/testing/selftests/bpf/progs/verifier_bswap.c b/tools/testing/selftests/bpf/progs/verifier_bswap.c index 8893094725f0..5d54f8eae6a1 100644 --- a/tools/testing/selftests/bpf/progs/verifier_bswap.c +++ b/tools/testing/selftests/bpf/progs/verifier_bswap.c @@ -5,7 +5,8 @@ #include "bpf_misc.h" #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && __clang_major__ >= 18 + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || defined(__TARGET_ARCH_arm)) && \ + __clang_major__ >= 18 SEC("socket") __description("BSWAP, 16") diff --git a/tools/testing/selftests/bpf/progs/verifier_gotol.c b/tools/testing/selftests/bpf/progs/verifier_gotol.c index 2dae5322a18e..aa54ecd5829e 100644 --- a/tools/testing/selftests/bpf/progs/verifier_gotol.c +++ b/tools/testing/selftests/bpf/progs/verifier_gotol.c @@ -5,7 +5,8 @@ #include "bpf_misc.h" #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && __clang_major__ >= 18 + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || defined(__TARGET_ARCH_arm)) && \ + __clang_major__ >= 18 SEC("socket") __description("gotol, small_imm") diff --git a/tools/testing/selftests/bpf/progs/verifier_ldsx.c b/tools/testing/selftests/bpf/progs/verifier_ldsx.c index 0c638f45aaf1..1e1bc379c44f 100644 --- a/tools/testing/selftests/bpf/progs/verifier_ldsx.c +++ b/tools/testing/selftests/bpf/progs/verifier_ldsx.c @@ -5,7 +5,8 @@ #include "bpf_misc.h" #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && __clang_major__ >= 18 + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || defined(__TARGET_ARCH_arm)) && \ + __clang_major__ >= 18 SEC("socket") __description("LDSX, S8") diff --git a/tools/testing/selftests/bpf/progs/verifier_movsx.c b/tools/testing/selftests/bpf/progs/verifier_movsx.c index 3c8ac2c57b1b..ca11fd5dafd1 100644 --- a/tools/testing/selftests/bpf/progs/verifier_movsx.c +++ b/tools/testing/selftests/bpf/progs/verifier_movsx.c @@ -5,7 +5,8 @@ #include "bpf_misc.h" #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && __clang_major__ >= 18 + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || defined(__TARGET_ARCH_arm)) && \ + __clang_major__ >= 18 SEC("socket") __description("MOV32SX, S8") diff --git a/tools/testing/selftests/bpf/progs/verifier_sdiv.c b/tools/testing/selftests/bpf/progs/verifier_sdiv.c index 0990f8825675..fb039722b639 100644 --- a/tools/testing/selftests/bpf/progs/verifier_sdiv.c +++ b/tools/testing/selftests/bpf/progs/verifier_sdiv.c @@ -5,7 +5,8 @@ #include "bpf_misc.h" #if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ - (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && __clang_major__ >= 18 + (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || defined(__TARGET_ARCH_arm)) && \ + __clang_major__ >= 18 SEC("socket") __description("SDIV32, non-zero imm divisor, check 1") diff --git a/tools/testing/selftests/bpf/test_bpftool_synctypes.py b/tools/testing/selftests/bpf/test_bpftool_synctypes.py index 0cfece7ff4f8..0ed67b6b31dd 100755 --- a/tools/testing/selftests/bpf/test_bpftool_synctypes.py +++ b/tools/testing/selftests/bpf/test_bpftool_synctypes.py @@ -509,6 +509,15 @@ def main(): source_map_types.remove('cgroup_storage_deprecated') source_map_types.add('cgroup_storage') + # The same applied to BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED and + # BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE which share the same enum value + # and source_map_types picks + # BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED/percpu_cgroup_storage_deprecated. + # Replace 'percpu_cgroup_storage_deprecated' with 'percpu_cgroup_storage' + # so it aligns with what `bpftool map help` shows. + source_map_types.remove('percpu_cgroup_storage_deprecated') + source_map_types.add('percpu_cgroup_storage') + help_map_types = map_info.get_map_help() help_map_options = map_info.get_options() map_info.close() diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 31f1c935cd07..98107e0452d3 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -1880,7 +1880,7 @@ int main(int argc, char **argv) } } - get_unpriv_disabled(); + unpriv_disabled = get_unpriv_disabled(); if (unpriv && unpriv_disabled) { printf("Cannot run as unprivileged user with sysctl %s.\n", UNPRIV_SYSCTL); diff --git a/tools/testing/selftests/bpf/test_xsk.sh b/tools/testing/selftests/bpf/test_xsk.sh index 2aa5a3445056..65aafe0003db 100755 --- a/tools/testing/selftests/bpf/test_xsk.sh +++ b/tools/testing/selftests/bpf/test_xsk.sh @@ -73,17 +73,33 @@ # # Run test suite for physical device in loopback mode # sudo ./test_xsk.sh -i IFACE +# +# Run test suite in a specific mode only [skb,drv,zc] +# sudo ./test_xsk.sh -m MODE +# +# List available tests +# ./test_xsk.sh -l +# +# Run a specific test from the test suite +# sudo ./test_xsk.sh -t TEST_NAME +# +# Display the available command line options +# ./test_xsk.sh -h . xsk_prereqs.sh ETH="" -while getopts "vi:d" flag +while getopts "vi:dm:lt:h" flag do case "${flag}" in v) verbose=1;; d) debug=1;; i) ETH=${OPTARG};; + m) MODE=${OPTARG};; + l) list=1;; + t) TEST=${OPTARG};; + h) help=1;; esac done @@ -131,6 +147,16 @@ setup_vethPairs() { ip link set ${VETH0} up } +if [[ $list -eq 1 ]]; then + ./${XSKOBJ} -l + exit +fi + +if [[ $help -eq 1 ]]; then + ./${XSKOBJ} + exit +fi + if [ ! -z $ETH ]; then VETH0=${ETH} VETH1=${ETH} @@ -153,6 +179,14 @@ if [[ $verbose -eq 1 ]]; then ARGS+="-v " fi +if [ -n "$MODE" ]; then + ARGS+="-m ${MODE} " +fi + +if [ -n "$TEST" ]; then + ARGS+="-t ${TEST} " +fi + retval=$? test_status $retval "${TEST_NAME}" @@ -175,6 +209,10 @@ else cleanup_iface ${ETH} ${MTU} fi +if [[ $list -eq 1 ]]; then + exit +fi + TEST_NAME="XSK_SELFTESTS_${VETH0}_BUSY_POLL" busy_poll=1 diff --git a/tools/testing/selftests/bpf/trace_helpers.c b/tools/testing/selftests/bpf/trace_helpers.c index f83d9f65c65b..4faa898ff7fc 100644 --- a/tools/testing/selftests/bpf/trace_helpers.c +++ b/tools/testing/selftests/bpf/trace_helpers.c @@ -7,6 +7,7 @@ #include <errno.h> #include <fcntl.h> #include <poll.h> +#include <pthread.h> #include <unistd.h> #include <linux/perf_event.h> #include <sys/mman.h> @@ -14,104 +15,165 @@ #include <linux/limits.h> #include <libelf.h> #include <gelf.h> +#include "bpf/libbpf_internal.h" #define TRACEFS_PIPE "/sys/kernel/tracing/trace_pipe" #define DEBUGFS_PIPE "/sys/kernel/debug/tracing/trace_pipe" -#define MAX_SYMS 400000 -static struct ksym syms[MAX_SYMS]; -static int sym_cnt; +struct ksyms { + struct ksym *syms; + size_t sym_cap; + size_t sym_cnt; +}; + +static struct ksyms *ksyms; +static pthread_mutex_t ksyms_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int ksyms__add_symbol(struct ksyms *ksyms, const char *name, + unsigned long addr) +{ + void *tmp; + + tmp = strdup(name); + if (!tmp) + return -ENOMEM; + ksyms->syms[ksyms->sym_cnt].addr = addr; + ksyms->syms[ksyms->sym_cnt].name = tmp; + ksyms->sym_cnt++; + return 0; +} + +void free_kallsyms_local(struct ksyms *ksyms) +{ + unsigned int i; + + if (!ksyms) + return; + + if (!ksyms->syms) { + free(ksyms); + return; + } + + for (i = 0; i < ksyms->sym_cnt; i++) + free(ksyms->syms[i].name); + free(ksyms->syms); + free(ksyms); +} static int ksym_cmp(const void *p1, const void *p2) { return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; } -int load_kallsyms_refresh(void) +struct ksyms *load_kallsyms_local(void) { FILE *f; char func[256], buf[256]; char symbol; void *addr; - int i = 0; - - sym_cnt = 0; + int ret; + struct ksyms *ksyms; f = fopen("/proc/kallsyms", "r"); if (!f) - return -ENOENT; + return NULL; + + ksyms = calloc(1, sizeof(struct ksyms)); + if (!ksyms) { + fclose(f); + return NULL; + } while (fgets(buf, sizeof(buf), f)) { if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) break; if (!addr) continue; - if (i >= MAX_SYMS) - return -EFBIG; - syms[i].addr = (long) addr; - syms[i].name = strdup(func); - i++; + ret = libbpf_ensure_mem((void **) &ksyms->syms, &ksyms->sym_cap, + sizeof(struct ksym), ksyms->sym_cnt + 1); + if (ret) + goto error; + ret = ksyms__add_symbol(ksyms, func, (unsigned long)addr); + if (ret) + goto error; } fclose(f); - sym_cnt = i; - qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); - return 0; + qsort(ksyms->syms, ksyms->sym_cnt, sizeof(struct ksym), ksym_cmp); + return ksyms; + +error: + fclose(f); + free_kallsyms_local(ksyms); + return NULL; } int load_kallsyms(void) { - /* - * This is called/used from multiplace places, - * load symbols just once. - */ - if (sym_cnt) - return 0; - return load_kallsyms_refresh(); + pthread_mutex_lock(&ksyms_mutex); + if (!ksyms) + ksyms = load_kallsyms_local(); + pthread_mutex_unlock(&ksyms_mutex); + return ksyms ? 0 : 1; } -struct ksym *ksym_search(long key) +struct ksym *ksym_search_local(struct ksyms *ksyms, long key) { - int start = 0, end = sym_cnt; + int start = 0, end = ksyms->sym_cnt; int result; /* kallsyms not loaded. return NULL */ - if (sym_cnt <= 0) + if (ksyms->sym_cnt <= 0) return NULL; while (start < end) { size_t mid = start + (end - start) / 2; - result = key - syms[mid].addr; + result = key - ksyms->syms[mid].addr; if (result < 0) end = mid; else if (result > 0) start = mid + 1; else - return &syms[mid]; + return &ksyms->syms[mid]; } - if (start >= 1 && syms[start - 1].addr < key && - key < syms[start].addr) + if (start >= 1 && ksyms->syms[start - 1].addr < key && + key < ksyms->syms[start].addr) /* valid ksym */ - return &syms[start - 1]; + return &ksyms->syms[start - 1]; /* out of range. return _stext */ - return &syms[0]; + return &ksyms->syms[0]; } -long ksym_get_addr(const char *name) +struct ksym *ksym_search(long key) +{ + if (!ksyms) + return NULL; + return ksym_search_local(ksyms, key); +} + +long ksym_get_addr_local(struct ksyms *ksyms, const char *name) { int i; - for (i = 0; i < sym_cnt; i++) { - if (strcmp(syms[i].name, name) == 0) - return syms[i].addr; + for (i = 0; i < ksyms->sym_cnt; i++) { + if (strcmp(ksyms->syms[i].name, name) == 0) + return ksyms->syms[i].addr; } return 0; } +long ksym_get_addr(const char *name) +{ + if (!ksyms) + return 0; + return ksym_get_addr_local(ksyms, name); +} + /* open kallsyms and read symbol addresses on the fly. Without caching all symbols, * this is faster than load + find. */ diff --git a/tools/testing/selftests/bpf/trace_helpers.h b/tools/testing/selftests/bpf/trace_helpers.h index 876f3e711df6..04fd1da7079d 100644 --- a/tools/testing/selftests/bpf/trace_helpers.h +++ b/tools/testing/selftests/bpf/trace_helpers.h @@ -11,13 +11,17 @@ struct ksym { long addr; char *name; }; +struct ksyms; int load_kallsyms(void); -int load_kallsyms_refresh(void); - struct ksym *ksym_search(long key); long ksym_get_addr(const char *name); +struct ksyms *load_kallsyms_local(void); +struct ksym *ksym_search_local(struct ksyms *ksyms, long key); +long ksym_get_addr_local(struct ksyms *ksyms, const char *name); +void free_kallsyms_local(struct ksyms *ksyms); + /* open kallsyms and find addresses on the fly, faster than load + search. */ int kallsyms_find(const char *sym, unsigned long long *addr); diff --git a/tools/testing/selftests/bpf/xsk_prereqs.sh b/tools/testing/selftests/bpf/xsk_prereqs.sh index 29175682c44d..47c7b8064f38 100755 --- a/tools/testing/selftests/bpf/xsk_prereqs.sh +++ b/tools/testing/selftests/bpf/xsk_prereqs.sh @@ -83,9 +83,11 @@ exec_xskxceiver() fi ./${XSKOBJ} -i ${VETH0} -i ${VETH1} ${ARGS} - retval=$? - test_status $retval "${TEST_NAME}" - statusList+=($retval) - nameList+=(${TEST_NAME}) + + if [[ $list -ne 1 ]]; then + test_status $retval "${TEST_NAME}" + statusList+=($retval) + nameList+=(${TEST_NAME}) + fi } diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c index 2827f2d7cf30..43e0a5796929 100644 --- a/tools/testing/selftests/bpf/xskxceiver.c +++ b/tools/testing/selftests/bpf/xskxceiver.c @@ -107,6 +107,11 @@ static const char *MAC1 = "\x00\x0A\x56\x9E\xEE\x62"; static const char *MAC2 = "\x00\x0A\x56\x9E\xEE\x61"; +static bool opt_verbose; +static bool opt_print_tests; +static enum test_mode opt_mode = TEST_MODE_ALL; +static u32 opt_run_test = RUN_ALL_TESTS; + static void __exit_with_error(int error, const char *file, const char *func, int line) { ksft_test_result_fail("[%s:%s:%i]: ERROR: %d/\"%s\"\n", file, func, line, error, @@ -310,19 +315,28 @@ static struct option long_options[] = { {"interface", required_argument, 0, 'i'}, {"busy-poll", no_argument, 0, 'b'}, {"verbose", no_argument, 0, 'v'}, + {"mode", required_argument, 0, 'm'}, + {"list", no_argument, 0, 'l'}, + {"test", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; -static void usage(const char *prog) +static void print_usage(char **argv) { const char *str = - " Usage: %s [OPTIONS]\n" + " Usage: xskxceiver [OPTIONS]\n" " Options:\n" " -i, --interface Use interface\n" " -v, --verbose Verbose output\n" - " -b, --busy-poll Enable busy poll\n"; + " -b, --busy-poll Enable busy poll\n" + " -m, --mode Run only mode skb, drv, or zc\n" + " -l, --list List all available tests\n" + " -t, --test Run a specific test. Enter number from -l option.\n" + " -h, --help Display this help and exit\n"; - ksft_print_msg(str, prog); + ksft_print_msg(str, basename(argv[0])); + ksft_exit_xfail(); } static bool validate_interface(struct ifobject *ifobj) @@ -342,7 +356,7 @@ static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj opterr = 0; for (;;) { - c = getopt_long(argc, argv, "i:vb", long_options, &option_index); + c = getopt_long(argc, argv, "i:vbm:lt:", long_options, &option_index); if (c == -1) break; @@ -371,9 +385,28 @@ static void parse_command_line(struct ifobject *ifobj_tx, struct ifobject *ifobj ifobj_tx->busy_poll = true; ifobj_rx->busy_poll = true; break; + case 'm': + if (!strncmp("skb", optarg, strlen(optarg))) + opt_mode = TEST_MODE_SKB; + else if (!strncmp("drv", optarg, strlen(optarg))) + opt_mode = TEST_MODE_DRV; + else if (!strncmp("zc", optarg, strlen(optarg))) + opt_mode = TEST_MODE_ZC; + else + print_usage(argv); + break; + case 'l': + opt_print_tests = true; + break; + case 't': + errno = 0; + opt_run_test = strtol(optarg, NULL, 0); + if (errno) + print_usage(argv); + break; + case 'h': default: - usage(basename(argv[0])); - ksft_exit_xfail(); + print_usage(argv); } } } @@ -427,7 +460,8 @@ static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx, } static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx, - struct ifobject *ifobj_rx, enum test_mode mode) + struct ifobject *ifobj_rx, enum test_mode mode, + const struct test_spec *test_to_run) { struct pkt_stream *tx_pkt_stream; struct pkt_stream *rx_pkt_stream; @@ -449,6 +483,8 @@ static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx, ifobj->bind_flags |= XDP_COPY; } + strncpy(test->name, test_to_run->name, MAX_TEST_NAME_SIZE); + test->test_func = test_to_run->test_func; test->mode = mode; __test_spec_init(test, ifobj_tx, ifobj_rx); } @@ -458,11 +494,6 @@ static void test_spec_reset(struct test_spec *test) __test_spec_init(test, test->ifobj_tx, test->ifobj_rx); } -static void test_spec_set_name(struct test_spec *test, const char *name) -{ - strncpy(test->name, name, MAX_TEST_NAME_SIZE); -} - static void test_spec_set_xdp_prog(struct test_spec *test, struct bpf_program *xdp_prog_rx, struct bpf_program *xdp_prog_tx, struct bpf_map *xskmap_rx, struct bpf_map *xskmap_tx) @@ -747,6 +778,9 @@ static struct pkt_stream *__pkt_stream_generate_custom(struct ifobject *ifobj, s len = 0; } + print_verbose("offset: %d len: %u valid: %u options: %u pkt_nb: %u\n", + pkt->offset, pkt->len, pkt->valid, pkt->options, pkt->pkt_nb); + if (pkt->valid && pkt->len > pkt_stream->max_pkt_len) pkt_stream->max_pkt_len = pkt->len; pkt_nb++; @@ -777,7 +811,7 @@ static void pkt_print_data(u32 *data, u32 cnt) seqnum = ntohl(*data) & 0xffff; pkt_nb = ntohl(*data) >> 16; - fprintf(stdout, "%u:%u ", pkt_nb, seqnum); + ksft_print_msg("%u:%u ", pkt_nb, seqnum); data++; } } @@ -789,13 +823,13 @@ static void pkt_dump(void *pkt, u32 len, bool eth_header) if (eth_header) { /*extract L2 frame */ - fprintf(stdout, "DEBUG>> L2: dst mac: "); + ksft_print_msg("DEBUG>> L2: dst mac: "); for (i = 0; i < ETH_ALEN; i++) - fprintf(stdout, "%02X", ethhdr->h_dest[i]); + ksft_print_msg("%02X", ethhdr->h_dest[i]); - fprintf(stdout, "\nDEBUG>> L2: src mac: "); + ksft_print_msg("\nDEBUG>> L2: src mac: "); for (i = 0; i < ETH_ALEN; i++) - fprintf(stdout, "%02X", ethhdr->h_source[i]); + ksft_print_msg("%02X", ethhdr->h_source[i]); data = pkt + PKT_HDR_SIZE; } else { @@ -803,15 +837,15 @@ static void pkt_dump(void *pkt, u32 len, bool eth_header) } /*extract L5 frame */ - fprintf(stdout, "\nDEBUG>> L5: seqnum: "); + ksft_print_msg("\nDEBUG>> L5: seqnum: "); pkt_print_data(data, PKT_DUMP_NB_TO_PRINT); - fprintf(stdout, "...."); + ksft_print_msg("...."); if (len > PKT_DUMP_NB_TO_PRINT * sizeof(u32)) { - fprintf(stdout, "\n.... "); + ksft_print_msg("\n.... "); pkt_print_data(data + len / sizeof(u32) - PKT_DUMP_NB_TO_PRINT, PKT_DUMP_NB_TO_PRINT); } - fprintf(stdout, "\n---------------------------------------\n"); + ksft_print_msg("\n---------------------------------------\n"); } static bool is_offset_correct(struct xsk_umem_info *umem, struct pkt *pkt, u64 addr) @@ -916,36 +950,42 @@ static bool is_pkt_valid(struct pkt *pkt, void *buffer, u64 addr, u32 len) return true; } -static void kick_tx(struct xsk_socket_info *xsk) +static int 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) - return; + return TEST_PASS; if (errno == ENOBUFS || errno == EAGAIN || errno == EBUSY || errno == ENETDOWN) { usleep(100); - return; + return TEST_PASS; } - exit_with_error(errno); + return TEST_FAILURE; } -static void kick_rx(struct xsk_socket_info *xsk) +static int kick_rx(struct xsk_socket_info *xsk) { int ret; ret = recvfrom(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL); if (ret < 0) - exit_with_error(errno); + return TEST_FAILURE; + + return TEST_PASS; } static int complete_pkts(struct xsk_socket_info *xsk, int batch_size) { unsigned int rcvd; u32 idx; + int ret; - if (xsk_ring_prod__needs_wakeup(&xsk->tx)) - kick_tx(xsk); + if (xsk_ring_prod__needs_wakeup(&xsk->tx)) { + ret = kick_tx(xsk); + if (ret) + return TEST_FAILURE; + } rcvd = xsk_ring_cons__peek(&xsk->umem->cq, batch_size, &idx); if (rcvd) { @@ -993,11 +1033,14 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds) return TEST_FAILURE; } - kick_rx(xsk); + ret = kick_rx(xsk); + if (ret) + return TEST_FAILURE; + if (ifobj->use_poll) { ret = poll(fds, 1, POLL_TMOUT); if (ret < 0) - exit_with_error(errno); + return TEST_FAILURE; if (!ret) { if (!is_umem_valid(test->ifobj_tx)) @@ -1018,12 +1061,10 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds) if (ifobj->use_fill_ring) { ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq); while (ret != rcvd) { - if (ret < 0) - exit_with_error(-ret); if (xsk_ring_prod__needs_wakeup(&umem->fq)) { ret = poll(fds, 1, POLL_TMOUT); if (ret < 0) - exit_with_error(errno); + return TEST_FAILURE; } ret = xsk_ring_prod__reserve(&umem->fq, rcvd, &idx_fq); } @@ -1042,6 +1083,9 @@ static int receive_pkts(struct test_spec *test, struct pollfd *fds) return TEST_FAILURE; } + print_verbose("Rx: addr: %lx len: %u options: %u pkt_nb: %u valid: %u\n", + addr, desc->len, desc->options, pkt->pkt_nb, pkt->valid); + if (!is_frag_valid(umem, addr, desc->len, pkt->pkt_nb, pkt_len) || !is_offset_correct(umem, pkt, addr) || (ifobj->use_metadata && !is_metadata_correct(pkt, umem->buffer, addr))) @@ -1104,7 +1148,9 @@ static int __send_pkts(struct ifobject *ifobject, struct pollfd *fds, bool timeo buffer_len = pkt_get_buffer_len(umem, pkt_stream->max_pkt_len); /* pkts_in_flight might be negative if many invalid packets are sent */ if (pkts_in_flight >= (int)((umem_size(umem) - BATCH_SIZE * buffer_len) / buffer_len)) { - kick_tx(xsk); + ret = kick_tx(xsk); + if (ret) + return TEST_FAILURE; return TEST_CONTINUE; } @@ -1165,6 +1211,9 @@ static int __send_pkts(struct ifobject *ifobject, struct pollfd *fds, bool timeo bytes_written); bytes_written += tx_desc->len; + print_verbose("Tx addr: %llx len: %u options: %u pkt_nb: %u\n", + tx_desc->addr, tx_desc->len, tx_desc->options, pkt->pkt_nb); + if (nb_frags_left) { i++; if (pkt_stream->verbatim) @@ -1207,10 +1256,29 @@ static int __send_pkts(struct ifobject *ifobject, struct pollfd *fds, bool timeo return TEST_CONTINUE; } -static void wait_for_tx_completion(struct xsk_socket_info *xsk) +static int wait_for_tx_completion(struct xsk_socket_info *xsk) { - while (xsk->outstanding_tx) + struct timeval tv_end, tv_now, tv_timeout = {THREAD_TMOUT, 0}; + int ret; + + ret = gettimeofday(&tv_now, NULL); + if (ret) + exit_with_error(errno); + timeradd(&tv_now, &tv_timeout, &tv_end); + + while (xsk->outstanding_tx) { + ret = gettimeofday(&tv_now, NULL); + if (ret) + exit_with_error(errno); + if (timercmp(&tv_now, &tv_end, >)) { + ksft_print_msg("ERROR: [%s] Transmission loop timed out\n", __func__); + return TEST_FAILURE; + } + complete_pkts(xsk, BATCH_SIZE); + } + + return TEST_PASS; } static int send_pkts(struct test_spec *test, struct ifobject *ifobject) @@ -1233,8 +1301,7 @@ static int send_pkts(struct test_spec *test, struct ifobject *ifobject) return ret; } - wait_for_tx_completion(ifobject->xsk); - return TEST_PASS; + return wait_for_tx_completion(ifobject->xsk); } static int get_xsk_stats(struct xsk_socket *xsk, struct xdp_statistics *stats) @@ -1266,7 +1333,9 @@ static int validate_rx_dropped(struct ifobject *ifobject) struct xdp_statistics stats; int err; - kick_rx(ifobject->xsk); + err = kick_rx(ifobject->xsk); + if (err) + return TEST_FAILURE; err = get_xsk_stats(xsk, &stats); if (err) @@ -1292,7 +1361,9 @@ static int validate_rx_full(struct ifobject *ifobject) int err; usleep(1000); - kick_rx(ifobject->xsk); + err = kick_rx(ifobject->xsk); + if (err) + return TEST_FAILURE; err = get_xsk_stats(xsk, &stats); if (err) @@ -1311,7 +1382,9 @@ static int validate_fill_empty(struct ifobject *ifobject) int err; usleep(1000); - kick_rx(ifobject->xsk); + err = kick_rx(ifobject->xsk); + if (err) + return TEST_FAILURE; err = get_xsk_stats(xsk, &stats); if (err) @@ -1475,8 +1548,6 @@ static void *worker_testapp_validate_tx(void *arg) thread_common_ops_tx(test, ifobject); } - print_verbose("Sending %d packets on interface %s\n", ifobject->pkt_stream->nb_pkts, - ifobject->ifname); err = send_pkts(test, ifobject); if (!err && ifobject->validation_func) @@ -1500,7 +1571,8 @@ static void *worker_testapp_validate_rx(void *arg) xsk_clear_xskmap(ifobject->xskmap); err = xsk_update_xskmap(ifobject->xskmap, ifobject->xsk->xsk); if (err) { - printf("Error: Failed to update xskmap, error %s\n", strerror(-err)); + ksft_print_msg("Error: Failed to update xskmap, error %s\n", + strerror(-err)); exit_with_error(-err); } } @@ -1564,7 +1636,7 @@ static void xsk_reattach_xdp(struct ifobject *ifobj, struct bpf_program *xdp_pro xsk_detach_xdp_program(ifobj->ifindex, mode_to_xdp_flags(ifobj->mode)); err = xsk_attach_xdp_program(xdp_prog, ifobj->ifindex, mode_to_xdp_flags(mode)); if (err) { - printf("Error attaching XDP program\n"); + ksft_print_msg("Error attaching XDP program\n"); exit_with_error(-err); } @@ -1682,7 +1754,6 @@ static int testapp_teardown(struct test_spec *test) { int i; - test_spec_set_name(test, "TEARDOWN"); for (i = 0; i < MAX_TEARDOWN_ITER; i++) { if (testapp_validate_traffic(test)) return TEST_FAILURE; @@ -1704,18 +1775,17 @@ static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2) *ifobj2 = tmp_ifobj; } -static int testapp_bidi(struct test_spec *test) +static int testapp_bidirectional(struct test_spec *test) { int res; - test_spec_set_name(test, "BIDIRECTIONAL"); test->ifobj_tx->rx_on = true; test->ifobj_rx->tx_on = true; test->total_steps = 2; if (testapp_validate_traffic(test)) return TEST_FAILURE; - print_verbose("Switching Tx/Rx vectors\n"); + print_verbose("Switching Tx/Rx direction\n"); swap_directions(&test->ifobj_rx, &test->ifobj_tx); res = __testapp_validate_traffic(test, test->ifobj_rx, test->ifobj_tx); @@ -1723,7 +1793,7 @@ static int testapp_bidi(struct test_spec *test) return res; } -static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx) +static int swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx) { int ret; @@ -1734,31 +1804,31 @@ static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj ret = xsk_update_xskmap(ifobj_rx->xskmap, ifobj_rx->xsk->xsk); if (ret) - exit_with_error(errno); + return TEST_FAILURE; + + return TEST_PASS; } -static int testapp_bpf_res(struct test_spec *test) +static int testapp_xdp_prog_cleanup(struct test_spec *test) { - test_spec_set_name(test, "BPF_RES"); test->total_steps = 2; test->nb_sockets = 2; if (testapp_validate_traffic(test)) return TEST_FAILURE; - swap_xsk_resources(test->ifobj_tx, test->ifobj_rx); + if (swap_xsk_resources(test->ifobj_tx, test->ifobj_rx)) + return TEST_FAILURE; return testapp_validate_traffic(test); } static int testapp_headroom(struct test_spec *test) { - test_spec_set_name(test, "UMEM_HEADROOM"); test->ifobj_rx->umem->frame_headroom = UMEM_HEADROOM_TEST_SIZE; return testapp_validate_traffic(test); } static int testapp_stats_rx_dropped(struct test_spec *test) { - test_spec_set_name(test, "STAT_RX_DROPPED"); if (test->mode == TEST_MODE_ZC) { ksft_test_result_skip("Can not run RX_DROPPED test for ZC mode\n"); return TEST_SKIP; @@ -1774,7 +1844,6 @@ static int testapp_stats_rx_dropped(struct test_spec *test) static int testapp_stats_tx_invalid_descs(struct test_spec *test) { - test_spec_set_name(test, "STAT_TX_INVALID"); pkt_stream_replace_half(test, XSK_UMEM__INVALID_FRAME_SIZE, 0); test->ifobj_tx->validation_func = validate_tx_invalid_descs; return testapp_validate_traffic(test); @@ -1782,7 +1851,6 @@ static int testapp_stats_tx_invalid_descs(struct test_spec *test) static int testapp_stats_rx_full(struct test_spec *test) { - test_spec_set_name(test, "STAT_RX_FULL"); pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE); test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem, DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE); @@ -1795,7 +1863,6 @@ static int testapp_stats_rx_full(struct test_spec *test) static int testapp_stats_fill_empty(struct test_spec *test) { - test_spec_set_name(test, "STAT_RX_FILL_EMPTY"); pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE); test->ifobj_rx->pkt_stream = pkt_stream_generate(test->ifobj_rx->umem, DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE); @@ -1805,9 +1872,8 @@ static int testapp_stats_fill_empty(struct test_spec *test) return testapp_validate_traffic(test); } -static int testapp_unaligned(struct test_spec *test) +static int testapp_send_receive_unaligned(struct test_spec *test) { - test_spec_set_name(test, "UNALIGNED_MODE"); test->ifobj_tx->umem->unaligned_mode = true; test->ifobj_rx->umem->unaligned_mode = true; /* Let half of the packets straddle a 4K buffer boundary */ @@ -1816,9 +1882,8 @@ static int testapp_unaligned(struct test_spec *test) return testapp_validate_traffic(test); } -static int testapp_unaligned_mb(struct test_spec *test) +static int testapp_send_receive_unaligned_mb(struct test_spec *test) { - test_spec_set_name(test, "UNALIGNED_MODE_9K"); test->mtu = MAX_ETH_JUMBO_SIZE; test->ifobj_tx->umem->unaligned_mode = true; test->ifobj_rx->umem->unaligned_mode = true; @@ -1834,9 +1899,8 @@ static int testapp_single_pkt(struct test_spec *test) return testapp_validate_traffic(test); } -static int testapp_multi_buffer(struct test_spec *test) +static int testapp_send_receive_mb(struct test_spec *test) { - test_spec_set_name(test, "RUN_TO_COMPLETION_9K_PACKETS"); test->mtu = MAX_ETH_JUMBO_SIZE; pkt_stream_replace(test, DEFAULT_PKT_CNT, MAX_ETH_JUMBO_SIZE); @@ -1933,7 +1997,6 @@ static int testapp_xdp_drop(struct test_spec *test) struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs; struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs; - test_spec_set_name(test, "XDP_DROP_HALF"); test_spec_set_xdp_prog(test, skel_rx->progs.xsk_xdp_drop, skel_tx->progs.xsk_xdp_drop, skel_rx->maps.xsk, skel_tx->maps.xsk); @@ -1941,7 +2004,7 @@ static int testapp_xdp_drop(struct test_spec *test) return testapp_validate_traffic(test); } -static int testapp_xdp_metadata_count(struct test_spec *test) +static int testapp_xdp_metadata_copy(struct test_spec *test) { struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs; struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs; @@ -1955,19 +2018,21 @@ static int testapp_xdp_metadata_count(struct test_spec *test) test->ifobj_rx->use_metadata = true; data_map = bpf_object__find_map_by_name(skel_rx->obj, "xsk_xdp_.bss"); - if (!data_map || !bpf_map__is_internal(data_map)) - exit_with_error(ENOMEM); + if (!data_map || !bpf_map__is_internal(data_map)) { + ksft_print_msg("Error: could not find bss section of XDP program\n"); + return TEST_FAILURE; + } - if (bpf_map_update_elem(bpf_map__fd(data_map), &key, &count, BPF_ANY)) - exit_with_error(errno); + if (bpf_map_update_elem(bpf_map__fd(data_map), &key, &count, BPF_ANY)) { + ksft_print_msg("Error: could not update count element\n"); + return TEST_FAILURE; + } return testapp_validate_traffic(test); } static int testapp_poll_txq_tmout(struct test_spec *test) { - test_spec_set_name(test, "POLL_TXQ_FULL"); - test->ifobj_tx->use_poll = true; /* create invalid frame by set umem frame_size and pkt length equal to 2048 */ test->ifobj_tx->umem->frame_size = 2048; @@ -1977,7 +2042,6 @@ static int testapp_poll_txq_tmout(struct test_spec *test) static int testapp_poll_rxq_tmout(struct test_spec *test) { - test_spec_set_name(test, "POLL_RXQ_EMPTY"); test->ifobj_rx->use_poll = true; return testapp_validate_traffic_single_thread(test, test->ifobj_rx); } @@ -1987,7 +2051,6 @@ static int testapp_too_many_frags(struct test_spec *test) struct pkt pkts[2 * XSK_DESC__MAX_SKB_FRAGS + 2] = {}; u32 max_frags, i; - test_spec_set_name(test, "TOO_MANY_FRAGS"); if (test->mode == TEST_MODE_ZC) max_frags = test->ifobj_tx->xdp_zc_max_segs; else @@ -2067,7 +2130,7 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char * err = xsk_load_xdp_programs(ifobj); if (err) { - printf("Error loading XDP program\n"); + ksft_print_msg("Error loading XDP program\n"); exit_with_error(err); } @@ -2091,138 +2154,98 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char * } } -static void run_pkt_test(struct test_spec *test, enum test_mode mode, enum test_type type) -{ - int ret = TEST_SKIP; - - switch (type) { - case TEST_TYPE_STATS_RX_DROPPED: - ret = testapp_stats_rx_dropped(test); - break; - case TEST_TYPE_STATS_TX_INVALID_DESCS: - ret = testapp_stats_tx_invalid_descs(test); - break; - case TEST_TYPE_STATS_RX_FULL: - ret = testapp_stats_rx_full(test); - break; - case TEST_TYPE_STATS_FILL_EMPTY: - ret = testapp_stats_fill_empty(test); - break; - case TEST_TYPE_TEARDOWN: - ret = testapp_teardown(test); - break; - case TEST_TYPE_BIDI: - ret = testapp_bidi(test); - break; - case TEST_TYPE_BPF_RES: - ret = testapp_bpf_res(test); - break; - case TEST_TYPE_RUN_TO_COMPLETION: - test_spec_set_name(test, "RUN_TO_COMPLETION"); - ret = testapp_validate_traffic(test); - break; - case TEST_TYPE_RUN_TO_COMPLETION_MB: - ret = testapp_multi_buffer(test); - break; - case TEST_TYPE_RUN_TO_COMPLETION_SINGLE_PKT: - test_spec_set_name(test, "RUN_TO_COMPLETION_SINGLE_PKT"); - ret = testapp_single_pkt(test); - break; - case TEST_TYPE_RUN_TO_COMPLETION_2K_FRAME: - test_spec_set_name(test, "RUN_TO_COMPLETION_2K_FRAME_SIZE"); - test->ifobj_tx->umem->frame_size = 2048; - test->ifobj_rx->umem->frame_size = 2048; - pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE); - ret = testapp_validate_traffic(test); - break; - case TEST_TYPE_RX_POLL: - test->ifobj_rx->use_poll = true; - test_spec_set_name(test, "POLL_RX"); - ret = testapp_validate_traffic(test); - break; - case TEST_TYPE_TX_POLL: - test->ifobj_tx->use_poll = true; - test_spec_set_name(test, "POLL_TX"); - ret = testapp_validate_traffic(test); - break; - case TEST_TYPE_POLL_TXQ_TMOUT: - ret = testapp_poll_txq_tmout(test); - break; - case TEST_TYPE_POLL_RXQ_TMOUT: - ret = testapp_poll_rxq_tmout(test); - break; - case TEST_TYPE_ALIGNED_INV_DESC: - test_spec_set_name(test, "ALIGNED_INV_DESC"); - ret = testapp_invalid_desc(test); - break; - case TEST_TYPE_ALIGNED_INV_DESC_2K_FRAME: - test_spec_set_name(test, "ALIGNED_INV_DESC_2K_FRAME_SIZE"); - test->ifobj_tx->umem->frame_size = 2048; - test->ifobj_rx->umem->frame_size = 2048; - ret = testapp_invalid_desc(test); - break; - case TEST_TYPE_UNALIGNED_INV_DESC: - test_spec_set_name(test, "UNALIGNED_INV_DESC"); - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; - ret = testapp_invalid_desc(test); - break; - case TEST_TYPE_UNALIGNED_INV_DESC_4K1_FRAME: { - u64 page_size, umem_size; - - test_spec_set_name(test, "UNALIGNED_INV_DESC_4K1_FRAME_SIZE"); - /* Odd frame size so the UMEM doesn't end near a page boundary. */ - test->ifobj_tx->umem->frame_size = 4001; - test->ifobj_rx->umem->frame_size = 4001; - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; - /* This test exists to test descriptors that staddle the end of - * the UMEM but not a page. - */ - page_size = sysconf(_SC_PAGESIZE); - umem_size = test->ifobj_tx->umem->num_frames * test->ifobj_tx->umem->frame_size; - assert(umem_size % page_size > MIN_PKT_SIZE); - assert(umem_size % page_size < page_size - MIN_PKT_SIZE); - ret = testapp_invalid_desc(test); - break; - } - case TEST_TYPE_ALIGNED_INV_DESC_MB: - test_spec_set_name(test, "ALIGNED_INV_DESC_MULTI_BUFF"); - ret = testapp_invalid_desc_mb(test); - break; - case TEST_TYPE_UNALIGNED_INV_DESC_MB: - test_spec_set_name(test, "UNALIGNED_INV_DESC_MULTI_BUFF"); - test->ifobj_tx->umem->unaligned_mode = true; - test->ifobj_rx->umem->unaligned_mode = true; - ret = testapp_invalid_desc_mb(test); - break; - case TEST_TYPE_UNALIGNED: - ret = testapp_unaligned(test); - break; - case TEST_TYPE_UNALIGNED_MB: - ret = testapp_unaligned_mb(test); - break; - case TEST_TYPE_HEADROOM: - ret = testapp_headroom(test); - break; - case TEST_TYPE_XDP_DROP_HALF: - ret = testapp_xdp_drop(test); - break; - case TEST_TYPE_XDP_METADATA_COUNT: - test_spec_set_name(test, "XDP_METADATA_COUNT"); - ret = testapp_xdp_metadata_count(test); - break; - case TEST_TYPE_XDP_METADATA_COUNT_MB: - test_spec_set_name(test, "XDP_METADATA_COUNT_MULTI_BUFF"); - test->mtu = MAX_ETH_JUMBO_SIZE; - ret = testapp_xdp_metadata_count(test); - break; - case TEST_TYPE_TOO_MANY_FRAGS: - ret = testapp_too_many_frags(test); - break; - default: - break; - } +static int testapp_send_receive(struct test_spec *test) +{ + return testapp_validate_traffic(test); +} + +static int testapp_send_receive_2k_frame(struct test_spec *test) +{ + test->ifobj_tx->umem->frame_size = 2048; + test->ifobj_rx->umem->frame_size = 2048; + pkt_stream_replace(test, DEFAULT_PKT_CNT, MIN_PKT_SIZE); + return testapp_validate_traffic(test); +} + +static int testapp_poll_rx(struct test_spec *test) +{ + test->ifobj_rx->use_poll = true; + return testapp_validate_traffic(test); +} + +static int testapp_poll_tx(struct test_spec *test) +{ + test->ifobj_tx->use_poll = true; + return testapp_validate_traffic(test); +} + +static int testapp_aligned_inv_desc(struct test_spec *test) +{ + return testapp_invalid_desc(test); +} + +static int testapp_aligned_inv_desc_2k_frame(struct test_spec *test) +{ + test->ifobj_tx->umem->frame_size = 2048; + test->ifobj_rx->umem->frame_size = 2048; + return testapp_invalid_desc(test); +} + +static int testapp_unaligned_inv_desc(struct test_spec *test) +{ + test->ifobj_tx->umem->unaligned_mode = true; + test->ifobj_rx->umem->unaligned_mode = true; + return testapp_invalid_desc(test); +} + +static int testapp_unaligned_inv_desc_4001_frame(struct test_spec *test) +{ + u64 page_size, umem_size; + + /* Odd frame size so the UMEM doesn't end near a page boundary. */ + test->ifobj_tx->umem->frame_size = 4001; + test->ifobj_rx->umem->frame_size = 4001; + test->ifobj_tx->umem->unaligned_mode = true; + test->ifobj_rx->umem->unaligned_mode = true; + /* This test exists to test descriptors that staddle the end of + * the UMEM but not a page. + */ + page_size = sysconf(_SC_PAGESIZE); + umem_size = test->ifobj_tx->umem->num_frames * test->ifobj_tx->umem->frame_size; + assert(umem_size % page_size > MIN_PKT_SIZE); + assert(umem_size % page_size < page_size - MIN_PKT_SIZE); + + return testapp_invalid_desc(test); +} + +static int testapp_aligned_inv_desc_mb(struct test_spec *test) +{ + return testapp_invalid_desc_mb(test); +} + +static int testapp_unaligned_inv_desc_mb(struct test_spec *test) +{ + test->ifobj_tx->umem->unaligned_mode = true; + test->ifobj_rx->umem->unaligned_mode = true; + return testapp_invalid_desc_mb(test); +} + +static int testapp_xdp_metadata(struct test_spec *test) +{ + return testapp_xdp_metadata_copy(test); +} + +static int testapp_xdp_metadata_mb(struct test_spec *test) +{ + test->mtu = MAX_ETH_JUMBO_SIZE; + return testapp_xdp_metadata_copy(test); +} + +static void run_pkt_test(struct test_spec *test) +{ + int ret; + + ret = test->test_func(test); if (ret == TEST_PASS) ksft_test_result_pass("PASS: %s %s%s\n", mode_string(test), busy_poll_string(test), @@ -2290,13 +2313,55 @@ static bool is_xdp_supported(int ifindex) return true; } +static const struct test_spec tests[] = { + {.name = "SEND_RECEIVE", .test_func = testapp_send_receive}, + {.name = "SEND_RECEIVE_2K_FRAME", .test_func = testapp_send_receive_2k_frame}, + {.name = "SEND_RECEIVE_SINGLE_PKT", .test_func = testapp_single_pkt}, + {.name = "POLL_RX", .test_func = testapp_poll_rx}, + {.name = "POLL_TX", .test_func = testapp_poll_tx}, + {.name = "POLL_RXQ_FULL", .test_func = testapp_poll_rxq_tmout}, + {.name = "POLL_TXQ_FULL", .test_func = testapp_poll_txq_tmout}, + {.name = "SEND_RECEIVE_UNALIGNED", .test_func = testapp_send_receive_unaligned}, + {.name = "ALIGNED_INV_DESC", .test_func = testapp_aligned_inv_desc}, + {.name = "ALIGNED_INV_DESC_2K_FRAME_SIZE", .test_func = testapp_aligned_inv_desc_2k_frame}, + {.name = "UNALIGNED_INV_DESC", .test_func = testapp_unaligned_inv_desc}, + {.name = "UNALIGNED_INV_DESC_4001_FRAME_SIZE", + .test_func = testapp_unaligned_inv_desc_4001_frame}, + {.name = "UMEM_HEADROOM", .test_func = testapp_headroom}, + {.name = "TEARDOWN", .test_func = testapp_teardown}, + {.name = "BIDIRECTIONAL", .test_func = testapp_bidirectional}, + {.name = "STAT_RX_DROPPED", .test_func = testapp_stats_rx_dropped}, + {.name = "STAT_TX_INVALID", .test_func = testapp_stats_tx_invalid_descs}, + {.name = "STAT_RX_FULL", .test_func = testapp_stats_rx_full}, + {.name = "STAT_FILL_EMPTY", .test_func = testapp_stats_fill_empty}, + {.name = "XDP_PROG_CLEANUP", .test_func = testapp_xdp_prog_cleanup}, + {.name = "XDP_DROP_HALF", .test_func = testapp_xdp_drop}, + {.name = "XDP_METADATA_COPY", .test_func = testapp_xdp_metadata}, + {.name = "XDP_METADATA_COPY_MULTI_BUFF", .test_func = testapp_xdp_metadata_mb}, + {.name = "SEND_RECEIVE_9K_PACKETS", .test_func = testapp_send_receive_mb}, + {.name = "SEND_RECEIVE_UNALIGNED_9K_PACKETS", + .test_func = testapp_send_receive_unaligned_mb}, + {.name = "ALIGNED_INV_DESC_MULTI_BUFF", .test_func = testapp_aligned_inv_desc_mb}, + {.name = "UNALIGNED_INV_DESC_MULTI_BUFF", .test_func = testapp_unaligned_inv_desc_mb}, + {.name = "TOO_MANY_FRAGS", .test_func = testapp_too_many_frags}, +}; + +static void print_tests(void) +{ + u32 i; + + printf("Tests:\n"); + for (i = 0; i < ARRAY_SIZE(tests); i++) + printf("%u: %s\n", i, tests[i].name); +} + int main(int argc, char **argv) { struct pkt_stream *rx_pkt_stream_default; struct pkt_stream *tx_pkt_stream_default; struct ifobject *ifobj_tx, *ifobj_rx; + u32 i, j, failed_tests = 0, nb_tests; int modes = TEST_MODE_SKB + 1; - u32 i, j, failed_tests = 0; struct test_spec test; bool shared_netdev; @@ -2314,14 +2379,21 @@ int main(int argc, char **argv) parse_command_line(ifobj_tx, ifobj_rx, argc, argv); + if (opt_print_tests) { + print_tests(); + ksft_exit_xpass(); + } + if (opt_run_test != RUN_ALL_TESTS && opt_run_test >= ARRAY_SIZE(tests)) { + ksft_print_msg("Error: test %u does not exist.\n", opt_run_test); + ksft_exit_xfail(); + } + shared_netdev = (ifobj_tx->ifindex == ifobj_rx->ifindex); ifobj_tx->shared_umem = shared_netdev; ifobj_rx->shared_umem = shared_netdev; - if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) { - usage(basename(argv[0])); - ksft_exit_xfail(); - } + if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) + print_usage(argv); if (is_xdp_supported(ifobj_tx->ifindex)) { modes++; @@ -2332,7 +2404,7 @@ int main(int argc, char **argv) init_iface(ifobj_rx, MAC1, MAC2, worker_testapp_validate_rx); init_iface(ifobj_tx, MAC2, MAC1, worker_testapp_validate_tx); - test_spec_init(&test, ifobj_tx, ifobj_rx, 0); + test_spec_init(&test, ifobj_tx, ifobj_rx, 0, &tests[0]); tx_pkt_stream_default = pkt_stream_generate(ifobj_tx->umem, DEFAULT_PKT_CNT, MIN_PKT_SIZE); rx_pkt_stream_default = pkt_stream_generate(ifobj_rx->umem, DEFAULT_PKT_CNT, MIN_PKT_SIZE); if (!tx_pkt_stream_default || !rx_pkt_stream_default) @@ -2340,12 +2412,35 @@ int main(int argc, char **argv) test.tx_pkt_stream_default = tx_pkt_stream_default; test.rx_pkt_stream_default = rx_pkt_stream_default; - ksft_set_plan(modes * TEST_TYPE_MAX); + if (opt_run_test == RUN_ALL_TESTS) + nb_tests = ARRAY_SIZE(tests); + else + nb_tests = 1; + if (opt_mode == TEST_MODE_ALL) { + ksft_set_plan(modes * nb_tests); + } else { + if (opt_mode == TEST_MODE_DRV && modes <= TEST_MODE_DRV) { + ksft_print_msg("Error: XDP_DRV mode not supported.\n"); + ksft_exit_xfail(); + } + if (opt_mode == TEST_MODE_ZC && modes <= TEST_MODE_ZC) { + ksft_print_msg("Error: zero-copy mode not supported.\n"); + ksft_exit_xfail(); + } + + ksft_set_plan(nb_tests); + } for (i = 0; i < modes; i++) { - for (j = 0; j < TEST_TYPE_MAX; j++) { - test_spec_init(&test, ifobj_tx, ifobj_rx, i); - run_pkt_test(&test, i, j); + if (opt_mode != TEST_MODE_ALL && i != opt_mode) + continue; + + for (j = 0; j < ARRAY_SIZE(tests); j++) { + if (opt_run_test != RUN_ALL_TESTS && j != opt_run_test) + continue; + + test_spec_init(&test, ifobj_tx, ifobj_rx, i, &tests[j]); + run_pkt_test(&test); usleep(USLEEP_MAX); if (test.fail) diff --git a/tools/testing/selftests/bpf/xskxceiver.h b/tools/testing/selftests/bpf/xskxceiver.h index 233b66cef64a..8015aeea839d 100644 --- a/tools/testing/selftests/bpf/xskxceiver.h +++ b/tools/testing/selftests/bpf/xskxceiver.h @@ -5,6 +5,8 @@ #ifndef XSKXCEIVER_H_ #define XSKXCEIVER_H_ +#include <limits.h> + #include "xsk_xdp_progs.skel.h" #ifndef SOL_XDP @@ -34,7 +36,7 @@ #define MAX_INTERFACES 2 #define MAX_INTERFACE_NAME_CHARS 16 #define MAX_SOCKETS 2 -#define MAX_TEST_NAME_SIZE 32 +#define MAX_TEST_NAME_SIZE 48 #define MAX_TEARDOWN_ITER 10 #define PKT_HDR_SIZE (sizeof(struct ethhdr) + 2) /* Just to align the data in the packet */ #define MIN_PKT_SIZE 64 @@ -56,6 +58,7 @@ #define XSK_DESC__MAX_SKB_FRAGS 18 #define HUGEPAGE_SIZE (2 * 1024 * 1024) #define PKT_DUMP_NB_TO_PRINT 16 +#define RUN_ALL_TESTS UINT_MAX #define print_verbose(x...) do { if (opt_verbose) ksft_print_msg(x); } while (0) @@ -63,43 +66,9 @@ enum test_mode { TEST_MODE_SKB, TEST_MODE_DRV, TEST_MODE_ZC, - TEST_MODE_MAX -}; - -enum test_type { - TEST_TYPE_RUN_TO_COMPLETION, - TEST_TYPE_RUN_TO_COMPLETION_2K_FRAME, - TEST_TYPE_RUN_TO_COMPLETION_SINGLE_PKT, - TEST_TYPE_RX_POLL, - TEST_TYPE_TX_POLL, - TEST_TYPE_POLL_RXQ_TMOUT, - TEST_TYPE_POLL_TXQ_TMOUT, - TEST_TYPE_UNALIGNED, - TEST_TYPE_ALIGNED_INV_DESC, - TEST_TYPE_ALIGNED_INV_DESC_2K_FRAME, - TEST_TYPE_UNALIGNED_INV_DESC, - TEST_TYPE_UNALIGNED_INV_DESC_4K1_FRAME, - TEST_TYPE_HEADROOM, - TEST_TYPE_TEARDOWN, - TEST_TYPE_BIDI, - TEST_TYPE_STATS_RX_DROPPED, - TEST_TYPE_STATS_TX_INVALID_DESCS, - TEST_TYPE_STATS_RX_FULL, - TEST_TYPE_STATS_FILL_EMPTY, - TEST_TYPE_BPF_RES, - TEST_TYPE_XDP_DROP_HALF, - TEST_TYPE_XDP_METADATA_COUNT, - TEST_TYPE_XDP_METADATA_COUNT_MB, - TEST_TYPE_RUN_TO_COMPLETION_MB, - TEST_TYPE_UNALIGNED_MB, - TEST_TYPE_ALIGNED_INV_DESC_MB, - TEST_TYPE_UNALIGNED_INV_DESC_MB, - TEST_TYPE_TOO_MANY_FRAGS, - TEST_TYPE_MAX + TEST_MODE_ALL }; -static bool opt_verbose; - struct xsk_umem_info { struct xsk_ring_prod fq; struct xsk_ring_cons cq; @@ -139,8 +108,10 @@ struct pkt_stream { }; struct ifobject; +struct test_spec; typedef int (*validation_func_t)(struct ifobject *ifobj); typedef void *(*thread_func_t)(void *arg); +typedef int (*test_func_t)(struct test_spec *test); struct ifobject { char ifname[MAX_INTERFACE_NAME_CHARS]; @@ -182,6 +153,7 @@ struct test_spec { struct bpf_program *xdp_prog_tx; struct bpf_map *xskmap_rx; struct bpf_map *xskmap_tx; + test_func_t test_func; int mtu; u16 total_steps; u16 current_step; diff --git a/tools/testing/selftests/cgroup/.gitignore b/tools/testing/selftests/cgroup/.gitignore index c4a57e69f749..af8c3f30b9c1 100644 --- a/tools/testing/selftests/cgroup/.gitignore +++ b/tools/testing/selftests/cgroup/.gitignore @@ -5,4 +5,6 @@ test_freezer test_kmem test_kill test_cpu +test_cpuset +test_zswap wait_inotify diff --git a/tools/testing/selftests/cgroup/Makefile b/tools/testing/selftests/cgroup/Makefile index 3d263747d2ad..c27f05f6ce9b 100644 --- a/tools/testing/selftests/cgroup/Makefile +++ b/tools/testing/selftests/cgroup/Makefile @@ -12,6 +12,8 @@ TEST_GEN_PROGS += test_core TEST_GEN_PROGS += test_freezer TEST_GEN_PROGS += test_kill TEST_GEN_PROGS += test_cpu +TEST_GEN_PROGS += test_cpuset +TEST_GEN_PROGS += test_zswap LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h @@ -23,3 +25,5 @@ $(OUTPUT)/test_core: cgroup_util.c $(OUTPUT)/test_freezer: cgroup_util.c $(OUTPUT)/test_kill: cgroup_util.c $(OUTPUT)/test_cpu: cgroup_util.c +$(OUTPUT)/test_cpuset: cgroup_util.c +$(OUTPUT)/test_zswap: cgroup_util.c diff --git a/tools/testing/selftests/cgroup/cgroup_util.c b/tools/testing/selftests/cgroup/cgroup_util.c index e8bbbdb77e0d..0340d4ca8f51 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.c +++ b/tools/testing/selftests/cgroup/cgroup_util.c @@ -286,6 +286,8 @@ int cg_destroy(const char *cgroup) { int ret; + if (!cgroup) + return 0; retry: ret = rmdir(cgroup); if (ret && errno == EBUSY) { diff --git a/tools/testing/selftests/cgroup/cgroup_util.h b/tools/testing/selftests/cgroup/cgroup_util.h index c92df4e5d395..1df7f202214a 100644 --- a/tools/testing/selftests/cgroup/cgroup_util.h +++ b/tools/testing/selftests/cgroup/cgroup_util.h @@ -11,6 +11,8 @@ #define USEC_PER_SEC 1000000L #define NSEC_PER_SEC 1000000000L +#define TEST_UID 65534 /* usually nobody, any !root is fine */ + /* * Checks if two given values differ by less than err% of their sum. */ diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c index 600123503063..80aa6b2373b9 100644 --- a/tools/testing/selftests/cgroup/test_core.c +++ b/tools/testing/selftests/cgroup/test_core.c @@ -683,7 +683,7 @@ cleanup: */ static int test_cgcore_lesser_euid_open(const char *root) { - const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ + const uid_t test_euid = TEST_UID; int ret = KSFT_FAIL; char *cg_test_a = NULL, *cg_test_b = NULL; char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; diff --git a/tools/testing/selftests/cgroup/test_cpuset.c b/tools/testing/selftests/cgroup/test_cpuset.c new file mode 100644 index 000000000000..b061ed1e05b4 --- /dev/null +++ b/tools/testing/selftests/cgroup/test_cpuset.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/limits.h> +#include <signal.h> + +#include "../kselftest.h" +#include "cgroup_util.h" + +static int idle_process_fn(const char *cgroup, void *arg) +{ + (void)pause(); + return 0; +} + +static int do_migration_fn(const char *cgroup, void *arg) +{ + int object_pid = (int)(size_t)arg; + + if (setuid(TEST_UID)) + return EXIT_FAILURE; + + // XXX checking /proc/$pid/cgroup would be quicker than wait + if (cg_enter(cgroup, object_pid) || + cg_wait_for_proc_count(cgroup, 1)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static int do_controller_fn(const char *cgroup, void *arg) +{ + const char *child = cgroup; + const char *parent = arg; + + if (setuid(TEST_UID)) + return EXIT_FAILURE; + + if (!cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + if (cg_write(parent, "cgroup.subtree_control", "+cpuset")) + return EXIT_FAILURE; + + if (cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + if (cg_write(parent, "cgroup.subtree_control", "-cpuset")) + return EXIT_FAILURE; + + if (!cg_read_strstr(child, "cgroup.controllers", "cpuset")) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +/* + * Migrate a process between two sibling cgroups. + * The success should only depend on the parent cgroup permissions and not the + * migrated process itself (cpuset controller is in place because it uses + * security_task_setscheduler() in cgroup v1). + * + * Deliberately don't set cpuset.cpus in children to avoid definining migration + * permissions between two different cpusets. + */ +static int test_cpuset_perms_object(const char *root, bool allow) +{ + char *parent = NULL, *child_src = NULL, *child_dst = NULL; + char *parent_procs = NULL, *child_src_procs = NULL, *child_dst_procs = NULL; + const uid_t test_euid = TEST_UID; + int object_pid = 0; + int ret = KSFT_FAIL; + + parent = cg_name(root, "cpuset_test_0"); + if (!parent) + goto cleanup; + parent_procs = cg_name(parent, "cgroup.procs"); + if (!parent_procs) + goto cleanup; + if (cg_create(parent)) + goto cleanup; + + child_src = cg_name(parent, "cpuset_test_1"); + if (!child_src) + goto cleanup; + child_src_procs = cg_name(child_src, "cgroup.procs"); + if (!child_src_procs) + goto cleanup; + if (cg_create(child_src)) + goto cleanup; + + child_dst = cg_name(parent, "cpuset_test_2"); + if (!child_dst) + goto cleanup; + child_dst_procs = cg_name(child_dst, "cgroup.procs"); + if (!child_dst_procs) + goto cleanup; + if (cg_create(child_dst)) + goto cleanup; + + if (cg_write(parent, "cgroup.subtree_control", "+cpuset")) + goto cleanup; + + if (cg_read_strstr(child_src, "cgroup.controllers", "cpuset") || + cg_read_strstr(child_dst, "cgroup.controllers", "cpuset")) + goto cleanup; + + /* Enable permissions along src->dst tree path */ + if (chown(child_src_procs, test_euid, -1) || + chown(child_dst_procs, test_euid, -1)) + goto cleanup; + + if (allow && chown(parent_procs, test_euid, -1)) + goto cleanup; + + /* Fork a privileged child as a test object */ + object_pid = cg_run_nowait(child_src, idle_process_fn, NULL); + if (object_pid < 0) + goto cleanup; + + /* Carry out migration in a child process that can drop all privileges + * (including capabilities), the main process must remain privileged for + * cleanup. + * Child process's cgroup is irrelevant but we place it into child_dst + * as hacky way to pass information about migration target to the child. + */ + if (allow ^ (cg_run(child_dst, do_migration_fn, (void *)(size_t)object_pid) == EXIT_SUCCESS)) + goto cleanup; + + ret = KSFT_PASS; + +cleanup: + if (object_pid > 0) { + (void)kill(object_pid, SIGTERM); + (void)clone_reap(object_pid, WEXITED); + } + + cg_destroy(child_dst); + free(child_dst_procs); + free(child_dst); + + cg_destroy(child_src); + free(child_src_procs); + free(child_src); + + cg_destroy(parent); + free(parent_procs); + free(parent); + + return ret; +} + +static int test_cpuset_perms_object_allow(const char *root) +{ + return test_cpuset_perms_object(root, true); +} + +static int test_cpuset_perms_object_deny(const char *root) +{ + return test_cpuset_perms_object(root, false); +} + +/* + * Migrate a process between parent and child implicitely + * Implicit migration happens when a controller is enabled/disabled. + * + */ +static int test_cpuset_perms_subtree(const char *root) +{ + char *parent = NULL, *child = NULL; + char *parent_procs = NULL, *parent_subctl = NULL, *child_procs = NULL; + const uid_t test_euid = TEST_UID; + int object_pid = 0; + int ret = KSFT_FAIL; + + parent = cg_name(root, "cpuset_test_0"); + if (!parent) + goto cleanup; + parent_procs = cg_name(parent, "cgroup.procs"); + if (!parent_procs) + goto cleanup; + parent_subctl = cg_name(parent, "cgroup.subtree_control"); + if (!parent_subctl) + goto cleanup; + if (cg_create(parent)) + goto cleanup; + + child = cg_name(parent, "cpuset_test_1"); + if (!child) + goto cleanup; + child_procs = cg_name(child, "cgroup.procs"); + if (!child_procs) + goto cleanup; + if (cg_create(child)) + goto cleanup; + + /* Enable permissions as in a delegated subtree */ + if (chown(parent_procs, test_euid, -1) || + chown(parent_subctl, test_euid, -1) || + chown(child_procs, test_euid, -1)) + goto cleanup; + + /* Put a privileged child in the subtree and modify controller state + * from an unprivileged process, the main process remains privileged + * for cleanup. + * The unprivileged child runs in subtree too to avoid parent and + * internal-node constraing violation. + */ + object_pid = cg_run_nowait(child, idle_process_fn, NULL); + if (object_pid < 0) + goto cleanup; + + if (cg_run(child, do_controller_fn, parent) != EXIT_SUCCESS) + goto cleanup; + + ret = KSFT_PASS; + +cleanup: + if (object_pid > 0) { + (void)kill(object_pid, SIGTERM); + (void)clone_reap(object_pid, WEXITED); + } + + cg_destroy(child); + free(child_procs); + free(child); + + cg_destroy(parent); + free(parent_subctl); + free(parent_procs); + free(parent); + + return ret; +} + + +#define T(x) { x, #x } +struct cpuset_test { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_cpuset_perms_object_allow), + T(test_cpuset_perms_object_deny), + T(test_cpuset_perms_subtree), +}; +#undef T + +int main(int argc, char *argv[]) +{ + char root[PATH_MAX]; + int i, ret = EXIT_SUCCESS; + + if (cg_find_unified_root(root, sizeof(root))) + ksft_exit_skip("cgroup v2 isn't mounted\n"); + + if (cg_read_strstr(root, "cgroup.subtree_control", "cpuset")) + if (cg_write(root, "cgroup.subtree_control", "+cpuset")) + ksft_exit_skip("Failed to set cpuset controller\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + case KSFT_SKIP: + ksft_test_result_skip("%s\n", tests[i].name); + break; + default: + ret = EXIT_FAILURE; + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + return ret; +} diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh index 2b5215cc599f..4afb132e4e4f 100755 --- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh +++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh @@ -10,7 +10,7 @@ skip_test() { echo "$1" echo "Test SKIPPED" - exit 0 + exit 4 # ksft_skip } [[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!" diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index ed2e50bb1e76..c82f974b85c9 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -162,11 +162,11 @@ static int cg_run_in_subcgroups(const char *parent, * allocates some slab memory (mostly negative dentries) using 2 * NR_CPUS * threads. Then it checks the sanity of numbers on the parent level: * the total size of the cgroups should be roughly equal to - * anon + file + slab + kernel_stack. + * anon + file + kernel + sock. */ static int test_kmem_memcg_deletion(const char *root) { - long current, slab, anon, file, kernel_stack, pagetables, percpu, sock, sum; + long current, anon, file, kernel, sock, sum; int ret = KSFT_FAIL; char *parent; @@ -184,29 +184,22 @@ static int test_kmem_memcg_deletion(const char *root) goto cleanup; current = cg_read_long(parent, "memory.current"); - slab = cg_read_key_long(parent, "memory.stat", "slab "); anon = cg_read_key_long(parent, "memory.stat", "anon "); file = cg_read_key_long(parent, "memory.stat", "file "); - kernel_stack = cg_read_key_long(parent, "memory.stat", "kernel_stack "); - pagetables = cg_read_key_long(parent, "memory.stat", "pagetables "); - percpu = cg_read_key_long(parent, "memory.stat", "percpu "); + kernel = cg_read_key_long(parent, "memory.stat", "kernel "); sock = cg_read_key_long(parent, "memory.stat", "sock "); - if (current < 0 || slab < 0 || anon < 0 || file < 0 || - kernel_stack < 0 || pagetables < 0 || percpu < 0 || sock < 0) + if (current < 0 || anon < 0 || file < 0 || kernel < 0 || sock < 0) goto cleanup; - sum = slab + anon + file + kernel_stack + pagetables + percpu + sock; + sum = anon + file + kernel + sock; if (abs(sum - current) < MAX_VMSTAT_ERROR) { ret = KSFT_PASS; } else { printf("memory.current = %ld\n", current); - printf("slab + anon + file + kernel_stack = %ld\n", sum); - printf("slab = %ld\n", slab); + printf("anon + file + kernel + sock = %ld\n", sum); printf("anon = %ld\n", anon); printf("file = %ld\n", file); - printf("kernel_stack = %ld\n", kernel_stack); - printf("pagetables = %ld\n", pagetables); - printf("percpu = %ld\n", percpu); + printf("kernel = %ld\n", kernel); printf("sock = %ld\n", sock); } diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c new file mode 100644 index 000000000000..49def87a909b --- /dev/null +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include <linux/limits.h> +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include <sys/sysinfo.h> +#include <string.h> +#include <sys/wait.h> +#include <sys/mman.h> + +#include "../kselftest.h" +#include "cgroup_util.h" + +static int read_int(const char *path, size_t *value) +{ + FILE *file; + int ret = 0; + + file = fopen(path, "r"); + if (!file) + return -1; + if (fscanf(file, "%ld", value) != 1) + ret = -1; + fclose(file); + return ret; +} + +static int set_min_free_kb(size_t value) +{ + FILE *file; + int ret; + + file = fopen("/proc/sys/vm/min_free_kbytes", "w"); + if (!file) + return -1; + ret = fprintf(file, "%ld\n", value); + fclose(file); + return ret; +} + +static int read_min_free_kb(size_t *value) +{ + return read_int("/proc/sys/vm/min_free_kbytes", value); +} + +static int get_zswap_stored_pages(size_t *value) +{ + return read_int("/sys/kernel/debug/zswap/stored_pages", value); +} + +static int get_zswap_written_back_pages(size_t *value) +{ + return read_int("/sys/kernel/debug/zswap/written_back_pages", value); +} + +static int allocate_bytes(const char *cgroup, void *arg) +{ + size_t size = (size_t)arg; + char *mem = (char *)malloc(size); + + if (!mem) + return -1; + for (int i = 0; i < size; i += 4095) + mem[i] = 'a'; + free(mem); + return 0; +} + +/* + * When trying to store a memcg page in zswap, if the memcg hits its memory + * limit in zswap, writeback should not be triggered. + * + * This was fixed with commit 0bdf0efa180a("zswap: do not shrink if cgroup may + * not zswap"). Needs to be revised when a per memcg writeback mechanism is + * implemented. + */ +static int test_no_invasive_cgroup_shrink(const char *root) +{ + size_t written_back_before, written_back_after; + int ret = KSFT_FAIL; + char *test_group; + + /* Set up */ + test_group = cg_name(root, "no_shrink_test"); + if (!test_group) + goto out; + if (cg_create(test_group)) + goto out; + if (cg_write(test_group, "memory.max", "1M")) + goto out; + if (cg_write(test_group, "memory.zswap.max", "10K")) + goto out; + if (get_zswap_written_back_pages(&written_back_before)) + goto out; + + /* Allocate 10x memory.max to push memory into zswap */ + if (cg_run(test_group, allocate_bytes, (void *)MB(10))) + goto out; + + /* Verify that no writeback happened because of the memcg allocation */ + if (get_zswap_written_back_pages(&written_back_after)) + goto out; + if (written_back_after == written_back_before) + ret = KSFT_PASS; +out: + cg_destroy(test_group); + free(test_group); + return ret; +} + +struct no_kmem_bypass_child_args { + size_t target_alloc_bytes; + size_t child_allocated; +}; + +static int no_kmem_bypass_child(const char *cgroup, void *arg) +{ + struct no_kmem_bypass_child_args *values = arg; + void *allocation; + + allocation = malloc(values->target_alloc_bytes); + if (!allocation) { + values->child_allocated = true; + return -1; + } + for (long i = 0; i < values->target_alloc_bytes; i += 4095) + ((char *)allocation)[i] = 'a'; + values->child_allocated = true; + pause(); + free(allocation); + return 0; +} + +/* + * When pages owned by a memcg are pushed to zswap by kswapd, they should be + * charged to that cgroup. This wasn't the case before commit + * cd08d80ecdac("mm: correctly charge compressed memory to its memcg"). + * + * The test first allocates memory in a memcg, then raises min_free_kbytes to + * a very high value so that the allocation falls below low wm, then makes + * another allocation to trigger kswapd that should push the memcg-owned pages + * to zswap and verifies that the zswap pages are correctly charged. + * + * To be run on a VM with at most 4G of memory. + */ +static int test_no_kmem_bypass(const char *root) +{ + size_t min_free_kb_high, min_free_kb_low, min_free_kb_original; + struct no_kmem_bypass_child_args *values; + size_t trigger_allocation_size; + int wait_child_iteration = 0; + long stored_pages_threshold; + struct sysinfo sys_info; + int ret = KSFT_FAIL; + int child_status; + char *test_group; + pid_t child_pid; + + /* Read sys info and compute test values accordingly */ + if (sysinfo(&sys_info) != 0) + return KSFT_FAIL; + if (sys_info.totalram > 5000000000) + return KSFT_SKIP; + values = mmap(0, sizeof(struct no_kmem_bypass_child_args), PROT_READ | + PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (values == MAP_FAILED) + return KSFT_FAIL; + if (read_min_free_kb(&min_free_kb_original)) + return KSFT_FAIL; + min_free_kb_high = sys_info.totalram / 2000; + min_free_kb_low = sys_info.totalram / 500000; + values->target_alloc_bytes = (sys_info.totalram - min_free_kb_high * 1000) + + sys_info.totalram * 5 / 100; + stored_pages_threshold = sys_info.totalram / 5 / 4096; + trigger_allocation_size = sys_info.totalram / 20; + + /* Set up test memcg */ + if (cg_write(root, "cgroup.subtree_control", "+memory")) + goto out; + test_group = cg_name(root, "kmem_bypass_test"); + if (!test_group) + goto out; + + /* Spawn memcg child and wait for it to allocate */ + set_min_free_kb(min_free_kb_low); + if (cg_create(test_group)) + goto out; + values->child_allocated = false; + child_pid = cg_run_nowait(test_group, no_kmem_bypass_child, values); + if (child_pid < 0) + goto out; + while (!values->child_allocated && wait_child_iteration++ < 10000) + usleep(1000); + + /* Try to wakeup kswapd and let it push child memory to zswap */ + set_min_free_kb(min_free_kb_high); + for (int i = 0; i < 20; i++) { + size_t stored_pages; + char *trigger_allocation = malloc(trigger_allocation_size); + + if (!trigger_allocation) + break; + for (int i = 0; i < trigger_allocation_size; i += 4095) + trigger_allocation[i] = 'b'; + usleep(100000); + free(trigger_allocation); + if (get_zswap_stored_pages(&stored_pages)) + break; + if (stored_pages < 0) + break; + /* If memory was pushed to zswap, verify it belongs to memcg */ + if (stored_pages > stored_pages_threshold) { + int zswapped = cg_read_key_long(test_group, "memory.stat", "zswapped "); + int delta = stored_pages * 4096 - zswapped; + int result_ok = delta < stored_pages * 4096 / 4; + + ret = result_ok ? KSFT_PASS : KSFT_FAIL; + break; + } + } + + kill(child_pid, SIGTERM); + waitpid(child_pid, &child_status, 0); +out: + set_min_free_kb(min_free_kb_original); + cg_destroy(test_group); + free(test_group); + return ret; +} + +#define T(x) { x, #x } +struct zswap_test { + int (*fn)(const char *root); + const char *name; +} tests[] = { + T(test_no_kmem_bypass), + T(test_no_invasive_cgroup_shrink), +}; +#undef T + +static bool zswap_configured(void) +{ + return access("/sys/module/zswap", F_OK) == 0; +} + +int main(int argc, char **argv) +{ + char root[PATH_MAX]; + int i, ret = EXIT_SUCCESS; + + if (cg_find_unified_root(root, sizeof(root))) + ksft_exit_skip("cgroup v2 isn't mounted\n"); + + if (!zswap_configured()) + ksft_exit_skip("zswap isn't configured\n"); + + /* + * Check that memory controller is available: + * memory is listed in cgroup.controllers + */ + if (cg_read_strstr(root, "cgroup.controllers", "memory")) + ksft_exit_skip("memory controller isn't available\n"); + + if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) + if (cg_write(root, "cgroup.subtree_control", "+memory")) + ksft_exit_skip("Failed to set memory controller\n"); + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + switch (tests[i].fn(root)) { + case KSFT_PASS: + ksft_test_result_pass("%s\n", tests[i].name); + break; + case KSFT_SKIP: + ksft_test_result_skip("%s\n", tests[i].name); + break; + default: + ret = EXIT_FAILURE; + ksft_test_result_fail("%s\n", tests[i].name); + break; + } + } + + return ret; +} diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh index bcd4734ca094..60a9a305aef0 100644 --- a/tools/testing/selftests/damon/sysfs.sh +++ b/tools/testing/selftests/damon/sysfs.sh @@ -84,6 +84,7 @@ test_tried_regions() { tried_regions_dir=$1 ensure_dir "$tried_regions_dir" "exist" + ensure_file "$tried_regions_dir/total_bytes" "exist" "400" } test_stats() @@ -102,9 +103,14 @@ test_filter() ensure_file "$filter_dir/type" "exist" "600" ensure_write_succ "$filter_dir/type" "anon" "valid input" ensure_write_succ "$filter_dir/type" "memcg" "valid input" + ensure_write_succ "$filter_dir/type" "addr" "valid input" + ensure_write_succ "$filter_dir/type" "target" "valid input" ensure_write_fail "$filter_dir/type" "foo" "invalid input" ensure_file "$filter_dir/matching" "exist" "600" ensure_file "$filter_dir/memcg_path" "exist" "600" + ensure_file "$filter_dir/addr_start" "exist" "600" + ensure_file "$filter_dir/addr_end" "exist" "600" + ensure_file "$filter_dir/damon_target_idx" "exist" "600" } test_filters() diff --git a/tools/testing/selftests/fchmodat2/Makefile b/tools/testing/selftests/fchmodat2/Makefile index 20839f8e43f2..71ec34bf1501 100644 --- a/tools/testing/selftests/fchmodat2/Makefile +++ b/tools/testing/selftests/fchmodat2/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later -CFLAGS += -Wall -O2 -g -fsanitize=address -fsanitize=undefined $(KHDR_INCLUDES) +CFLAGS += -Wall -O2 -g -fsanitize=address -fsanitize=undefined -static-libasan $(KHDR_INCLUDES) TEST_GEN_PROGS := fchmodat2_test include ../lib.mk diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index cb5f18c06593..c778d4dcc17e 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -31,6 +31,9 @@ err_ret=1 # kselftest skip code is 4 err_skip=4 +# umount required +UMOUNT_DIR="" + # cgroup RT scheduling prevents chrt commands from succeeding, which # induces failures in test wakeup tests. Disable for the duration of # the tests. @@ -45,6 +48,9 @@ setup() { cleanup() { echo $sched_rt_runtime_orig > $sched_rt_runtime + if [ -n "${UMOUNT_DIR}" ]; then + umount ${UMOUNT_DIR} ||: + fi } errexit() { # message @@ -124,6 +130,7 @@ parse_opts() { # opts ;; --logdir|-l) LOG_DIR=$2 + LINK_PTR= shift 2 ;; *.tc) @@ -160,11 +167,13 @@ if [ -z "$TRACING_DIR" ]; then mount -t tracefs nodev /sys/kernel/tracing || errexit "Failed to mount /sys/kernel/tracing" TRACING_DIR="/sys/kernel/tracing" + UMOUNT_DIR=${TRACING_DIR} # If debugfs exists, then so does /sys/kernel/debug elif [ -d "/sys/kernel/debug" ]; then mount -t debugfs nodev /sys/kernel/debug || errexit "Failed to mount /sys/kernel/debug" TRACING_DIR="/sys/kernel/debug/tracing" + UMOUNT_DIR=${TRACING_DIR} else err_ret=$err_skip errexit "debugfs and tracefs are not configured in this kernel" @@ -181,7 +190,10 @@ fi TOP_DIR=`absdir $0` TEST_DIR=$TOP_DIR/test.d TEST_CASES=`find_testcases $TEST_DIR` -LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/ +LOG_TOP_DIR=$TOP_DIR/logs +LOG_DATE=`date +%Y%m%d-%H%M%S` +LOG_DIR=$LOG_TOP_DIR/$LOG_DATE/ +LINK_PTR=$LOG_TOP_DIR/latest KEEP_LOG=0 KTAP=0 DEBUG=0 @@ -207,6 +219,10 @@ else LOG_FILE=$LOG_DIR/ftracetest.log mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" date > $LOG_FILE + if [ "x-$LINK_PTR" != "x-" ]; then + unlink $LINK_PTR + ln -fs $LOG_DATE $LINK_PTR + fi fi # Define text colors diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_btfarg.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_btfarg.tc index f34b14ef9781..b9c21a81d248 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_btfarg.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_btfarg.tc @@ -5,6 +5,7 @@ KPROBES= FPROBES= +FIELDS= if grep -qF "p[:[<group>/][<event>]] <place> [<args>]" README ; then KPROBES=yes @@ -12,6 +13,9 @@ fi if grep -qF "f[:[<group>/][<event>]] <func-name>[%return] [<args>]" README ; then FPROBES=yes fi +if grep -qF "<argname>[->field[->field|.field...]]" README ; then + FIELDS=yes +fi if [ -z "$KPROBES" -a -z "$FPROBES" ] ; then exit_unsupported @@ -21,6 +25,9 @@ echo 0 > events/enable echo > dynamic_events TP=kfree +TP2=kmem_cache_alloc +TP3=getname_flags +TP4=sched_wakeup if [ "$FPROBES" ] ; then echo "f:fpevent $TP object" >> dynamic_events @@ -33,6 +40,7 @@ echo > dynamic_events echo "f:fpevent $TP "'$arg1' >> dynamic_events grep -q "fpevent.*object=object" dynamic_events + echo > dynamic_events echo "f:fpevent $TP "'$arg*' >> dynamic_events @@ -45,6 +53,18 @@ fi echo > dynamic_events +if [ "$FIELDS" ] ; then +echo "t:tpevent ${TP2} obj_size=s->object_size" >> dynamic_events +echo "f:fpevent ${TP3}%return path=\$retval->name:string" >> dynamic_events +echo "t:tpevent2 ${TP4} p->se.group_node.next->prev" >> dynamic_events + +grep -q "tpevent .*obj_size=s->object_size" dynamic_events +grep -q "fpevent.*path=\$retval->name:string" dynamic_events +grep -q 'tpevent2 .*p->se.group_node.next->prev' dynamic_events + +echo > dynamic_events +fi + if [ "$KPROBES" ] ; then echo "p:kpevent $TP object" >> dynamic_events grep -q "kpevent.*object=object" dynamic_events diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc index 812f5b3f6055..20e42c030095 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc @@ -30,11 +30,11 @@ check_error 'f:^ vfs_read' # NO_EVENT_NAME check_error 'f:foo/^12345678901234567890123456789012345678901234567890123456789012345 vfs_read' # EVENT_TOO_LONG check_error 'f:foo/^bar.1 vfs_read' # BAD_EVENT_NAME -check_error 'f vfs_read ^$retval' # RETVAL_ON_PROBE check_error 'f vfs_read ^$stack10000' # BAD_STACK_NUM check_error 'f vfs_read ^$arg10000' # BAD_ARG_NUM +check_error 'f vfs_read $retval ^$arg1' # BAD_VAR check_error 'f vfs_read ^$none_var' # BAD_VAR check_error 'f vfs_read ^'$REG # BAD_VAR @@ -103,6 +103,14 @@ check_error 'f vfs_read%return ^$arg*' # NOFENTRY_ARGS check_error 'f vfs_read ^hoge' # NO_BTFARG check_error 'f kfree ^$arg10' # NO_BTFARG (exceed the number of parameters) check_error 'f kfree%return ^$retval' # NO_RETVAL + +if grep -qF "<argname>[->field[->field|.field...]]" README ; then +check_error 'f vfs_read%return $retval->^foo' # NO_PTR_STRCT +check_error 'f vfs_read file->^foo' # NO_BTF_FIELD +check_error 'f vfs_read file^-.foo' # BAD_HYPHEN +check_error 'f vfs_read ^file:string' # BAD_TYPE4STR +fi + else check_error 'f vfs_read ^$arg*' # NOSUP_BTFARG check_error 't kfree ^$arg*' # NOSUP_BTFARG diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index 0eb47fbb3f44..42422e425107 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -39,7 +39,7 @@ instance_read() { instance_set() { while :; do - echo 1 > foo/events/sched/sched_switch + echo 1 > foo/events/sched/sched_switch/enable done 2> /dev/null } diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc index 285b4770efad..ff7499eb98d6 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_char.tc @@ -34,14 +34,19 @@ mips*) esac : "Test get argument (1)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char" > kprobe_events +if grep -q eventfs_add_dir available_filter_functions; then + DIR_NAME="eventfs_add_dir" +else + DIR_NAME="tracefs_create_dir" +fi +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):char" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1='t'" trace echo 0 > events/kprobes/testprobe/enable : "Test get argument (2)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):char arg2=+0(${ARG1}):char[4]" > kprobe_events +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):char arg2=+0(${ARG1}):char[4]" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1='t' arg2={'t','e','s','t'}" trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc index a4f8e7c53c1f..a202b2ea4baf 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc @@ -37,14 +37,19 @@ loongarch*) esac : "Test get argument (1)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string" > kprobe_events +if grep -q eventfs_add_dir available_filter_functions; then + DIR_NAME="eventfs_add_dir" +else + DIR_NAME="tracefs_create_dir" +fi +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):string" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1=\"test\"" trace echo 0 > events/kprobes/testprobe/enable : "Test get argument (2)" -echo "p:testprobe tracefs_create_dir arg1=+0(${ARG1}):string arg2=+0(${ARG1}):string" > kprobe_events +echo "p:testprobe ${DIR_NAME} arg1=+0(${ARG1}):string arg2=+0(${ARG1}):string" > kprobe_events echo 1 > events/kprobes/testprobe/enable echo "p:test $FUNCTION_FORK" >> kprobe_events grep -qe "testprobe.* arg1=\"test\" arg2=\"test\"" trace diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc index 213d890ed188..174376ddbc6c 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test inter-event histogram trigger trace action with dynamic string param -# requires: set_event synthetic_events events/sched/sched_process_exec/hist "char name[]' >> synthetic_events":README ping:program +# requires: set_event synthetic_events events/sched/sched_process_exec/hist "' >> synthetic_events":README ping:program fail() { #msg echo $1 diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc index 955e3ceea44b..b927ee54c02d 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test synthetic_events syntax parser errors -# requires: synthetic_events error_log "char name[]' >> synthetic_events":README +# requires: synthetic_events error_log "' >> synthetic_events":README check_error() { # command-with-error-pos-by-^ ftrace_errlog_check 'synthetic_events' "$1" 'synthetic_events' diff --git a/tools/testing/selftests/iommu/iommufd.c b/tools/testing/selftests/iommu/iommufd.c index e4a6b33cfde4..33d08600be13 100644 --- a/tools/testing/selftests/iommu/iommufd.c +++ b/tools/testing/selftests/iommu/iommufd.c @@ -9,9 +9,6 @@ #include "iommufd_utils.h" -static void *buffer; - -static unsigned long PAGE_SIZE; static unsigned long HUGEPAGE_SIZE; #define MOCK_PAGE_SIZE (PAGE_SIZE / 2) @@ -116,6 +113,7 @@ TEST_F(iommufd, cmd_length) } TEST_LENGTH(iommu_destroy, IOMMU_DESTROY); + TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO); TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC); TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES); TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS); @@ -188,6 +186,7 @@ FIXTURE(iommufd_ioas) uint32_t ioas_id; uint32_t stdev_id; uint32_t hwpt_id; + uint32_t device_id; uint64_t base_iova; }; @@ -214,7 +213,7 @@ FIXTURE_SETUP(iommufd_ioas) for (i = 0; i != variant->mock_domains; i++) { test_cmd_mock_domain(self->ioas_id, &self->stdev_id, - &self->hwpt_id); + &self->hwpt_id, &self->device_id); self->base_iova = MOCK_APERTURE_START; } } @@ -265,7 +264,7 @@ TEST_F(iommufd_ioas, hwpt_attach) { /* Create a device attached directly to a hwpt */ if (self->stdev_id) { - test_cmd_mock_domain(self->hwpt_id, NULL, NULL); + test_cmd_mock_domain(self->hwpt_id, NULL, NULL, NULL); } else { test_err_mock_domain(ENOENT, self->hwpt_id, NULL, NULL); } @@ -293,6 +292,40 @@ TEST_F(iommufd_ioas, ioas_area_auto_destroy) } } +TEST_F(iommufd_ioas, get_hw_info) +{ + struct iommu_test_hw_info buffer_exact; + struct iommu_test_hw_info_buffer_larger { + struct iommu_test_hw_info info; + uint64_t trailing_bytes; + } buffer_larger; + struct iommu_test_hw_info_buffer_smaller { + __u32 flags; + } buffer_smaller; + + if (self->device_id) { + /* Provide a zero-size user_buffer */ + test_cmd_get_hw_info(self->device_id, NULL, 0); + /* Provide a user_buffer with exact size */ + test_cmd_get_hw_info(self->device_id, &buffer_exact, sizeof(buffer_exact)); + /* + * Provide a user_buffer with size larger than the exact size to check if + * kernel zero the trailing bytes. + */ + test_cmd_get_hw_info(self->device_id, &buffer_larger, sizeof(buffer_larger)); + /* + * Provide a user_buffer with size smaller than the exact size to check if + * the fields within the size range still gets updated. + */ + test_cmd_get_hw_info(self->device_id, &buffer_smaller, sizeof(buffer_smaller)); + } else { + test_err_get_hw_info(ENOENT, self->device_id, + &buffer_exact, sizeof(buffer_exact)); + test_err_get_hw_info(ENOENT, self->device_id, + &buffer_larger, sizeof(buffer_larger)); + } +} + TEST_F(iommufd_ioas, area) { int i; @@ -684,7 +717,7 @@ TEST_F(iommufd_ioas, access_pin) _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES), &access_cmd)); test_cmd_mock_domain(self->ioas_id, &mock_stdev_id, - &mock_hwpt_id); + &mock_hwpt_id, NULL); check_map_cmd.id = mock_hwpt_id; ASSERT_EQ(0, ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_MAP), @@ -839,7 +872,7 @@ TEST_F(iommufd_ioas, fork_gone) * If a domain already existed then everything was pinned within * the fork, so this copies from one domain to another. */ - test_cmd_mock_domain(self->ioas_id, NULL, NULL); + test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL); check_access_rw(_metadata, self->fd, access_id, MOCK_APERTURE_START, 0); @@ -888,7 +921,7 @@ TEST_F(iommufd_ioas, fork_present) ASSERT_EQ(8, read(efd, &tmp, sizeof(tmp))); /* Read pages from the remote process */ - test_cmd_mock_domain(self->ioas_id, NULL, NULL); + test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL); check_access_rw(_metadata, self->fd, access_id, MOCK_APERTURE_START, 0); ASSERT_EQ(0, close(pipefds[1])); @@ -1035,6 +1068,8 @@ FIXTURE(iommufd_mock_domain) uint32_t ioas_id; uint32_t hwpt_id; uint32_t hwpt_ids[2]; + uint32_t stdev_ids[2]; + uint32_t idev_ids[2]; int mmap_flags; size_t mmap_buf_size; }; @@ -1056,7 +1091,8 @@ FIXTURE_SETUP(iommufd_mock_domain) ASSERT_GE(ARRAY_SIZE(self->hwpt_ids), variant->mock_domains); for (i = 0; i != variant->mock_domains; i++) - test_cmd_mock_domain(self->ioas_id, NULL, &self->hwpt_ids[i]); + test_cmd_mock_domain(self->ioas_id, &self->stdev_ids[i], + &self->hwpt_ids[i], &self->idev_ids[i]); self->hwpt_id = self->hwpt_ids[0]; self->mmap_flags = MAP_SHARED | MAP_ANONYMOUS; @@ -1250,7 +1286,7 @@ TEST_F(iommufd_mock_domain, all_aligns_copy) /* Add and destroy a domain while the area exists */ old_id = self->hwpt_ids[1]; test_cmd_mock_domain(self->ioas_id, &mock_stdev_id, - &self->hwpt_ids[1]); + &self->hwpt_ids[1], NULL); check_mock_iova(buf + start, iova, length); check_refs(buf + start / PAGE_SIZE * PAGE_SIZE, @@ -1283,7 +1319,13 @@ TEST_F(iommufd_mock_domain, user_copy) .dst_iova = MOCK_APERTURE_START, .length = BUFFER_SIZE, }; - unsigned int ioas_id; + struct iommu_ioas_unmap unmap_cmd = { + .size = sizeof(unmap_cmd), + .ioas_id = self->ioas_id, + .iova = MOCK_APERTURE_START, + .length = BUFFER_SIZE, + }; + unsigned int new_ioas_id, ioas_id; /* Pin the pages in an IOAS with no domains then copy to an IOAS with domains */ test_ioctl_ioas_alloc(&ioas_id); @@ -1301,13 +1343,77 @@ TEST_F(iommufd_mock_domain, user_copy) ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd)); check_mock_iova(buffer, MOCK_APERTURE_START, BUFFER_SIZE); + /* Now replace the ioas with a new one */ + test_ioctl_ioas_alloc(&new_ioas_id); + test_ioctl_ioas_map_id(new_ioas_id, buffer, BUFFER_SIZE, + ©_cmd.src_iova); + test_cmd_access_replace_ioas(access_cmd.id, new_ioas_id); + + /* Destroy the old ioas and cleanup copied mapping */ + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_UNMAP, &unmap_cmd)); + test_ioctl_destroy(ioas_id); + + /* Then run the same test again with the new ioas */ + access_cmd.access_pages.iova = copy_cmd.src_iova; + ASSERT_EQ(0, + ioctl(self->fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_ACCESS_PAGES), + &access_cmd)); + copy_cmd.src_ioas_id = new_ioas_id; + ASSERT_EQ(0, ioctl(self->fd, IOMMU_IOAS_COPY, ©_cmd)); + check_mock_iova(buffer, MOCK_APERTURE_START, BUFFER_SIZE); + test_cmd_destroy_access_pages( access_cmd.id, access_cmd.access_pages.out_access_pages_id); test_cmd_destroy_access(access_cmd.id); + test_ioctl_destroy(new_ioas_id); +} + +TEST_F(iommufd_mock_domain, replace) +{ + uint32_t ioas_id; + + test_ioctl_ioas_alloc(&ioas_id); + + test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id); + + /* + * Replacing the IOAS causes the prior HWPT to be deallocated, thus we + * should get enoent when we try to use it. + */ + if (variant->mock_domains == 1) + test_err_mock_domain_replace(ENOENT, self->stdev_ids[0], + self->hwpt_ids[0]); + + test_cmd_mock_domain_replace(self->stdev_ids[0], ioas_id); + if (variant->mock_domains >= 2) { + test_cmd_mock_domain_replace(self->stdev_ids[0], + self->hwpt_ids[1]); + test_cmd_mock_domain_replace(self->stdev_ids[0], + self->hwpt_ids[1]); + test_cmd_mock_domain_replace(self->stdev_ids[0], + self->hwpt_ids[0]); + } + + test_cmd_mock_domain_replace(self->stdev_ids[0], self->ioas_id); test_ioctl_destroy(ioas_id); } +TEST_F(iommufd_mock_domain, alloc_hwpt) +{ + int i; + + for (i = 0; i != variant->mock_domains; i++) { + uint32_t stddev_id; + uint32_t hwpt_id; + + test_cmd_hwpt_alloc(self->idev_ids[0], self->ioas_id, &hwpt_id); + test_cmd_mock_domain(hwpt_id, &stddev_id, NULL, NULL); + test_ioctl_destroy(stddev_id); + test_ioctl_destroy(hwpt_id); + } +} + /* VFIO compatibility IOCTLs */ TEST_F(iommufd, simple_ioctls) @@ -1429,7 +1535,7 @@ FIXTURE_SETUP(vfio_compat_mock_domain) /* Create what VFIO would consider a group */ test_ioctl_ioas_alloc(&self->ioas_id); - test_cmd_mock_domain(self->ioas_id, NULL, NULL); + test_cmd_mock_domain(self->ioas_id, NULL, NULL, NULL); /* Attach it to the vfio compat */ vfio_ioas_cmd.ioas_id = self->ioas_id; diff --git a/tools/testing/selftests/iommu/iommufd_fail_nth.c b/tools/testing/selftests/iommu/iommufd_fail_nth.c index d9afcb23810e..a220ca2a689d 100644 --- a/tools/testing/selftests/iommu/iommufd_fail_nth.c +++ b/tools/testing/selftests/iommu/iommufd_fail_nth.c @@ -41,6 +41,8 @@ static int writeat(int dfd, const char *fn, const char *val) static __attribute__((constructor)) void setup_buffer(void) { + PAGE_SIZE = sysconf(_SC_PAGE_SIZE); + BUFFER_SIZE = 2*1024*1024; buffer = mmap(0, BUFFER_SIZE, PROT_READ | PROT_WRITE, @@ -313,7 +315,7 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) fail_nth_enable(); - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova, @@ -324,7 +326,7 @@ TEST_FAIL_NTH(basic_fail_nth, map_domain) if (_test_ioctl_destroy(self->fd, stdev_id)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL)) return -1; return 0; } @@ -348,12 +350,13 @@ TEST_FAIL_NTH(basic_fail_nth, map_two_domains) if (_test_ioctl_set_temp_memory_limit(self->fd, 32)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL)) return -1; fail_nth_enable(); - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2, + NULL)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, 262144, &iova, @@ -367,9 +370,10 @@ TEST_FAIL_NTH(basic_fail_nth, map_two_domains) if (_test_ioctl_destroy(self->fd, stdev_id2)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id2, &hwpt_id2, + NULL)) return -1; return 0; } @@ -526,7 +530,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) if (_test_ioctl_set_temp_memory_limit(self->fd, 32)) return -1; - if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id)) + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, &hwpt_id, NULL)) return -1; if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, BUFFER_SIZE, &iova, @@ -569,4 +573,57 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain) return 0; } +/* device.c */ +TEST_FAIL_NTH(basic_fail_nth, device) +{ + struct iommu_test_hw_info info; + uint32_t ioas_id; + uint32_t ioas_id2; + uint32_t stdev_id; + uint32_t idev_id; + uint32_t hwpt_id; + __u64 iova; + + self->fd = open("/dev/iommu", O_RDWR); + if (self->fd == -1) + return -1; + + if (_test_ioctl_ioas_alloc(self->fd, &ioas_id)) + return -1; + + if (_test_ioctl_ioas_alloc(self->fd, &ioas_id2)) + return -1; + + iova = MOCK_APERTURE_START; + if (_test_ioctl_ioas_map(self->fd, ioas_id, buffer, PAGE_SIZE, &iova, + IOMMU_IOAS_MAP_FIXED_IOVA | + IOMMU_IOAS_MAP_WRITEABLE | + IOMMU_IOAS_MAP_READABLE)) + return -1; + if (_test_ioctl_ioas_map(self->fd, ioas_id2, buffer, PAGE_SIZE, &iova, + IOMMU_IOAS_MAP_FIXED_IOVA | + IOMMU_IOAS_MAP_WRITEABLE | + IOMMU_IOAS_MAP_READABLE)) + return -1; + + fail_nth_enable(); + + if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, NULL, + &idev_id)) + return -1; + + if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info))) + return -1; + + if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, &hwpt_id)) + return -1; + + if (_test_cmd_mock_domain_replace(self->fd, stdev_id, ioas_id2, NULL)) + return -1; + + if (_test_cmd_mock_domain_replace(self->fd, stdev_id, hwpt_id, NULL)) + return -1; + return 0; +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/iommu/iommufd_utils.h b/tools/testing/selftests/iommu/iommufd_utils.h index 85d6662ef8e8..e0753d03ecaa 100644 --- a/tools/testing/selftests/iommu/iommufd_utils.h +++ b/tools/testing/selftests/iommu/iommufd_utils.h @@ -19,6 +19,12 @@ static void *buffer; static unsigned long BUFFER_SIZE; +static unsigned long PAGE_SIZE; + +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) +#define offsetofend(TYPE, MEMBER) \ + (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) + /* * Have the kernel check the refcount on pages. I don't know why a freshly * mmap'd anon non-compound page starts out with a ref of 3 @@ -39,7 +45,7 @@ static unsigned long BUFFER_SIZE; }) static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, - __u32 *hwpt_id) + __u32 *hwpt_id, __u32 *idev_id) { struct iommu_test_cmd cmd = { .size = sizeof(cmd), @@ -57,14 +63,84 @@ static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, assert(cmd.id != 0); if (hwpt_id) *hwpt_id = cmd.mock_domain.out_hwpt_id; + if (idev_id) + *idev_id = cmd.mock_domain.out_idev_id; return 0; } -#define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id) \ - ASSERT_EQ(0, \ - _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, hwpt_id)) +#define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id, idev_id) \ + ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, \ + hwpt_id, idev_id)) #define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \ EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \ - stdev_id, hwpt_id)) + stdev_id, hwpt_id, NULL)) + +static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id, + __u32 *hwpt_id) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE, + .id = stdev_id, + .mock_domain_replace = { + .pt_id = pt_id, + }, + }; + int ret; + + ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); + if (ret) + return ret; + if (hwpt_id) + *hwpt_id = cmd.mock_domain_replace.pt_id; + return 0; +} + +#define test_cmd_mock_domain_replace(stdev_id, pt_id) \ + ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \ + NULL)) +#define test_err_mock_domain_replace(_errno, stdev_id, pt_id) \ + EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \ + pt_id, NULL)) + +static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, + __u32 *hwpt_id) +{ + struct iommu_hwpt_alloc cmd = { + .size = sizeof(cmd), + .dev_id = device_id, + .pt_id = pt_id, + }; + int ret; + + ret = ioctl(fd, IOMMU_HWPT_ALLOC, &cmd); + if (ret) + return ret; + if (hwpt_id) + *hwpt_id = cmd.out_hwpt_id; + return 0; +} + +#define test_cmd_hwpt_alloc(device_id, pt_id, hwpt_id) \ + ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, hwpt_id)) + +static int _test_cmd_access_replace_ioas(int fd, __u32 access_id, + unsigned int ioas_id) +{ + struct iommu_test_cmd cmd = { + .size = sizeof(cmd), + .op = IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, + .id = access_id, + .access_replace_ioas = { .ioas_id = ioas_id }, + }; + int ret; + + ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); + if (ret) + return ret; + return 0; +} +#define test_cmd_access_replace_ioas(access_id, ioas_id) \ + ASSERT_EQ(0, _test_cmd_access_replace_ioas(self->fd, access_id, ioas_id)) static int _test_cmd_create_access(int fd, unsigned int ioas_id, __u32 *access_id, unsigned int flags) @@ -276,3 +352,61 @@ static void teardown_iommufd(int fd, struct __test_metadata *_metadata) }) #endif + +/* @data can be NULL */ +static int _test_cmd_get_hw_info(int fd, __u32 device_id, + void *data, size_t data_len) +{ + struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; + struct iommu_hw_info cmd = { + .size = sizeof(cmd), + .dev_id = device_id, + .data_len = data_len, + .data_uptr = (uint64_t)data, + }; + int ret; + + ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); + if (ret) + return ret; + + assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST); + + /* + * The struct iommu_test_hw_info should be the one defined + * by the current kernel. + */ + assert(cmd.data_len == sizeof(struct iommu_test_hw_info)); + + /* + * Trailing bytes should be 0 if user buffer is larger than + * the data that kernel reports. + */ + if (data_len > cmd.data_len) { + char *ptr = (char *)(data + cmd.data_len); + int idx = 0; + + while (idx < data_len - cmd.data_len) { + assert(!*(ptr + idx)); + idx++; + } + } + + if (info) { + if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg)) + assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL); + if (data_len >= offsetofend(struct iommu_test_hw_info, flags)) + assert(!info->flags); + } + + return 0; +} + +#define test_cmd_get_hw_info(device_id, data, data_len) \ + ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ + data, data_len)) + +#define test_err_get_hw_info(_errno, device_id, data, data_len) \ + EXPECT_ERRNO(_errno, \ + _test_cmd_get_hw_info(self->fd, device_id, \ + data, data_len)) diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index 829be379545a..529d29a35900 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -113,6 +113,15 @@ static inline int ksft_get_error_cnt(void) { return ksft_cnt.ksft_error; } static inline void ksft_print_header(void) { + /* + * Force line buffering; If stdout is not connected to a terminal, it + * will otherwise default to fully buffered, which can cause output + * duplication if there is content in the buffer when fork()ing. If + * there is a crash, line buffering also means the most recent output + * line will be visible. + */ + setvbuf(stdout, NULL, _IOLBF, 0); + if (!(getenv("KSFT_TAP_LEVEL"))) printf("TAP version 13\n"); } diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 1c952d1401d4..cd2fb43eea61 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -36,7 +36,8 @@ tap_timeout() { # Make sure tests will time out if utility is available. if [ -x /usr/bin/timeout ] ; then - /usr/bin/timeout --foreground "$kselftest_timeout" $1 + /usr/bin/timeout --foreground "$kselftest_timeout" \ + /usr/bin/timeout "$kselftest_timeout" $1 else $1 fi @@ -105,15 +106,18 @@ run_one() echo "# Warning: file $TEST is missing!" echo "not ok $test_num $TEST_HDR_MSG" else + if [ -x /usr/bin/stdbuf ]; then + stdbuf="/usr/bin/stdbuf --output=L " + fi eval kselftest_cmd_args="\$${kselftest_cmd_args_ref:-}" - cmd="./$BASENAME_TEST $kselftest_cmd_args" + cmd="$stdbuf ./$BASENAME_TEST $kselftest_cmd_args" if [ ! -x "$TEST" ]; then echo "# Warning: file $TEST is not executable" if [ $(head -n 1 "$TEST" | cut -c -2) = "#!" ] then interpreter=$(head -n 1 "$TEST" | cut -c 3-) - cmd="$interpreter ./$BASENAME_TEST" + cmd="$stdbuf $interpreter ./$BASENAME_TEST" else echo "not ok $test_num $TEST_HDR_MSG" return diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh index 4bc14d9e8ff1..de59cc8f03c3 100755 --- a/tools/testing/selftests/kselftest_deps.sh +++ b/tools/testing/selftests/kselftest_deps.sh @@ -46,11 +46,11 @@ fi print_targets=0 while getopts "p" arg; do - case $arg in - p) + case $arg in + p) print_targets=1 shift;; - esac + esac done if [ $# -eq 0 ] @@ -92,6 +92,10 @@ pass_cnt=0 # Get all TARGETS from selftests Makefile targets=$(grep -E "^TARGETS +|^TARGETS =" Makefile | cut -d "=" -f2) +# Initially, in LDLIBS related lines, the dep checker needs +# to ignore lines containing the following strings: +filter="\$(VAR_LDLIBS)\|pkg-config\|PKG_CONFIG\|IOURING_EXTRA_LIBS" + # Single test case if [ $# -eq 2 ] then @@ -100,6 +104,8 @@ then l1_test $test l2_test $test l3_test $test + l4_test $test + l5_test $test print_results $1 $2 exit $? @@ -113,7 +119,7 @@ fi # Append space at the end of the list to append more tests. l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \ - grep -v "VAR_LDLIBS" | awk -F: '{print $1}') + grep -v "$filter" | awk -F: '{print $1}' | uniq) # Level 2: LDLIBS set dynamically. # @@ -126,7 +132,7 @@ l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \ # Append space at the end of the list to append more tests. l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ - grep -v "VAR_LDLIBS" | awk -F: '{print $1}') + grep -v "$filter" | awk -F: '{print $1}' | uniq) # Level 3 # memfd and others use pkg-config to find mount and fuse libs @@ -138,11 +144,32 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ # VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null) l3_tests=$(grep -r --include=Makefile "^VAR_LDLIBS" | \ - grep -v "pkg-config" | awk -F: '{print $1}') + grep -v "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) -#echo $l1_tests -#echo $l2_1_tests -#echo $l3_tests +# Level 4 +# some tests may fall back to default using `|| echo -l<libname>` +# if pkg-config doesn't find the libs, instead of using VAR_LDLIBS +# as per level 3 checks. +# e.g: +# netfilter/Makefile +# LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) +l4_tests=$(grep -r --include=Makefile "^LDLIBS" | \ + grep "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) + +# Level 5 +# some tests may use IOURING_EXTRA_LIBS to add extra libs to LDLIBS, +# which in turn may be defined in a sub-Makefile +# e.g.: +# mm/Makefile +# $(OUTPUT)/gup_longterm: LDLIBS += $(IOURING_EXTRA_LIBS) +l5_tests=$(grep -r --include=Makefile "LDLIBS +=.*\$(IOURING_EXTRA_LIBS)" | \ + awk -F: '{print $1}' | uniq) + +#echo l1_tests $l1_tests +#echo l2_tests $l2_tests +#echo l3_tests $l3_tests +#echo l4_tests $l4_tests +#echo l5_tests $l5_tests all_tests print_results $1 $2 @@ -164,24 +191,32 @@ all_tests() for test in $l3_tests; do l3_test $test done + + for test in $l4_tests; do + l4_test $test + done + + for test in $l5_tests; do + l5_test $test + done } # Use same parsing used for l1_tests and pick libraries this time. l1_test() { test_libs=$(grep --include=Makefile "^LDLIBS" $test | \ - grep -v "VAR_LDLIBS" | \ + grep -v "$filter" | \ sed -e 's/\:/ /' | \ sed -e 's/+/ /' | cut -d "=" -f 2) check_libs $test $test_libs } -# Use same parsing used for l2__tests and pick libraries this time. +# Use same parsing used for l2_tests and pick libraries this time. l2_test() { test_libs=$(grep --include=Makefile ": LDLIBS" $test | \ - grep -v "VAR_LDLIBS" | \ + grep -v "$filter" | \ sed -e 's/\:/ /' | sed -e 's/+/ /' | \ cut -d "=" -f 2) @@ -197,6 +232,24 @@ l3_test() check_libs $test $test_libs } +l4_test() +{ + test_libs=$(grep --include=Makefile "^VAR_LDLIBS\|^LDLIBS" $test | \ + grep "\(pkg-config\|PKG_CONFIG\).*|| echo " | \ + sed -e 's/.*|| echo //' | sed -e 's/)$//') + + check_libs $test $test_libs +} + +l5_test() +{ + tests=$(find $(dirname "$test") -type f -name "*.mk") + test_libs=$(grep "^IOURING_EXTRA_LIBS +\?=" $tests | \ + cut -d "=" -f 2) + + check_libs $test $test_libs +} + check_libs() { diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index c692cc86e7da..a3bb36fb3cfc 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -23,6 +23,7 @@ LIBKVM += lib/guest_modes.c LIBKVM += lib/io.c LIBKVM += lib/kvm_util.c LIBKVM += lib/memstress.c +LIBKVM += lib/guest_sprintf.c LIBKVM += lib/rbtree.c LIBKVM += lib/sparsebit.c LIBKVM += lib/test_util.c @@ -122,6 +123,7 @@ TEST_GEN_PROGS_x86_64 += access_tracking_perf_test TEST_GEN_PROGS_x86_64 += demand_paging_test TEST_GEN_PROGS_x86_64 += dirty_log_test TEST_GEN_PROGS_x86_64 += dirty_log_perf_test +TEST_GEN_PROGS_x86_64 += guest_print_test TEST_GEN_PROGS_x86_64 += hardware_disable_test TEST_GEN_PROGS_x86_64 += kvm_create_max_vcpus TEST_GEN_PROGS_x86_64 += kvm_page_table_test @@ -140,7 +142,6 @@ TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test TEST_GEN_PROGS_aarch64 += aarch64/aarch32_id_regs TEST_GEN_PROGS_aarch64 += aarch64/arch_timer TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions -TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list TEST_GEN_PROGS_aarch64 += aarch64/hypercalls TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test TEST_GEN_PROGS_aarch64 += aarch64/psci_test @@ -152,6 +153,8 @@ TEST_GEN_PROGS_aarch64 += access_tracking_perf_test TEST_GEN_PROGS_aarch64 += demand_paging_test TEST_GEN_PROGS_aarch64 += dirty_log_test TEST_GEN_PROGS_aarch64 += dirty_log_perf_test +TEST_GEN_PROGS_aarch64 += guest_print_test +TEST_GEN_PROGS_aarch64 += get-reg-list TEST_GEN_PROGS_aarch64 += kvm_create_max_vcpus TEST_GEN_PROGS_aarch64 += kvm_page_table_test TEST_GEN_PROGS_aarch64 += memslot_modification_stress_test @@ -166,8 +169,10 @@ TEST_GEN_PROGS_s390x += s390x/resets TEST_GEN_PROGS_s390x += s390x/sync_regs_test TEST_GEN_PROGS_s390x += s390x/tprot TEST_GEN_PROGS_s390x += s390x/cmma_test +TEST_GEN_PROGS_s390x += s390x/debug_test TEST_GEN_PROGS_s390x += demand_paging_test TEST_GEN_PROGS_s390x += dirty_log_test +TEST_GEN_PROGS_s390x += guest_print_test TEST_GEN_PROGS_s390x += kvm_create_max_vcpus TEST_GEN_PROGS_s390x += kvm_page_table_test TEST_GEN_PROGS_s390x += rseq_test @@ -176,11 +181,15 @@ TEST_GEN_PROGS_s390x += kvm_binary_stats_test TEST_GEN_PROGS_riscv += demand_paging_test TEST_GEN_PROGS_riscv += dirty_log_test +TEST_GEN_PROGS_riscv += guest_print_test +TEST_GEN_PROGS_riscv += get-reg-list TEST_GEN_PROGS_riscv += kvm_create_max_vcpus TEST_GEN_PROGS_riscv += kvm_page_table_test TEST_GEN_PROGS_riscv += set_memory_region_test TEST_GEN_PROGS_riscv += kvm_binary_stats_test +SPLIT_TESTS += get-reg-list + TEST_PROGS += $(TEST_PROGS_$(ARCH_DIR)) TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(ARCH_DIR)) TEST_GEN_PROGS_EXTENDED += $(TEST_GEN_PROGS_EXTENDED_$(ARCH_DIR)) @@ -204,6 +213,7 @@ endif CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ -Wno-gnu-variable-sized-type-not-at-end -MD\ -fno-builtin-memcmp -fno-builtin-memcpy -fno-builtin-memset \ + -fno-builtin-strnlen \ -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \ -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \ -I$(<D) -Iinclude/$(ARCH_DIR) -I ../rseq -I.. $(EXTRA_CFLAGS) \ @@ -228,11 +238,14 @@ LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING)) LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ) +SPLIT_TESTS_TARGETS := $(patsubst %, $(OUTPUT)/%, $(SPLIT_TESTS)) +SPLIT_TESTS_OBJS := $(patsubst %, $(ARCH_DIR)/%.o, $(SPLIT_TESTS)) TEST_GEN_OBJ = $(patsubst %, %.o, $(TEST_GEN_PROGS)) TEST_GEN_OBJ += $(patsubst %, %.o, $(TEST_GEN_PROGS_EXTENDED)) TEST_DEP_FILES = $(patsubst %.o, %.d, $(TEST_GEN_OBJ)) TEST_DEP_FILES += $(patsubst %.o, %.d, $(LIBKVM_OBJS)) +TEST_DEP_FILES += $(patsubst %.o, %.d, $(SPLIT_TESTS_OBJS)) -include $(TEST_DEP_FILES) $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): %: %.o @@ -240,7 +253,10 @@ $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): %: %.o $(TEST_GEN_OBJ): $(OUTPUT)/%.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ -EXTRA_CLEAN += $(LIBKVM_OBJS) $(TEST_DEP_FILES) $(TEST_GEN_OBJ) cscope.* +$(SPLIT_TESTS_TARGETS): %: %.o $(SPLIT_TESTS_OBJS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@ + +EXTRA_CLEAN += $(LIBKVM_OBJS) $(TEST_DEP_FILES) $(TEST_GEN_OBJ) $(SPLIT_TESTS_OBJS) cscope.* x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)))) $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c diff --git a/tools/testing/selftests/kvm/aarch64/aarch32_id_regs.c b/tools/testing/selftests/kvm/aarch64/aarch32_id_regs.c index 4951ac53d1f8..b90580840b22 100644 --- a/tools/testing/selftests/kvm/aarch64/aarch32_id_regs.c +++ b/tools/testing/selftests/kvm/aarch64/aarch32_id_regs.c @@ -98,7 +98,7 @@ static void test_user_raz_wi(struct kvm_vcpu *vcpu) uint64_t val; vcpu_get_reg(vcpu, reg_id, &val); - ASSERT_EQ(val, 0); + TEST_ASSERT_EQ(val, 0); /* * Expect the ioctl to succeed with no effect on the register @@ -107,7 +107,7 @@ static void test_user_raz_wi(struct kvm_vcpu *vcpu) vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL); vcpu_get_reg(vcpu, reg_id, &val); - ASSERT_EQ(val, 0); + TEST_ASSERT_EQ(val, 0); } } @@ -127,14 +127,14 @@ static void test_user_raz_invariant(struct kvm_vcpu *vcpu) uint64_t val; vcpu_get_reg(vcpu, reg_id, &val); - ASSERT_EQ(val, 0); + TEST_ASSERT_EQ(val, 0); r = __vcpu_set_reg(vcpu, reg_id, BAD_ID_REG_VAL); TEST_ASSERT(r < 0 && errno == EINVAL, "unexpected KVM_SET_ONE_REG error: r=%d, errno=%d", r, errno); vcpu_get_reg(vcpu, reg_id, &val); - ASSERT_EQ(val, 0); + TEST_ASSERT_EQ(val, 0); } } diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c index 8ef370924a02..274b8465b42a 100644 --- a/tools/testing/selftests/kvm/aarch64/arch_timer.c +++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c @@ -19,7 +19,6 @@ * * Copyright (c) 2021, Google LLC. */ - #define _GNU_SOURCE #include <stdlib.h> @@ -155,11 +154,13 @@ static void guest_validate_irq(unsigned int intid, xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt); /* Make sure we are dealing with the correct timer IRQ */ - GUEST_ASSERT_2(intid == timer_irq, intid, timer_irq); + GUEST_ASSERT_EQ(intid, timer_irq); /* Basic 'timer condition met' check */ - GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us); - GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl); + __GUEST_ASSERT(xcnt >= cval, + "xcnt = 0x%llx, cval = 0x%llx, xcnt_diff_us = 0x%llx", + xcnt, cval, xcnt_diff_us); + __GUEST_ASSERT(xctl & CTL_ISTATUS, "xcnt = 0x%llx", xcnt); WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1); } @@ -192,8 +193,7 @@ static void guest_run_stage(struct test_vcpu_shared_data *shared_data, TIMER_TEST_ERR_MARGIN_US); irq_iter = READ_ONCE(shared_data->nr_iter); - GUEST_ASSERT_2(config_iter + 1 == irq_iter, - config_iter + 1, irq_iter); + GUEST_ASSERT_EQ(config_iter + 1, irq_iter); } } @@ -243,13 +243,9 @@ static void *test_vcpu_run(void *arg) break; case UCALL_ABORT: sync_global_from_guest(vm, *shared_data); - REPORT_GUEST_ASSERT_N(uc, "values: %lu, %lu; %lu, vcpu %u; stage; %u; iter: %u", - GUEST_ASSERT_ARG(uc, 0), - GUEST_ASSERT_ARG(uc, 1), - GUEST_ASSERT_ARG(uc, 2), - vcpu_idx, - shared_data->guest_stage, - shared_data->nr_iter); + fprintf(stderr, "Guest assert failed, vcpu %u; stage; %u; iter: %u\n", + vcpu_idx, shared_data->guest_stage, shared_data->nr_iter); + REPORT_GUEST_ASSERT(uc); break; default: TEST_FAIL("Unexpected guest exit\n"); diff --git a/tools/testing/selftests/kvm/aarch64/debug-exceptions.c b/tools/testing/selftests/kvm/aarch64/debug-exceptions.c index 637be796086f..f5b6cb3a0019 100644 --- a/tools/testing/selftests/kvm/aarch64/debug-exceptions.c +++ b/tools/testing/selftests/kvm/aarch64/debug-exceptions.c @@ -365,7 +365,7 @@ static void guest_wp_handler(struct ex_regs *regs) static void guest_ss_handler(struct ex_regs *regs) { - GUEST_ASSERT_1(ss_idx < 4, ss_idx); + __GUEST_ASSERT(ss_idx < 4, "Expected index < 4, got '%u'", ss_idx); ss_addr[ss_idx++] = regs->pc; regs->pstate |= SPSR_SS; } @@ -410,8 +410,8 @@ static void guest_code_ss(int test_cnt) /* Userspace disables Single Step when the end is nigh. */ asm volatile("iter_ss_end:\n"); - GUEST_ASSERT(bvr == w_bvr); - GUEST_ASSERT(wvr == w_wvr); + GUEST_ASSERT_EQ(bvr, w_bvr); + GUEST_ASSERT_EQ(wvr, w_wvr); } GUEST_DONE(); } @@ -450,7 +450,7 @@ static void test_guest_debug_exceptions(uint8_t bpn, uint8_t wpn, uint8_t ctx_bp vcpu_run(vcpu); switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c index 4f10055af2aa..709d7d721760 100644 --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c @@ -4,50 +4,17 @@ * * Copyright (C) 2020, Red Hat, Inc. * - * When attempting to migrate from a host with an older kernel to a host - * with a newer kernel we allow the newer kernel on the destination to - * list new registers with get-reg-list. We assume they'll be unused, at - * least until the guest reboots, and so they're relatively harmless. - * However, if the destination host with the newer kernel is missing - * registers which the source host with the older kernel has, then that's - * a regression in get-reg-list. This test checks for that regression by - * checking the current list against a blessed list. We should never have - * missing registers, but if new ones appear then they can probably be - * added to the blessed list. A completely new blessed list can be created - * by running the test with the --list command line argument. - * - * Note, the blessed list should be created from the oldest possible - * kernel. We can't go older than v4.15, though, because that's the first - * release to expose the ID system registers in KVM_GET_REG_LIST, see - * commit 93390c0a1b20 ("arm64: KVM: Hide unsupported AArch64 CPU features - * from guests"). Also, one must use the --core-reg-fixup command line - * option when running on an older kernel that doesn't include df205b5c6328 - * ("KVM: arm64: Filter out invalid core register IDs in KVM_GET_REG_LIST") + * While the blessed list should be created from the oldest possible + * kernel, we can't go older than v5.2, though, because that's the first + * release which includes df205b5c6328 ("KVM: arm64: Filter out invalid + * core register IDs in KVM_GET_REG_LIST"). Without that commit the core + * registers won't match expectations. */ #include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/wait.h> #include "kvm_util.h" #include "test_util.h" #include "processor.h" -static struct kvm_reg_list *reg_list; -static __u64 *blessed_reg, blessed_n; - -struct reg_sublist { - const char *name; - long capability; - int feature; - bool finalize; - __u64 *regs; - __u64 regs_n; - __u64 *rejects_set; - __u64 rejects_set_n; -}; - struct feature_id_reg { __u64 reg; __u64 id_reg; @@ -76,70 +43,7 @@ static struct feature_id_reg feat_id_regs[] = { } }; -struct vcpu_config { - char *name; - struct reg_sublist sublists[]; -}; - -static struct vcpu_config *vcpu_configs[]; -static int vcpu_configs_n; - -#define for_each_sublist(c, s) \ - for ((s) = &(c)->sublists[0]; (s)->regs; ++(s)) - -#define for_each_reg(i) \ - for ((i) = 0; (i) < reg_list->n; ++(i)) - -#define for_each_reg_filtered(i) \ - for_each_reg(i) \ - if (!filter_reg(reg_list->reg[i])) - -#define for_each_missing_reg(i) \ - for ((i) = 0; (i) < blessed_n; ++(i)) \ - if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ - if (check_supported_feat_reg(vcpu, blessed_reg[i])) - -#define for_each_new_reg(i) \ - for_each_reg_filtered(i) \ - if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) - -static const char *config_name(struct vcpu_config *c) -{ - struct reg_sublist *s; - int len = 0; - - if (c->name) - return c->name; - - for_each_sublist(c, s) - len += strlen(s->name) + 1; - - c->name = malloc(len); - - len = 0; - for_each_sublist(c, s) { - if (!strcmp(s->name, "base")) - continue; - strcat(c->name + len, s->name); - len += strlen(s->name) + 1; - c->name[len - 1] = '+'; - } - c->name[len - 1] = '\0'; - - return c->name; -} - -static bool has_cap(struct vcpu_config *c, long capability) -{ - struct reg_sublist *s; - - for_each_sublist(c, s) - if (s->capability == capability) - return true; - return false; -} - -static bool filter_reg(__u64 reg) +bool filter_reg(__u64 reg) { /* * DEMUX register presence depends on the host's CLIDR_EL1. @@ -151,16 +55,6 @@ static bool filter_reg(__u64 reg) return false; } -static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) -{ - int i; - - for (i = 0; i < nr_regs; ++i) - if (reg == regs[i]) - return true; - return false; -} - static bool check_supported_feat_reg(struct kvm_vcpu *vcpu, __u64 reg) { int i, ret; @@ -180,17 +74,27 @@ static bool check_supported_feat_reg(struct kvm_vcpu *vcpu, __u64 reg) return true; } -static const char *str_with_index(const char *template, __u64 index) +bool check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) { - char *str, *p; - int n; + return check_supported_feat_reg(vcpu, reg); +} - str = strdup(template); - p = strstr(str, "##"); - n = sprintf(p, "%lld", index); - strcat(p + n, strstr(template, "##") + 2); +bool check_reject_set(int err) +{ + return err == EPERM; +} - return (const char *)str; +void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) +{ + struct vcpu_reg_sublist *s; + int feature; + + for_each_sublist(c, s) { + if (s->finalize) { + feature = s->feature; + vcpu_ioctl(vcpu, KVM_ARM_VCPU_FINALIZE, &feature); + } + } } #define REG_MASK (KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_COPROC_MASK) @@ -199,7 +103,7 @@ static const char *str_with_index(const char *template, __u64 index) #define CORE_SPSR_XX_NR_WORDS 2 #define CORE_FPREGS_XX_NR_WORDS 4 -static const char *core_id_to_str(struct vcpu_config *c, __u64 id) +static const char *core_id_to_str(const char *prefix, __u64 id) { __u64 core_off = id & ~REG_MASK, idx; @@ -210,8 +114,8 @@ static const char *core_id_to_str(struct vcpu_config *c, __u64 id) case KVM_REG_ARM_CORE_REG(regs.regs[0]) ... KVM_REG_ARM_CORE_REG(regs.regs[30]): idx = (core_off - KVM_REG_ARM_CORE_REG(regs.regs[0])) / CORE_REGS_XX_NR_WORDS; - TEST_ASSERT(idx < 31, "%s: Unexpected regs.regs index: %lld", config_name(c), idx); - return str_with_index("KVM_REG_ARM_CORE_REG(regs.regs[##])", idx); + TEST_ASSERT(idx < 31, "%s: Unexpected regs.regs index: %lld", prefix, idx); + return strdup_printf("KVM_REG_ARM_CORE_REG(regs.regs[%lld])", idx); case KVM_REG_ARM_CORE_REG(regs.sp): return "KVM_REG_ARM_CORE_REG(regs.sp)"; case KVM_REG_ARM_CORE_REG(regs.pc): @@ -225,24 +129,24 @@ static const char *core_id_to_str(struct vcpu_config *c, __u64 id) case KVM_REG_ARM_CORE_REG(spsr[0]) ... KVM_REG_ARM_CORE_REG(spsr[KVM_NR_SPSR - 1]): idx = (core_off - KVM_REG_ARM_CORE_REG(spsr[0])) / CORE_SPSR_XX_NR_WORDS; - TEST_ASSERT(idx < KVM_NR_SPSR, "%s: Unexpected spsr index: %lld", config_name(c), idx); - return str_with_index("KVM_REG_ARM_CORE_REG(spsr[##])", idx); + TEST_ASSERT(idx < KVM_NR_SPSR, "%s: Unexpected spsr index: %lld", prefix, idx); + return strdup_printf("KVM_REG_ARM_CORE_REG(spsr[%lld])", idx); case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ... KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]): idx = (core_off - KVM_REG_ARM_CORE_REG(fp_regs.vregs[0])) / CORE_FPREGS_XX_NR_WORDS; - TEST_ASSERT(idx < 32, "%s: Unexpected fp_regs.vregs index: %lld", config_name(c), idx); - return str_with_index("KVM_REG_ARM_CORE_REG(fp_regs.vregs[##])", idx); + TEST_ASSERT(idx < 32, "%s: Unexpected fp_regs.vregs index: %lld", prefix, idx); + return strdup_printf("KVM_REG_ARM_CORE_REG(fp_regs.vregs[%lld])", idx); case KVM_REG_ARM_CORE_REG(fp_regs.fpsr): return "KVM_REG_ARM_CORE_REG(fp_regs.fpsr)"; case KVM_REG_ARM_CORE_REG(fp_regs.fpcr): return "KVM_REG_ARM_CORE_REG(fp_regs.fpcr)"; } - TEST_FAIL("%s: Unknown core reg id: 0x%llx", config_name(c), id); + TEST_FAIL("%s: Unknown core reg id: 0x%llx", prefix, id); return NULL; } -static const char *sve_id_to_str(struct vcpu_config *c, __u64 id) +static const char *sve_id_to_str(const char *prefix, __u64 id) { __u64 sve_off, n, i; @@ -252,37 +156,37 @@ static const char *sve_id_to_str(struct vcpu_config *c, __u64 id) sve_off = id & ~(REG_MASK | ((1ULL << 5) - 1)); i = id & (KVM_ARM64_SVE_MAX_SLICES - 1); - TEST_ASSERT(i == 0, "%s: Currently we don't expect slice > 0, reg id 0x%llx", config_name(c), id); + TEST_ASSERT(i == 0, "%s: Currently we don't expect slice > 0, reg id 0x%llx", prefix, id); switch (sve_off) { case KVM_REG_ARM64_SVE_ZREG_BASE ... KVM_REG_ARM64_SVE_ZREG_BASE + (1ULL << 5) * KVM_ARM64_SVE_NUM_ZREGS - 1: n = (id >> 5) & (KVM_ARM64_SVE_NUM_ZREGS - 1); TEST_ASSERT(id == KVM_REG_ARM64_SVE_ZREG(n, 0), - "%s: Unexpected bits set in SVE ZREG id: 0x%llx", config_name(c), id); - return str_with_index("KVM_REG_ARM64_SVE_ZREG(##, 0)", n); + "%s: Unexpected bits set in SVE ZREG id: 0x%llx", prefix, id); + return strdup_printf("KVM_REG_ARM64_SVE_ZREG(%lld, 0)", n); case KVM_REG_ARM64_SVE_PREG_BASE ... KVM_REG_ARM64_SVE_PREG_BASE + (1ULL << 5) * KVM_ARM64_SVE_NUM_PREGS - 1: n = (id >> 5) & (KVM_ARM64_SVE_NUM_PREGS - 1); TEST_ASSERT(id == KVM_REG_ARM64_SVE_PREG(n, 0), - "%s: Unexpected bits set in SVE PREG id: 0x%llx", config_name(c), id); - return str_with_index("KVM_REG_ARM64_SVE_PREG(##, 0)", n); + "%s: Unexpected bits set in SVE PREG id: 0x%llx", prefix, id); + return strdup_printf("KVM_REG_ARM64_SVE_PREG(%lld, 0)", n); case KVM_REG_ARM64_SVE_FFR_BASE: TEST_ASSERT(id == KVM_REG_ARM64_SVE_FFR(0), - "%s: Unexpected bits set in SVE FFR id: 0x%llx", config_name(c), id); + "%s: Unexpected bits set in SVE FFR id: 0x%llx", prefix, id); return "KVM_REG_ARM64_SVE_FFR(0)"; } return NULL; } -static void print_reg(struct vcpu_config *c, __u64 id) +void print_reg(const char *prefix, __u64 id) { unsigned op0, op1, crn, crm, op2; const char *reg_size = NULL; TEST_ASSERT((id & KVM_REG_ARCH_MASK) == KVM_REG_ARM64, - "%s: KVM_REG_ARM64 missing in reg id: 0x%llx", config_name(c), id); + "%s: KVM_REG_ARM64 missing in reg id: 0x%llx", prefix, id); switch (id & KVM_REG_SIZE_MASK) { case KVM_REG_SIZE_U8: @@ -314,16 +218,16 @@ static void print_reg(struct vcpu_config *c, __u64 id) break; default: TEST_FAIL("%s: Unexpected reg size: 0x%llx in reg id: 0x%llx", - config_name(c), (id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id); + prefix, (id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id); } switch (id & KVM_REG_ARM_COPROC_MASK) { case KVM_REG_ARM_CORE: - printf("\tKVM_REG_ARM64 | %s | KVM_REG_ARM_CORE | %s,\n", reg_size, core_id_to_str(c, id)); + printf("\tKVM_REG_ARM64 | %s | KVM_REG_ARM_CORE | %s,\n", reg_size, core_id_to_str(prefix, id)); break; case KVM_REG_ARM_DEMUX: TEST_ASSERT(!(id & ~(REG_MASK | KVM_REG_ARM_DEMUX_ID_MASK | KVM_REG_ARM_DEMUX_VAL_MASK)), - "%s: Unexpected bits set in DEMUX reg id: 0x%llx", config_name(c), id); + "%s: Unexpected bits set in DEMUX reg id: 0x%llx", prefix, id); printf("\tKVM_REG_ARM64 | %s | KVM_REG_ARM_DEMUX | KVM_REG_ARM_DEMUX_ID_CCSIDR | %lld,\n", reg_size, id & KVM_REG_ARM_DEMUX_VAL_MASK); break; @@ -334,370 +238,34 @@ static void print_reg(struct vcpu_config *c, __u64 id) crm = (id & KVM_REG_ARM64_SYSREG_CRM_MASK) >> KVM_REG_ARM64_SYSREG_CRM_SHIFT; op2 = (id & KVM_REG_ARM64_SYSREG_OP2_MASK) >> KVM_REG_ARM64_SYSREG_OP2_SHIFT; TEST_ASSERT(id == ARM64_SYS_REG(op0, op1, crn, crm, op2), - "%s: Unexpected bits set in SYSREG reg id: 0x%llx", config_name(c), id); + "%s: Unexpected bits set in SYSREG reg id: 0x%llx", prefix, id); printf("\tARM64_SYS_REG(%d, %d, %d, %d, %d),\n", op0, op1, crn, crm, op2); break; case KVM_REG_ARM_FW: TEST_ASSERT(id == KVM_REG_ARM_FW_REG(id & 0xffff), - "%s: Unexpected bits set in FW reg id: 0x%llx", config_name(c), id); + "%s: Unexpected bits set in FW reg id: 0x%llx", prefix, id); printf("\tKVM_REG_ARM_FW_REG(%lld),\n", id & 0xffff); break; case KVM_REG_ARM_FW_FEAT_BMAP: TEST_ASSERT(id == KVM_REG_ARM_FW_FEAT_BMAP_REG(id & 0xffff), - "%s: Unexpected bits set in the bitmap feature FW reg id: 0x%llx", config_name(c), id); + "%s: Unexpected bits set in the bitmap feature FW reg id: 0x%llx", prefix, id); printf("\tKVM_REG_ARM_FW_FEAT_BMAP_REG(%lld),\n", id & 0xffff); break; case KVM_REG_ARM64_SVE: - if (has_cap(c, KVM_CAP_ARM_SVE)) - printf("\t%s,\n", sve_id_to_str(c, id)); - else - TEST_FAIL("%s: KVM_REG_ARM64_SVE is an unexpected coproc type in reg id: 0x%llx", config_name(c), id); + printf("\t%s,\n", sve_id_to_str(prefix, id)); break; default: TEST_FAIL("%s: Unexpected coproc type: 0x%llx in reg id: 0x%llx", - config_name(c), (id & KVM_REG_ARM_COPROC_MASK) >> KVM_REG_ARM_COPROC_SHIFT, id); - } -} - -/* - * Older kernels listed each 32-bit word of CORE registers separately. - * For 64 and 128-bit registers we need to ignore the extra words. We - * also need to fixup the sizes, because the older kernels stated all - * registers were 64-bit, even when they weren't. - */ -static void core_reg_fixup(void) -{ - struct kvm_reg_list *tmp; - __u64 id, core_off; - int i; - - tmp = calloc(1, sizeof(*tmp) + reg_list->n * sizeof(__u64)); - - for (i = 0; i < reg_list->n; ++i) { - id = reg_list->reg[i]; - - if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM_CORE) { - tmp->reg[tmp->n++] = id; - continue; - } - - core_off = id & ~REG_MASK; - - switch (core_off) { - case 0x52: case 0xd2: case 0xd6: - /* - * These offsets are pointing at padding. - * We need to ignore them too. - */ - continue; - case KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]) ... - KVM_REG_ARM_CORE_REG(fp_regs.vregs[31]): - if (core_off & 3) - continue; - id &= ~KVM_REG_SIZE_MASK; - id |= KVM_REG_SIZE_U128; - tmp->reg[tmp->n++] = id; - continue; - case KVM_REG_ARM_CORE_REG(fp_regs.fpsr): - case KVM_REG_ARM_CORE_REG(fp_regs.fpcr): - id &= ~KVM_REG_SIZE_MASK; - id |= KVM_REG_SIZE_U32; - tmp->reg[tmp->n++] = id; - continue; - default: - if (core_off & 1) - continue; - tmp->reg[tmp->n++] = id; - break; - } + prefix, (id & KVM_REG_ARM_COPROC_MASK) >> KVM_REG_ARM_COPROC_SHIFT, id); } - - free(reg_list); - reg_list = tmp; -} - -static void prepare_vcpu_init(struct vcpu_config *c, struct kvm_vcpu_init *init) -{ - struct reg_sublist *s; - - for_each_sublist(c, s) - if (s->capability) - init->features[s->feature / 32] |= 1 << (s->feature % 32); -} - -static void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_config *c) -{ - struct reg_sublist *s; - int feature; - - for_each_sublist(c, s) { - if (s->finalize) { - feature = s->feature; - vcpu_ioctl(vcpu, KVM_ARM_VCPU_FINALIZE, &feature); - } - } -} - -static void check_supported(struct vcpu_config *c) -{ - struct reg_sublist *s; - - for_each_sublist(c, s) { - if (!s->capability) - continue; - - __TEST_REQUIRE(kvm_has_cap(s->capability), - "%s: %s not available, skipping tests\n", - config_name(c), s->name); - } -} - -static bool print_list; -static bool print_filtered; -static bool fixup_core_regs; - -static void run_test(struct vcpu_config *c) -{ - struct kvm_vcpu_init init = { .target = -1, }; - int new_regs = 0, missing_regs = 0, i, n; - int failed_get = 0, failed_set = 0, failed_reject = 0; - struct kvm_vcpu *vcpu; - struct kvm_vm *vm; - struct reg_sublist *s; - - check_supported(c); - - vm = vm_create_barebones(); - prepare_vcpu_init(c, &init); - vcpu = __vm_vcpu_add(vm, 0); - aarch64_vcpu_setup(vcpu, &init); - finalize_vcpu(vcpu, c); - - reg_list = vcpu_get_reg_list(vcpu); - - if (fixup_core_regs) - core_reg_fixup(); - - if (print_list || print_filtered) { - putchar('\n'); - for_each_reg(i) { - __u64 id = reg_list->reg[i]; - if ((print_list && !filter_reg(id)) || - (print_filtered && filter_reg(id))) - print_reg(c, id); - } - putchar('\n'); - return; - } - - /* - * We only test that we can get the register and then write back the - * same value. Some registers may allow other values to be written - * back, but others only allow some bits to be changed, and at least - * for ID registers set will fail if the value does not exactly match - * what was returned by get. If registers that allow other values to - * be written need to have the other values tested, then we should - * create a new set of tests for those in a new independent test - * executable. - */ - for_each_reg(i) { - uint8_t addr[2048 / 8]; - struct kvm_one_reg reg = { - .id = reg_list->reg[i], - .addr = (__u64)&addr, - }; - bool reject_reg = false; - int ret; - - ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); - if (ret) { - printf("%s: Failed to get ", config_name(c)); - print_reg(c, reg.id); - putchar('\n'); - ++failed_get; - } - - /* rejects_set registers are rejected after KVM_ARM_VCPU_FINALIZE */ - for_each_sublist(c, s) { - if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { - reject_reg = true; - ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); - if (ret != -1 || errno != EPERM) { - printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); - print_reg(c, reg.id); - putchar('\n'); - ++failed_reject; - } - break; - } - } - - if (!reject_reg) { - ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); - if (ret) { - printf("%s: Failed to set ", config_name(c)); - print_reg(c, reg.id); - putchar('\n'); - ++failed_set; - } - } - } - - for_each_sublist(c, s) - blessed_n += s->regs_n; - blessed_reg = calloc(blessed_n, sizeof(__u64)); - - n = 0; - for_each_sublist(c, s) { - for (i = 0; i < s->regs_n; ++i) - blessed_reg[n++] = s->regs[i]; - } - - for_each_new_reg(i) - ++new_regs; - - for_each_missing_reg(i) - ++missing_regs; - - if (new_regs || missing_regs) { - n = 0; - for_each_reg_filtered(i) - ++n; - - printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); - printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", - config_name(c), reg_list->n, reg_list->n - n); - } - - if (new_regs) { - printf("\n%s: There are %d new registers.\n" - "Consider adding them to the blessed reg " - "list with the following lines:\n\n", config_name(c), new_regs); - for_each_new_reg(i) - print_reg(c, reg_list->reg[i]); - putchar('\n'); - } - - if (missing_regs) { - printf("\n%s: There are %d missing registers.\n" - "The following lines are missing registers:\n\n", config_name(c), missing_regs); - for_each_missing_reg(i) - print_reg(c, blessed_reg[i]); - putchar('\n'); - } - - TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, - "%s: There are %d missing registers; " - "%d registers failed get; %d registers failed set; %d registers failed reject", - config_name(c), missing_regs, failed_get, failed_set, failed_reject); - - pr_info("%s: PASS\n", config_name(c)); - blessed_n = 0; - free(blessed_reg); - free(reg_list); - kvm_vm_free(vm); -} - -static void help(void) -{ - struct vcpu_config *c; - int i; - - printf( - "\n" - "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered] [--core-reg-fixup]\n\n" - " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" - " '<selection>' may be\n"); - - for (i = 0; i < vcpu_configs_n; ++i) { - c = vcpu_configs[i]; - printf( - " '%s'\n", config_name(c)); - } - - printf( - "\n" - " --list Print the register list rather than test it (requires --config)\n" - " --list-filtered Print registers that would normally be filtered out (requires --config)\n" - " --core-reg-fixup Needed when running on old kernels with broken core reg listings\n" - "\n" - ); -} - -static struct vcpu_config *parse_config(const char *config) -{ - struct vcpu_config *c; - int i; - - if (config[8] != '=') - help(), exit(1); - - for (i = 0; i < vcpu_configs_n; ++i) { - c = vcpu_configs[i]; - if (strcmp(config_name(c), &config[9]) == 0) - break; - } - - if (i == vcpu_configs_n) - help(), exit(1); - - return c; -} - -int main(int ac, char **av) -{ - struct vcpu_config *c, *sel = NULL; - int i, ret = 0; - pid_t pid; - - for (i = 1; i < ac; ++i) { - if (strcmp(av[i], "--core-reg-fixup") == 0) - fixup_core_regs = true; - else if (strncmp(av[i], "--config", 8) == 0) - sel = parse_config(av[i]); - else if (strcmp(av[i], "--list") == 0) - print_list = true; - else if (strcmp(av[i], "--list-filtered") == 0) - print_filtered = true; - else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) - help(), exit(0); - else - help(), exit(1); - } - - if (print_list || print_filtered) { - /* - * We only want to print the register list of a single config. - */ - if (!sel) - help(), exit(1); - } - - for (i = 0; i < vcpu_configs_n; ++i) { - c = vcpu_configs[i]; - if (sel && c != sel) - continue; - - pid = fork(); - - if (!pid) { - run_test(c); - exit(0); - } else { - int wstatus; - pid_t wpid = wait(&wstatus); - TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); - if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) - ret = KSFT_FAIL; - } - } - - return ret; } /* - * The current blessed list was primed with the output of kernel version + * The original blessed list was primed with the output of kernel version * v4.15 with --core-reg-fixup and then later updated with new registers. + * (The --core-reg-fixup option and it's fixup function have been removed + * from the test, as it's unlikely to use this type of test on a kernel + * older than v5.2.) * * The blessed list is up to date with kernel version v6.4 (or so we hope) */ @@ -1130,14 +698,14 @@ static __u64 pauth_generic_regs[] = { .regs_n = ARRAY_SIZE(pauth_generic_regs), \ } -static struct vcpu_config vregs_config = { +static struct vcpu_reg_list vregs_config = { .sublists = { BASE_SUBLIST, VREGS_SUBLIST, {0}, }, }; -static struct vcpu_config vregs_pmu_config = { +static struct vcpu_reg_list vregs_pmu_config = { .sublists = { BASE_SUBLIST, VREGS_SUBLIST, @@ -1145,14 +713,14 @@ static struct vcpu_config vregs_pmu_config = { {0}, }, }; -static struct vcpu_config sve_config = { +static struct vcpu_reg_list sve_config = { .sublists = { BASE_SUBLIST, SVE_SUBLIST, {0}, }, }; -static struct vcpu_config sve_pmu_config = { +static struct vcpu_reg_list sve_pmu_config = { .sublists = { BASE_SUBLIST, SVE_SUBLIST, @@ -1160,7 +728,7 @@ static struct vcpu_config sve_pmu_config = { {0}, }, }; -static struct vcpu_config pauth_config = { +static struct vcpu_reg_list pauth_config = { .sublists = { BASE_SUBLIST, VREGS_SUBLIST, @@ -1168,7 +736,7 @@ static struct vcpu_config pauth_config = { {0}, }, }; -static struct vcpu_config pauth_pmu_config = { +static struct vcpu_reg_list pauth_pmu_config = { .sublists = { BASE_SUBLIST, VREGS_SUBLIST, @@ -1178,7 +746,7 @@ static struct vcpu_config pauth_pmu_config = { }, }; -static struct vcpu_config *vcpu_configs[] = { +struct vcpu_reg_list *vcpu_configs[] = { &vregs_config, &vregs_pmu_config, &sve_config, @@ -1186,4 +754,4 @@ static struct vcpu_config *vcpu_configs[] = { &pauth_config, &pauth_pmu_config, }; -static int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); +int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c index bef1499fb465..31f66ba97228 100644 --- a/tools/testing/selftests/kvm/aarch64/hypercalls.c +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c @@ -8,7 +8,6 @@ * hypercalls are properly masked or unmasked to the guest when disabled or * enabled from the KVM userspace, respectively. */ - #include <errno.h> #include <linux/arm-smccc.h> #include <asm/kvm.h> @@ -105,15 +104,17 @@ static void guest_test_hvc(const struct test_hvc_info *hc_info) switch (stage) { case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: case TEST_STAGE_HVC_IFACE_FALSE_INFO: - GUEST_ASSERT_3(res.a0 == SMCCC_RET_NOT_SUPPORTED, - res.a0, hc_info->func_id, hc_info->arg1); + __GUEST_ASSERT(res.a0 == SMCCC_RET_NOT_SUPPORTED, + "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u", + res.a0, hc_info->func_id, hc_info->arg1, stage); break; case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: - GUEST_ASSERT_3(res.a0 != SMCCC_RET_NOT_SUPPORTED, - res.a0, hc_info->func_id, hc_info->arg1); + __GUEST_ASSERT(res.a0 != SMCCC_RET_NOT_SUPPORTED, + "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u", + res.a0, hc_info->func_id, hc_info->arg1, stage); break; default: - GUEST_ASSERT_1(0, stage); + GUEST_FAIL("Unexpected stage = %u", stage); } } } @@ -132,7 +133,7 @@ static void guest_code(void) guest_test_hvc(false_hvc_info); break; default: - GUEST_ASSERT_1(0, stage); + GUEST_FAIL("Unexpected stage = %u", stage); } GUEST_SYNC(stage); @@ -290,10 +291,7 @@ static void test_run(void) guest_done = true; break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_N(uc, "values: 0x%lx, 0x%lx; 0x%lx, stage: %u", - GUEST_ASSERT_ARG(uc, 0), - GUEST_ASSERT_ARG(uc, 1), - GUEST_ASSERT_ARG(uc, 2), stage); + REPORT_GUEST_ASSERT(uc); break; default: TEST_FAIL("Unexpected guest exit\n"); diff --git a/tools/testing/selftests/kvm/aarch64/page_fault_test.c b/tools/testing/selftests/kvm/aarch64/page_fault_test.c index df10f1ffa20d..47bb914ab2fa 100644 --- a/tools/testing/selftests/kvm/aarch64/page_fault_test.c +++ b/tools/testing/selftests/kvm/aarch64/page_fault_test.c @@ -7,7 +7,6 @@ * hugetlbfs with a hole). It checks that the expected handling method is * called (e.g., uffd faults with the right address and write/read flag). */ - #define _GNU_SOURCE #include <linux/bitmap.h> #include <fcntl.h> @@ -293,12 +292,12 @@ static void guest_code(struct test_desc *test) static void no_dabt_handler(struct ex_regs *regs) { - GUEST_ASSERT_1(false, read_sysreg(far_el1)); + GUEST_FAIL("Unexpected dabt, far_el1 = 0x%llx", read_sysreg(far_el1)); } static void no_iabt_handler(struct ex_regs *regs) { - GUEST_ASSERT_1(false, regs->pc); + GUEST_FAIL("Unexpected iabt, pc = 0x%lx", regs->pc); } static struct uffd_args { @@ -318,7 +317,7 @@ static int uffd_generic_handler(int uffd_mode, int uffd, struct uffd_msg *msg, TEST_ASSERT(uffd_mode == UFFDIO_REGISTER_MODE_MISSING, "The only expected UFFD mode is MISSING"); - ASSERT_EQ(addr, (uint64_t)args->hva); + TEST_ASSERT_EQ(addr, (uint64_t)args->hva); pr_debug("uffd fault: addr=%p write=%d\n", (void *)addr, !!(flags & UFFD_PAGEFAULT_FLAG_WRITE)); @@ -432,7 +431,7 @@ static void mmio_on_test_gpa_handler(struct kvm_vm *vm, struct kvm_run *run) region = vm_get_mem_region(vm, MEM_REGION_TEST_DATA); hva = (void *)region->region.userspace_addr; - ASSERT_EQ(run->mmio.phys_addr, region->region.guest_phys_addr); + TEST_ASSERT_EQ(run->mmio.phys_addr, region->region.guest_phys_addr); memcpy(hva, run->mmio.data, run->mmio.len); events.mmio_exits += 1; @@ -631,9 +630,9 @@ static void setup_default_handlers(struct test_desc *test) static void check_event_counts(struct test_desc *test) { - ASSERT_EQ(test->expected_events.uffd_faults, events.uffd_faults); - ASSERT_EQ(test->expected_events.mmio_exits, events.mmio_exits); - ASSERT_EQ(test->expected_events.fail_vcpu_runs, events.fail_vcpu_runs); + TEST_ASSERT_EQ(test->expected_events.uffd_faults, events.uffd_faults); + TEST_ASSERT_EQ(test->expected_events.mmio_exits, events.mmio_exits); + TEST_ASSERT_EQ(test->expected_events.fail_vcpu_runs, events.fail_vcpu_runs); } static void print_test_banner(enum vm_guest_mode mode, struct test_params *p) @@ -679,7 +678,7 @@ static void vcpu_run_loop(struct kvm_vm *vm, struct kvm_vcpu *vcpu, } break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 90d854e0fcff..2e64b4856e38 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -7,7 +7,6 @@ * host to inject a specific intid via a GUEST_SYNC call, and then checks that * it received it. */ - #include <asm/kvm.h> #include <asm/kvm_para.h> #include <sys/eventfd.h> @@ -781,7 +780,7 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) run_guest_cmd(vcpu, gic_fd, &inject_args, &args); break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/get-reg-list.c b/tools/testing/selftests/kvm/get-reg-list.c new file mode 100644 index 000000000000..be7bf5224434 --- /dev/null +++ b/tools/testing/selftests/kvm/get-reg-list.c @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Check for KVM_GET_REG_LIST regressions. + * + * Copyright (C) 2020, Red Hat, Inc. + * + * When attempting to migrate from a host with an older kernel to a host + * with a newer kernel we allow the newer kernel on the destination to + * list new registers with get-reg-list. We assume they'll be unused, at + * least until the guest reboots, and so they're relatively harmless. + * However, if the destination host with the newer kernel is missing + * registers which the source host with the older kernel has, then that's + * a regression in get-reg-list. This test checks for that regression by + * checking the current list against a blessed list. We should never have + * missing registers, but if new ones appear then they can probably be + * added to the blessed list. A completely new blessed list can be created + * by running the test with the --list command line argument. + * + * The blessed list should be created from the oldest possible kernel. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include "kvm_util.h" +#include "test_util.h" +#include "processor.h" + +static struct kvm_reg_list *reg_list; +static __u64 *blessed_reg, blessed_n; + +extern struct vcpu_reg_list *vcpu_configs[]; +extern int vcpu_configs_n; + +#define for_each_reg(i) \ + for ((i) = 0; (i) < reg_list->n; ++(i)) + +#define for_each_reg_filtered(i) \ + for_each_reg(i) \ + if (!filter_reg(reg_list->reg[i])) + +#define for_each_missing_reg(i) \ + for ((i) = 0; (i) < blessed_n; ++(i)) \ + if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ + if (check_supported_reg(vcpu, blessed_reg[i])) + +#define for_each_new_reg(i) \ + for_each_reg_filtered(i) \ + if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) + +#define for_each_present_blessed_reg(i) \ + for_each_reg(i) \ + if (find_reg(blessed_reg, blessed_n, reg_list->reg[i])) + +static const char *config_name(struct vcpu_reg_list *c) +{ + struct vcpu_reg_sublist *s; + int len = 0; + + if (c->name) + return c->name; + + for_each_sublist(c, s) + len += strlen(s->name) + 1; + + c->name = malloc(len); + + len = 0; + for_each_sublist(c, s) { + if (!strcmp(s->name, "base")) + continue; + strcat(c->name + len, s->name); + len += strlen(s->name) + 1; + c->name[len - 1] = '+'; + } + c->name[len - 1] = '\0'; + + return c->name; +} + +bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) +{ + return true; +} + +bool __weak filter_reg(__u64 reg) +{ + return false; +} + +static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) +{ + int i; + + for (i = 0; i < nr_regs; ++i) + if (reg == regs[i]) + return true; + return false; +} + +void __weak print_reg(const char *prefix, __u64 id) +{ + printf("\t0x%llx,\n", id); +} + +bool __weak check_reject_set(int err) +{ + return true; +} + +void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) +{ +} + +#ifdef __aarch64__ +static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init) +{ + struct vcpu_reg_sublist *s; + + for_each_sublist(c, s) + if (s->capability) + init->features[s->feature / 32] |= 1 << (s->feature % 32); +} + +static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) +{ + struct kvm_vcpu_init init = { .target = -1, }; + struct kvm_vcpu *vcpu; + + prepare_vcpu_init(c, &init); + vcpu = __vm_vcpu_add(vm, 0); + aarch64_vcpu_setup(vcpu, &init); + + return vcpu; +} +#else +static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) +{ + return __vm_vcpu_add(vm, 0); +} +#endif + +static void check_supported(struct vcpu_reg_list *c) +{ + struct vcpu_reg_sublist *s; + + for_each_sublist(c, s) { + if (!s->capability) + continue; + + __TEST_REQUIRE(kvm_has_cap(s->capability), + "%s: %s not available, skipping tests\n", + config_name(c), s->name); + } +} + +static bool print_list; +static bool print_filtered; + +static void run_test(struct vcpu_reg_list *c) +{ + int new_regs = 0, missing_regs = 0, i, n; + int failed_get = 0, failed_set = 0, failed_reject = 0; + int skipped_set = 0; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + struct vcpu_reg_sublist *s; + + check_supported(c); + + vm = vm_create_barebones(); + vcpu = vcpu_config_get_vcpu(c, vm); + finalize_vcpu(vcpu, c); + + reg_list = vcpu_get_reg_list(vcpu); + + if (print_list || print_filtered) { + putchar('\n'); + for_each_reg(i) { + __u64 id = reg_list->reg[i]; + if ((print_list && !filter_reg(id)) || + (print_filtered && filter_reg(id))) + print_reg(config_name(c), id); + } + putchar('\n'); + return; + } + + for_each_sublist(c, s) + blessed_n += s->regs_n; + blessed_reg = calloc(blessed_n, sizeof(__u64)); + + n = 0; + for_each_sublist(c, s) { + for (i = 0; i < s->regs_n; ++i) + blessed_reg[n++] = s->regs[i]; + } + + /* + * We only test that we can get the register and then write back the + * same value. Some registers may allow other values to be written + * back, but others only allow some bits to be changed, and at least + * for ID registers set will fail if the value does not exactly match + * what was returned by get. If registers that allow other values to + * be written need to have the other values tested, then we should + * create a new set of tests for those in a new independent test + * executable. + * + * Only do the get/set tests on present, blessed list registers, + * since we don't know the capabilities of any new registers. + */ + for_each_present_blessed_reg(i) { + uint8_t addr[2048 / 8]; + struct kvm_one_reg reg = { + .id = reg_list->reg[i], + .addr = (__u64)&addr, + }; + bool reject_reg = false, skip_reg = false; + int ret; + + ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); + if (ret) { + printf("%s: Failed to get ", config_name(c)); + print_reg(config_name(c), reg.id); + putchar('\n'); + ++failed_get; + } + + for_each_sublist(c, s) { + /* rejects_set registers are rejected for set operation */ + if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { + reject_reg = true; + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); + if (ret != -1 || !check_reject_set(errno)) { + printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); + print_reg(config_name(c), reg.id); + putchar('\n'); + ++failed_reject; + } + break; + } + + /* skips_set registers are skipped for set operation */ + if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) { + skip_reg = true; + ++skipped_set; + break; + } + } + + if (!reject_reg && !skip_reg) { + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); + if (ret) { + printf("%s: Failed to set ", config_name(c)); + print_reg(config_name(c), reg.id); + putchar('\n'); + ++failed_set; + } + } + } + + for_each_new_reg(i) + ++new_regs; + + for_each_missing_reg(i) + ++missing_regs; + + if (new_regs || missing_regs) { + n = 0; + for_each_reg_filtered(i) + ++n; + + printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); + printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", + config_name(c), reg_list->n, reg_list->n - n); + } + + if (new_regs) { + printf("\n%s: There are %d new registers.\n" + "Consider adding them to the blessed reg " + "list with the following lines:\n\n", config_name(c), new_regs); + for_each_new_reg(i) + print_reg(config_name(c), reg_list->reg[i]); + putchar('\n'); + } + + if (missing_regs) { + printf("\n%s: There are %d missing registers.\n" + "The following lines are missing registers:\n\n", config_name(c), missing_regs); + for_each_missing_reg(i) + print_reg(config_name(c), blessed_reg[i]); + putchar('\n'); + } + + TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, + "%s: There are %d missing registers; %d registers failed get; " + "%d registers failed set; %d registers failed reject; %d registers skipped set", + config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set); + + pr_info("%s: PASS\n", config_name(c)); + blessed_n = 0; + free(blessed_reg); + free(reg_list); + kvm_vm_free(vm); +} + +static void help(void) +{ + struct vcpu_reg_list *c; + int i; + + printf( + "\n" + "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n" + " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" + " '<selection>' may be\n"); + + for (i = 0; i < vcpu_configs_n; ++i) { + c = vcpu_configs[i]; + printf( + " '%s'\n", config_name(c)); + } + + printf( + "\n" + " --list Print the register list rather than test it (requires --config)\n" + " --list-filtered Print registers that would normally be filtered out (requires --config)\n" + "\n" + ); +} + +static struct vcpu_reg_list *parse_config(const char *config) +{ + struct vcpu_reg_list *c = NULL; + int i; + + if (config[8] != '=') + help(), exit(1); + + for (i = 0; i < vcpu_configs_n; ++i) { + c = vcpu_configs[i]; + if (strcmp(config_name(c), &config[9]) == 0) + break; + } + + if (i == vcpu_configs_n) + help(), exit(1); + + return c; +} + +int main(int ac, char **av) +{ + struct vcpu_reg_list *c, *sel = NULL; + int i, ret = 0; + pid_t pid; + + for (i = 1; i < ac; ++i) { + if (strncmp(av[i], "--config", 8) == 0) + sel = parse_config(av[i]); + else if (strcmp(av[i], "--list") == 0) + print_list = true; + else if (strcmp(av[i], "--list-filtered") == 0) + print_filtered = true; + else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) + help(), exit(0); + else + help(), exit(1); + } + + if (print_list || print_filtered) { + /* + * We only want to print the register list of a single config. + */ + if (!sel) + help(), exit(1); + } + + for (i = 0; i < vcpu_configs_n; ++i) { + c = vcpu_configs[i]; + if (sel && c != sel) + continue; + + pid = fork(); + + if (!pid) { + run_test(c); + exit(0); + } else { + int wstatus; + pid_t wpid = wait(&wstatus); + TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); + if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) + ret = KSFT_FAIL; + } + } + + return ret; +} diff --git a/tools/testing/selftests/kvm/guest_print_test.c b/tools/testing/selftests/kvm/guest_print_test.c new file mode 100644 index 000000000000..41230b746190 --- /dev/null +++ b/tools/testing/selftests/kvm/guest_print_test.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A test for GUEST_PRINTF + * + * Copyright 2022, Google, Inc. and/or its affiliates. + */ +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +struct guest_vals { + uint64_t a; + uint64_t b; + uint64_t type; +}; + +static struct guest_vals vals; + +/* GUEST_PRINTF()/GUEST_ASSERT_FMT() does not support float or double. */ +#define TYPE_LIST \ +TYPE(test_type_i64, I64, "%ld", int64_t) \ +TYPE(test_type_u64, U64u, "%lu", uint64_t) \ +TYPE(test_type_x64, U64x, "0x%lx", uint64_t) \ +TYPE(test_type_X64, U64X, "0x%lX", uint64_t) \ +TYPE(test_type_u32, U32u, "%u", uint32_t) \ +TYPE(test_type_x32, U32x, "0x%x", uint32_t) \ +TYPE(test_type_X32, U32X, "0x%X", uint32_t) \ +TYPE(test_type_int, INT, "%d", int) \ +TYPE(test_type_char, CHAR, "%c", char) \ +TYPE(test_type_str, STR, "'%s'", const char *) \ +TYPE(test_type_ptr, PTR, "%p", uintptr_t) + +enum args_type { +#define TYPE(fn, ext, fmt_t, T) TYPE_##ext, + TYPE_LIST +#undef TYPE +}; + +static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf, + const char *expected_assert); + +#define BUILD_TYPE_STRINGS_AND_HELPER(fn, ext, fmt_t, T) \ +const char *PRINTF_FMT_##ext = "Got params a = " fmt_t " and b = " fmt_t; \ +const char *ASSERT_FMT_##ext = "Expected " fmt_t ", got " fmt_t " instead"; \ +static void fn(struct kvm_vcpu *vcpu, T a, T b) \ +{ \ + char expected_printf[UCALL_BUFFER_LEN]; \ + char expected_assert[UCALL_BUFFER_LEN]; \ + \ + snprintf(expected_printf, UCALL_BUFFER_LEN, PRINTF_FMT_##ext, a, b); \ + snprintf(expected_assert, UCALL_BUFFER_LEN, ASSERT_FMT_##ext, a, b); \ + vals = (struct guest_vals){ (uint64_t)a, (uint64_t)b, TYPE_##ext }; \ + sync_global_to_guest(vcpu->vm, vals); \ + run_test(vcpu, expected_printf, expected_assert); \ +} + +#define TYPE(fn, ext, fmt_t, T) \ + BUILD_TYPE_STRINGS_AND_HELPER(fn, ext, fmt_t, T) + TYPE_LIST +#undef TYPE + +static void guest_code(void) +{ + while (1) { + switch (vals.type) { +#define TYPE(fn, ext, fmt_t, T) \ + case TYPE_##ext: \ + GUEST_PRINTF(PRINTF_FMT_##ext, vals.a, vals.b); \ + __GUEST_ASSERT(vals.a == vals.b, \ + ASSERT_FMT_##ext, vals.a, vals.b); \ + break; + TYPE_LIST +#undef TYPE + default: + GUEST_SYNC(vals.type); + } + + GUEST_DONE(); + } +} + +/* + * Unfortunately this gets a little messy because 'assert_msg' doesn't + * just contains the matching string, it also contains additional assert + * info. Fortunately the part that matches should be at the very end of + * 'assert_msg'. + */ +static void ucall_abort(const char *assert_msg, const char *expected_assert_msg) +{ + int len_str = strlen(assert_msg); + int len_substr = strlen(expected_assert_msg); + int offset = len_str - len_substr; + + TEST_ASSERT(len_substr <= len_str, + "Expected '%s' to be a substring of '%s'\n", + assert_msg, expected_assert_msg); + + TEST_ASSERT(strcmp(&assert_msg[offset], expected_assert_msg) == 0, + "Unexpected mismatch. Expected: '%s', got: '%s'", + expected_assert_msg, &assert_msg[offset]); +} + +static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf, + const char *expected_assert) +{ + struct kvm_run *run = vcpu->run; + struct ucall uc; + + while (1) { + vcpu_run(vcpu); + + TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, exit_reason_str(run->exit_reason)); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + TEST_FAIL("Unknown 'args_type' = %lu", uc.args[1]); + break; + case UCALL_PRINTF: + TEST_ASSERT(strcmp(uc.buffer, expected_printf) == 0, + "Unexpected mismatch. Expected: '%s', got: '%s'", + expected_printf, uc.buffer); + break; + case UCALL_ABORT: + ucall_abort(uc.buffer, expected_assert); + break; + case UCALL_DONE: + return; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + } + } +} + +static void guest_code_limits(void) +{ + char test_str[UCALL_BUFFER_LEN + 10]; + + memset(test_str, 'a', sizeof(test_str)); + test_str[sizeof(test_str) - 1] = 0; + + GUEST_PRINTF("%s", test_str); +} + +static void test_limits(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + struct ucall uc; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code_limits); + run = vcpu->run; + vcpu_run(vcpu); + + TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, exit_reason_str(run->exit_reason)); + + TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_ABORT, + "Unexpected ucall command: %lu, Expected: %u (UCALL_ABORT)\n", + uc.cmd, UCALL_ABORT); + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + test_type_i64(vcpu, -1, -1); + test_type_i64(vcpu, -1, 1); + test_type_i64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef); + test_type_i64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee); + + test_type_u64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef); + test_type_u64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee); + test_type_x64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef); + test_type_x64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee); + test_type_X64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef); + test_type_X64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee); + + test_type_u32(vcpu, 0x90abcdef, 0x90abcdef); + test_type_u32(vcpu, 0x90abcdef, 0x90abcdee); + test_type_x32(vcpu, 0x90abcdef, 0x90abcdef); + test_type_x32(vcpu, 0x90abcdef, 0x90abcdee); + test_type_X32(vcpu, 0x90abcdef, 0x90abcdef); + test_type_X32(vcpu, 0x90abcdef, 0x90abcdee); + + test_type_int(vcpu, -1, -1); + test_type_int(vcpu, -1, 1); + test_type_int(vcpu, 1, 1); + + test_type_char(vcpu, 'a', 'a'); + test_type_char(vcpu, 'a', 'A'); + test_type_char(vcpu, 'a', 'b'); + + test_type_str(vcpu, "foo", "foo"); + test_type_str(vcpu, "foo", "bar"); + + test_type_ptr(vcpu, 0x1234567890abcdef, 0x1234567890abcdef); + test_type_ptr(vcpu, 0x1234567890abcdef, 0x1234567890abcdee); + + kvm_vm_free(vm); + + test_limits(); + + return 0; +} diff --git a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h index cb7c03de3a21..b3e97525cb55 100644 --- a/tools/testing/selftests/kvm/include/aarch64/arch_timer.h +++ b/tools/testing/selftests/kvm/include/aarch64/arch_timer.h @@ -41,7 +41,7 @@ static inline uint64_t timer_get_cntct(enum arch_timer timer) case PHYSICAL: return read_sysreg(cntpct_el0); default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } /* We should not reach here */ @@ -58,7 +58,7 @@ static inline void timer_set_cval(enum arch_timer timer, uint64_t cval) write_sysreg(cval, cntp_cval_el0); break; default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } isb(); @@ -72,7 +72,7 @@ static inline uint64_t timer_get_cval(enum arch_timer timer) case PHYSICAL: return read_sysreg(cntp_cval_el0); default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } /* We should not reach here */ @@ -89,7 +89,7 @@ static inline void timer_set_tval(enum arch_timer timer, uint32_t tval) write_sysreg(tval, cntp_tval_el0); break; default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } isb(); @@ -105,7 +105,7 @@ static inline void timer_set_ctl(enum arch_timer timer, uint32_t ctl) write_sysreg(ctl, cntp_ctl_el0); break; default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } isb(); @@ -119,7 +119,7 @@ static inline uint32_t timer_get_ctl(enum arch_timer timer) case PHYSICAL: return read_sysreg(cntp_ctl_el0); default: - GUEST_ASSERT_1(0, timer); + GUEST_FAIL("Unexpected timer type = %u", timer); } /* We should not reach here */ diff --git a/tools/testing/selftests/kvm/include/aarch64/ucall.h b/tools/testing/selftests/kvm/include/aarch64/ucall.h new file mode 100644 index 000000000000..4b68f37efd36 --- /dev/null +++ b/tools/testing/selftests/kvm/include/aarch64/ucall.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_UCALL_H +#define SELFTEST_KVM_UCALL_H + +#include "kvm_util_base.h" + +#define UCALL_EXIT_REASON KVM_EXIT_MMIO + +/* + * ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each + * VM), it must not be accessed from host code. + */ +extern vm_vaddr_t *ucall_exit_mmio_addr; + +static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +{ + WRITE_ONCE(*ucall_exit_mmio_addr, uc); +} + +#endif diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h index eb1ff597bcca..a18db6a7b3cf 100644 --- a/tools/testing/selftests/kvm/include/kvm_util_base.h +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/kvm.h> #include "linux/rbtree.h" +#include <linux/types.h> #include <asm/atomic.h> @@ -124,6 +125,26 @@ struct kvm_vm { uint32_t memslots[NR_MEM_REGIONS]; }; +struct vcpu_reg_sublist { + const char *name; + long capability; + int feature; + bool finalize; + __u64 *regs; + __u64 regs_n; + __u64 *rejects_set; + __u64 rejects_set_n; + __u64 *skips_set; + __u64 skips_set_n; +}; + +struct vcpu_reg_list { + char *name; + struct vcpu_reg_sublist sublists[]; +}; + +#define for_each_sublist(c, s) \ + for ((s) = &(c)->sublists[0]; (s)->regs; ++(s)) #define kvm_for_each_vcpu(vm, i, vcpu) \ for ((i) = 0; (i) <= (vm)->last_vcpu_id; (i)++) \ diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h index d00d213c3805..5b62a3d2aa9b 100644 --- a/tools/testing/selftests/kvm/include/riscv/processor.h +++ b/tools/testing/selftests/kvm/include/riscv/processor.h @@ -38,6 +38,9 @@ static inline uint64_t __kvm_reg_id(uint64_t type, uint64_t idx, KVM_REG_RISCV_TIMER_REG(name), \ KVM_REG_SIZE_U64) +#define RISCV_ISA_EXT_REG(idx) __kvm_reg_id(KVM_REG_RISCV_ISA_EXT, \ + idx, KVM_REG_SIZE_ULONG) + /* L3 index Bit[47:39] */ #define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL #define PGTBL_L3_INDEX_SHIFT 39 diff --git a/tools/testing/selftests/kvm/include/riscv/ucall.h b/tools/testing/selftests/kvm/include/riscv/ucall.h new file mode 100644 index 000000000000..be46eb32ec27 --- /dev/null +++ b/tools/testing/selftests/kvm/include/riscv/ucall.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_UCALL_H +#define SELFTEST_KVM_UCALL_H + +#include "processor.h" + +#define UCALL_EXIT_REASON KVM_EXIT_RISCV_SBI + +static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +{ +} + +static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +{ + sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT, + KVM_RISCV_SELFTESTS_SBI_UCALL, + uc, 0, 0, 0, 0, 0); +} + +#endif diff --git a/tools/testing/selftests/kvm/include/s390x/ucall.h b/tools/testing/selftests/kvm/include/s390x/ucall.h new file mode 100644 index 000000000000..b231bf2e49d6 --- /dev/null +++ b/tools/testing/selftests/kvm/include/s390x/ucall.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_UCALL_H +#define SELFTEST_KVM_UCALL_H + +#include "kvm_util_base.h" + +#define UCALL_EXIT_REASON KVM_EXIT_S390_SIEIC + +static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +{ +} + +static inline void ucall_arch_do_ucall(vm_vaddr_t uc) +{ + /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */ + asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory"); +} + +#endif diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index a6e9f215ce70..7e614adc6cf4 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -53,14 +53,13 @@ void test_assert(bool exp, const char *exp_str, #define TEST_ASSERT(e, fmt, ...) \ test_assert((e), #e, __FILE__, __LINE__, fmt, ##__VA_ARGS__) -#define ASSERT_EQ(a, b) do { \ - typeof(a) __a = (a); \ - typeof(b) __b = (b); \ - TEST_ASSERT(__a == __b, \ - "ASSERT_EQ(%s, %s) failed.\n" \ - "\t%s is %#lx\n" \ - "\t%s is %#lx", \ - #a, #b, #a, (unsigned long) __a, #b, (unsigned long) __b); \ +#define TEST_ASSERT_EQ(a, b) \ +do { \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + test_assert(__a == __b, #a " == " #b, __FILE__, __LINE__, \ + "%#lx != %#lx (%s != %s)", \ + (unsigned long)(__a), (unsigned long)(__b), #a, #b);\ } while (0) #define TEST_ASSERT_KVM_EXIT_REASON(vcpu, expected) do { \ @@ -186,4 +185,9 @@ static inline uint32_t atoi_non_negative(const char *name, const char *num_str) return num; } +int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args); +int guest_snprintf(char *buf, int n, const char *fmt, ...); + +char *strdup_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2), nonnull(1))); + #endif /* SELFTEST_KVM_TEST_UTIL_H */ diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h index 1a6aaef5ccae..112bc1da732a 100644 --- a/tools/testing/selftests/kvm/include/ucall_common.h +++ b/tools/testing/selftests/kvm/include/ucall_common.h @@ -7,21 +7,25 @@ #ifndef SELFTEST_KVM_UCALL_COMMON_H #define SELFTEST_KVM_UCALL_COMMON_H #include "test_util.h" +#include "ucall.h" /* Common ucalls */ enum { UCALL_NONE, UCALL_SYNC, UCALL_ABORT, + UCALL_PRINTF, UCALL_DONE, UCALL_UNHANDLED, }; #define UCALL_MAX_ARGS 7 +#define UCALL_BUFFER_LEN 1024 struct ucall { uint64_t cmd; uint64_t args[UCALL_MAX_ARGS]; + char buffer[UCALL_BUFFER_LEN]; /* Host virtual address of this struct. */ struct ucall *hva; @@ -32,8 +36,12 @@ void ucall_arch_do_ucall(vm_vaddr_t uc); void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu); void ucall(uint64_t cmd, int nargs, ...); +void ucall_fmt(uint64_t cmd, const char *fmt, ...); +void ucall_assert(uint64_t cmd, const char *exp, const char *file, + unsigned int line, const char *fmt, ...); uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc); void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa); +int ucall_nr_pages_required(uint64_t page_size); /* * Perform userspace call without any associated data. This bare call avoids @@ -46,8 +54,11 @@ void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa); #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \ ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4) #define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage) +#define GUEST_PRINTF(_fmt, _args...) ucall_fmt(UCALL_PRINTF, _fmt, ##_args) #define GUEST_DONE() ucall(UCALL_DONE, 0) +#define REPORT_GUEST_PRINTF(ucall) pr_info("%s", (ucall).buffer) + enum guest_assert_builtin_args { GUEST_ERROR_STRING, GUEST_FILE, @@ -55,70 +66,41 @@ enum guest_assert_builtin_args { GUEST_ASSERT_BUILTIN_NARGS }; -#define __GUEST_ASSERT(_condition, _condstr, _nargs, _args...) \ -do { \ - if (!(_condition)) \ - ucall(UCALL_ABORT, GUEST_ASSERT_BUILTIN_NARGS + _nargs, \ - "Failed guest assert: " _condstr, \ - __FILE__, __LINE__, ##_args); \ +#define ____GUEST_ASSERT(_condition, _exp, _fmt, _args...) \ +do { \ + if (!(_condition)) \ + ucall_assert(UCALL_ABORT, _exp, __FILE__, __LINE__, _fmt, ##_args); \ } while (0) -#define GUEST_ASSERT(_condition) \ - __GUEST_ASSERT(_condition, #_condition, 0, 0) - -#define GUEST_ASSERT_1(_condition, arg1) \ - __GUEST_ASSERT(_condition, #_condition, 1, (arg1)) - -#define GUEST_ASSERT_2(_condition, arg1, arg2) \ - __GUEST_ASSERT(_condition, #_condition, 2, (arg1), (arg2)) - -#define GUEST_ASSERT_3(_condition, arg1, arg2, arg3) \ - __GUEST_ASSERT(_condition, #_condition, 3, (arg1), (arg2), (arg3)) - -#define GUEST_ASSERT_4(_condition, arg1, arg2, arg3, arg4) \ - __GUEST_ASSERT(_condition, #_condition, 4, (arg1), (arg2), (arg3), (arg4)) - -#define GUEST_ASSERT_EQ(a, b) __GUEST_ASSERT((a) == (b), #a " == " #b, 2, a, b) +#define __GUEST_ASSERT(_condition, _fmt, _args...) \ + ____GUEST_ASSERT(_condition, #_condition, _fmt, ##_args) -#define __REPORT_GUEST_ASSERT(_ucall, fmt, _args...) \ - TEST_FAIL("%s at %s:%ld\n" fmt, \ - (const char *)(_ucall).args[GUEST_ERROR_STRING], \ - (const char *)(_ucall).args[GUEST_FILE], \ - (_ucall).args[GUEST_LINE], \ - ##_args) +#define GUEST_ASSERT(_condition) \ + __GUEST_ASSERT(_condition, #_condition) -#define GUEST_ASSERT_ARG(ucall, i) ((ucall).args[GUEST_ASSERT_BUILTIN_NARGS + i]) +#define GUEST_FAIL(_fmt, _args...) \ + ucall_assert(UCALL_ABORT, "Unconditional guest failure", \ + __FILE__, __LINE__, _fmt, ##_args) -#define REPORT_GUEST_ASSERT(ucall) \ - __REPORT_GUEST_ASSERT((ucall), "") - -#define REPORT_GUEST_ASSERT_1(ucall, fmt) \ - __REPORT_GUEST_ASSERT((ucall), \ - fmt, \ - GUEST_ASSERT_ARG((ucall), 0)) - -#define REPORT_GUEST_ASSERT_2(ucall, fmt) \ - __REPORT_GUEST_ASSERT((ucall), \ - fmt, \ - GUEST_ASSERT_ARG((ucall), 0), \ - GUEST_ASSERT_ARG((ucall), 1)) - -#define REPORT_GUEST_ASSERT_3(ucall, fmt) \ - __REPORT_GUEST_ASSERT((ucall), \ - fmt, \ - GUEST_ASSERT_ARG((ucall), 0), \ - GUEST_ASSERT_ARG((ucall), 1), \ - GUEST_ASSERT_ARG((ucall), 2)) +#define GUEST_ASSERT_EQ(a, b) \ +do { \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + ____GUEST_ASSERT(__a == __b, #a " == " #b, "%#lx != %#lx (%s != %s)", \ + (unsigned long)(__a), (unsigned long)(__b), #a, #b); \ +} while (0) -#define REPORT_GUEST_ASSERT_4(ucall, fmt) \ - __REPORT_GUEST_ASSERT((ucall), \ - fmt, \ - GUEST_ASSERT_ARG((ucall), 0), \ - GUEST_ASSERT_ARG((ucall), 1), \ - GUEST_ASSERT_ARG((ucall), 2), \ - GUEST_ASSERT_ARG((ucall), 3)) +#define GUEST_ASSERT_NE(a, b) \ +do { \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + ____GUEST_ASSERT(__a != __b, #a " != " #b, "%#lx == %#lx (%s == %s)", \ + (unsigned long)(__a), (unsigned long)(__b), #a, #b); \ +} while (0) -#define REPORT_GUEST_ASSERT_N(ucall, fmt, args...) \ - __REPORT_GUEST_ASSERT((ucall), fmt, ##args) +#define REPORT_GUEST_ASSERT(ucall) \ + test_assert(false, (const char *)(ucall).args[GUEST_ERROR_STRING], \ + (const char *)(ucall).args[GUEST_FILE], \ + (ucall).args[GUEST_LINE], "%s", (ucall).buffer) #endif /* SELFTEST_KVM_UCALL_COMMON_H */ diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index aa434c8f19c5..4fd042112526 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -239,7 +239,12 @@ struct kvm_x86_cpu_property { #define X86_PROPERTY_MAX_BASIC_LEAF KVM_X86_CPU_PROPERTY(0, 0, EAX, 0, 31) #define X86_PROPERTY_PMU_VERSION KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 0, 7) #define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15) +#define X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 16, 23) #define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31) +#define X86_PROPERTY_PMU_EVENTS_MASK KVM_X86_CPU_PROPERTY(0xa, 0, EBX, 0, 7) +#define X86_PROPERTY_PMU_FIXED_COUNTERS_BITMASK KVM_X86_CPU_PROPERTY(0xa, 0, ECX, 0, 31) +#define X86_PROPERTY_PMU_NR_FIXED_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 0, 4) +#define X86_PROPERTY_PMU_FIXED_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 5, 12) #define X86_PROPERTY_SUPPORTED_XCR0_LO KVM_X86_CPU_PROPERTY(0xd, 0, EAX, 0, 31) #define X86_PROPERTY_XSTATE_MAX_SIZE_XCR0 KVM_X86_CPU_PROPERTY(0xd, 0, EBX, 0, 31) diff --git a/tools/testing/selftests/kvm/include/x86_64/ucall.h b/tools/testing/selftests/kvm/include/x86_64/ucall.h new file mode 100644 index 000000000000..06b244bd06ee --- /dev/null +++ b/tools/testing/selftests/kvm/include/x86_64/ucall.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef SELFTEST_KVM_UCALL_H +#define SELFTEST_KVM_UCALL_H + +#include "kvm_util_base.h" + +#define UCALL_EXIT_REASON KVM_EXIT_IO + +static inline void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) +{ +} + +#endif diff --git a/tools/testing/selftests/kvm/kvm_page_table_test.c b/tools/testing/selftests/kvm/kvm_page_table_test.c index b3b00be1ef82..69f26d80c821 100644 --- a/tools/testing/selftests/kvm/kvm_page_table_test.c +++ b/tools/testing/selftests/kvm/kvm_page_table_test.c @@ -200,7 +200,7 @@ static void *vcpu_worker(void *data) if (READ_ONCE(host_quit)) return NULL; - clock_gettime(CLOCK_MONOTONIC_RAW, &start); + clock_gettime(CLOCK_MONOTONIC, &start); ret = _vcpu_run(vcpu); ts_diff = timespec_elapsed(start); @@ -367,7 +367,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) /* Test the stage of KVM creating mappings */ *current_stage = KVM_CREATE_MAPPINGS; - clock_gettime(CLOCK_MONOTONIC_RAW, &start); + clock_gettime(CLOCK_MONOTONIC, &start); vcpus_complete_new_stage(*current_stage); ts_diff = timespec_elapsed(start); @@ -380,7 +380,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) *current_stage = KVM_UPDATE_MAPPINGS; - clock_gettime(CLOCK_MONOTONIC_RAW, &start); + clock_gettime(CLOCK_MONOTONIC, &start); vcpus_complete_new_stage(*current_stage); ts_diff = timespec_elapsed(start); @@ -392,7 +392,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) *current_stage = KVM_ADJUST_MAPPINGS; - clock_gettime(CLOCK_MONOTONIC_RAW, &start); + clock_gettime(CLOCK_MONOTONIC, &start); vcpus_complete_new_stage(*current_stage); ts_diff = timespec_elapsed(start); diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c index f212bd8ab93d..ddab0ce89d4d 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c @@ -6,11 +6,7 @@ */ #include "kvm_util.h" -/* - * ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each - * VM), it must not be accessed from host code. - */ -static vm_vaddr_t *ucall_exit_mmio_addr; +vm_vaddr_t *ucall_exit_mmio_addr; void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) { @@ -23,11 +19,6 @@ void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gva); } -void ucall_arch_do_ucall(vm_vaddr_t uc) -{ - WRITE_ONCE(*ucall_exit_mmio_addr, uc); -} - void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; diff --git a/tools/testing/selftests/kvm/lib/guest_sprintf.c b/tools/testing/selftests/kvm/lib/guest_sprintf.c new file mode 100644 index 000000000000..c4a69d8aeb68 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/guest_sprintf.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "test_util.h" +#include "kvm_util.h" +#include "ucall_common.h" + +#define APPEND_BUFFER_SAFE(str, end, v) \ +do { \ + GUEST_ASSERT(str < end); \ + *str++ = (v); \ +} while (0) + +static int isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +static int skip_atoi(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SMALL 32 /* Must be 32 == 0x20 */ +#define SPECIAL 64 /* 0x */ + +#define __do_div(n, base) \ +({ \ + int __res; \ + \ + __res = ((uint64_t) n) % (uint32_t) base; \ + n = ((uint64_t) n) / (uint32_t) base; \ + __res; \ +}) + +static char *number(char *str, const char *end, long num, int base, int size, + int precision, int type) +{ + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + + char tmp[66]; + char c, sign, locase; + int i; + + /* + * locase = 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters + */ + locase = (type & SMALL); + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 16) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++] = '0'; + else + while (num != 0) + tmp[i++] = (digits[__do_div(num, base)] | locase); + if (i > precision) + precision = i; + size -= precision; + if (!(type & (ZEROPAD + LEFT))) + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + if (sign) + APPEND_BUFFER_SAFE(str, end, sign); + if (type & SPECIAL) { + if (base == 8) + APPEND_BUFFER_SAFE(str, end, '0'); + else if (base == 16) { + APPEND_BUFFER_SAFE(str, end, '0'); + APPEND_BUFFER_SAFE(str, end, 'x'); + } + } + if (!(type & LEFT)) + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, c); + while (i < precision--) + APPEND_BUFFER_SAFE(str, end, '0'); + while (i-- > 0) + APPEND_BUFFER_SAFE(str, end, tmp[i]); + while (size-- > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + + return str; +} + +int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) +{ + char *str, *end; + const char *s; + uint64_t num; + int i, base; + int len; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* + * min. # of digits for integers; max + * number of chars for from string + */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + end = buf + n; + GUEST_ASSERT(buf < end); + GUEST_ASSERT(n > 0); + + for (str = buf; *fmt; ++fmt) { + if (*fmt != '%') { + APPEND_BUFFER_SAFE(str, end, *fmt); + continue; + } + + /* process flags */ + flags = 0; +repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= SPECIAL; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + APPEND_BUFFER_SAFE(str, end, + (uint8_t)va_arg(args, int)); + while (--field_width > 0) + APPEND_BUFFER_SAFE(str, end, ' '); + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + APPEND_BUFFER_SAFE(str, end, ' '); + for (i = 0; i < len; ++i) + APPEND_BUFFER_SAFE(str, end, *s++); + while (len < field_width--) + APPEND_BUFFER_SAFE(str, end, ' '); + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= SPECIAL | SMALL | ZEROPAD; + } + str = number(str, end, + (uint64_t)va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int *ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + APPEND_BUFFER_SAFE(str, end, '%'); + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'x': + flags |= SMALL; + case 'X': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + APPEND_BUFFER_SAFE(str, end, '%'); + if (*fmt) + APPEND_BUFFER_SAFE(str, end, *fmt); + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, uint64_t); + else if (qualifier == 'h') { + num = (uint16_t)va_arg(args, int); + if (flags & SIGN) + num = (int16_t)num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, uint32_t); + str = number(str, end, num, base, field_width, precision, flags); + } + + GUEST_ASSERT(str < end); + *str = '\0'; + return str - buf; +} + +int guest_snprintf(char *buf, int n, const char *fmt, ...) +{ + va_list va; + int len; + + va_start(va, fmt); + len = guest_vsnprintf(buf, n, fmt, va); + va_end(va); + + return len; +} diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 9741a7ff6380..7a8af1821f5d 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -312,6 +312,7 @@ static uint64_t vm_nr_pages_required(enum vm_guest_mode mode, uint32_t nr_runnable_vcpus, uint64_t extra_mem_pages) { + uint64_t page_size = vm_guest_mode_params[mode].page_size; uint64_t nr_pages; TEST_ASSERT(nr_runnable_vcpus, @@ -340,6 +341,9 @@ static uint64_t vm_nr_pages_required(enum vm_guest_mode mode, */ nr_pages += (nr_pages + extra_mem_pages) / PTES_PER_MIN_PAGE * 2; + /* Account for the number of pages needed by ucall. */ + nr_pages += ucall_nr_pages_required(page_size); + return vm_adjust_num_guest_pages(mode, nr_pages); } @@ -994,7 +998,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, if (src_type == VM_MEM_SRC_ANONYMOUS_THP) alignment = max(backing_src_pagesz, alignment); - ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz)); + TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz)); /* Add enough memory to align up if necessary */ if (alignment > 1) diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c index 9a3476a2dfca..fe6d1004f018 100644 --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c @@ -10,10 +10,6 @@ #include "kvm_util.h" #include "processor.h" -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) -{ -} - struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, @@ -40,13 +36,6 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, return ret; } -void ucall_arch_do_ucall(vm_vaddr_t uc) -{ - sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT, - KVM_RISCV_SELFTESTS_SBI_UCALL, - uc, 0, 0, 0, 0, 0); -} - void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c index a7f02dc372cf..cca98734653d 100644 --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c @@ -6,16 +6,6 @@ */ #include "kvm_util.h" -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) -{ -} - -void ucall_arch_do_ucall(vm_vaddr_t uc) -{ - /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */ - asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory"); -} - void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) { struct kvm_run *run = vcpu->run; diff --git a/tools/testing/selftests/kvm/lib/sparsebit.c b/tools/testing/selftests/kvm/lib/sparsebit.c index 50e0cf41a7dd..88cb6b84e6f3 100644 --- a/tools/testing/selftests/kvm/lib/sparsebit.c +++ b/tools/testing/selftests/kvm/lib/sparsebit.c @@ -634,7 +634,6 @@ static void node_reduce(struct sparsebit *s, struct node *nodep) tmp = node_prev(s, nodep); node_rm(s, nodep); - nodep = NULL; nodep = tmp; reduction_performed = true; diff --git a/tools/testing/selftests/kvm/lib/string_override.c b/tools/testing/selftests/kvm/lib/string_override.c index 632398adc229..5d1c87277c49 100644 --- a/tools/testing/selftests/kvm/lib/string_override.c +++ b/tools/testing/selftests/kvm/lib/string_override.c @@ -37,3 +37,12 @@ void *memset(void *s, int c, size_t count) *xs++ = c; return s; } + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c index b772193f6c18..5d7f28b02d73 100644 --- a/tools/testing/selftests/kvm/lib/test_util.c +++ b/tools/testing/selftests/kvm/lib/test_util.c @@ -5,6 +5,9 @@ * Copyright (C) 2020, Google LLC. */ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> #include <assert.h> #include <ctype.h> #include <limits.h> @@ -377,3 +380,15 @@ int atoi_paranoid(const char *num_str) return num; } + +char *strdup_printf(const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start(ap, fmt); + TEST_ASSERT(vasprintf(&str, fmt, ap) >= 0, "vasprintf() failed"); + va_end(ap); + + return str; +} diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c index 2f0e2ea941cc..816a3fa109bf 100644 --- a/tools/testing/selftests/kvm/lib/ucall_common.c +++ b/tools/testing/selftests/kvm/lib/ucall_common.c @@ -11,6 +11,11 @@ struct ucall_header { struct ucall ucalls[KVM_MAX_VCPUS]; }; +int ucall_nr_pages_required(uint64_t page_size) +{ + return align_up(sizeof(struct ucall_header), page_size) / page_size; +} + /* * ucall_pool holds per-VM values (global data is duplicated by each VM), it * must not be accessed from host code. @@ -70,6 +75,45 @@ static void ucall_free(struct ucall *uc) clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use); } +void ucall_assert(uint64_t cmd, const char *exp, const char *file, + unsigned int line, const char *fmt, ...) +{ + struct ucall *uc; + va_list va; + + uc = ucall_alloc(); + uc->cmd = cmd; + + WRITE_ONCE(uc->args[GUEST_ERROR_STRING], (uint64_t)(exp)); + WRITE_ONCE(uc->args[GUEST_FILE], (uint64_t)(file)); + WRITE_ONCE(uc->args[GUEST_LINE], line); + + va_start(va, fmt); + guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); + va_end(va); + + ucall_arch_do_ucall((vm_vaddr_t)uc->hva); + + ucall_free(uc); +} + +void ucall_fmt(uint64_t cmd, const char *fmt, ...) +{ + struct ucall *uc; + va_list va; + + uc = ucall_alloc(); + uc->cmd = cmd; + + va_start(va, fmt); + guest_vsnprintf(uc->buffer, UCALL_BUFFER_LEN, fmt, va); + va_end(va); + + ucall_arch_do_ucall((vm_vaddr_t)uc->hva); + + ucall_free(uc); +} + void ucall(uint64_t cmd, int nargs, ...) { struct ucall *uc; diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index d4a0b504b1e0..d8288374078e 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -1074,11 +1074,6 @@ static bool kvm_fixup_exception(struct ex_regs *regs) return true; } -void kvm_exit_unexpected_vector(uint32_t value) -{ - ucall(UCALL_UNHANDLED, 1, value); -} - void route_exception(struct ex_regs *regs) { typedef void(*handler)(struct ex_regs *); @@ -1092,7 +1087,10 @@ void route_exception(struct ex_regs *regs) if (kvm_fixup_exception(regs)) return; - kvm_exit_unexpected_vector(regs->vector); + ucall_assert(UCALL_UNHANDLED, + "Unhandled exception in guest", __FILE__, __LINE__, + "Unhandled exception '0x%lx' at guest RIP '0x%lx'", + regs->vector, regs->rip); } void vm_init_descriptor_tables(struct kvm_vm *vm) @@ -1135,12 +1133,8 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) { struct ucall uc; - if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) { - uint64_t vector = uc.args[0]; - - TEST_FAIL("Unexpected vectored event in guest (vector:0x%lx)", - vector); - } + if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) + REPORT_GUEST_ASSERT(uc); } const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c index 4d41dc63cc9e..1265cecc7dd1 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c @@ -8,14 +8,38 @@ #define UCALL_PIO_PORT ((uint16_t)0x1000) -void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) -{ -} - void ucall_arch_do_ucall(vm_vaddr_t uc) { - asm volatile("in %[port], %%al" - : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory"); + /* + * FIXME: Revert this hack (the entire commit that added it) once nVMX + * preserves L2 GPRs across a nested VM-Exit. If a ucall from L2, e.g. + * to do a GUEST_SYNC(), lands the vCPU in L1, any and all GPRs can be + * clobbered by L1. Save and restore non-volatile GPRs (clobbering RBP + * in particular is problematic) along with RDX and RDI (which are + * inputs), and clobber volatile GPRs. *sigh* + */ +#define HORRIFIC_L2_UCALL_CLOBBER_HACK \ + "rcx", "rsi", "r8", "r9", "r10", "r11" + + asm volatile("push %%rbp\n\t" + "push %%r15\n\t" + "push %%r14\n\t" + "push %%r13\n\t" + "push %%r12\n\t" + "push %%rbx\n\t" + "push %%rdx\n\t" + "push %%rdi\n\t" + "in %[port], %%al\n\t" + "pop %%rdi\n\t" + "pop %%rdx\n\t" + "pop %%rbx\n\t" + "pop %%r12\n\t" + "pop %%r13\n\t" + "pop %%r14\n\t" + "pop %%r15\n\t" + "pop %%rbp\n\t" + : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory", + HORRIFIC_L2_UCALL_CLOBBER_HACK); } void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) diff --git a/tools/testing/selftests/kvm/max_guest_memory_test.c b/tools/testing/selftests/kvm/max_guest_memory_test.c index feaf2be20ff2..6628dc4dda89 100644 --- a/tools/testing/selftests/kvm/max_guest_memory_test.c +++ b/tools/testing/selftests/kvm/max_guest_memory_test.c @@ -55,7 +55,7 @@ static void rendezvous_with_boss(void) static void run_vcpu(struct kvm_vcpu *vcpu) { vcpu_run(vcpu); - ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); } static void *vcpu_worker(void *data) diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index 4210cd21d159..20eb2e730800 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -157,7 +157,7 @@ static void *vcpu_worker(void *__data) goto done; break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_1(uc, "val = %lu"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; @@ -560,7 +560,7 @@ static void guest_code_test_memslot_rw(void) ptr < MEM_TEST_GPA + MEM_TEST_SIZE; ptr += page_size) { uint64_t val = *(uint64_t *)ptr; - GUEST_ASSERT_1(val == MEM_TEST_VAL_2, val); + GUEST_ASSERT_EQ(val, MEM_TEST_VAL_2); *(uint64_t *)ptr = 0; } diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c new file mode 100644 index 000000000000..9f99ea42f45f --- /dev/null +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Check for KVM_GET_REG_LIST regressions. + * + * Copyright (c) 2023 Intel Corporation + * + */ +#include <stdio.h> +#include "kvm_util.h" +#include "test_util.h" +#include "processor.h" + +#define REG_MASK (KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK) + +static bool isa_ext_cant_disable[KVM_RISCV_ISA_EXT_MAX]; + +bool filter_reg(__u64 reg) +{ + switch (reg & ~REG_MASK) { + /* + * Same set of ISA_EXT registers are not present on all host because + * ISA_EXT registers are visible to the KVM user space based on the + * ISA extensions available on the host. Also, disabling an ISA + * extension using corresponding ISA_EXT register does not affect + * the visibility of the ISA_EXT register itself. + * + * Based on above, we should filter-out all ISA_EXT registers. + */ + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_A: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_C: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_D: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_F: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_H: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_I: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_M: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVPBMT: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSTC: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVINVAL: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHINTPAUSE: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOM: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOZ: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBB: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_V: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVNAPOT: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBA: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBS: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICNTR: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICSR: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIFENCEI: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHPM: + return true; + /* AIA registers are always available when Ssaia can't be disabled */ + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siselect): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(sieh): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siph): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1h): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2h): + return isa_ext_cant_disable[KVM_RISCV_ISA_EXT_SSAIA]; + default: + break; + } + + return false; +} + +bool check_reject_set(int err) +{ + return err == EINVAL; +} + +static inline bool vcpu_has_ext(struct kvm_vcpu *vcpu, int ext) +{ + int ret; + unsigned long value; + + ret = __vcpu_get_reg(vcpu, RISCV_ISA_EXT_REG(ext), &value); + return (ret) ? false : !!value; +} + +void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) +{ + unsigned long isa_ext_state[KVM_RISCV_ISA_EXT_MAX] = { 0 }; + struct vcpu_reg_sublist *s; + int rc; + + for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) + __vcpu_get_reg(vcpu, RISCV_ISA_EXT_REG(i), &isa_ext_state[i]); + + /* + * Disable all extensions which were enabled by default + * if they were available in the risc-v host. + */ + for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + rc = __vcpu_set_reg(vcpu, RISCV_ISA_EXT_REG(i), 0); + if (rc && isa_ext_state[i]) + isa_ext_cant_disable[i] = true; + } + + for_each_sublist(c, s) { + if (!s->feature) + continue; + + /* Try to enable the desired extension */ + __vcpu_set_reg(vcpu, RISCV_ISA_EXT_REG(s->feature), 1); + + /* Double check whether the desired extension was enabled */ + __TEST_REQUIRE(vcpu_has_ext(vcpu, s->feature), + "%s not available, skipping tests\n", s->name); + } +} + +static const char *config_id_to_str(__u64 id) +{ + /* reg_off is the offset into struct kvm_riscv_config */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CONFIG); + + switch (reg_off) { + case KVM_REG_RISCV_CONFIG_REG(isa): + return "KVM_REG_RISCV_CONFIG_REG(isa)"; + case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): + return "KVM_REG_RISCV_CONFIG_REG(zicbom_block_size)"; + case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): + return "KVM_REG_RISCV_CONFIG_REG(zicboz_block_size)"; + case KVM_REG_RISCV_CONFIG_REG(mvendorid): + return "KVM_REG_RISCV_CONFIG_REG(mvendorid)"; + case KVM_REG_RISCV_CONFIG_REG(marchid): + return "KVM_REG_RISCV_CONFIG_REG(marchid)"; + case KVM_REG_RISCV_CONFIG_REG(mimpid): + return "KVM_REG_RISCV_CONFIG_REG(mimpid)"; + case KVM_REG_RISCV_CONFIG_REG(satp_mode): + return "KVM_REG_RISCV_CONFIG_REG(satp_mode)"; + } + + /* + * Config regs would grow regularly with new pseudo reg added, so + * just show raw id to indicate a new pseudo config reg. + */ + return strdup_printf("KVM_REG_RISCV_CONFIG_REG(%lld) /* UNKNOWN */", reg_off); +} + +static const char *core_id_to_str(const char *prefix, __u64 id) +{ + /* reg_off is the offset into struct kvm_riscv_core */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CORE); + + switch (reg_off) { + case KVM_REG_RISCV_CORE_REG(regs.pc): + return "KVM_REG_RISCV_CORE_REG(regs.pc)"; + case KVM_REG_RISCV_CORE_REG(regs.ra): + return "KVM_REG_RISCV_CORE_REG(regs.ra)"; + case KVM_REG_RISCV_CORE_REG(regs.sp): + return "KVM_REG_RISCV_CORE_REG(regs.sp)"; + case KVM_REG_RISCV_CORE_REG(regs.gp): + return "KVM_REG_RISCV_CORE_REG(regs.gp)"; + case KVM_REG_RISCV_CORE_REG(regs.tp): + return "KVM_REG_RISCV_CORE_REG(regs.tp)"; + case KVM_REG_RISCV_CORE_REG(regs.t0) ... KVM_REG_RISCV_CORE_REG(regs.t2): + return strdup_printf("KVM_REG_RISCV_CORE_REG(regs.t%lld)", + reg_off - KVM_REG_RISCV_CORE_REG(regs.t0)); + case KVM_REG_RISCV_CORE_REG(regs.s0) ... KVM_REG_RISCV_CORE_REG(regs.s1): + return strdup_printf("KVM_REG_RISCV_CORE_REG(regs.s%lld)", + reg_off - KVM_REG_RISCV_CORE_REG(regs.s0)); + case KVM_REG_RISCV_CORE_REG(regs.a0) ... KVM_REG_RISCV_CORE_REG(regs.a7): + return strdup_printf("KVM_REG_RISCV_CORE_REG(regs.a%lld)", + reg_off - KVM_REG_RISCV_CORE_REG(regs.a0)); + case KVM_REG_RISCV_CORE_REG(regs.s2) ... KVM_REG_RISCV_CORE_REG(regs.s11): + return strdup_printf("KVM_REG_RISCV_CORE_REG(regs.s%lld)", + reg_off - KVM_REG_RISCV_CORE_REG(regs.s2) + 2); + case KVM_REG_RISCV_CORE_REG(regs.t3) ... KVM_REG_RISCV_CORE_REG(regs.t6): + return strdup_printf("KVM_REG_RISCV_CORE_REG(regs.t%lld)", + reg_off - KVM_REG_RISCV_CORE_REG(regs.t3) + 3); + case KVM_REG_RISCV_CORE_REG(mode): + return "KVM_REG_RISCV_CORE_REG(mode)"; + } + + TEST_FAIL("%s: Unknown core reg id: 0x%llx", prefix, id); + return NULL; +} + +#define RISCV_CSR_GENERAL(csr) \ + "KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(" #csr ")" +#define RISCV_CSR_AIA(csr) \ + "KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_REG(" #csr ")" + +static const char *general_csr_id_to_str(__u64 reg_off) +{ + /* reg_off is the offset into struct kvm_riscv_csr */ + switch (reg_off) { + case KVM_REG_RISCV_CSR_REG(sstatus): + return RISCV_CSR_GENERAL(sstatus); + case KVM_REG_RISCV_CSR_REG(sie): + return RISCV_CSR_GENERAL(sie); + case KVM_REG_RISCV_CSR_REG(stvec): + return RISCV_CSR_GENERAL(stvec); + case KVM_REG_RISCV_CSR_REG(sscratch): + return RISCV_CSR_GENERAL(sscratch); + case KVM_REG_RISCV_CSR_REG(sepc): + return RISCV_CSR_GENERAL(sepc); + case KVM_REG_RISCV_CSR_REG(scause): + return RISCV_CSR_GENERAL(scause); + case KVM_REG_RISCV_CSR_REG(stval): + return RISCV_CSR_GENERAL(stval); + case KVM_REG_RISCV_CSR_REG(sip): + return RISCV_CSR_GENERAL(sip); + case KVM_REG_RISCV_CSR_REG(satp): + return RISCV_CSR_GENERAL(satp); + case KVM_REG_RISCV_CSR_REG(scounteren): + return RISCV_CSR_GENERAL(scounteren); + } + + TEST_FAIL("Unknown general csr reg: 0x%llx", reg_off); + return NULL; +} + +static const char *aia_csr_id_to_str(__u64 reg_off) +{ + /* reg_off is the offset into struct kvm_riscv_aia_csr */ + switch (reg_off) { + case KVM_REG_RISCV_CSR_AIA_REG(siselect): + return RISCV_CSR_AIA(siselect); + case KVM_REG_RISCV_CSR_AIA_REG(iprio1): + return RISCV_CSR_AIA(iprio1); + case KVM_REG_RISCV_CSR_AIA_REG(iprio2): + return RISCV_CSR_AIA(iprio2); + case KVM_REG_RISCV_CSR_AIA_REG(sieh): + return RISCV_CSR_AIA(sieh); + case KVM_REG_RISCV_CSR_AIA_REG(siph): + return RISCV_CSR_AIA(siph); + case KVM_REG_RISCV_CSR_AIA_REG(iprio1h): + return RISCV_CSR_AIA(iprio1h); + case KVM_REG_RISCV_CSR_AIA_REG(iprio2h): + return RISCV_CSR_AIA(iprio2h); + } + + TEST_FAIL("Unknown aia csr reg: 0x%llx", reg_off); + return NULL; +} + +static const char *csr_id_to_str(const char *prefix, __u64 id) +{ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_CSR); + __u64 reg_subtype = reg_off & KVM_REG_RISCV_SUBTYPE_MASK; + + reg_off &= ~KVM_REG_RISCV_SUBTYPE_MASK; + + switch (reg_subtype) { + case KVM_REG_RISCV_CSR_GENERAL: + return general_csr_id_to_str(reg_off); + case KVM_REG_RISCV_CSR_AIA: + return aia_csr_id_to_str(reg_off); + } + + TEST_FAIL("%s: Unknown csr subtype: 0x%llx", prefix, reg_subtype); + return NULL; +} + +static const char *timer_id_to_str(const char *prefix, __u64 id) +{ + /* reg_off is the offset into struct kvm_riscv_timer */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_TIMER); + + switch (reg_off) { + case KVM_REG_RISCV_TIMER_REG(frequency): + return "KVM_REG_RISCV_TIMER_REG(frequency)"; + case KVM_REG_RISCV_TIMER_REG(time): + return "KVM_REG_RISCV_TIMER_REG(time)"; + case KVM_REG_RISCV_TIMER_REG(compare): + return "KVM_REG_RISCV_TIMER_REG(compare)"; + case KVM_REG_RISCV_TIMER_REG(state): + return "KVM_REG_RISCV_TIMER_REG(state)"; + } + + TEST_FAIL("%s: Unknown timer reg id: 0x%llx", prefix, id); + return NULL; +} + +static const char *fp_f_id_to_str(const char *prefix, __u64 id) +{ + /* reg_off is the offset into struct __riscv_f_ext_state */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_FP_F); + + switch (reg_off) { + case KVM_REG_RISCV_FP_F_REG(f[0]) ... + KVM_REG_RISCV_FP_F_REG(f[31]): + return strdup_printf("KVM_REG_RISCV_FP_F_REG(f[%lld])", reg_off); + case KVM_REG_RISCV_FP_F_REG(fcsr): + return "KVM_REG_RISCV_FP_F_REG(fcsr)"; + } + + TEST_FAIL("%s: Unknown fp_f reg id: 0x%llx", prefix, id); + return NULL; +} + +static const char *fp_d_id_to_str(const char *prefix, __u64 id) +{ + /* reg_off is the offset into struct __riscv_d_ext_state */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_FP_D); + + switch (reg_off) { + case KVM_REG_RISCV_FP_D_REG(f[0]) ... + KVM_REG_RISCV_FP_D_REG(f[31]): + return strdup_printf("KVM_REG_RISCV_FP_D_REG(f[%lld])", reg_off); + case KVM_REG_RISCV_FP_D_REG(fcsr): + return "KVM_REG_RISCV_FP_D_REG(fcsr)"; + } + + TEST_FAIL("%s: Unknown fp_d reg id: 0x%llx", prefix, id); + return NULL; +} + +static const char *isa_ext_id_to_str(__u64 id) +{ + /* reg_off is the offset into unsigned long kvm_isa_ext_arr[] */ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_ISA_EXT); + + static const char * const kvm_isa_ext_reg_name[] = { + "KVM_RISCV_ISA_EXT_A", + "KVM_RISCV_ISA_EXT_C", + "KVM_RISCV_ISA_EXT_D", + "KVM_RISCV_ISA_EXT_F", + "KVM_RISCV_ISA_EXT_H", + "KVM_RISCV_ISA_EXT_I", + "KVM_RISCV_ISA_EXT_M", + "KVM_RISCV_ISA_EXT_SVPBMT", + "KVM_RISCV_ISA_EXT_SSTC", + "KVM_RISCV_ISA_EXT_SVINVAL", + "KVM_RISCV_ISA_EXT_ZIHINTPAUSE", + "KVM_RISCV_ISA_EXT_ZICBOM", + "KVM_RISCV_ISA_EXT_ZICBOZ", + "KVM_RISCV_ISA_EXT_ZBB", + "KVM_RISCV_ISA_EXT_SSAIA", + "KVM_RISCV_ISA_EXT_V", + "KVM_RISCV_ISA_EXT_SVNAPOT", + "KVM_RISCV_ISA_EXT_ZBA", + "KVM_RISCV_ISA_EXT_ZBS", + "KVM_RISCV_ISA_EXT_ZICNTR", + "KVM_RISCV_ISA_EXT_ZICSR", + "KVM_RISCV_ISA_EXT_ZIFENCEI", + "KVM_RISCV_ISA_EXT_ZIHPM", + }; + + if (reg_off >= ARRAY_SIZE(kvm_isa_ext_reg_name)) { + /* + * isa_ext regs would grow regularly with new isa extension added, so + * just show "reg" to indicate a new extension. + */ + return strdup_printf("%lld /* UNKNOWN */", reg_off); + } + + return kvm_isa_ext_reg_name[reg_off]; +} + +static const char *sbi_ext_single_id_to_str(__u64 reg_off) +{ + /* reg_off is KVM_RISCV_SBI_EXT_ID */ + static const char * const kvm_sbi_ext_reg_name[] = { + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_V01", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_TIME", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_IPI", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_RFENCE", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_SRST", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_HSM", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_PMU", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_EXPERIMENTAL", + "KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_VENDOR", + }; + + if (reg_off >= ARRAY_SIZE(kvm_sbi_ext_reg_name)) { + /* + * sbi_ext regs would grow regularly with new sbi extension added, so + * just show "reg" to indicate a new extension. + */ + return strdup_printf("KVM_REG_RISCV_SBI_SINGLE | %lld /* UNKNOWN */", reg_off); + } + + return kvm_sbi_ext_reg_name[reg_off]; +} + +static const char *sbi_ext_multi_id_to_str(__u64 reg_subtype, __u64 reg_off) +{ + if (reg_off > KVM_REG_RISCV_SBI_MULTI_REG_LAST) { + /* + * sbi_ext regs would grow regularly with new sbi extension added, so + * just show "reg" to indicate a new extension. + */ + return strdup_printf("%lld /* UNKNOWN */", reg_off); + } + + switch (reg_subtype) { + case KVM_REG_RISCV_SBI_MULTI_EN: + return strdup_printf("KVM_REG_RISCV_SBI_MULTI_EN | %lld", reg_off); + case KVM_REG_RISCV_SBI_MULTI_DIS: + return strdup_printf("KVM_REG_RISCV_SBI_MULTI_DIS | %lld", reg_off); + } + + return NULL; +} + +static const char *sbi_ext_id_to_str(const char *prefix, __u64 id) +{ + __u64 reg_off = id & ~(REG_MASK | KVM_REG_RISCV_SBI_EXT); + __u64 reg_subtype = reg_off & KVM_REG_RISCV_SUBTYPE_MASK; + + reg_off &= ~KVM_REG_RISCV_SUBTYPE_MASK; + + switch (reg_subtype) { + case KVM_REG_RISCV_SBI_SINGLE: + return sbi_ext_single_id_to_str(reg_off); + case KVM_REG_RISCV_SBI_MULTI_EN: + case KVM_REG_RISCV_SBI_MULTI_DIS: + return sbi_ext_multi_id_to_str(reg_subtype, reg_off); + } + + TEST_FAIL("%s: Unknown sbi ext subtype: 0x%llx", prefix, reg_subtype); + return NULL; +} + +void print_reg(const char *prefix, __u64 id) +{ + const char *reg_size = NULL; + + TEST_ASSERT((id & KVM_REG_ARCH_MASK) == KVM_REG_RISCV, + "%s: KVM_REG_RISCV missing in reg id: 0x%llx", prefix, id); + + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + reg_size = "KVM_REG_SIZE_U32"; + break; + case KVM_REG_SIZE_U64: + reg_size = "KVM_REG_SIZE_U64"; + break; + case KVM_REG_SIZE_U128: + reg_size = "KVM_REG_SIZE_U128"; + break; + default: + TEST_FAIL("%s: Unexpected reg size: 0x%llx in reg id: 0x%llx", + prefix, (id & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT, id); + } + + switch (id & KVM_REG_RISCV_TYPE_MASK) { + case KVM_REG_RISCV_CONFIG: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_CONFIG | %s,\n", + reg_size, config_id_to_str(id)); + break; + case KVM_REG_RISCV_CORE: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_CORE | %s,\n", + reg_size, core_id_to_str(prefix, id)); + break; + case KVM_REG_RISCV_CSR: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_CSR | %s,\n", + reg_size, csr_id_to_str(prefix, id)); + break; + case KVM_REG_RISCV_TIMER: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_TIMER | %s,\n", + reg_size, timer_id_to_str(prefix, id)); + break; + case KVM_REG_RISCV_FP_F: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_FP_F | %s,\n", + reg_size, fp_f_id_to_str(prefix, id)); + break; + case KVM_REG_RISCV_FP_D: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_FP_D | %s,\n", + reg_size, fp_d_id_to_str(prefix, id)); + break; + case KVM_REG_RISCV_ISA_EXT: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_ISA_EXT | %s,\n", + reg_size, isa_ext_id_to_str(id)); + break; + case KVM_REG_RISCV_SBI_EXT: + printf("\tKVM_REG_RISCV | %s | KVM_REG_RISCV_SBI_EXT | %s,\n", + reg_size, sbi_ext_id_to_str(prefix, id)); + break; + default: + TEST_FAIL("%s: Unexpected reg type: 0x%llx in reg id: 0x%llx", prefix, + (id & KVM_REG_RISCV_TYPE_MASK) >> KVM_REG_RISCV_TYPE_SHIFT, id); + } +} + +/* + * The current blessed list was primed with the output of kernel version + * v6.5-rc3 and then later updated with new registers. + */ +static __u64 base_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(isa), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(mvendorid), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(marchid), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(mimpid), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(satp_mode), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.pc), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.ra), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.sp), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.gp), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.tp), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t0), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t1), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t2), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s0), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s1), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a0), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a1), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a2), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a3), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a4), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a5), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a6), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.a7), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s2), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s3), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s4), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s5), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s6), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s7), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s8), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s9), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s10), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.s11), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t3), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t4), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t5), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(regs.t6), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CORE | KVM_REG_RISCV_CORE_REG(mode), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sstatus), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sie), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(stvec), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sscratch), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sepc), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(scause), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(stval), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(sip), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(satp), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_GENERAL | KVM_REG_RISCV_CSR_REG(scounteren), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(frequency), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(time), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(compare), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(state), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_V01, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_TIME, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_IPI, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_RFENCE, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_SRST, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_HSM, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_PMU, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_EXPERIMENTAL, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_VENDOR, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_MULTI_EN | 0, + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_MULTI_DIS | 0, +}; + +/* + * The skips_set list registers that should skip set test. + * - KVM_REG_RISCV_TIMER_REG(state): set would fail if it was not initialized properly. + */ +static __u64 base_skips_set[] = { + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(state), +}; + +static __u64 h_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_H, +}; + +static __u64 zicbom_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(zicbom_block_size), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOM, +}; + +static __u64 zicboz_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CONFIG | KVM_REG_RISCV_CONFIG_REG(zicboz_block_size), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOZ, +}; + +static __u64 svpbmt_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVPBMT, +}; + +static __u64 sstc_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSTC, +}; + +static __u64 svinval_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVINVAL, +}; + +static __u64 zihintpause_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHINTPAUSE, +}; + +static __u64 zba_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBA, +}; + +static __u64 zbb_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBB, +}; + +static __u64 zbs_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBS, +}; + +static __u64 zicntr_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICNTR, +}; + +static __u64 zicsr_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICSR, +}; + +static __u64 zifencei_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIFENCEI, +}; + +static __u64 zihpm_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHPM, +}; + +static __u64 aia_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siselect), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(sieh), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siph), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1h), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2h), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA, +}; + +static __u64 fp_f_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[0]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[1]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[2]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[3]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[4]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[5]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[6]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[7]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[8]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[9]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[10]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[11]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[12]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[13]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[14]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[15]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[16]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[17]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[18]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[19]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[20]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[21]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[22]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[23]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[24]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[25]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[26]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[27]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[28]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[29]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[30]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(f[31]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_F | KVM_REG_RISCV_FP_F_REG(fcsr), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_F, +}; + +static __u64 fp_d_regs[] = { + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[0]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[1]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[2]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[3]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[4]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[5]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[6]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[7]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[8]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[9]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[10]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[11]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[12]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[13]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[14]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[15]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[16]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[17]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[18]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[19]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[20]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[21]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[22]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[23]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[24]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[25]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[26]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[27]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[28]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[29]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[30]), + KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(f[31]), + KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_D | KVM_REG_RISCV_FP_D_REG(fcsr), + KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_D, +}; + +#define BASE_SUBLIST \ + {"base", .regs = base_regs, .regs_n = ARRAY_SIZE(base_regs), \ + .skips_set = base_skips_set, .skips_set_n = ARRAY_SIZE(base_skips_set),} +#define H_REGS_SUBLIST \ + {"h", .feature = KVM_RISCV_ISA_EXT_H, .regs = h_regs, .regs_n = ARRAY_SIZE(h_regs),} +#define ZICBOM_REGS_SUBLIST \ + {"zicbom", .feature = KVM_RISCV_ISA_EXT_ZICBOM, .regs = zicbom_regs, .regs_n = ARRAY_SIZE(zicbom_regs),} +#define ZICBOZ_REGS_SUBLIST \ + {"zicboz", .feature = KVM_RISCV_ISA_EXT_ZICBOZ, .regs = zicboz_regs, .regs_n = ARRAY_SIZE(zicboz_regs),} +#define SVPBMT_REGS_SUBLIST \ + {"svpbmt", .feature = KVM_RISCV_ISA_EXT_SVPBMT, .regs = svpbmt_regs, .regs_n = ARRAY_SIZE(svpbmt_regs),} +#define SSTC_REGS_SUBLIST \ + {"sstc", .feature = KVM_RISCV_ISA_EXT_SSTC, .regs = sstc_regs, .regs_n = ARRAY_SIZE(sstc_regs),} +#define SVINVAL_REGS_SUBLIST \ + {"svinval", .feature = KVM_RISCV_ISA_EXT_SVINVAL, .regs = svinval_regs, .regs_n = ARRAY_SIZE(svinval_regs),} +#define ZIHINTPAUSE_REGS_SUBLIST \ + {"zihintpause", .feature = KVM_RISCV_ISA_EXT_ZIHINTPAUSE, .regs = zihintpause_regs, .regs_n = ARRAY_SIZE(zihintpause_regs),} +#define ZBA_REGS_SUBLIST \ + {"zba", .feature = KVM_RISCV_ISA_EXT_ZBA, .regs = zba_regs, .regs_n = ARRAY_SIZE(zba_regs),} +#define ZBB_REGS_SUBLIST \ + {"zbb", .feature = KVM_RISCV_ISA_EXT_ZBB, .regs = zbb_regs, .regs_n = ARRAY_SIZE(zbb_regs),} +#define ZBS_REGS_SUBLIST \ + {"zbs", .feature = KVM_RISCV_ISA_EXT_ZBS, .regs = zbs_regs, .regs_n = ARRAY_SIZE(zbs_regs),} +#define ZICNTR_REGS_SUBLIST \ + {"zicntr", .feature = KVM_RISCV_ISA_EXT_ZICNTR, .regs = zicntr_regs, .regs_n = ARRAY_SIZE(zicntr_regs),} +#define ZICSR_REGS_SUBLIST \ + {"zicsr", .feature = KVM_RISCV_ISA_EXT_ZICSR, .regs = zicsr_regs, .regs_n = ARRAY_SIZE(zicsr_regs),} +#define ZIFENCEI_REGS_SUBLIST \ + {"zifencei", .feature = KVM_RISCV_ISA_EXT_ZIFENCEI, .regs = zifencei_regs, .regs_n = ARRAY_SIZE(zifencei_regs),} +#define ZIHPM_REGS_SUBLIST \ + {"zihpm", .feature = KVM_RISCV_ISA_EXT_ZIHPM, .regs = zihpm_regs, .regs_n = ARRAY_SIZE(zihpm_regs),} +#define AIA_REGS_SUBLIST \ + {"aia", .feature = KVM_RISCV_ISA_EXT_SSAIA, .regs = aia_regs, .regs_n = ARRAY_SIZE(aia_regs),} +#define FP_F_REGS_SUBLIST \ + {"fp_f", .feature = KVM_RISCV_ISA_EXT_F, .regs = fp_f_regs, \ + .regs_n = ARRAY_SIZE(fp_f_regs),} +#define FP_D_REGS_SUBLIST \ + {"fp_d", .feature = KVM_RISCV_ISA_EXT_D, .regs = fp_d_regs, \ + .regs_n = ARRAY_SIZE(fp_d_regs),} + +static struct vcpu_reg_list h_config = { + .sublists = { + BASE_SUBLIST, + H_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zicbom_config = { + .sublists = { + BASE_SUBLIST, + ZICBOM_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zicboz_config = { + .sublists = { + BASE_SUBLIST, + ZICBOZ_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list svpbmt_config = { + .sublists = { + BASE_SUBLIST, + SVPBMT_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list sstc_config = { + .sublists = { + BASE_SUBLIST, + SSTC_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list svinval_config = { + .sublists = { + BASE_SUBLIST, + SVINVAL_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zihintpause_config = { + .sublists = { + BASE_SUBLIST, + ZIHINTPAUSE_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zba_config = { + .sublists = { + BASE_SUBLIST, + ZBA_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zbb_config = { + .sublists = { + BASE_SUBLIST, + ZBB_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zbs_config = { + .sublists = { + BASE_SUBLIST, + ZBS_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zicntr_config = { + .sublists = { + BASE_SUBLIST, + ZICNTR_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zicsr_config = { + .sublists = { + BASE_SUBLIST, + ZICSR_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zifencei_config = { + .sublists = { + BASE_SUBLIST, + ZIFENCEI_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list zihpm_config = { + .sublists = { + BASE_SUBLIST, + ZIHPM_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list aia_config = { + .sublists = { + BASE_SUBLIST, + AIA_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list fp_f_config = { + .sublists = { + BASE_SUBLIST, + FP_F_REGS_SUBLIST, + {0}, + }, +}; + +static struct vcpu_reg_list fp_d_config = { + .sublists = { + BASE_SUBLIST, + FP_D_REGS_SUBLIST, + {0}, + }, +}; + +struct vcpu_reg_list *vcpu_configs[] = { + &h_config, + &zicbom_config, + &zicboz_config, + &svpbmt_config, + &sstc_config, + &svinval_config, + &zihintpause_config, + &zba_config, + &zbb_config, + &zbs_config, + &zicntr_config, + &zicsr_config, + &zifencei_config, + &zihpm_config, + &aia_config, + &fp_f_config, + &fp_d_config, +}; +int vcpu_configs_n = ARRAY_SIZE(vcpu_configs); diff --git a/tools/testing/selftests/kvm/s390x/cmma_test.c b/tools/testing/selftests/kvm/s390x/cmma_test.c index 1d73e78e8fa7..c8e0a6495a63 100644 --- a/tools/testing/selftests/kvm/s390x/cmma_test.c +++ b/tools/testing/selftests/kvm/s390x/cmma_test.c @@ -237,8 +237,8 @@ static void test_get_cmma_basic(void) /* GET_CMMA_BITS without CMMA enabled should fail */ rc = vm_get_cmma_bits(vm, 0, &errno_out); - ASSERT_EQ(rc, -1); - ASSERT_EQ(errno_out, ENXIO); + TEST_ASSERT_EQ(rc, -1); + TEST_ASSERT_EQ(errno_out, ENXIO); enable_cmma(vm); vcpu = vm_vcpu_add(vm, 1, guest_do_one_essa); @@ -247,31 +247,31 @@ static void test_get_cmma_basic(void) /* GET_CMMA_BITS without migration mode and without peeking should fail */ rc = vm_get_cmma_bits(vm, 0, &errno_out); - ASSERT_EQ(rc, -1); - ASSERT_EQ(errno_out, EINVAL); + TEST_ASSERT_EQ(rc, -1); + TEST_ASSERT_EQ(errno_out, EINVAL); /* GET_CMMA_BITS without migration mode and with peeking should work */ rc = vm_get_cmma_bits(vm, KVM_S390_CMMA_PEEK, &errno_out); - ASSERT_EQ(rc, 0); - ASSERT_EQ(errno_out, 0); + TEST_ASSERT_EQ(rc, 0); + TEST_ASSERT_EQ(errno_out, 0); enable_dirty_tracking(vm); enable_migration_mode(vm); /* GET_CMMA_BITS with invalid flags */ rc = vm_get_cmma_bits(vm, 0xfeedc0fe, &errno_out); - ASSERT_EQ(rc, -1); - ASSERT_EQ(errno_out, EINVAL); + TEST_ASSERT_EQ(rc, -1); + TEST_ASSERT_EQ(errno_out, EINVAL); kvm_vm_free(vm); } static void assert_exit_was_hypercall(struct kvm_vcpu *vcpu) { - ASSERT_EQ(vcpu->run->exit_reason, 13); - ASSERT_EQ(vcpu->run->s390_sieic.icptcode, 4); - ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x8300); - ASSERT_EQ(vcpu->run->s390_sieic.ipb, 0x5010000); + TEST_ASSERT_EQ(vcpu->run->exit_reason, 13); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, 4); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa, 0x8300); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipb, 0x5010000); } static void test_migration_mode(void) @@ -283,8 +283,8 @@ static void test_migration_mode(void) /* enabling migration mode on a VM without memory should fail */ rc = __enable_migration_mode(vm); - ASSERT_EQ(rc, -1); - ASSERT_EQ(errno, EINVAL); + TEST_ASSERT_EQ(rc, -1); + TEST_ASSERT_EQ(errno, EINVAL); TEST_ASSERT(!is_migration_mode_on(vm), "migration mode should still be off"); errno = 0; @@ -304,8 +304,8 @@ static void test_migration_mode(void) /* migration mode when memslots have dirty tracking off should fail */ rc = __enable_migration_mode(vm); - ASSERT_EQ(rc, -1); - ASSERT_EQ(errno, EINVAL); + TEST_ASSERT_EQ(rc, -1); + TEST_ASSERT_EQ(errno, EINVAL); TEST_ASSERT(!is_migration_mode_on(vm), "migration mode should still be off"); errno = 0; @@ -314,7 +314,7 @@ static void test_migration_mode(void) /* enabling migration mode should work now */ rc = __enable_migration_mode(vm); - ASSERT_EQ(rc, 0); + TEST_ASSERT_EQ(rc, 0); TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on"); errno = 0; @@ -350,7 +350,7 @@ static void test_migration_mode(void) */ vm_mem_region_set_flags(vm, TEST_DATA_TWO_MEMSLOT, KVM_MEM_LOG_DIRTY_PAGES); rc = __enable_migration_mode(vm); - ASSERT_EQ(rc, 0); + TEST_ASSERT_EQ(rc, 0); TEST_ASSERT(is_migration_mode_on(vm), "migration mode should be on"); errno = 0; @@ -394,9 +394,9 @@ static void assert_all_slots_cmma_dirty(struct kvm_vm *vm) }; memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf)); vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args); - ASSERT_EQ(args.count, MAIN_PAGE_COUNT); - ASSERT_EQ(args.remaining, TEST_DATA_PAGE_COUNT); - ASSERT_EQ(args.start_gfn, 0); + TEST_ASSERT_EQ(args.count, MAIN_PAGE_COUNT); + TEST_ASSERT_EQ(args.remaining, TEST_DATA_PAGE_COUNT); + TEST_ASSERT_EQ(args.start_gfn, 0); /* ...and then - after a hole - the TEST_DATA memslot should follow */ args = (struct kvm_s390_cmma_log){ @@ -407,9 +407,9 @@ static void assert_all_slots_cmma_dirty(struct kvm_vm *vm) }; memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf)); vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args); - ASSERT_EQ(args.count, TEST_DATA_PAGE_COUNT); - ASSERT_EQ(args.start_gfn, TEST_DATA_START_GFN); - ASSERT_EQ(args.remaining, 0); + TEST_ASSERT_EQ(args.count, TEST_DATA_PAGE_COUNT); + TEST_ASSERT_EQ(args.start_gfn, TEST_DATA_START_GFN); + TEST_ASSERT_EQ(args.remaining, 0); /* ...and nothing else should be there */ args = (struct kvm_s390_cmma_log){ @@ -420,9 +420,9 @@ static void assert_all_slots_cmma_dirty(struct kvm_vm *vm) }; memset(cmma_value_buf, 0xff, sizeof(cmma_value_buf)); vm_ioctl(vm, KVM_S390_GET_CMMA_BITS, &args); - ASSERT_EQ(args.count, 0); - ASSERT_EQ(args.start_gfn, 0); - ASSERT_EQ(args.remaining, 0); + TEST_ASSERT_EQ(args.count, 0); + TEST_ASSERT_EQ(args.start_gfn, 0); + TEST_ASSERT_EQ(args.remaining, 0); } /** @@ -498,11 +498,11 @@ static void assert_cmma_dirty(u64 first_dirty_gfn, u64 dirty_gfn_count, const struct kvm_s390_cmma_log *res) { - ASSERT_EQ(res->start_gfn, first_dirty_gfn); - ASSERT_EQ(res->count, dirty_gfn_count); + TEST_ASSERT_EQ(res->start_gfn, first_dirty_gfn); + TEST_ASSERT_EQ(res->count, dirty_gfn_count); for (size_t i = 0; i < dirty_gfn_count; i++) - ASSERT_EQ(cmma_value_buf[0], 0x0); /* stable state */ - ASSERT_EQ(cmma_value_buf[dirty_gfn_count], 0xff); /* not touched */ + TEST_ASSERT_EQ(cmma_value_buf[0], 0x0); /* stable state */ + TEST_ASSERT_EQ(cmma_value_buf[dirty_gfn_count], 0xff); /* not touched */ } static void test_get_skip_holes(void) diff --git a/tools/testing/selftests/kvm/s390x/debug_test.c b/tools/testing/selftests/kvm/s390x/debug_test.c new file mode 100644 index 000000000000..84313fb27529 --- /dev/null +++ b/tools/testing/selftests/kvm/s390x/debug_test.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Test KVM debugging features. */ +#include "kvm_util.h" +#include "test_util.h" + +#include <linux/kvm.h> + +#define __LC_SVC_NEW_PSW 0x1c0 +#define __LC_PGM_NEW_PSW 0x1d0 +#define ICPT_INSTRUCTION 0x04 +#define IPA0_DIAG 0x8300 +#define PGM_SPECIFICATION 0x06 + +/* Common code for testing single-stepping interruptions. */ +extern char int_handler[]; +asm("int_handler:\n" + "j .\n"); + +static struct kvm_vm *test_step_int_1(struct kvm_vcpu **vcpu, void *guest_code, + size_t new_psw_off, uint64_t *new_psw) +{ + struct kvm_guest_debug debug = {}; + struct kvm_regs regs; + struct kvm_vm *vm; + char *lowcore; + + vm = vm_create_with_one_vcpu(vcpu, guest_code); + lowcore = addr_gpa2hva(vm, 0); + new_psw[0] = (*vcpu)->run->psw_mask; + new_psw[1] = (uint64_t)int_handler; + memcpy(lowcore + new_psw_off, new_psw, 16); + vcpu_regs_get(*vcpu, ®s); + regs.gprs[2] = -1; + vcpu_regs_set(*vcpu, ®s); + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + vcpu_guest_debug_set(*vcpu, &debug); + vcpu_run(*vcpu); + + return vm; +} + +static void test_step_int(void *guest_code, size_t new_psw_off) +{ + struct kvm_vcpu *vcpu; + uint64_t new_psw[2]; + struct kvm_vm *vm; + + vm = test_step_int_1(&vcpu, guest_code, new_psw_off, new_psw); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_DEBUG); + TEST_ASSERT_EQ(vcpu->run->psw_mask, new_psw[0]); + TEST_ASSERT_EQ(vcpu->run->psw_addr, new_psw[1]); + kvm_vm_free(vm); +} + +/* Test single-stepping "boring" program interruptions. */ +extern char test_step_pgm_guest_code[]; +asm("test_step_pgm_guest_code:\n" + ".insn rr,0x1d00,%r1,%r0 /* dr %r1,%r0 */\n" + "j .\n"); + +static void test_step_pgm(void) +{ + test_step_int(test_step_pgm_guest_code, __LC_PGM_NEW_PSW); +} + +/* + * Test single-stepping program interruptions caused by DIAG. + * Userspace emulation must not interfere with single-stepping. + */ +extern char test_step_pgm_diag_guest_code[]; +asm("test_step_pgm_diag_guest_code:\n" + "diag %r0,%r0,0\n" + "j .\n"); + +static void test_step_pgm_diag(void) +{ + struct kvm_s390_irq irq = { + .type = KVM_S390_PROGRAM_INT, + .u.pgm.code = PGM_SPECIFICATION, + }; + struct kvm_vcpu *vcpu; + uint64_t new_psw[2]; + struct kvm_vm *vm; + + vm = test_step_int_1(&vcpu, test_step_pgm_diag_guest_code, + __LC_PGM_NEW_PSW, new_psw); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.icptcode, ICPT_INSTRUCTION); + TEST_ASSERT_EQ(vcpu->run->s390_sieic.ipa & 0xff00, IPA0_DIAG); + vcpu_ioctl(vcpu, KVM_S390_IRQ, &irq); + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_DEBUG); + TEST_ASSERT_EQ(vcpu->run->psw_mask, new_psw[0]); + TEST_ASSERT_EQ(vcpu->run->psw_addr, new_psw[1]); + kvm_vm_free(vm); +} + +/* + * Test single-stepping program interruptions caused by ISKE. + * CPUSTAT_KSS handling must not interfere with single-stepping. + */ +extern char test_step_pgm_iske_guest_code[]; +asm("test_step_pgm_iske_guest_code:\n" + "iske %r2,%r2\n" + "j .\n"); + +static void test_step_pgm_iske(void) +{ + test_step_int(test_step_pgm_iske_guest_code, __LC_PGM_NEW_PSW); +} + +/* + * Test single-stepping program interruptions caused by LCTL. + * KVM emulation must not interfere with single-stepping. + */ +extern char test_step_pgm_lctl_guest_code[]; +asm("test_step_pgm_lctl_guest_code:\n" + "lctl %c0,%c0,1\n" + "j .\n"); + +static void test_step_pgm_lctl(void) +{ + test_step_int(test_step_pgm_lctl_guest_code, __LC_PGM_NEW_PSW); +} + +/* Test single-stepping supervisor-call interruptions. */ +extern char test_step_svc_guest_code[]; +asm("test_step_svc_guest_code:\n" + "svc 0\n" + "j .\n"); + +static void test_step_svc(void) +{ + test_step_int(test_step_svc_guest_code, __LC_SVC_NEW_PSW); +} + +/* Run all tests above. */ +static struct testdef { + const char *name; + void (*test)(void); +} testlist[] = { + { "single-step pgm", test_step_pgm }, + { "single-step pgm caused by diag", test_step_pgm_diag }, + { "single-step pgm caused by iske", test_step_pgm_iske }, + { "single-step pgm caused by lctl", test_step_pgm_lctl }, + { "single-step svc", test_step_svc }, +}; + +int main(int argc, char *argv[]) +{ + int idx; + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(testlist)); + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { + testlist[idx].test(); + ksft_test_result_pass("%s\n", testlist[idx].name); + } + ksft_finished(); +} diff --git a/tools/testing/selftests/kvm/s390x/memop.c b/tools/testing/selftests/kvm/s390x/memop.c index 8e4b94d7b8dd..bb3ca9a5d731 100644 --- a/tools/testing/selftests/kvm/s390x/memop.c +++ b/tools/testing/selftests/kvm/s390x/memop.c @@ -4,7 +4,6 @@ * * Copyright (C) 2019, Red Hat, Inc. */ - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -279,10 +278,10 @@ enum stage { vcpu_run(__vcpu); \ get_ucall(__vcpu, &uc); \ if (uc.cmd == UCALL_ABORT) { \ - REPORT_GUEST_ASSERT_2(uc, "hints: %lu, %lu"); \ + REPORT_GUEST_ASSERT(uc); \ } \ - ASSERT_EQ(uc.cmd, UCALL_SYNC); \ - ASSERT_EQ(uc.args[1], __stage); \ + TEST_ASSERT_EQ(uc.cmd, UCALL_SYNC); \ + TEST_ASSERT_EQ(uc.args[1], __stage); \ }) \ static void prepare_mem12(void) @@ -469,7 +468,7 @@ static __uint128_t cut_to_size(int size, __uint128_t val) case 16: return val; } - GUEST_ASSERT_1(false, "Invalid size"); + GUEST_FAIL("Invalid size = %u", size); return 0; } @@ -598,7 +597,7 @@ static bool _cmpxchg(int size, void *target, __uint128_t *old_addr, __uint128_t return ret; } } - GUEST_ASSERT_1(false, "Invalid size"); + GUEST_FAIL("Invalid size = %u", size); return 0; } @@ -808,7 +807,7 @@ static void test_termination(void) HOST_SYNC(t.vcpu, STAGE_IDLED); MOP(t.vm, ABSOLUTE, READ, &teid, sizeof(teid), GADDR(prefix + 168)); /* Bits 56, 60, 61 form a code, 0 being the only one allowing for termination */ - ASSERT_EQ(teid & teid_mask, 0); + TEST_ASSERT_EQ(teid & teid_mask, 0); kvm_vm_free(t.kvm_vm); } diff --git a/tools/testing/selftests/kvm/s390x/tprot.c b/tools/testing/selftests/kvm/s390x/tprot.c index a9a0b76e5fa4..c73f948c9b63 100644 --- a/tools/testing/selftests/kvm/s390x/tprot.c +++ b/tools/testing/selftests/kvm/s390x/tprot.c @@ -4,7 +4,6 @@ * * Copyright IBM Corp. 2021 */ - #include <sys/mman.h> #include "test_util.h" #include "kvm_util.h" @@ -156,7 +155,9 @@ static enum stage perform_next_stage(int *i, bool mapped_0) !mapped_0; if (!skip) { result = test_protection(tests[*i].addr, tests[*i].key); - GUEST_ASSERT_2(result == tests[*i].expected, *i, result); + __GUEST_ASSERT(result == tests[*i].expected, + "Wanted %u, got %u, for i = %u", + tests[*i].expected, result, *i); } } return stage; @@ -190,9 +191,9 @@ static void guest_code(void) vcpu_run(__vcpu); \ get_ucall(__vcpu, &uc); \ if (uc.cmd == UCALL_ABORT) \ - REPORT_GUEST_ASSERT_2(uc, "hints: %lu, %lu"); \ - ASSERT_EQ(uc.cmd, UCALL_SYNC); \ - ASSERT_EQ(uc.args[1], __stage); \ + REPORT_GUEST_ASSERT(uc); \ + TEST_ASSERT_EQ(uc.cmd, UCALL_SYNC); \ + TEST_ASSERT_EQ(uc.args[1], __stage); \ }) #define HOST_SYNC(vcpu, stage) \ diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index a849ce23ca97..b32960189f5f 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -88,7 +88,7 @@ static void *vcpu_worker(void *data) } if (run->exit_reason == KVM_EXIT_IO && cmd == UCALL_ABORT) - REPORT_GUEST_ASSERT_1(uc, "val = %lu"); + REPORT_GUEST_ASSERT(uc); return NULL; } @@ -156,19 +156,22 @@ static void guest_code_move_memory_region(void) * window where the memslot is invalid is usually quite small. */ val = guest_spin_on_val(0); - GUEST_ASSERT_1(val == 1 || val == MMIO_VAL, val); + __GUEST_ASSERT(val == 1 || val == MMIO_VAL, + "Expected '1' or MMIO ('%llx'), got '%llx'", MMIO_VAL, val); /* Spin until the misaligning memory region move completes. */ val = guest_spin_on_val(MMIO_VAL); - GUEST_ASSERT_1(val == 1 || val == 0, val); + __GUEST_ASSERT(val == 1 || val == 0, + "Expected '0' or '1' (no MMIO), got '%llx'", val); /* Spin until the memory region starts to get re-aligned. */ val = guest_spin_on_val(0); - GUEST_ASSERT_1(val == 1 || val == MMIO_VAL, val); + __GUEST_ASSERT(val == 1 || val == MMIO_VAL, + "Expected '1' or MMIO ('%llx'), got '%llx'", MMIO_VAL, val); /* Spin until the re-aligning memory region move completes. */ val = guest_spin_on_val(MMIO_VAL); - GUEST_ASSERT_1(val == 1, val); + GUEST_ASSERT_EQ(val, 1); GUEST_DONE(); } @@ -224,15 +227,15 @@ static void guest_code_delete_memory_region(void) /* Spin until the memory region is deleted. */ val = guest_spin_on_val(0); - GUEST_ASSERT_1(val == MMIO_VAL, val); + GUEST_ASSERT_EQ(val, MMIO_VAL); /* Spin until the memory region is recreated. */ val = guest_spin_on_val(MMIO_VAL); - GUEST_ASSERT_1(val == 0, val); + GUEST_ASSERT_EQ(val, 0); /* Spin until the memory region is deleted. */ val = guest_spin_on_val(0); - GUEST_ASSERT_1(val == MMIO_VAL, val); + GUEST_ASSERT_EQ(val, MMIO_VAL); asm("1:\n\t" ".pushsection .rodata\n\t" @@ -249,7 +252,7 @@ static void guest_code_delete_memory_region(void) "final_rip_end: .quad 1b\n\t" ".popsection"); - GUEST_ASSERT_1(0, 0); + GUEST_ASSERT(0); } static void test_delete_memory_region(void) diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c index c87f38712073..171adfb2a6cb 100644 --- a/tools/testing/selftests/kvm/steal_time.c +++ b/tools/testing/selftests/kvm/steal_time.c @@ -31,8 +31,8 @@ static uint64_t guest_stolen_time[NR_VCPUS]; static void check_status(struct kvm_steal_time *st) { GUEST_ASSERT(!(READ_ONCE(st->version) & 1)); - GUEST_ASSERT(READ_ONCE(st->flags) == 0); - GUEST_ASSERT(READ_ONCE(st->preempted) == 0); + GUEST_ASSERT_EQ(READ_ONCE(st->flags), 0); + GUEST_ASSERT_EQ(READ_ONCE(st->preempted), 0); } static void guest_code(int cpu) @@ -40,7 +40,7 @@ static void guest_code(int cpu) struct kvm_steal_time *st = st_gva[cpu]; uint32_t version; - GUEST_ASSERT(rdmsr(MSR_KVM_STEAL_TIME) == ((uint64_t)st_gva[cpu] | KVM_MSR_ENABLED)); + GUEST_ASSERT_EQ(rdmsr(MSR_KVM_STEAL_TIME), ((uint64_t)st_gva[cpu] | KVM_MSR_ENABLED)); memset(st, 0, sizeof(*st)); GUEST_SYNC(0); @@ -122,8 +122,8 @@ static int64_t smccc(uint32_t func, uint64_t arg) static void check_status(struct st_time *st) { - GUEST_ASSERT(READ_ONCE(st->rev) == 0); - GUEST_ASSERT(READ_ONCE(st->attr) == 0); + GUEST_ASSERT_EQ(READ_ONCE(st->rev), 0); + GUEST_ASSERT_EQ(READ_ONCE(st->attr), 0); } static void guest_code(int cpu) @@ -132,15 +132,15 @@ static void guest_code(int cpu) int64_t status; status = smccc(SMCCC_ARCH_FEATURES, PV_TIME_FEATURES); - GUEST_ASSERT(status == 0); + GUEST_ASSERT_EQ(status, 0); status = smccc(PV_TIME_FEATURES, PV_TIME_FEATURES); - GUEST_ASSERT(status == 0); + GUEST_ASSERT_EQ(status, 0); status = smccc(PV_TIME_FEATURES, PV_TIME_ST); - GUEST_ASSERT(status == 0); + GUEST_ASSERT_EQ(status, 0); status = smccc(PV_TIME_ST, 0); - GUEST_ASSERT(status != -1); - GUEST_ASSERT(status == (ulong)st_gva[cpu]); + GUEST_ASSERT_NE(status, -1); + GUEST_ASSERT_EQ(status, (ulong)st_gva[cpu]); st = (struct st_time *)status; GUEST_SYNC(0); diff --git a/tools/testing/selftests/kvm/x86_64/cpuid_test.c b/tools/testing/selftests/kvm/x86_64/cpuid_test.c index d3c3aa93f090..3b34d8156d1c 100644 --- a/tools/testing/selftests/kvm/x86_64/cpuid_test.c +++ b/tools/testing/selftests/kvm/x86_64/cpuid_test.c @@ -35,10 +35,10 @@ static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid) guest_cpuid->entries[i].index, &eax, &ebx, &ecx, &edx); - GUEST_ASSERT(eax == guest_cpuid->entries[i].eax && - ebx == guest_cpuid->entries[i].ebx && - ecx == guest_cpuid->entries[i].ecx && - edx == guest_cpuid->entries[i].edx); + GUEST_ASSERT_EQ(eax, guest_cpuid->entries[i].eax); + GUEST_ASSERT_EQ(ebx, guest_cpuid->entries[i].ebx); + GUEST_ASSERT_EQ(ecx, guest_cpuid->entries[i].ecx); + GUEST_ASSERT_EQ(edx, guest_cpuid->entries[i].edx); } } @@ -51,7 +51,7 @@ static void guest_main(struct kvm_cpuid2 *guest_cpuid) GUEST_SYNC(2); - GUEST_ASSERT(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF) == 0x40000001); + GUEST_ASSERT_EQ(this_cpu_property(X86_PROPERTY_MAX_KVM_LEAF), 0x40000001); GUEST_DONE(); } @@ -116,7 +116,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage) case UCALL_DONE: return; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); default: TEST_ASSERT(false, "Unexpected exit: %s", exit_reason_str(vcpu->run->exit_reason)); diff --git a/tools/testing/selftests/kvm/x86_64/dirty_log_page_splitting_test.c b/tools/testing/selftests/kvm/x86_64/dirty_log_page_splitting_test.c index beb7e2c10211..634c6bfcd572 100644 --- a/tools/testing/selftests/kvm/x86_64/dirty_log_page_splitting_test.c +++ b/tools/testing/selftests/kvm/x86_64/dirty_log_page_splitting_test.c @@ -72,7 +72,7 @@ static void vcpu_worker(struct memstress_vcpu_args *vcpu_args) vcpu_run(vcpu); - ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC); vcpu_last_completed_iteration[vcpu_idx] = current_iteration; @@ -179,12 +179,12 @@ static void run_test(enum vm_guest_mode mode, void *unused) * with that capability. */ if (dirty_log_manual_caps) { - ASSERT_EQ(stats_clear_pass[0].hugepages, 0); - ASSERT_EQ(stats_clear_pass[0].pages_4k, total_4k_pages); - ASSERT_EQ(stats_dirty_logging_enabled.hugepages, stats_populated.hugepages); + TEST_ASSERT_EQ(stats_clear_pass[0].hugepages, 0); + TEST_ASSERT_EQ(stats_clear_pass[0].pages_4k, total_4k_pages); + TEST_ASSERT_EQ(stats_dirty_logging_enabled.hugepages, stats_populated.hugepages); } else { - ASSERT_EQ(stats_dirty_logging_enabled.hugepages, 0); - ASSERT_EQ(stats_dirty_logging_enabled.pages_4k, total_4k_pages); + TEST_ASSERT_EQ(stats_dirty_logging_enabled.hugepages, 0); + TEST_ASSERT_EQ(stats_dirty_logging_enabled.pages_4k, total_4k_pages); } /* @@ -192,9 +192,9 @@ static void run_test(enum vm_guest_mode mode, void *unused) * memory again, the page counts should be the same as they were * right after initial population of memory. */ - ASSERT_EQ(stats_populated.pages_4k, stats_repopulated.pages_4k); - ASSERT_EQ(stats_populated.pages_2m, stats_repopulated.pages_2m); - ASSERT_EQ(stats_populated.pages_1g, stats_repopulated.pages_1g); + TEST_ASSERT_EQ(stats_populated.pages_4k, stats_repopulated.pages_4k); + TEST_ASSERT_EQ(stats_populated.pages_2m, stats_repopulated.pages_2m); + TEST_ASSERT_EQ(stats_populated.pages_1g, stats_repopulated.pages_1g); } static void help(char *name) diff --git a/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c b/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c index e334844d6e1d..6c2e5e0ceb1f 100644 --- a/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c +++ b/tools/testing/selftests/kvm/x86_64/exit_on_emulation_failure_test.c @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) vcpu_run(vcpu); handle_flds_emulation_failure_exit(vcpu); vcpu_run(vcpu); - ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); + TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); kvm_vm_free(vm); return 0; diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c b/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c index 73af44d2167f..e036db1f32b9 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_extended_hypercalls.c @@ -8,7 +8,6 @@ * Copyright 2022 Google LLC * Author: Vipin Sharma <vipinsh@google.com> */ - #include "kvm_util.h" #include "processor.h" #include "hyperv.h" @@ -84,7 +83,7 @@ int main(void) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "arg1 = %ld, arg2 = %ld"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: break; diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_features.c b/tools/testing/selftests/kvm/x86_64/hyperv_features.c index 78606de9385d..9f28aa276c4e 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_features.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_features.c @@ -53,16 +53,21 @@ static void guest_msr(struct msr_data *msr) vector = rdmsr_safe(msr->idx, &msr_val); if (msr->fault_expected) - GUEST_ASSERT_3(vector == GP_VECTOR, msr->idx, vector, GP_VECTOR); + __GUEST_ASSERT(vector == GP_VECTOR, + "Expected #GP on %sMSR(0x%x), got vector '0x%x'", + msr->idx, msr->write ? "WR" : "RD", vector); else - GUEST_ASSERT_3(!vector, msr->idx, vector, 0); + __GUEST_ASSERT(!vector, + "Expected success on %sMSR(0x%x), got vector '0x%x'", + msr->idx, msr->write ? "WR" : "RD", vector); if (vector || is_write_only_msr(msr->idx)) goto done; if (msr->write) - GUEST_ASSERT_3(msr_val == msr->write_val, msr->idx, - msr_val, msr->write_val); + __GUEST_ASSERT(!vector, + "WRMSR(0x%x) to '0x%llx', RDMSR read '0x%llx'", + msr->idx, msr->write_val, msr_val); /* Invariant TSC bit appears when TSC invariant control MSR is written to */ if (msr->idx == HV_X64_MSR_TSC_INVARIANT_CONTROL) { @@ -82,7 +87,7 @@ static void guest_hcall(vm_vaddr_t pgs_gpa, struct hcall_data *hcall) u64 res, input, output; uint8_t vector; - GUEST_ASSERT(hcall->control); + GUEST_ASSERT_NE(hcall->control, 0); wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID); wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa); @@ -96,10 +101,14 @@ static void guest_hcall(vm_vaddr_t pgs_gpa, struct hcall_data *hcall) vector = __hyperv_hypercall(hcall->control, input, output, &res); if (hcall->ud_expected) { - GUEST_ASSERT_2(vector == UD_VECTOR, hcall->control, vector); + __GUEST_ASSERT(vector == UD_VECTOR, + "Expected #UD for control '%u', got vector '0x%x'", + hcall->control, vector); } else { - GUEST_ASSERT_2(!vector, hcall->control, vector); - GUEST_ASSERT_2(res == hcall->expect, hcall->expect, res); + __GUEST_ASSERT(!vector, + "Expected no exception for control '%u', got vector '0x%x'", + hcall->control, vector); + GUEST_ASSERT_EQ(res, hcall->expect); } GUEST_DONE(); @@ -495,7 +504,7 @@ static void guest_test_msrs_access(void) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_3(uc, "MSR = %lx, arg1 = %lx, arg2 = %lx"); + REPORT_GUEST_ASSERT(uc); return; case UCALL_DONE: break; @@ -665,7 +674,7 @@ static void guest_test_hcalls_access(void) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "arg1 = %lx, arg2 = %lx"); + REPORT_GUEST_ASSERT(uc); return; case UCALL_DONE: break; diff --git a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c index f774a9e62858..9e2879af7c20 100644 --- a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c @@ -46,10 +46,10 @@ static void test_msr(struct msr_data *msr) PR_MSR(msr); vector = rdmsr_safe(msr->idx, &ignored); - GUEST_ASSERT_1(vector == GP_VECTOR, vector); + GUEST_ASSERT_EQ(vector, GP_VECTOR); vector = wrmsr_safe(msr->idx, 0); - GUEST_ASSERT_1(vector == GP_VECTOR, vector); + GUEST_ASSERT_EQ(vector, GP_VECTOR); } struct hcall_data { @@ -77,7 +77,7 @@ static void test_hcall(struct hcall_data *hc) PR_HCALL(hc); r = kvm_hypercall(hc->nr, 0, 0, 0, 0); - GUEST_ASSERT(r == -KVM_ENOSYS); + GUEST_ASSERT_EQ(r, -KVM_ENOSYS); } static void guest_main(void) @@ -125,7 +125,7 @@ static void enter_guest(struct kvm_vcpu *vcpu) pr_hcall(&uc); break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_1(uc, "vector = %lu"); + REPORT_GUEST_ASSERT(uc); return; case UCALL_DONE: return; diff --git a/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c index 72812644d7f5..80aa3d8b18f8 100644 --- a/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c +++ b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c @@ -16,14 +16,25 @@ enum monitor_mwait_testcases { MWAIT_DISABLED = BIT(2), }; +/* + * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, in all + * other scenarios KVM should emulate them as nops. + */ +#define GUEST_ASSERT_MONITOR_MWAIT(insn, testcase, vector) \ +do { \ + bool fault_wanted = ((testcase) & MWAIT_QUIRK_DISABLED) && \ + ((testcase) & MWAIT_DISABLED); \ + \ + if (fault_wanted) \ + __GUEST_ASSERT((vector) == UD_VECTOR, \ + "Expected #UD on " insn " for testcase '0x%x', got '0x%x'", vector); \ + else \ + __GUEST_ASSERT(!(vector), \ + "Expected success on " insn " for testcase '0x%x', got '0x%x'", vector); \ +} while (0) + static void guest_monitor_wait(int testcase) { - /* - * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, - * in all other scenarios KVM should emulate them as nops. - */ - bool fault_wanted = (testcase & MWAIT_QUIRK_DISABLED) && - (testcase & MWAIT_DISABLED); u8 vector; GUEST_SYNC(testcase); @@ -33,16 +44,10 @@ static void guest_monitor_wait(int testcase) * intercept checks, so the inputs for MONITOR and MWAIT must be valid. */ vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0)); - if (fault_wanted) - GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); - else - GUEST_ASSERT_2(!vector, testcase, vector); + GUEST_ASSERT_MONITOR_MWAIT("MONITOR", testcase, vector); vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0)); - if (fault_wanted) - GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); - else - GUEST_ASSERT_2(!vector, testcase, vector); + GUEST_ASSERT_MONITOR_MWAIT("MWAIT", testcase, vector); } static void guest_code(void) @@ -85,7 +90,7 @@ int main(int argc, char *argv[]) testcase = uc.args[1]; break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "testcase = %lx, vector = %ld"); + REPORT_GUEST_ASSERT(uc); goto done; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c b/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c index 6502aa23c2f8..3670331adf21 100644 --- a/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c +++ b/tools/testing/selftests/kvm/x86_64/nested_exceptions_test.c @@ -180,9 +180,7 @@ static void assert_ucall_vector(struct kvm_vcpu *vcpu, int vector) "Expected L2 to ask for %d, L2 says it's done", vector); break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld (0x%lx != 0x%lx)", - (const char *)uc.args[0], __FILE__, uc.args[1], - uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT(uc); break; default: TEST_FAIL("Expected L2 to ask for %d, got unexpected ucall %lu", vector, uc.cmd); @@ -247,12 +245,12 @@ int main(int argc, char *argv[]) /* Verify the pending events comes back out the same as it went in. */ vcpu_events_get(vcpu, &events); - ASSERT_EQ(events.flags & KVM_VCPUEVENT_VALID_PAYLOAD, - KVM_VCPUEVENT_VALID_PAYLOAD); - ASSERT_EQ(events.exception.pending, true); - ASSERT_EQ(events.exception.nr, SS_VECTOR); - ASSERT_EQ(events.exception.has_error_code, true); - ASSERT_EQ(events.exception.error_code, SS_ERROR_CODE); + TEST_ASSERT_EQ(events.flags & KVM_VCPUEVENT_VALID_PAYLOAD, + KVM_VCPUEVENT_VALID_PAYLOAD); + TEST_ASSERT_EQ(events.exception.pending, true); + TEST_ASSERT_EQ(events.exception.nr, SS_VECTOR); + TEST_ASSERT_EQ(events.exception.has_error_code, true); + TEST_ASSERT_EQ(events.exception.error_code, SS_ERROR_CODE); /* * Run for real with the pending #SS, L1 should get a VM-Exit due to diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c index 40507ed9fe8a..283cc55597a4 100644 --- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c @@ -27,6 +27,15 @@ #define ARCH_PERFMON_BRANCHES_RETIRED 5 #define NUM_BRANCHES 42 +#define INTEL_PMC_IDX_FIXED 32 + +/* Matches KVM_PMU_EVENT_FILTER_MAX_EVENTS in pmu.c */ +#define MAX_FILTER_EVENTS 300 +#define MAX_TEST_EVENTS 10 + +#define PMU_EVENT_FILTER_INVALID_ACTION (KVM_PMU_EVENT_DENY + 1) +#define PMU_EVENT_FILTER_INVALID_FLAGS (KVM_PMU_EVENT_FLAGS_VALID_MASK << 1) +#define PMU_EVENT_FILTER_INVALID_NEVENTS (MAX_FILTER_EVENTS + 1) /* * This is how the event selector and unit mask are stored in an AMD @@ -69,21 +78,33 @@ #define INST_RETIRED EVENT(0xc0, 0) +struct __kvm_pmu_event_filter { + __u32 action; + __u32 nevents; + __u32 fixed_counter_bitmap; + __u32 flags; + __u32 pad[4]; + __u64 events[MAX_FILTER_EVENTS]; +}; + /* * This event list comprises Intel's eight architectural events plus * AMD's "retired branch instructions" for Zen[123] (and possibly * other AMD CPUs). */ -static const uint64_t event_list[] = { - EVENT(0x3c, 0), - INST_RETIRED, - EVENT(0x3c, 1), - EVENT(0x2e, 0x4f), - EVENT(0x2e, 0x41), - EVENT(0xc4, 0), - EVENT(0xc5, 0), - EVENT(0xa4, 1), - AMD_ZEN_BR_RETIRED, +static const struct __kvm_pmu_event_filter base_event_filter = { + .nevents = ARRAY_SIZE(base_event_filter.events), + .events = { + EVENT(0x3c, 0), + INST_RETIRED, + EVENT(0x3c, 1), + EVENT(0x2e, 0x4f), + EVENT(0x2e, 0x41), + EVENT(0xc4, 0), + EVENT(0xc5, 0), + EVENT(0xa4, 1), + AMD_ZEN_BR_RETIRED, + }, }; struct { @@ -225,48 +246,11 @@ static bool sanity_check_pmu(struct kvm_vcpu *vcpu) return !r; } -static struct kvm_pmu_event_filter *alloc_pmu_event_filter(uint32_t nevents) -{ - struct kvm_pmu_event_filter *f; - int size = sizeof(*f) + nevents * sizeof(f->events[0]); - - f = malloc(size); - TEST_ASSERT(f, "Out of memory"); - memset(f, 0, size); - f->nevents = nevents; - return f; -} - - -static struct kvm_pmu_event_filter * -create_pmu_event_filter(const uint64_t event_list[], int nevents, - uint32_t action, uint32_t flags) -{ - struct kvm_pmu_event_filter *f; - int i; - - f = alloc_pmu_event_filter(nevents); - f->action = action; - f->flags = flags; - for (i = 0; i < nevents; i++) - f->events[i] = event_list[i]; - - return f; -} - -static struct kvm_pmu_event_filter *event_filter(uint32_t action) -{ - return create_pmu_event_filter(event_list, - ARRAY_SIZE(event_list), - action, 0); -} - /* * Remove the first occurrence of 'event' (if any) from the filter's * event list. */ -static struct kvm_pmu_event_filter *remove_event(struct kvm_pmu_event_filter *f, - uint64_t event) +static void remove_event(struct __kvm_pmu_event_filter *f, uint64_t event) { bool found = false; int i; @@ -279,7 +263,6 @@ static struct kvm_pmu_event_filter *remove_event(struct kvm_pmu_event_filter *f, } if (found) f->nevents--; - return f; } #define ASSERT_PMC_COUNTING_INSTRUCTIONS() \ @@ -315,66 +298,73 @@ static void test_without_filter(struct kvm_vcpu *vcpu) } static void test_with_filter(struct kvm_vcpu *vcpu, - struct kvm_pmu_event_filter *f) + struct __kvm_pmu_event_filter *__f) { + struct kvm_pmu_event_filter *f = (void *)__f; + vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f); run_vcpu_and_sync_pmc_results(vcpu); } static void test_amd_deny_list(struct kvm_vcpu *vcpu) { - uint64_t event = EVENT(0x1C2, 0); - struct kvm_pmu_event_filter *f; + struct __kvm_pmu_event_filter f = { + .action = KVM_PMU_EVENT_DENY, + .nevents = 1, + .events = { + EVENT(0x1C2, 0), + }, + }; - f = create_pmu_event_filter(&event, 1, KVM_PMU_EVENT_DENY, 0); - test_with_filter(vcpu, f); - free(f); + test_with_filter(vcpu, &f); ASSERT_PMC_COUNTING_INSTRUCTIONS(); } static void test_member_deny_list(struct kvm_vcpu *vcpu) { - struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY); + struct __kvm_pmu_event_filter f = base_event_filter; - test_with_filter(vcpu, f); - free(f); + f.action = KVM_PMU_EVENT_DENY; + test_with_filter(vcpu, &f); ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS(); } static void test_member_allow_list(struct kvm_vcpu *vcpu) { - struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW); + struct __kvm_pmu_event_filter f = base_event_filter; - test_with_filter(vcpu, f); - free(f); + f.action = KVM_PMU_EVENT_ALLOW; + test_with_filter(vcpu, &f); ASSERT_PMC_COUNTING_INSTRUCTIONS(); } static void test_not_member_deny_list(struct kvm_vcpu *vcpu) { - struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY); + struct __kvm_pmu_event_filter f = base_event_filter; - remove_event(f, INST_RETIRED); - remove_event(f, INTEL_BR_RETIRED); - remove_event(f, AMD_ZEN_BR_RETIRED); - test_with_filter(vcpu, f); - free(f); + f.action = KVM_PMU_EVENT_DENY; + + remove_event(&f, INST_RETIRED); + remove_event(&f, INTEL_BR_RETIRED); + remove_event(&f, AMD_ZEN_BR_RETIRED); + test_with_filter(vcpu, &f); ASSERT_PMC_COUNTING_INSTRUCTIONS(); } static void test_not_member_allow_list(struct kvm_vcpu *vcpu) { - struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW); + struct __kvm_pmu_event_filter f = base_event_filter; + + f.action = KVM_PMU_EVENT_ALLOW; - remove_event(f, INST_RETIRED); - remove_event(f, INTEL_BR_RETIRED); - remove_event(f, AMD_ZEN_BR_RETIRED); - test_with_filter(vcpu, f); - free(f); + remove_event(&f, INST_RETIRED); + remove_event(&f, INTEL_BR_RETIRED); + remove_event(&f, AMD_ZEN_BR_RETIRED); + test_with_filter(vcpu, &f); ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS(); } @@ -569,19 +559,16 @@ static void run_masked_events_test(struct kvm_vcpu *vcpu, const uint64_t masked_events[], const int nmasked_events) { - struct kvm_pmu_event_filter *f; + struct __kvm_pmu_event_filter f = { + .nevents = nmasked_events, + .action = KVM_PMU_EVENT_ALLOW, + .flags = KVM_PMU_EVENT_FLAG_MASKED_EVENTS, + }; - f = create_pmu_event_filter(masked_events, nmasked_events, - KVM_PMU_EVENT_ALLOW, - KVM_PMU_EVENT_FLAG_MASKED_EVENTS); - test_with_filter(vcpu, f); - free(f); + memcpy(f.events, masked_events, sizeof(uint64_t) * nmasked_events); + test_with_filter(vcpu, &f); } -/* Matches KVM_PMU_EVENT_FILTER_MAX_EVENTS in pmu.c */ -#define MAX_FILTER_EVENTS 300 -#define MAX_TEST_EVENTS 10 - #define ALLOW_LOADS BIT(0) #define ALLOW_STORES BIT(1) #define ALLOW_LOADS_STORES BIT(2) @@ -753,21 +740,33 @@ static void test_masked_events(struct kvm_vcpu *vcpu) run_masked_events_tests(vcpu, events, nevents); } -static int run_filter_test(struct kvm_vcpu *vcpu, const uint64_t *events, - int nevents, uint32_t flags) +static int set_pmu_event_filter(struct kvm_vcpu *vcpu, + struct __kvm_pmu_event_filter *__f) { - struct kvm_pmu_event_filter *f; - int r; + struct kvm_pmu_event_filter *f = (void *)__f; - f = create_pmu_event_filter(events, nevents, KVM_PMU_EVENT_ALLOW, flags); - r = __vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f); - free(f); + return __vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f); +} - return r; +static int set_pmu_single_event_filter(struct kvm_vcpu *vcpu, uint64_t event, + uint32_t flags, uint32_t action) +{ + struct __kvm_pmu_event_filter f = { + .nevents = 1, + .flags = flags, + .action = action, + .events = { + event, + }, + }; + + return set_pmu_event_filter(vcpu, &f); } static void test_filter_ioctl(struct kvm_vcpu *vcpu) { + uint8_t nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + struct __kvm_pmu_event_filter f; uint64_t e = ~0ul; int r; @@ -775,15 +774,144 @@ static void test_filter_ioctl(struct kvm_vcpu *vcpu) * Unfortunately having invalid bits set in event data is expected to * pass when flags == 0 (bits other than eventsel+umask). */ - r = run_filter_test(vcpu, &e, 1, 0); + r = set_pmu_single_event_filter(vcpu, e, 0, KVM_PMU_EVENT_ALLOW); TEST_ASSERT(r == 0, "Valid PMU Event Filter is failing"); - r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS); + r = set_pmu_single_event_filter(vcpu, e, + KVM_PMU_EVENT_FLAG_MASKED_EVENTS, + KVM_PMU_EVENT_ALLOW); TEST_ASSERT(r != 0, "Invalid PMU Event Filter is expected to fail"); e = KVM_PMU_ENCODE_MASKED_ENTRY(0xff, 0xff, 0xff, 0xf); - r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS); + r = set_pmu_single_event_filter(vcpu, e, + KVM_PMU_EVENT_FLAG_MASKED_EVENTS, + KVM_PMU_EVENT_ALLOW); TEST_ASSERT(r == 0, "Valid PMU Event Filter is failing"); + + f = base_event_filter; + f.action = PMU_EVENT_FILTER_INVALID_ACTION; + r = set_pmu_event_filter(vcpu, &f); + TEST_ASSERT(r, "Set invalid action is expected to fail"); + + f = base_event_filter; + f.flags = PMU_EVENT_FILTER_INVALID_FLAGS; + r = set_pmu_event_filter(vcpu, &f); + TEST_ASSERT(r, "Set invalid flags is expected to fail"); + + f = base_event_filter; + f.nevents = PMU_EVENT_FILTER_INVALID_NEVENTS; + r = set_pmu_event_filter(vcpu, &f); + TEST_ASSERT(r, "Exceeding the max number of filter events should fail"); + + f = base_event_filter; + f.fixed_counter_bitmap = ~GENMASK_ULL(nr_fixed_counters, 0); + r = set_pmu_event_filter(vcpu, &f); + TEST_ASSERT(!r, "Masking non-existent fixed counters should be allowed"); +} + +static void intel_run_fixed_counter_guest_code(uint8_t fixed_ctr_idx) +{ + for (;;) { + wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); + wrmsr(MSR_CORE_PERF_FIXED_CTR0 + fixed_ctr_idx, 0); + + /* Only OS_EN bit is enabled for fixed counter[idx]. */ + wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, BIT_ULL(4 * fixed_ctr_idx)); + wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, + BIT_ULL(INTEL_PMC_IDX_FIXED + fixed_ctr_idx)); + __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); + wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); + + GUEST_SYNC(rdmsr(MSR_CORE_PERF_FIXED_CTR0 + fixed_ctr_idx)); + } +} + +static uint64_t test_with_fixed_counter_filter(struct kvm_vcpu *vcpu, + uint32_t action, uint32_t bitmap) +{ + struct __kvm_pmu_event_filter f = { + .action = action, + .fixed_counter_bitmap = bitmap, + }; + set_pmu_event_filter(vcpu, &f); + + return run_vcpu_to_sync(vcpu); +} + +static uint64_t test_set_gp_and_fixed_event_filter(struct kvm_vcpu *vcpu, + uint32_t action, + uint32_t bitmap) +{ + struct __kvm_pmu_event_filter f = base_event_filter; + + f.action = action; + f.fixed_counter_bitmap = bitmap; + set_pmu_event_filter(vcpu, &f); + + return run_vcpu_to_sync(vcpu); +} + +static void __test_fixed_counter_bitmap(struct kvm_vcpu *vcpu, uint8_t idx, + uint8_t nr_fixed_counters) +{ + unsigned int i; + uint32_t bitmap; + uint64_t count; + + TEST_ASSERT(nr_fixed_counters < sizeof(bitmap) * 8, + "Invalid nr_fixed_counters"); + + /* + * Check the fixed performance counter can count normally when KVM + * userspace doesn't set any pmu filter. + */ + count = run_vcpu_to_sync(vcpu); + TEST_ASSERT(count, "Unexpected count value: %ld\n", count); + + for (i = 0; i < BIT(nr_fixed_counters); i++) { + bitmap = BIT(i); + count = test_with_fixed_counter_filter(vcpu, KVM_PMU_EVENT_ALLOW, + bitmap); + TEST_ASSERT_EQ(!!count, !!(bitmap & BIT(idx))); + + count = test_with_fixed_counter_filter(vcpu, KVM_PMU_EVENT_DENY, + bitmap); + TEST_ASSERT_EQ(!!count, !(bitmap & BIT(idx))); + + /* + * Check that fixed_counter_bitmap has higher priority than + * events[] when both are set. + */ + count = test_set_gp_and_fixed_event_filter(vcpu, + KVM_PMU_EVENT_ALLOW, + bitmap); + TEST_ASSERT_EQ(!!count, !!(bitmap & BIT(idx))); + + count = test_set_gp_and_fixed_event_filter(vcpu, + KVM_PMU_EVENT_DENY, + bitmap); + TEST_ASSERT_EQ(!!count, !(bitmap & BIT(idx))); + } +} + +static void test_fixed_counter_bitmap(void) +{ + uint8_t nr_fixed_counters = kvm_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS); + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + uint8_t idx; + + /* + * Check that pmu_event_filter works as expected when it's applied to + * fixed performance counters. + */ + for (idx = 0; idx < nr_fixed_counters; idx++) { + vm = vm_create_with_one_vcpu(&vcpu, + intel_run_fixed_counter_guest_code); + vcpu_args_set(vcpu, 1, idx); + __test_fixed_counter_bitmap(vcpu, idx, nr_fixed_counters); + kvm_vm_free(vm); + } } int main(int argc, char *argv[]) @@ -829,6 +957,7 @@ int main(int argc, char *argv[]) kvm_vm_free(vm); test_pmu_config_disable(guest_code); + test_fixed_counter_bitmap(); return 0; } diff --git a/tools/testing/selftests/kvm/x86_64/recalc_apic_map_test.c b/tools/testing/selftests/kvm/x86_64/recalc_apic_map_test.c index 4c416ebe7d66..cbc92a862ea9 100644 --- a/tools/testing/selftests/kvm/x86_64/recalc_apic_map_test.c +++ b/tools/testing/selftests/kvm/x86_64/recalc_apic_map_test.c @@ -57,7 +57,7 @@ int main(void) for (i = 0; i < KVM_MAX_VCPUS; i++) vcpu_set_msr(vcpus[i], MSR_IA32_APICBASE, LAPIC_X2APIC); - ASSERT_EQ(pthread_create(&thread, NULL, race, vcpus[0]), 0); + TEST_ASSERT_EQ(pthread_create(&thread, NULL, race, vcpus[0]), 0); vcpuN = vcpus[KVM_MAX_VCPUS - 1]; for (t = time(NULL) + TIMEOUT; time(NULL) < t;) { @@ -65,8 +65,8 @@ int main(void) vcpu_set_msr(vcpuN, MSR_IA32_APICBASE, LAPIC_DISABLED); } - ASSERT_EQ(pthread_cancel(thread), 0); - ASSERT_EQ(pthread_join(thread, NULL), 0); + TEST_ASSERT_EQ(pthread_cancel(thread), 0); + TEST_ASSERT_EQ(pthread_join(thread, NULL), 0); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c b/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c index b25d7556b638..366cf18600bc 100644 --- a/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c +++ b/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c @@ -20,7 +20,7 @@ static void guest_bsp_vcpu(void *arg) { GUEST_SYNC(1); - GUEST_ASSERT(get_bsp_flag() != 0); + GUEST_ASSERT_NE(get_bsp_flag(), 0); GUEST_DONE(); } @@ -29,7 +29,7 @@ static void guest_not_bsp_vcpu(void *arg) { GUEST_SYNC(1); - GUEST_ASSERT(get_bsp_flag() == 0); + GUEST_ASSERT_EQ(get_bsp_flag(), 0); GUEST_DONE(); } @@ -65,7 +65,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu) stage); break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); default: TEST_ASSERT(false, "Unexpected exit: %s", exit_reason_str(vcpu->run->exit_reason)); diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c index 4e2479716da6..7ee44496cf97 100644 --- a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c +++ b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c @@ -8,7 +8,6 @@ * Copyright (C) 2021, Red Hat, Inc. * */ - #include <stdatomic.h> #include <stdio.h> #include <unistd.h> @@ -34,13 +33,12 @@ static void l2_guest_code_int(void); static void guest_int_handler(struct ex_regs *regs) { int_fired++; - GUEST_ASSERT_2(regs->rip == (unsigned long)l2_guest_code_int, - regs->rip, (unsigned long)l2_guest_code_int); + GUEST_ASSERT_EQ(regs->rip, (unsigned long)l2_guest_code_int); } static void l2_guest_code_int(void) { - GUEST_ASSERT_1(int_fired == 1, int_fired); + GUEST_ASSERT_EQ(int_fired, 1); /* * Same as the vmmcall() function, but with a ud2 sneaked after the @@ -53,7 +51,7 @@ static void l2_guest_code_int(void) : "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); - GUEST_ASSERT_1(bp_fired == 1, bp_fired); + GUEST_ASSERT_EQ(bp_fired, 1); hlt(); } @@ -66,9 +64,9 @@ static void guest_nmi_handler(struct ex_regs *regs) if (nmi_stage_get() == 1) { vmmcall(); - GUEST_ASSERT(false); + GUEST_FAIL("Unexpected resume after VMMCALL"); } else { - GUEST_ASSERT_1(nmi_stage_get() == 3, nmi_stage_get()); + GUEST_ASSERT_EQ(nmi_stage_get(), 3); GUEST_DONE(); } } @@ -104,7 +102,8 @@ static void l1_guest_code(struct svm_test_data *svm, uint64_t is_nmi, uint64_t i } run_guest(vmcb, svm->vmcb_gpa); - GUEST_ASSERT_3(vmcb->control.exit_code == SVM_EXIT_VMMCALL, + __GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL, + "Expected VMMCAL #VMEXIT, got '0x%x', info1 = '0x%llx, info2 = '0x%llx'", vmcb->control.exit_code, vmcb->control.exit_info_1, vmcb->control.exit_info_2); @@ -112,7 +111,7 @@ static void l1_guest_code(struct svm_test_data *svm, uint64_t is_nmi, uint64_t i clgi(); x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_NMI); - GUEST_ASSERT_1(nmi_stage_get() == 1, nmi_stage_get()); + GUEST_ASSERT_EQ(nmi_stage_get(), 1); nmi_stage_inc(); stgi(); @@ -133,7 +132,8 @@ static void l1_guest_code(struct svm_test_data *svm, uint64_t is_nmi, uint64_t i vmcb->control.next_rip = vmcb->save.rip + 2; run_guest(vmcb, svm->vmcb_gpa); - GUEST_ASSERT_3(vmcb->control.exit_code == SVM_EXIT_HLT, + __GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_HLT, + "Expected HLT #VMEXIT, got '0x%x', info1 = '0x%llx, info2 = '0x%llx'", vmcb->control.exit_code, vmcb->control.exit_info_1, vmcb->control.exit_info_2); @@ -185,7 +185,7 @@ static void run_test(bool is_nmi) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_3(uc, "vals = 0x%lx 0x%lx 0x%lx"); + REPORT_GUEST_ASSERT(uc); break; /* NOT REACHED */ case UCALL_DONE: diff --git a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c index 2da89fdc2471..00965ba33f73 100644 --- a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c +++ b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c @@ -15,6 +15,7 @@ #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> +#include <pthread.h> #include "test_util.h" #include "kvm_util.h" @@ -80,6 +81,133 @@ static void compare_vcpu_events(struct kvm_vcpu_events *left, #define TEST_SYNC_FIELDS (KVM_SYNC_X86_REGS|KVM_SYNC_X86_SREGS|KVM_SYNC_X86_EVENTS) #define INVALID_SYNC_FIELD 0x80000000 +/* + * Set an exception as pending *and* injected while KVM is processing events. + * KVM is supposed to ignore/drop pending exceptions if userspace is also + * requesting that an exception be injected. + */ +static void *race_events_inj_pen(void *arg) +{ + struct kvm_run *run = (struct kvm_run *)arg; + struct kvm_vcpu_events *events = &run->s.regs.events; + + WRITE_ONCE(events->exception.nr, UD_VECTOR); + + for (;;) { + WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_EVENTS); + WRITE_ONCE(events->flags, 0); + WRITE_ONCE(events->exception.injected, 1); + WRITE_ONCE(events->exception.pending, 1); + + pthread_testcancel(); + } + + return NULL; +} + +/* + * Set an invalid exception vector while KVM is processing events. KVM is + * supposed to reject any vector >= 32, as well as NMIs (vector 2). + */ +static void *race_events_exc(void *arg) +{ + struct kvm_run *run = (struct kvm_run *)arg; + struct kvm_vcpu_events *events = &run->s.regs.events; + + for (;;) { + WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_EVENTS); + WRITE_ONCE(events->flags, 0); + WRITE_ONCE(events->exception.nr, UD_VECTOR); + WRITE_ONCE(events->exception.pending, 1); + WRITE_ONCE(events->exception.nr, 255); + + pthread_testcancel(); + } + + return NULL; +} + +/* + * Toggle CR4.PAE while KVM is processing SREGS, EFER.LME=1 with CR4.PAE=0 is + * illegal, and KVM's MMU heavily relies on vCPU state being valid. + */ +static noinline void *race_sregs_cr4(void *arg) +{ + struct kvm_run *run = (struct kvm_run *)arg; + __u64 *cr4 = &run->s.regs.sregs.cr4; + __u64 pae_enabled = *cr4; + __u64 pae_disabled = *cr4 & ~X86_CR4_PAE; + + for (;;) { + WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_SREGS); + WRITE_ONCE(*cr4, pae_enabled); + asm volatile(".rept 512\n\t" + "nop\n\t" + ".endr"); + WRITE_ONCE(*cr4, pae_disabled); + + pthread_testcancel(); + } + + return NULL; +} + +static void race_sync_regs(void *racer) +{ + const time_t TIMEOUT = 2; /* seconds, roughly */ + struct kvm_x86_state *state; + struct kvm_translation tr; + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + pthread_t thread; + time_t t; + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; + + run->kvm_valid_regs = KVM_SYNC_X86_SREGS; + vcpu_run(vcpu); + run->kvm_valid_regs = 0; + + /* Save state *before* spawning the thread that mucks with vCPU state. */ + state = vcpu_save_state(vcpu); + + /* + * Selftests run 64-bit guests by default, both EFER.LME and CR4.PAE + * should already be set in guest state. + */ + TEST_ASSERT((run->s.regs.sregs.cr4 & X86_CR4_PAE) && + (run->s.regs.sregs.efer & EFER_LME), + "vCPU should be in long mode, CR4.PAE=%d, EFER.LME=%d", + !!(run->s.regs.sregs.cr4 & X86_CR4_PAE), + !!(run->s.regs.sregs.efer & EFER_LME)); + + TEST_ASSERT_EQ(pthread_create(&thread, NULL, racer, (void *)run), 0); + + for (t = time(NULL) + TIMEOUT; time(NULL) < t;) { + /* + * Reload known good state if the vCPU triple faults, e.g. due + * to the unhandled #GPs being injected. VMX preserves state + * on shutdown, but SVM synthesizes an INIT as the VMCB state + * is architecturally undefined on triple fault. + */ + if (!__vcpu_run(vcpu) && run->exit_reason == KVM_EXIT_SHUTDOWN) + vcpu_load_state(vcpu, state); + + if (racer == race_sregs_cr4) { + tr = (struct kvm_translation) { .linear_address = 0 }; + __vcpu_ioctl(vcpu, KVM_TRANSLATE, &tr); + } + } + + TEST_ASSERT_EQ(pthread_cancel(thread), 0); + TEST_ASSERT_EQ(pthread_join(thread, NULL), 0); + + kvm_x86_state_cleanup(state); + kvm_vm_free(vm); +} + int main(int argc, char *argv[]) { struct kvm_vcpu *vcpu; @@ -218,5 +346,9 @@ int main(int argc, char *argv[]) kvm_vm_free(vm); + race_sync_regs(race_sregs_cr4); + race_sync_regs(race_events_exc); + race_sync_regs(race_events_inj_pen); + return 0; } diff --git a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c index c9f67702f657..12b0964f4f13 100644 --- a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c +++ b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c @@ -84,7 +84,7 @@ static void run_vcpu(struct kvm_vcpu *vcpu, int stage) ksft_test_result_pass("stage %d passed\n", stage + 1); return; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); + REPORT_GUEST_ASSERT(uc); default: TEST_ASSERT(false, "Unexpected exit: %s", exit_reason_str(vcpu->run->exit_reason)); @@ -103,39 +103,39 @@ int main(void) vm = vm_create_with_one_vcpu(&vcpu, guest_code); val = 0; - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* Guest: writes to MSR_IA32_TSC affect both MSRs. */ run_vcpu(vcpu, 1); val = 1ull * GUEST_STEP; - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* Guest: writes to MSR_IA32_TSC_ADJUST affect both MSRs. */ run_vcpu(vcpu, 2); val = 2ull * GUEST_STEP; - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* * Host: writes to MSR_IA32_TSC set the host-side offset * and therefore do not change MSR_IA32_TSC_ADJUST. */ vcpu_set_msr(vcpu, MSR_IA32_TSC, HOST_ADJUST + val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); run_vcpu(vcpu, 3); /* Host: writes to MSR_IA32_TSC_ADJUST do not modify the TSC. */ vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, UNITY * 123456); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST), UNITY * 123456); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); + TEST_ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST), UNITY * 123456); /* Restore previous value. */ vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* * Guest: writes to MSR_IA32_TSC_ADJUST do not destroy the @@ -143,8 +143,8 @@ int main(void) */ run_vcpu(vcpu, 4); val = 3ull * GUEST_STEP; - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* * Guest: writes to MSR_IA32_TSC affect both MSRs, so the host-side @@ -152,8 +152,8 @@ int main(void) */ run_vcpu(vcpu, 5); val = 4ull * GUEST_STEP; - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); - ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val - HOST_ADJUST); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); + TEST_ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val - HOST_ADJUST); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c index 0cb51fa42773..255c50b0dc32 100644 --- a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c +++ b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c @@ -20,8 +20,8 @@ static void guest_ins_port80(uint8_t *buffer, unsigned int count) end = (unsigned long)buffer + 8192; asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory"); - GUEST_ASSERT_1(count == 0, count); - GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end); + GUEST_ASSERT_EQ(count, 0); + GUEST_ASSERT_EQ((unsigned long)buffer, end); } static void guest_code(void) @@ -43,7 +43,9 @@ static void guest_code(void) memset(buffer, 0, sizeof(buffer)); guest_ins_port80(buffer, 8192); for (i = 0; i < 8192; i++) - GUEST_ASSERT_2(buffer[i] == 0xaa, i, buffer[i]); + __GUEST_ASSERT(buffer[i] == 0xaa, + "Expected '0xaa', got '0x%x' at buffer[%u]", + buffer[i], i); GUEST_DONE(); } @@ -91,7 +93,7 @@ int main(int argc, char *argv[]) case UCALL_DONE: break; case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "argN+1 = 0x%lx, argN+2 = 0x%lx"); + REPORT_GUEST_ASSERT(uc); default: TEST_FAIL("Unknown ucall %lu", uc.cmd); } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c index be0bdb8c6f78..a9b827c69f32 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c @@ -50,7 +50,7 @@ static void set_timer(void) timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 200; timer.it_interval = timer.it_value; - ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0); + TEST_ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0); } static void set_or_clear_invalid_guest_state(struct kvm_vcpu *vcpu, bool set) diff --git a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c index 4c90f76930f9..ebbcb0a3f743 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c @@ -10,7 +10,6 @@ * and check it can be retrieved with KVM_GET_MSR, also test * the invalid LBR formats are rejected. */ - #define _GNU_SOURCE /* for program_invocation_short_name */ #include <sys/ioctl.h> @@ -52,23 +51,24 @@ static const union perf_capabilities format_caps = { .pebs_format = -1, }; +static void guest_test_perf_capabilities_gp(uint64_t val) +{ + uint8_t vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, val); + + __GUEST_ASSERT(vector == GP_VECTOR, + "Expected #GP for value '0x%llx', got vector '0x%x'", + val, vector); +} + static void guest_code(uint64_t current_val) { - uint8_t vector; int i; - vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, current_val); - GUEST_ASSERT_2(vector == GP_VECTOR, current_val, vector); - - vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, 0); - GUEST_ASSERT_2(vector == GP_VECTOR, 0, vector); + guest_test_perf_capabilities_gp(current_val); + guest_test_perf_capabilities_gp(0); - for (i = 0; i < 64; i++) { - vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, - current_val ^ BIT_ULL(i)); - GUEST_ASSERT_2(vector == GP_VECTOR, - current_val ^ BIT_ULL(i), vector); - } + for (i = 0; i < 64; i++) + guest_test_perf_capabilities_gp(current_val ^ BIT_ULL(i)); GUEST_DONE(); } @@ -95,7 +95,7 @@ static void test_guest_wrmsr_perf_capabilities(union perf_capabilities host_cap) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_2(uc, "val = 0x%lx, vector = %lu"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: break; @@ -103,7 +103,8 @@ static void test_guest_wrmsr_perf_capabilities(union perf_capabilities host_cap) TEST_FAIL("Unexpected ucall: %lu", uc.cmd); } - ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), host_cap.capabilities); + TEST_ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), + host_cap.capabilities); vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities); diff --git a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c index 396c13f42457..ab75b873a4ad 100644 --- a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c +++ b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c @@ -65,17 +65,17 @@ static void ____test_icr(struct xapic_vcpu *x, uint64_t val) vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic); vcpu_run(vcpu); - ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); - ASSERT_EQ(uc.args[1], val); + TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); + TEST_ASSERT_EQ(uc.args[1], val); vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); icr = (u64)(*((u32 *)&xapic.regs[APIC_ICR])) | (u64)(*((u32 *)&xapic.regs[APIC_ICR2])) << 32; if (!x->is_x2apic) { val &= (-1u | (0xffull << (32 + 24))); - ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); + TEST_ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); } else { - ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY); + TEST_ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY); } } diff --git a/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c b/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c index 905bd5ae4431..77d04a7bdadd 100644 --- a/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c +++ b/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c @@ -4,7 +4,6 @@ * * Copyright (C) 2022, Google LLC. */ - #include <fcntl.h> #include <stdio.h> #include <stdlib.h> @@ -20,13 +19,14 @@ * Assert that architectural dependency rules are satisfied, e.g. that AVX is * supported if and only if SSE is supported. */ -#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \ -do { \ - uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \ - \ - GUEST_ASSERT_3((__supported & (xfeatures)) != (xfeatures) || \ - __supported == ((xfeatures) | (dependencies)), \ - __supported, (xfeatures), (dependencies)); \ +#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \ +do { \ + uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \ + \ + __GUEST_ASSERT((__supported & (xfeatures)) != (xfeatures) || \ + __supported == ((xfeatures) | (dependencies)), \ + "supported = 0x%llx, xfeatures = 0x%llx, dependencies = 0x%llx", \ + __supported, (xfeatures), (dependencies)); \ } while (0) /* @@ -41,7 +41,8 @@ do { \ do { \ uint64_t __supported = (supported_xcr0) & (xfeatures); \ \ - GUEST_ASSERT_2(!__supported || __supported == (xfeatures), \ + __GUEST_ASSERT(!__supported || __supported == (xfeatures), \ + "supported = 0x%llx, xfeatures = 0x%llx", \ __supported, (xfeatures)); \ } while (0) @@ -79,14 +80,18 @@ static void guest_code(void) XFEATURE_MASK_XTILE); vector = xsetbv_safe(0, supported_xcr0); - GUEST_ASSERT_2(!vector, supported_xcr0, vector); + __GUEST_ASSERT(!vector, + "Expected success on XSETBV(0x%llx), got vector '0x%x'", + supported_xcr0, vector); for (i = 0; i < 64; i++) { if (supported_xcr0 & BIT_ULL(i)) continue; vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i)); - GUEST_ASSERT_3(vector == GP_VECTOR, supported_xcr0, vector, BIT_ULL(i)); + __GUEST_ASSERT(vector == GP_VECTOR, + "Expected #GP on XSETBV(0x%llx), supported XCR0 = %llx, got vector '0x%x'", + BIT_ULL(i), supported_xcr0, vector); } GUEST_DONE(); @@ -117,7 +122,7 @@ int main(int argc, char *argv[]) switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - REPORT_GUEST_ASSERT_3(uc, "0x%lx 0x%lx 0x%lx"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c index c94cde3b523f..e149d0574961 100644 --- a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c +++ b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c @@ -108,16 +108,16 @@ int main(int argc, char *argv[]) vcpu_run(vcpu); if (run->exit_reason == KVM_EXIT_XEN) { - ASSERT_EQ(run->xen.type, KVM_EXIT_XEN_HCALL); - ASSERT_EQ(run->xen.u.hcall.cpl, 0); - ASSERT_EQ(run->xen.u.hcall.longmode, 1); - ASSERT_EQ(run->xen.u.hcall.input, INPUTVALUE); - ASSERT_EQ(run->xen.u.hcall.params[0], ARGVALUE(1)); - ASSERT_EQ(run->xen.u.hcall.params[1], ARGVALUE(2)); - ASSERT_EQ(run->xen.u.hcall.params[2], ARGVALUE(3)); - ASSERT_EQ(run->xen.u.hcall.params[3], ARGVALUE(4)); - ASSERT_EQ(run->xen.u.hcall.params[4], ARGVALUE(5)); - ASSERT_EQ(run->xen.u.hcall.params[5], ARGVALUE(6)); + TEST_ASSERT_EQ(run->xen.type, KVM_EXIT_XEN_HCALL); + TEST_ASSERT_EQ(run->xen.u.hcall.cpl, 0); + TEST_ASSERT_EQ(run->xen.u.hcall.longmode, 1); + TEST_ASSERT_EQ(run->xen.u.hcall.input, INPUTVALUE); + TEST_ASSERT_EQ(run->xen.u.hcall.params[0], ARGVALUE(1)); + TEST_ASSERT_EQ(run->xen.u.hcall.params[1], ARGVALUE(2)); + TEST_ASSERT_EQ(run->xen.u.hcall.params[2], ARGVALUE(3)); + TEST_ASSERT_EQ(run->xen.u.hcall.params[3], ARGVALUE(4)); + TEST_ASSERT_EQ(run->xen.u.hcall.params[4], ARGVALUE(5)); + TEST_ASSERT_EQ(run->xen.u.hcall.params[5], ARGVALUE(6)); run->xen.u.hcall.result = RETVALUE; continue; } diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 83d565569512..251594306d40 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -113,7 +113,7 @@ static bool supports_filesystem(const char *const filesystem) { char str[32]; int len; - bool res; + bool res = true; FILE *const inf = fopen("/proc/filesystems", "r"); /* @@ -125,14 +125,16 @@ static bool supports_filesystem(const char *const filesystem) /* filesystem can be null for bind mounts. */ if (!filesystem) - return true; + goto out; len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); if (len >= sizeof(str)) /* Ignores too-long filesystem names. */ - return true; + goto out; res = fgrep(inf, str); + +out: fclose(inf); return res; } diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index d17854285f2b..118e0964bda9 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -106,7 +106,7 @@ endef run_tests: all ifdef building_out_of_srctree @if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \ - rsync -aLq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT); \ + rsync -aq --copy-unsafe-links $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT); \ fi @if [ "X$(TEST_PROGS)" != "X" ]; then \ $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) \ @@ -120,7 +120,7 @@ endif define INSTALL_SINGLE_RULE $(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH)) - $(if $(INSTALL_LIST),rsync -aL $(INSTALL_LIST) $(INSTALL_PATH)/) + $(if $(INSTALL_LIST),rsync -a --copy-unsafe-links $(INSTALL_LIST) $(INSTALL_PATH)/) endef define INSTALL_RULE diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index dba0e8ba002f..3df008677239 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -18,6 +18,7 @@ #include <sys/syscall.h> #include <sys/wait.h> #include <unistd.h> +#include <ctype.h> #include "common.h" @@ -43,7 +44,6 @@ */ static size_t mfd_def_size = MFD_DEF_SIZE; static const char *memfd_str = MEMFD_STR; -static pid_t spawn_newpid_thread(unsigned int flags, int (*fn)(void *)); static int newpid_thread_fn2(void *arg); static void join_newpid_thread(pid_t pid); @@ -96,12 +96,12 @@ static void sysctl_assert_write(const char *val) int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC); if (fd < 0) { - printf("open sysctl failed\n"); + printf("open sysctl failed: %m\n"); abort(); } if (write(fd, val, strlen(val)) < 0) { - printf("write sysctl failed\n"); + printf("write sysctl %s failed: %m\n", val); abort(); } } @@ -111,7 +111,7 @@ static void sysctl_fail_write(const char *val) int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC); if (fd < 0) { - printf("open sysctl failed\n"); + printf("open sysctl failed: %m\n"); abort(); } @@ -122,6 +122,33 @@ static void sysctl_fail_write(const char *val) } } +static void sysctl_assert_equal(const char *val) +{ + char *p, buf[128] = {}; + int fd = open("/proc/sys/vm/memfd_noexec", O_RDONLY | O_CLOEXEC); + + if (fd < 0) { + printf("open sysctl failed: %m\n"); + abort(); + } + + if (read(fd, buf, sizeof(buf)) < 0) { + printf("read sysctl failed: %m\n"); + abort(); + } + + /* Strip trailing whitespace. */ + p = buf; + while (!isspace(*p)) + p++; + *p = '\0'; + + if (strcmp(buf, val) != 0) { + printf("unexpected sysctl value: expected %s, got %s\n", val, buf); + abort(); + } +} + static int mfd_assert_reopen_fd(int fd_in) { int fd; @@ -736,7 +763,7 @@ static int idle_thread_fn(void *arg) return 0; } -static pid_t spawn_idle_thread(unsigned int flags) +static pid_t spawn_thread(unsigned int flags, int (*fn)(void *), void *arg) { uint8_t *stack; pid_t pid; @@ -747,10 +774,7 @@ static pid_t spawn_idle_thread(unsigned int flags) abort(); } - pid = clone(idle_thread_fn, - stack + STACK_SIZE, - SIGCHLD | flags, - NULL); + pid = clone(fn, stack + STACK_SIZE, SIGCHLD | flags, arg); if (pid < 0) { printf("clone() failed: %m\n"); abort(); @@ -759,6 +783,33 @@ static pid_t spawn_idle_thread(unsigned int flags) return pid; } +static void join_thread(pid_t pid) +{ + int wstatus; + + if (waitpid(pid, &wstatus, 0) < 0) { + printf("newpid thread: waitpid() failed: %m\n"); + abort(); + } + + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { + printf("newpid thread: exited with non-zero error code %d\n", + WEXITSTATUS(wstatus)); + abort(); + } + + if (WIFSIGNALED(wstatus)) { + printf("newpid thread: killed by signal %d\n", + WTERMSIG(wstatus)); + abort(); + } +} + +static pid_t spawn_idle_thread(unsigned int flags) +{ + return spawn_thread(flags, idle_thread_fn, NULL); +} + static void join_idle_thread(pid_t pid) { kill(pid, SIGTERM); @@ -1111,109 +1162,260 @@ static void test_noexec_seal(void) close(fd); } -static void test_sysctl_child(void) +static void test_sysctl_sysctl0(void) { int fd; - int pid; - printf("%s sysctl 0\n", memfd_str); - sysctl_assert_write("0"); - fd = mfd_assert_new("kern_memfd_sysctl_0", + sysctl_assert_equal("0"); + + fd = mfd_assert_new("kern_memfd_sysctl_0_dfl", mfd_def_size, MFD_CLOEXEC | MFD_ALLOW_SEALING); - mfd_assert_mode(fd, 0777); mfd_assert_has_seals(fd, 0); mfd_assert_chmod(fd, 0644); close(fd); +} - printf("%s sysctl 1\n", memfd_str); - sysctl_assert_write("1"); - fd = mfd_assert_new("kern_memfd_sysctl_1", +static void test_sysctl_set_sysctl0(void) +{ + sysctl_assert_write("0"); + test_sysctl_sysctl0(); +} + +static void test_sysctl_sysctl1(void) +{ + int fd; + + sysctl_assert_equal("1"); + + fd = mfd_assert_new("kern_memfd_sysctl_1_dfl", mfd_def_size, MFD_CLOEXEC | MFD_ALLOW_SEALING); + mfd_assert_mode(fd, 0666); + mfd_assert_has_seals(fd, F_SEAL_EXEC); + mfd_fail_chmod(fd, 0777); + close(fd); - printf("%s child ns\n", memfd_str); - pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn2); - join_newpid_thread(pid); + fd = mfd_assert_new("kern_memfd_sysctl_1_exec", + mfd_def_size, + MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING); + mfd_assert_mode(fd, 0777); + mfd_assert_has_seals(fd, 0); + mfd_assert_chmod(fd, 0644); + close(fd); + fd = mfd_assert_new("kern_memfd_sysctl_1_noexec", + mfd_def_size, + MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING); mfd_assert_mode(fd, 0666); mfd_assert_has_seals(fd, F_SEAL_EXEC); mfd_fail_chmod(fd, 0777); - sysctl_fail_write("0"); close(fd); - - printf("%s sysctl 2\n", memfd_str); - sysctl_assert_write("2"); - mfd_fail_new("kern_memfd_sysctl_2", - MFD_CLOEXEC | MFD_ALLOW_SEALING); - sysctl_fail_write("0"); - sysctl_fail_write("1"); } -static int newpid_thread_fn(void *arg) +static void test_sysctl_set_sysctl1(void) { - test_sysctl_child(); - return 0; + sysctl_assert_write("1"); + test_sysctl_sysctl1(); } -static void test_sysctl_child2(void) +static void test_sysctl_sysctl2(void) { int fd; - sysctl_fail_write("0"); - fd = mfd_assert_new("kern_memfd_sysctl_1", + sysctl_assert_equal("2"); + + fd = mfd_assert_new("kern_memfd_sysctl_2_dfl", mfd_def_size, MFD_CLOEXEC | MFD_ALLOW_SEALING); + mfd_assert_mode(fd, 0666); + mfd_assert_has_seals(fd, F_SEAL_EXEC); + mfd_fail_chmod(fd, 0777); + close(fd); + + mfd_fail_new("kern_memfd_sysctl_2_exec", + MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING); + fd = mfd_assert_new("kern_memfd_sysctl_2_noexec", + mfd_def_size, + MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING); mfd_assert_mode(fd, 0666); mfd_assert_has_seals(fd, F_SEAL_EXEC); mfd_fail_chmod(fd, 0777); close(fd); } -static int newpid_thread_fn2(void *arg) +static void test_sysctl_set_sysctl2(void) +{ + sysctl_assert_write("2"); + test_sysctl_sysctl2(); +} + +static int sysctl_simple_child(void *arg) +{ + int fd; + int pid; + + printf("%s sysctl 0\n", memfd_str); + test_sysctl_set_sysctl0(); + + printf("%s sysctl 1\n", memfd_str); + test_sysctl_set_sysctl1(); + + printf("%s sysctl 0\n", memfd_str); + test_sysctl_set_sysctl0(); + + printf("%s sysctl 2\n", memfd_str); + test_sysctl_set_sysctl2(); + + printf("%s sysctl 1\n", memfd_str); + test_sysctl_set_sysctl1(); + + printf("%s sysctl 0\n", memfd_str); + test_sysctl_set_sysctl0(); + + return 0; +} + +/* + * Test sysctl + * A very basic test to make sure the core sysctl semantics work. + */ +static void test_sysctl_simple(void) +{ + int pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL); + + join_thread(pid); +} + +static int sysctl_nested(void *arg) { - test_sysctl_child2(); + void (*fn)(void) = arg; + + fn(); return 0; } -static pid_t spawn_newpid_thread(unsigned int flags, int (*fn)(void *)) + +static int sysctl_nested_wait(void *arg) { - uint8_t *stack; - pid_t pid; + /* Wait for a SIGCONT. */ + kill(getpid(), SIGSTOP); + return sysctl_nested(arg); +} - stack = malloc(STACK_SIZE); - if (!stack) { - printf("malloc(STACK_SIZE) failed: %m\n"); - abort(); - } +static void test_sysctl_sysctl1_failset(void) +{ + sysctl_fail_write("0"); + test_sysctl_sysctl1(); +} - pid = clone(fn, - stack + STACK_SIZE, - SIGCHLD | flags, - NULL); - if (pid < 0) { - printf("clone() failed: %m\n"); - abort(); - } +static void test_sysctl_sysctl2_failset(void) +{ + sysctl_fail_write("1"); + test_sysctl_sysctl2(); - return pid; + sysctl_fail_write("0"); + test_sysctl_sysctl2(); } -static void join_newpid_thread(pid_t pid) +static int sysctl_nested_child(void *arg) { - waitpid(pid, NULL, 0); + int fd; + int pid; + + printf("%s nested sysctl 0\n", memfd_str); + sysctl_assert_write("0"); + /* A further nested pidns works the same. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL); + join_thread(pid); + + printf("%s nested sysctl 1\n", memfd_str); + sysctl_assert_write("1"); + /* Child inherits our setting. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl1); + join_thread(pid); + /* Child cannot raise the setting. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, + test_sysctl_sysctl1_failset); + join_thread(pid); + /* Child can lower the setting. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, + test_sysctl_set_sysctl2); + join_thread(pid); + /* Child lowering the setting has no effect on our setting. */ + test_sysctl_sysctl1(); + + printf("%s nested sysctl 2\n", memfd_str); + sysctl_assert_write("2"); + /* Child inherits our setting. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl2); + join_thread(pid); + /* Child cannot raise the setting. */ + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, + test_sysctl_sysctl2_failset); + join_thread(pid); + + /* Verify that the rules are actually inherited after fork. */ + printf("%s nested sysctl 0 -> 1 after fork\n", memfd_str); + sysctl_assert_write("0"); + + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, + test_sysctl_sysctl1_failset); + sysctl_assert_write("1"); + kill(pid, SIGCONT); + join_thread(pid); + + printf("%s nested sysctl 0 -> 2 after fork\n", memfd_str); + sysctl_assert_write("0"); + + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, + test_sysctl_sysctl2_failset); + sysctl_assert_write("2"); + kill(pid, SIGCONT); + join_thread(pid); + + /* + * Verify that the current effective setting is saved on fork, meaning + * that the parent lowering the sysctl doesn't affect already-forked + * children. + */ + printf("%s nested sysctl 2 -> 1 after fork\n", memfd_str); + sysctl_assert_write("2"); + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, + test_sysctl_sysctl2); + sysctl_assert_write("1"); + kill(pid, SIGCONT); + join_thread(pid); + + printf("%s nested sysctl 2 -> 0 after fork\n", memfd_str); + sysctl_assert_write("2"); + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, + test_sysctl_sysctl2); + sysctl_assert_write("0"); + kill(pid, SIGCONT); + join_thread(pid); + + printf("%s nested sysctl 1 -> 0 after fork\n", memfd_str); + sysctl_assert_write("1"); + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, + test_sysctl_sysctl1); + sysctl_assert_write("0"); + kill(pid, SIGCONT); + join_thread(pid); + + return 0; } /* - * Test sysctl - * A very basic sealing test to see whether setting/retrieving seals works. + * Test sysctl with nested pid namespaces + * Make sure that the sysctl nesting semantics work correctly. */ -static void test_sysctl(void) +static void test_sysctl_nested(void) { - int pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn); + int pid = spawn_thread(CLONE_NEWPID, sysctl_nested_child, NULL); - join_newpid_thread(pid); + join_thread(pid); } /* @@ -1399,6 +1601,9 @@ int main(int argc, char **argv) test_seal_grow(); test_seal_resize(); + test_sysctl_simple(); + test_sysctl_nested(); + test_share_dup("SHARE-DUP", ""); test_share_mmap("SHARE-MMAP", ""); test_share_open("SHARE-OPEN", ""); @@ -1413,8 +1618,6 @@ int main(int argc, char **argv) test_share_fork("SHARE-FORK", SHARED_FT_STR); join_idle_thread(pid); - test_sysctl(); - printf("memfd: DONE\n"); return 0; diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index 7e2a982383c0..cdc9ce4426b9 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -5,6 +5,7 @@ hugepage-mremap hugepage-shm hugepage-vmemmap hugetlb-madvise +hugetlb-read-hwpoison khugepaged map_hugetlb map_populate diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 66d7c07dc177..6a9fc5693145 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -35,39 +35,43 @@ MAKEFLAGS += --no-builtin-rules CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) LDLIBS = -lrt -lpthread -TEST_GEN_PROGS = cow -TEST_GEN_PROGS += compaction_test -TEST_GEN_PROGS += gup_longterm -TEST_GEN_PROGS += gup_test -TEST_GEN_PROGS += hmm-tests -TEST_GEN_PROGS += hugetlb-madvise -TEST_GEN_PROGS += hugepage-mmap -TEST_GEN_PROGS += hugepage-mremap -TEST_GEN_PROGS += hugepage-shm -TEST_GEN_PROGS += hugepage-vmemmap -TEST_GEN_PROGS += khugepaged -TEST_GEN_PROGS += madv_populate -TEST_GEN_PROGS += map_fixed_noreplace -TEST_GEN_PROGS += map_hugetlb -TEST_GEN_PROGS += map_populate -TEST_GEN_PROGS += memfd_secret -TEST_GEN_PROGS += migration -TEST_GEN_PROGS += mkdirty -TEST_GEN_PROGS += mlock-random-test -TEST_GEN_PROGS += mlock2-tests -TEST_GEN_PROGS += mrelease_test -TEST_GEN_PROGS += mremap_dontunmap -TEST_GEN_PROGS += mremap_test -TEST_GEN_PROGS += on-fault-limit -TEST_GEN_PROGS += thuge-gen -TEST_GEN_PROGS += transhuge-stress -TEST_GEN_PROGS += uffd-stress -TEST_GEN_PROGS += uffd-unit-tests +TEST_GEN_FILES = cow +TEST_GEN_FILES += compaction_test +TEST_GEN_FILES += gup_longterm +TEST_GEN_FILES += gup_test +TEST_GEN_FILES += hmm-tests +TEST_GEN_FILES += hugetlb-madvise +TEST_GEN_FILES += hugetlb-read-hwpoison +TEST_GEN_FILES += hugepage-mmap +TEST_GEN_FILES += hugepage-mremap +TEST_GEN_FILES += hugepage-shm +TEST_GEN_FILES += hugepage-vmemmap +TEST_GEN_FILES += khugepaged +TEST_GEN_FILES += madv_populate +TEST_GEN_FILES += map_fixed_noreplace +TEST_GEN_FILES += map_hugetlb +TEST_GEN_FILES += map_populate +TEST_GEN_FILES += memfd_secret +TEST_GEN_FILES += migration +TEST_GEN_FILES += mkdirty +TEST_GEN_FILES += mlock-random-test +TEST_GEN_FILES += mlock2-tests +TEST_GEN_FILES += mrelease_test +TEST_GEN_FILES += mremap_dontunmap +TEST_GEN_FILES += mremap_test +TEST_GEN_FILES += on-fault-limit +TEST_GEN_FILES += thuge-gen +TEST_GEN_FILES += transhuge-stress +TEST_GEN_FILES += uffd-stress +TEST_GEN_FILES += uffd-unit-tests +TEST_GEN_FILES += split_huge_page_test +TEST_GEN_FILES += ksm_tests +TEST_GEN_FILES += ksm_functional_tests +TEST_GEN_FILES += mdwe_test + +ifneq ($(ARCH),arm64) TEST_GEN_PROGS += soft-dirty -TEST_GEN_PROGS += split_huge_page_test -TEST_GEN_PROGS += ksm_tests -TEST_GEN_PROGS += ksm_functional_tests -TEST_GEN_PROGS += mdwe_test +endif ifeq ($(ARCH),x86_64) CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_32bit_program.c -m32) @@ -83,24 +87,24 @@ CFLAGS += -no-pie endif ifeq ($(CAN_BUILD_I386),1) -TEST_GEN_PROGS += $(BINARIES_32) +TEST_GEN_FILES += $(BINARIES_32) endif ifeq ($(CAN_BUILD_X86_64),1) -TEST_GEN_PROGS += $(BINARIES_64) +TEST_GEN_FILES += $(BINARIES_64) endif else ifneq (,$(findstring $(ARCH),ppc64)) -TEST_GEN_PROGS += protection_keys +TEST_GEN_FILES += protection_keys endif endif ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sparc64 x86_64)) -TEST_GEN_PROGS += va_high_addr_switch -TEST_GEN_PROGS += virtual_address_range -TEST_GEN_PROGS += write_to_hugetlbfs +TEST_GEN_FILES += va_high_addr_switch +TEST_GEN_FILES += virtual_address_range +TEST_GEN_FILES += write_to_hugetlbfs endif TEST_PROGS := run_vmtests.sh @@ -112,6 +116,7 @@ TEST_FILES += va_high_addr_switch.sh include ../lib.mk $(TEST_GEN_PROGS): vm_util.c +$(TEST_GEN_FILES): vm_util.c $(OUTPUT)/uffd-stress: uffd-common.c $(OUTPUT)/uffd-unit-tests: uffd-common.c diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh index a5cb4b09a46c..0899019a7fcb 100755 --- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh +++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh @@ -25,7 +25,7 @@ if [[ "$1" == "-cgroup-v2" ]]; then fi if [[ $cgroup2 ]]; then - cgroup_path=$(mount -t cgroup2 | head -1 | awk -e '{print $3}') + cgroup_path=$(mount -t cgroup2 | head -1 | awk '{print $3}') if [[ -z "$cgroup_path" ]]; then cgroup_path=/dev/cgroup/memory mount -t cgroup2 none $cgroup_path @@ -33,7 +33,7 @@ if [[ $cgroup2 ]]; then fi echo "+hugetlb" >$cgroup_path/cgroup.subtree_control else - cgroup_path=$(mount -t cgroup | grep ",hugetlb" | awk -e '{print $3}') + cgroup_path=$(mount -t cgroup | grep ",hugetlb" | awk '{print $3}') if [[ -z "$cgroup_path" ]]; then cgroup_path=/dev/cgroup/memory mount -t cgroup memory,hugetlb $cgroup_path diff --git a/tools/testing/selftests/mm/hugetlb-read-hwpoison.c b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c new file mode 100644 index 000000000000..ba6cc6f9cabc --- /dev/null +++ b/tools/testing/selftests/mm/hugetlb-read-hwpoison.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <linux/magic.h> +#include <sys/mman.h> +#include <sys/statfs.h> +#include <errno.h> +#include <stdbool.h> + +#include "../kselftest.h" + +#define PREFIX " ... " +#define ERROR_PREFIX " !!! " + +#define MAX_WRITE_READ_CHUNK_SIZE (getpagesize() * 16) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +enum test_status { + TEST_PASSED = 0, + TEST_FAILED = 1, + TEST_SKIPPED = 2, +}; + +static char *status_to_str(enum test_status status) +{ + switch (status) { + case TEST_PASSED: + return "TEST_PASSED"; + case TEST_FAILED: + return "TEST_FAILED"; + case TEST_SKIPPED: + return "TEST_SKIPPED"; + default: + return "TEST_???"; + } +} + +static int setup_filemap(char *filemap, size_t len, size_t wr_chunk_size) +{ + char iter = 0; + + for (size_t offset = 0; offset < len; + offset += wr_chunk_size) { + iter++; + memset(filemap + offset, iter, wr_chunk_size); + } + + return 0; +} + +static bool verify_chunk(char *buf, size_t len, char val) +{ + size_t i; + + for (i = 0; i < len; ++i) { + if (buf[i] != val) { + printf(PREFIX ERROR_PREFIX "check fail: buf[%lu] = %u != %u\n", + i, buf[i], val); + return false; + } + } + + return true; +} + +static bool seek_read_hugepage_filemap(int fd, size_t len, size_t wr_chunk_size, + off_t offset, size_t expected) +{ + char buf[MAX_WRITE_READ_CHUNK_SIZE]; + ssize_t ret_count = 0; + ssize_t total_ret_count = 0; + char val = offset / wr_chunk_size + offset % wr_chunk_size; + + printf(PREFIX PREFIX "init val=%u with offset=0x%lx\n", val, offset); + printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n", + expected); + if (lseek(fd, offset, SEEK_SET) < 0) { + perror(PREFIX ERROR_PREFIX "seek failed"); + return false; + } + + while (offset + total_ret_count < len) { + ret_count = read(fd, buf, wr_chunk_size); + if (ret_count == 0) { + printf(PREFIX PREFIX "read reach end of the file\n"); + break; + } else if (ret_count < 0) { + perror(PREFIX ERROR_PREFIX "read failed"); + break; + } + ++val; + if (!verify_chunk(buf, ret_count, val)) + return false; + + total_ret_count += ret_count; + } + printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n", + total_ret_count); + + return total_ret_count == expected; +} + +static bool read_hugepage_filemap(int fd, size_t len, + size_t wr_chunk_size, size_t expected) +{ + char buf[MAX_WRITE_READ_CHUNK_SIZE]; + ssize_t ret_count = 0; + ssize_t total_ret_count = 0; + char val = 0; + + printf(PREFIX PREFIX "expect to read 0x%lx bytes of data in total\n", + expected); + while (total_ret_count < len) { + ret_count = read(fd, buf, wr_chunk_size); + if (ret_count == 0) { + printf(PREFIX PREFIX "read reach end of the file\n"); + break; + } else if (ret_count < 0) { + perror(PREFIX ERROR_PREFIX "read failed"); + break; + } + ++val; + if (!verify_chunk(buf, ret_count, val)) + return false; + + total_ret_count += ret_count; + } + printf(PREFIX PREFIX "actually read 0x%lx bytes of data in total\n", + total_ret_count); + + return total_ret_count == expected; +} + +static enum test_status +test_hugetlb_read(int fd, size_t len, size_t wr_chunk_size) +{ + enum test_status status = TEST_SKIPPED; + char *filemap = NULL; + + if (ftruncate(fd, len) < 0) { + perror(PREFIX ERROR_PREFIX "ftruncate failed"); + return status; + } + + filemap = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, fd, 0); + if (filemap == MAP_FAILED) { + perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed"); + goto done; + } + + setup_filemap(filemap, len, wr_chunk_size); + status = TEST_FAILED; + + if (read_hugepage_filemap(fd, len, wr_chunk_size, len)) + status = TEST_PASSED; + + munmap(filemap, len); +done: + if (ftruncate(fd, 0) < 0) { + perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed"); + status = TEST_FAILED; + } + + return status; +} + +static enum test_status +test_hugetlb_read_hwpoison(int fd, size_t len, size_t wr_chunk_size, + bool skip_hwpoison_page) +{ + enum test_status status = TEST_SKIPPED; + char *filemap = NULL; + char *hwp_addr = NULL; + const unsigned long pagesize = getpagesize(); + + if (ftruncate(fd, len) < 0) { + perror(PREFIX ERROR_PREFIX "ftruncate failed"); + return status; + } + + filemap = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, fd, 0); + if (filemap == MAP_FAILED) { + perror(PREFIX ERROR_PREFIX "mmap for primary mapping failed"); + goto done; + } + + setup_filemap(filemap, len, wr_chunk_size); + status = TEST_FAILED; + + /* + * Poisoned hugetlb page layout (assume hugepagesize=2MB): + * |<---------------------- 1MB ---------------------->| + * |<---- healthy page ---->|<---- HWPOISON page ----->| + * |<------------------- (1MB - 8KB) ----------------->| + */ + hwp_addr = filemap + len / 2 + pagesize; + if (madvise(hwp_addr, pagesize, MADV_HWPOISON) < 0) { + perror(PREFIX ERROR_PREFIX "MADV_HWPOISON failed"); + goto unmap; + } + + if (!skip_hwpoison_page) { + /* + * Userspace should be able to read (1MB + 1 page) from + * the beginning of the HWPOISONed hugepage. + */ + if (read_hugepage_filemap(fd, len, wr_chunk_size, + len / 2 + pagesize)) + status = TEST_PASSED; + } else { + /* + * Userspace should be able to read (1MB - 2 pages) from + * HWPOISONed hugepage. + */ + if (seek_read_hugepage_filemap(fd, len, wr_chunk_size, + len / 2 + MAX(2 * pagesize, wr_chunk_size), + len / 2 - MAX(2 * pagesize, wr_chunk_size))) + status = TEST_PASSED; + } + +unmap: + munmap(filemap, len); +done: + if (ftruncate(fd, 0) < 0) { + perror(PREFIX ERROR_PREFIX "ftruncate back to 0 failed"); + status = TEST_FAILED; + } + + return status; +} + +static int create_hugetlbfs_file(struct statfs *file_stat) +{ + int fd; + + fd = memfd_create("hugetlb_tmp", MFD_HUGETLB); + if (fd < 0) { + perror(PREFIX ERROR_PREFIX "could not open hugetlbfs file"); + return -1; + } + + memset(file_stat, 0, sizeof(*file_stat)); + if (fstatfs(fd, file_stat)) { + perror(PREFIX ERROR_PREFIX "fstatfs failed"); + goto close; + } + if (file_stat->f_type != HUGETLBFS_MAGIC) { + printf(PREFIX ERROR_PREFIX "not hugetlbfs file\n"); + goto close; + } + + return fd; +close: + close(fd); + return -1; +} + +int main(void) +{ + int fd; + struct statfs file_stat; + enum test_status status; + /* Test read() in different granularity. */ + size_t wr_chunk_sizes[] = { + getpagesize() / 2, getpagesize(), + getpagesize() * 2, getpagesize() * 4 + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(wr_chunk_sizes); ++i) { + printf("Write/read chunk size=0x%lx\n", + wr_chunk_sizes[i]); + + fd = create_hugetlbfs_file(&file_stat); + if (fd < 0) + goto create_failure; + printf(PREFIX "HugeTLB read regression test...\n"); + status = test_hugetlb_read(fd, file_stat.f_bsize, + wr_chunk_sizes[i]); + printf(PREFIX "HugeTLB read regression test...%s\n", + status_to_str(status)); + close(fd); + if (status == TEST_FAILED) + return -1; + + fd = create_hugetlbfs_file(&file_stat); + if (fd < 0) + goto create_failure; + printf(PREFIX "HugeTLB read HWPOISON test...\n"); + status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize, + wr_chunk_sizes[i], false); + printf(PREFIX "HugeTLB read HWPOISON test...%s\n", + status_to_str(status)); + close(fd); + if (status == TEST_FAILED) + return -1; + + fd = create_hugetlbfs_file(&file_stat); + if (fd < 0) + goto create_failure; + printf(PREFIX "HugeTLB seek then read HWPOISON test...\n"); + status = test_hugetlb_read_hwpoison(fd, file_stat.f_bsize, + wr_chunk_sizes[i], true); + printf(PREFIX "HugeTLB seek then read HWPOISON test...%s\n", + status_to_str(status)); + close(fd); + if (status == TEST_FAILED) + return -1; + } + + return 0; + +create_failure: + printf(ERROR_PREFIX "Abort test: failed to create hugetlbfs file\n"); + return -1; +} diff --git a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh index bf2d2a684edf..14d26075c863 100755 --- a/tools/testing/selftests/mm/hugetlb_reparenting_test.sh +++ b/tools/testing/selftests/mm/hugetlb_reparenting_test.sh @@ -20,7 +20,7 @@ fi if [[ $cgroup2 ]]; then - CGROUP_ROOT=$(mount -t cgroup2 | head -1 | awk -e '{print $3}') + CGROUP_ROOT=$(mount -t cgroup2 | head -1 | awk '{print $3}') if [[ -z "$CGROUP_ROOT" ]]; then CGROUP_ROOT=/dev/cgroup/memory mount -t cgroup2 none $CGROUP_ROOT @@ -28,7 +28,7 @@ if [[ $cgroup2 ]]; then fi echo "+hugetlb +memory" >$CGROUP_ROOT/cgroup.subtree_control else - CGROUP_ROOT=$(mount -t cgroup | grep ",hugetlb" | awk -e '{print $3}') + CGROUP_ROOT=$(mount -t cgroup | grep ",hugetlb" | awk '{print $3}') if [[ -z "$CGROUP_ROOT" ]]; then CGROUP_ROOT=/dev/cgroup/memory mount -t cgroup memory,hugetlb $CGROUP_ROOT diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c index 26853badae70..901e950f9138 100644 --- a/tools/testing/selftests/mm/ksm_functional_tests.c +++ b/tools/testing/selftests/mm/ksm_functional_tests.c @@ -27,8 +27,12 @@ #define KiB 1024u #define MiB (1024 * KiB) +static int mem_fd; static int ksm_fd; static int ksm_full_scans_fd; +static int proc_self_ksm_stat_fd; +static int proc_self_ksm_merging_pages_fd; +static int ksm_use_zero_pages_fd; static int pagemap_fd; static size_t pagesize; @@ -59,6 +63,49 @@ static bool range_maps_duplicates(char *addr, unsigned long size) return false; } +static long get_my_ksm_zero_pages(void) +{ + char buf[200]; + char *substr_ksm_zero; + size_t value_pos; + ssize_t read_size; + unsigned long my_ksm_zero_pages; + + if (!proc_self_ksm_stat_fd) + return 0; + + read_size = pread(proc_self_ksm_stat_fd, buf, sizeof(buf) - 1, 0); + if (read_size < 0) + return -errno; + + buf[read_size] = 0; + + substr_ksm_zero = strstr(buf, "ksm_zero_pages"); + if (!substr_ksm_zero) + return 0; + + value_pos = strcspn(substr_ksm_zero, "0123456789"); + my_ksm_zero_pages = strtol(substr_ksm_zero + value_pos, NULL, 10); + + return my_ksm_zero_pages; +} + +static long get_my_merging_pages(void) +{ + char buf[10]; + ssize_t ret; + + if (proc_self_ksm_merging_pages_fd < 0) + return proc_self_ksm_merging_pages_fd; + + ret = pread(proc_self_ksm_merging_pages_fd, buf, sizeof(buf) - 1, 0); + if (ret <= 0) + return -errno; + buf[ret] = 0; + + return strtol(buf, NULL, 10); +} + static long ksm_get_full_scans(void) { char buf[10]; @@ -91,11 +138,30 @@ static int ksm_merge(void) return 0; } -static char *mmap_and_merge_range(char val, unsigned long size, bool use_prctl) +static int ksm_unmerge(void) +{ + if (write(ksm_fd, "2", 1) != 1) + return -errno; + return 0; +} + +static char *mmap_and_merge_range(char val, unsigned long size, int prot, + bool use_prctl) { char *map; int ret; + /* Stabilize accounting by disabling KSM completely. */ + if (ksm_unmerge()) { + ksft_test_result_fail("Disabling (unmerging) KSM failed\n"); + goto unmap; + } + + if (get_my_merging_pages() > 0) { + ksft_test_result_fail("Still pages merged\n"); + goto unmap; + } + map = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (map == MAP_FAILED) { @@ -112,6 +178,11 @@ static char *mmap_and_merge_range(char val, unsigned long size, bool use_prctl) /* Make sure each page contains the same values to merge them. */ memset(map, val, size); + if (mprotect(map, size, prot)) { + ksft_test_result_skip("mprotect() failed\n"); + goto unmap; + } + if (use_prctl) { ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0); if (ret < 0 && errno == EINVAL) { @@ -131,6 +202,16 @@ static char *mmap_and_merge_range(char val, unsigned long size, bool use_prctl) ksft_test_result_fail("Running KSM failed\n"); goto unmap; } + + /* + * Check if anything was merged at all. Ignore the zero page that is + * accounted differently (depending on kernel support). + */ + if (val && !get_my_merging_pages()) { + ksft_test_result_fail("No pages got merged\n"); + goto unmap; + } + return map; unmap: munmap(map, size); @@ -144,7 +225,7 @@ static void test_unmerge(void) ksft_print_msg("[RUN] %s\n", __func__); - map = mmap_and_merge_range(0xcf, size, false); + map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); if (map == MAP_FAILED) return; @@ -159,6 +240,70 @@ unmap: munmap(map, size); } +static void test_unmerge_zero_pages(void) +{ + const unsigned int size = 2 * MiB; + char *map; + unsigned int offs; + unsigned long pages_expected; + + ksft_print_msg("[RUN] %s\n", __func__); + + if (proc_self_ksm_stat_fd < 0) { + ksft_test_result_skip("open(\"/proc/self/ksm_stat\") failed\n"); + return; + } + if (ksm_use_zero_pages_fd < 0) { + ksft_test_result_skip("open \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n"); + return; + } + if (write(ksm_use_zero_pages_fd, "1", 1) != 1) { + ksft_test_result_skip("write \"/sys/kernel/mm/ksm/use_zero_pages\" failed\n"); + return; + } + + /* Let KSM deduplicate zero pages. */ + map = mmap_and_merge_range(0x00, size, PROT_READ | PROT_WRITE, false); + if (map == MAP_FAILED) + return; + + /* Check if ksm_zero_pages is updated correctly after KSM merging */ + pages_expected = size / pagesize; + if (pages_expected != get_my_ksm_zero_pages()) { + ksft_test_result_fail("'ksm_zero_pages' updated after merging\n"); + goto unmap; + } + + /* Try to unmerge half of the region */ + if (madvise(map, size / 2, MADV_UNMERGEABLE)) { + ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); + goto unmap; + } + + /* Check if ksm_zero_pages is updated correctly after unmerging */ + pages_expected /= 2; + if (pages_expected != get_my_ksm_zero_pages()) { + ksft_test_result_fail("'ksm_zero_pages' updated after unmerging\n"); + goto unmap; + } + + /* Trigger unmerging of the other half by writing to the pages. */ + for (offs = size / 2; offs < size; offs += pagesize) + *((unsigned int *)&map[offs]) = offs; + + /* Now we should have no zeropages remaining. */ + if (get_my_ksm_zero_pages()) { + ksft_test_result_fail("'ksm_zero_pages' updated after write fault\n"); + goto unmap; + } + + /* Check if ksm zero pages are really unmerged */ + ksft_test_result(!range_maps_duplicates(map, size), + "KSM zero pages were unmerged\n"); +unmap: + munmap(map, size); +} + static void test_unmerge_discarded(void) { const unsigned int size = 2 * MiB; @@ -166,7 +311,7 @@ static void test_unmerge_discarded(void) ksft_print_msg("[RUN] %s\n", __func__); - map = mmap_and_merge_range(0xcf, size, false); + map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); if (map == MAP_FAILED) return; @@ -198,7 +343,7 @@ static void test_unmerge_uffd_wp(void) ksft_print_msg("[RUN] %s\n", __func__); - map = mmap_and_merge_range(0xcf, size, false); + map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, false); if (map == MAP_FAILED) return; @@ -341,7 +486,7 @@ static void test_prctl_unmerge(void) ksft_print_msg("[RUN] %s\n", __func__); - map = mmap_and_merge_range(0xcf, size, true); + map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, true); if (map == MAP_FAILED) return; @@ -356,9 +501,42 @@ unmap: munmap(map, size); } +static void test_prot_none(void) +{ + const unsigned int size = 2 * MiB; + char *map; + int i; + + ksft_print_msg("[RUN] %s\n", __func__); + + map = mmap_and_merge_range(0x11, size, PROT_NONE, false); + if (map == MAP_FAILED) + goto unmap; + + /* Store a unique value in each page on one half using ptrace */ + for (i = 0; i < size / 2; i += pagesize) { + lseek(mem_fd, (uintptr_t) map + i, SEEK_SET); + if (write(mem_fd, &i, sizeof(i)) != sizeof(i)) { + ksft_test_result_fail("ptrace write failed\n"); + goto unmap; + } + } + + /* Trigger unsharing on the other half. */ + if (madvise(map + size / 2, size / 2, MADV_UNMERGEABLE)) { + ksft_test_result_fail("MADV_UNMERGEABLE failed\n"); + goto unmap; + } + + ksft_test_result(!range_maps_duplicates(map, size), + "Pages were unmerged\n"); +unmap: + munmap(map, size); +} + int main(int argc, char **argv) { - unsigned int tests = 5; + unsigned int tests = 7; int err; #ifdef __NR_userfaultfd @@ -370,6 +548,9 @@ int main(int argc, char **argv) pagesize = getpagesize(); + mem_fd = open("/proc/self/mem", O_RDWR); + if (mem_fd < 0) + ksft_exit_fail_msg("opening /proc/self/mem failed\n"); ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR); if (ksm_fd < 0) ksft_exit_skip("open(\"/sys/kernel/mm/ksm/run\") failed\n"); @@ -379,13 +560,20 @@ int main(int argc, char **argv) pagemap_fd = open("/proc/self/pagemap", O_RDONLY); if (pagemap_fd < 0) ksft_exit_skip("open(\"/proc/self/pagemap\") failed\n"); + proc_self_ksm_stat_fd = open("/proc/self/ksm_stat", O_RDONLY); + proc_self_ksm_merging_pages_fd = open("/proc/self/ksm_merging_pages", + O_RDONLY); + ksm_use_zero_pages_fd = open("/sys/kernel/mm/ksm/use_zero_pages", O_RDWR); test_unmerge(); + test_unmerge_zero_pages(); test_unmerge_discarded(); #ifdef __NR_userfaultfd test_unmerge_uffd_wp(); #endif + test_prot_none(); + test_prctl(); test_prctl_fork(); test_prctl_unmerge(); diff --git a/tools/testing/selftests/mm/madv_populate.c b/tools/testing/selftests/mm/madv_populate.c index 60547245e479..17bcb07f19f3 100644 --- a/tools/testing/selftests/mm/madv_populate.c +++ b/tools/testing/selftests/mm/madv_populate.c @@ -264,14 +264,35 @@ static void test_softdirty(void) munmap(addr, SIZE); } +static int system_has_softdirty(void) +{ + /* + * There is no way to check if the kernel supports soft-dirty, other + * than by writing to a page and seeing if the bit was set. But the + * tests are intended to check that the bit gets set when it should, so + * doing that check would turn a potentially legitimate fail into a + * skip. Fortunately, we know for sure that arm64 does not support + * soft-dirty. So for now, let's just use the arch as a corse guide. + */ +#if defined(__aarch64__) + return 0; +#else + return 1; +#endif +} + int main(int argc, char **argv) { + int nr_tests = 16; int err; pagesize = getpagesize(); + if (system_has_softdirty()) + nr_tests += 5; + ksft_print_header(); - ksft_set_plan(21); + ksft_set_plan(nr_tests); sense_support(); test_prot_read(); @@ -279,7 +300,8 @@ int main(int argc, char **argv) test_holes(); test_populate_read(); test_populate_write(); - test_softdirty(); + if (system_has_softdirty()) + test_softdirty(); err = ksft_get_fail_cnt(); if (err) diff --git a/tools/testing/selftests/mm/map_populate.c b/tools/testing/selftests/mm/map_populate.c index 240f2d9dae7a..7945d0754875 100644 --- a/tools/testing/selftests/mm/map_populate.c +++ b/tools/testing/selftests/mm/map_populate.c @@ -77,7 +77,7 @@ int main(int argc, char **argv) unsigned long *smap; ftmp = tmpfile(); - BUG_ON(ftmp == 0, "tmpfile()"); + BUG_ON(!ftmp, "tmpfile()"); ret = ftruncate(fileno(ftmp), MMAP_SZ); BUG_ON(ret, "ftruncate()"); diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c index 379581567f27..6908569ef406 100644 --- a/tools/testing/selftests/mm/migration.c +++ b/tools/testing/selftests/mm/migration.c @@ -10,12 +10,13 @@ #include <numa.h> #include <numaif.h> #include <sys/mman.h> +#include <sys/prctl.h> #include <sys/types.h> #include <signal.h> #include <time.h> #define TWOMEG (2<<20) -#define RUNTIME (60) +#define RUNTIME (20) #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) @@ -155,10 +156,15 @@ TEST_F_TIMEOUT(migration, shared_anon, 2*RUNTIME) memset(ptr, 0xde, TWOMEG); for (i = 0; i < self->nthreads - 1; i++) { pid = fork(); - if (!pid) + if (!pid) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + /* Parent may have died before prctl so check now. */ + if (getppid() == 1) + kill(getpid(), SIGHUP); access_mem(ptr); - else + } else { self->pids[i] = pid; + } } ASSERT_EQ(migrate(ptr, self->n1, self->n2), 0); diff --git a/tools/testing/selftests/mm/mrelease_test.c b/tools/testing/selftests/mm/mrelease_test.c index dca21042b679..d822004a374e 100644 --- a/tools/testing/selftests/mm/mrelease_test.c +++ b/tools/testing/selftests/mm/mrelease_test.c @@ -7,6 +7,7 @@ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> +#include <sys/syscall.h> #include <sys/wait.h> #include <unistd.h> #include <asm-generic/unistd.h> diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index 3f26f6e15b2a..3e2bc818d566 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -12,11 +12,14 @@ exitcode=0 usage() { cat <<EOF -usage: ${BASH_SOURCE[0]:-$0} [ -h | -t "<categories>"] +usage: ${BASH_SOURCE[0]:-$0} [ options ] + + -a: run all tests, including extra ones -t: specify specific categories to tests to run -h: display this message -The default behavior is to run all tests. +The default behavior is to run required tests only. If -a is specified, +will run all tests. Alternatively, specific groups tests can be run by passing a string to the -t argument containing one or more of the following categories @@ -55,14 +58,27 @@ separated by spaces: test soft dirty page bit semantics - cow test copy-on-write semantics +- thp + test transparent huge pages +- migration + invoke move_pages(2) to exercise the migration entry code + paths in the kernel +- mkdirty + test handling of code that might set PTE/PMD dirty in + read-only VMAs +- mdwe + test prctl(PR_SET_MDWE, ...) + example: ./run_vmtests.sh -t "hmm mmap ksm" EOF exit 0 } +RUN_ALL=false -while getopts "ht:" OPT; do +while getopts "aht:" OPT; do case ${OPT} in + "a") RUN_ALL=true ;; "h") usage ;; "t") VM_SELFTEST_ITEMS=${OPTARG} ;; esac @@ -85,6 +101,30 @@ test_selected() { fi } +run_gup_matrix() { + # -t: thp=on, -T: thp=off, -H: hugetlb=on + local hugetlb_mb=$(( needmem_KB / 1024 )) + + for huge in -t -T "-H -m $hugetlb_mb"; do + # -u: gup-fast, -U: gup-basic, -a: pin-fast, -b: pin-basic, -L: pin-longterm + for test_cmd in -u -U -a -b -L; do + # -w: write=1, -W: write=0 + for write in -w -W; do + # -S: shared + for share in -S " "; do + # -n: How many pages to fetch together? 512 is special + # because it's default thp size (or 2M on x86), 123 to + # just test partial gup when hit a huge in whatever form + for num in "-n 1" "-n 512" "-n 123"; do + CATEGORY="gup_test" run_test ./gup_test \ + $huge $test_cmd $write $share $num + done + done + done + done + done +} + # get huge pagesize and freepages from /proc/meminfo while read -r name size unit; do if [ "$name" = "HugePages_Free:" ]; then @@ -189,13 +229,16 @@ fi CATEGORY="mmap" run_test ./map_fixed_noreplace -# get_user_pages_fast() benchmark -CATEGORY="gup_test" run_test ./gup_test -u -# pin_user_pages_fast() benchmark -CATEGORY="gup_test" run_test ./gup_test -a +if $RUN_ALL; then + run_gup_matrix +else + # get_user_pages_fast() benchmark + CATEGORY="gup_test" run_test ./gup_test -u + # pin_user_pages_fast() benchmark + CATEGORY="gup_test" run_test ./gup_test -a +fi # Dump pages 0, 19, and 4096, using pin_user_pages: CATEGORY="gup_test" run_test ./gup_test -ct -F 0x1 0 19 0x1000 - CATEGORY="gup_test" run_test ./gup_longterm CATEGORY="userfaultfd" run_test ./uffd-unit-tests @@ -262,6 +305,10 @@ CATEGORY="madv_populate" run_test ./madv_populate CATEGORY="memfd_secret" run_test ./memfd_secret +# KSM KSM_MERGE_TIME_HUGE_PAGES test with size of 100 +CATEGORY="ksm" run_test ./ksm_tests -H -s 100 +# KSM KSM_MERGE_TIME test with size of 100 +CATEGORY="ksm" run_test ./ksm_tests -P -s 100 # KSM MADV_MERGEABLE test with 10 identical pages CATEGORY="ksm" run_test ./ksm_tests -M -p 10 # KSM unmerge test @@ -290,11 +337,26 @@ then CATEGORY="pkey" run_test ./protection_keys_64 fi -CATEGORY="soft_dirty" run_test ./soft-dirty +if [ -x ./soft-dirty ] +then + CATEGORY="soft_dirty" run_test ./soft-dirty +fi # COW tests CATEGORY="cow" run_test ./cow +CATEGORY="thp" run_test ./khugepaged + +CATEGORY="thp" run_test ./transhuge-stress -d 20 + +CATEGORY="thp" run_test ./split_huge_page_test + +CATEGORY="migration" run_test ./migration + +CATEGORY="mkdirty" run_test ./mkdirty + +CATEGORY="mdwe" run_test ./mdwe_test + echo "SUMMARY: PASS=${count_pass} SKIP=${count_skip} FAIL=${count_fail}" exit $exitcode diff --git a/tools/testing/selftests/mm/settings b/tools/testing/selftests/mm/settings index 9abfc60e9e6f..a953c96aa16e 100644 --- a/tools/testing/selftests/mm/settings +++ b/tools/testing/selftests/mm/settings @@ -1 +1 @@ -timeout=45 +timeout=180 diff --git a/tools/testing/selftests/mm/thuge-gen.c b/tools/testing/selftests/mm/thuge-gen.c index 380ab5f0a534..16ed4dfa7359 100644 --- a/tools/testing/selftests/mm/thuge-gen.c +++ b/tools/testing/selftests/mm/thuge-gen.c @@ -139,7 +139,7 @@ void test_mmap(unsigned long size, unsigned flags) before, after, before - after, size); assert(size == getpagesize() || (before - after) == NUM_PAGES); show(size); - err = munmap(map, size); + err = munmap(map, size * NUM_PAGES); assert(!err); } @@ -222,7 +222,7 @@ int main(void) test_mmap(ps, MAP_HUGETLB | arg); } printf("Testing default huge mmap\n"); - test_mmap(default_hps, SHM_HUGETLB); + test_mmap(default_hps, MAP_HUGETLB); puts("Testing non-huge shmget"); test_shmget(getpagesize(), 0); diff --git a/tools/testing/selftests/mm/transhuge-stress.c b/tools/testing/selftests/mm/transhuge-stress.c index ba9d37ad3a89..c61fb9350b8c 100644 --- a/tools/testing/selftests/mm/transhuge-stress.c +++ b/tools/testing/selftests/mm/transhuge-stress.c @@ -25,13 +25,14 @@ int main(int argc, char **argv) { size_t ram, len; void *ptr, *p; - struct timespec a, b; + struct timespec start, a, b; int i = 0; char *name = NULL; double s; uint8_t *map; size_t map_len; int pagemap_fd; + int duration = 0; ram = sysconf(_SC_PHYS_PAGES); if (ram > SIZE_MAX / psize() / 4) @@ -42,9 +43,11 @@ int main(int argc, char **argv) while (++i < argc) { if (!strcmp(argv[i], "-h")) - errx(1, "usage: %s [size in MiB]", argv[0]); + errx(1, "usage: %s [-f <filename>] [-d <duration>] [size in MiB]", argv[0]); else if (!strcmp(argv[i], "-f")) name = argv[++i]; + else if (!strcmp(argv[i], "-d")) + duration = atoi(argv[++i]); else len = atoll(argv[i]) << 20; } @@ -78,6 +81,8 @@ int main(int argc, char **argv) if (!map) errx(2, "map malloc"); + clock_gettime(CLOCK_MONOTONIC, &start); + while (1) { int nr_succeed = 0, nr_failed = 0, nr_pages = 0; @@ -118,5 +123,8 @@ int main(int argc, char **argv) "%4d succeed, %4d failed, %4d different pages", s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20), nr_succeed, nr_failed, nr_pages); + + if (duration > 0 && b.tv_sec - start.tv_sec >= duration) + return 0; } } diff --git a/tools/testing/selftests/mm/uffd-common.c b/tools/testing/selftests/mm/uffd-common.c index ba20d7504022..02b89860e193 100644 --- a/tools/testing/selftests/mm/uffd-common.c +++ b/tools/testing/selftests/mm/uffd-common.c @@ -499,6 +499,9 @@ void *uffd_poll_thread(void *arg) int ret; char tmp_chr; + if (!args->handle_fault) + args->handle_fault = uffd_handle_page_fault; + pollfd[0].fd = uffd; pollfd[0].events = POLLIN; pollfd[1].fd = pipefd[cpu*2]; @@ -527,7 +530,7 @@ void *uffd_poll_thread(void *arg) err("unexpected msg event %u\n", msg.event); break; case UFFD_EVENT_PAGEFAULT: - uffd_handle_page_fault(&msg, args); + args->handle_fault(&msg, args); break; case UFFD_EVENT_FORK: close(uffd); diff --git a/tools/testing/selftests/mm/uffd-common.h b/tools/testing/selftests/mm/uffd-common.h index 197f5262fe0d..7c4fa964c3b0 100644 --- a/tools/testing/selftests/mm/uffd-common.h +++ b/tools/testing/selftests/mm/uffd-common.h @@ -77,6 +77,9 @@ struct uffd_args { unsigned long missing_faults; unsigned long wp_faults; unsigned long minor_faults; + + /* A custom fault handler; defaults to uffd_handle_page_fault. */ + void (*handle_fault)(struct uffd_msg *msg, struct uffd_args *args); }; struct uffd_test_ops { diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index 995ff13e74c7..469e0476af26 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -53,21 +53,21 @@ pthread_attr_t attr; do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) const char *examples = - "# Run anonymous memory test on 100MiB region with 99999 bounces:\n" - "./userfaultfd anon 100 99999\n\n" - "# Run share memory test on 1GiB region with 99 bounces:\n" - "./userfaultfd shmem 1000 99\n\n" - "# Run hugetlb memory test on 256MiB region with 50 bounces:\n" - "./userfaultfd hugetlb 256 50\n\n" - "# Run the same hugetlb test but using private file:\n" - "./userfaultfd hugetlb-private 256 50\n\n" - "# 10MiB-~6GiB 999 bounces anonymous test, " - "continue forever unless an error triggers\n" - "while ./userfaultfd anon $[RANDOM % 6000 + 10] 999; do true; done\n\n"; + "# Run anonymous memory test on 100MiB region with 99999 bounces:\n" + "./uffd-stress anon 100 99999\n\n" + "# Run share memory test on 1GiB region with 99 bounces:\n" + "./uffd-stress shmem 1000 99\n\n" + "# Run hugetlb memory test on 256MiB region with 50 bounces:\n" + "./uffd-stress hugetlb 256 50\n\n" + "# Run the same hugetlb test but using private file:\n" + "./uffd-stress hugetlb-private 256 50\n\n" + "# 10MiB-~6GiB 999 bounces anonymous test, " + "continue forever unless an error triggers\n" + "while ./uffd-stress anon $[RANDOM % 6000 + 10] 999; do true; done\n\n"; static void usage(void) { - fprintf(stderr, "\nUsage: ./userfaultfd <test type> <MiB> <bounces>\n\n"); + fprintf(stderr, "\nUsage: ./uffd-stress <test type> <MiB> <bounces>\n\n"); fprintf(stderr, "Supported <test type>: anon, hugetlb, " "hugetlb-private, shmem, shmem-private\n\n"); fprintf(stderr, "Examples:\n\n"); @@ -189,10 +189,8 @@ static int stress(struct uffd_args *args) locking_thread, (void *)cpu)) return 1; if (bounces & BOUNCE_POLL) { - if (pthread_create(&uffd_threads[cpu], &attr, - uffd_poll_thread, - (void *)&args[cpu])) - return 1; + if (pthread_create(&uffd_threads[cpu], &attr, uffd_poll_thread, &args[cpu])) + err("uffd_poll_thread create"); } else { if (pthread_create(&uffd_threads[cpu], &attr, uffd_read_thread, @@ -250,6 +248,8 @@ static int userfaultfd_stress(void) struct uffd_args args[nr_cpus]; uint64_t mem_size = nr_pages * page_size; + memset(args, 0, sizeof(struct uffd_args) * nr_cpus); + if (uffd_test_ctx_init(UFFD_FEATURE_WP_UNPOPULATED, NULL)) err("context init failed"); diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index 04d91f144d1c..2709a34a39c5 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -951,6 +951,117 @@ static void uffd_zeropage_test(uffd_test_args_t *args) uffd_test_pass(); } +static void uffd_register_poison(int uffd, void *addr, uint64_t len) +{ + uint64_t ioctls = 0; + uint64_t expected = (1 << _UFFDIO_COPY) | (1 << _UFFDIO_POISON); + + if (uffd_register_with_ioctls(uffd, addr, len, true, + false, false, &ioctls)) + err("poison register fail"); + + if ((ioctls & expected) != expected) + err("registered area doesn't support COPY and POISON ioctls"); +} + +static void do_uffdio_poison(int uffd, unsigned long offset) +{ + struct uffdio_poison uffdio_poison = { 0 }; + int ret; + __s64 res; + + uffdio_poison.range.start = (unsigned long) area_dst + offset; + uffdio_poison.range.len = page_size; + uffdio_poison.mode = 0; + ret = ioctl(uffd, UFFDIO_POISON, &uffdio_poison); + res = uffdio_poison.updated; + + if (ret) + err("UFFDIO_POISON error: %"PRId64, (int64_t)res); + else if (res != page_size) + err("UFFDIO_POISON unexpected size: %"PRId64, (int64_t)res); +} + +static void uffd_poison_handle_fault( + struct uffd_msg *msg, struct uffd_args *args) +{ + unsigned long offset; + + if (msg->event != UFFD_EVENT_PAGEFAULT) + err("unexpected msg event %u", msg->event); + + if (msg->arg.pagefault.flags & + (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_MINOR)) + err("unexpected fault type %llu", msg->arg.pagefault.flags); + + offset = (char *)(unsigned long)msg->arg.pagefault.address - area_dst; + offset &= ~(page_size-1); + + /* Odd pages -> copy zeroed page; even pages -> poison. */ + if (offset & page_size) + copy_page(uffd, offset, false); + else + do_uffdio_poison(uffd, offset); +} + +static void uffd_poison_test(uffd_test_args_t *targs) +{ + pthread_t uffd_mon; + char c; + struct uffd_args args = { 0 }; + struct sigaction act = { 0 }; + unsigned long nr_sigbus = 0; + unsigned long nr; + + fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); + + uffd_register_poison(uffd, area_dst, nr_pages * page_size); + memset(area_src, 0, nr_pages * page_size); + + args.handle_fault = uffd_poison_handle_fault; + if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) + err("uffd_poll_thread create"); + + sigbuf = &jbuf; + act.sa_sigaction = sighndl; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGBUS, &act, 0)) + err("sigaction"); + + for (nr = 0; nr < nr_pages; ++nr) { + unsigned long offset = nr * page_size; + const char *bytes = (const char *) area_dst + offset; + const char *i; + + if (sigsetjmp(*sigbuf, 1)) { + /* + * Access below triggered a SIGBUS, which was caught by + * sighndl, which then jumped here. Count this SIGBUS, + * and move on to next page. + */ + ++nr_sigbus; + continue; + } + + for (i = bytes; i < bytes + page_size; ++i) { + if (*i) + err("nonzero byte in area_dst (%p) at %p: %u", + area_dst, i, *i); + } + } + + if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) + err("pipe write"); + if (pthread_join(uffd_mon, NULL)) + err("pthread_join()"); + + if (nr_sigbus != nr_pages / 2) + err("expected to receive %lu SIGBUS, actually received %lu", + nr_pages / 2, nr_sigbus); + + uffd_test_pass(); +} + /* * Test the returned uffdio_register.ioctls with different register modes. * Note that _UFFDIO_ZEROPAGE is tested separately in the zeropage test. @@ -1126,6 +1237,12 @@ uffd_test_case_t uffd_tests[] = { UFFD_FEATURE_PAGEFAULT_FLAG_WP | UFFD_FEATURE_WP_HUGETLBFS_SHMEM, }, + { + .name = "poison", + .uffd_fn = uffd_poison_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_POISON, + }, }; static void usage(const char *prog) diff --git a/tools/testing/selftests/mm/va_high_addr_switch.c b/tools/testing/selftests/mm/va_high_addr_switch.c index 7cfaf4a74c57..cfbc501290d3 100644 --- a/tools/testing/selftests/mm/va_high_addr_switch.c +++ b/tools/testing/selftests/mm/va_high_addr_switch.c @@ -292,7 +292,7 @@ static int supported_arch(void) #elif defined(__x86_64__) return 1; #elif defined(__aarch64__) - return 1; + return getpagesize() == PAGE_SIZE; #else return 0; #endif diff --git a/tools/testing/selftests/net/bind_bhash.sh b/tools/testing/selftests/net/bind_bhash.sh index ca0292d4b441..a28563bdaae0 100755 --- a/tools/testing/selftests/net/bind_bhash.sh +++ b/tools/testing/selftests/net/bind_bhash.sh @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 NR_FILES=32768 -SAVED_NR_FILES=$(ulimit -n) +readonly NETNS="ns-$(mktemp -u XXXXXX)" # default values port=443 @@ -36,21 +36,21 @@ while getopts "ha:p:64" opt; do done setup() { + ip netns add "${NETNS}" + ip -netns "${NETNS}" link add veth0 type veth peer name veth1 + ip -netns "${NETNS}" link set lo up + ip -netns "${NETNS}" link set veth0 up + ip -netns "${NETNS}" link set veth1 up + if [[ "$use_v6" == true ]]; then - ip addr add $addr_v6 nodad dev eth0 + ip -netns "${NETNS}" addr add $addr_v6 nodad dev veth0 else - ip addr add $addr_v4 dev lo + ip -netns "${NETNS}" addr add $addr_v4 dev lo fi - ulimit -n $NR_FILES } cleanup() { - if [[ "$use_v6" == true ]]; then - ip addr del $addr_v6 dev eth0 - else - ip addr del $addr_v4/32 dev lo - fi - ulimit -n $SAVED_NR_FILES + ip netns del "${NETNS}" } if [[ "$addr" != "" ]]; then @@ -59,8 +59,10 @@ if [[ "$addr" != "" ]]; then fi setup if [[ "$use_v6" == true ]] ; then - ./bind_bhash $port "ipv6" $addr_v6 + ip netns exec "${NETNS}" sh -c \ + "ulimit -n ${NR_FILES};./bind_bhash ${port} ipv6 ${addr_v6}" else - ./bind_bhash $port "ipv4" $addr_v4 + ip netns exec "${NETNS}" sh -c \ + "ulimit -n ${NR_FILES};./bind_bhash ${port} ipv4 ${addr_v4}" fi cleanup diff --git a/tools/testing/selftests/net/bind_wildcard.c b/tools/testing/selftests/net/bind_wildcard.c index 58edfc15d28b..a2662348cdb1 100644 --- a/tools/testing/selftests/net/bind_wildcard.c +++ b/tools/testing/selftests/net/bind_wildcard.c @@ -6,41 +6,91 @@ #include "../kselftest_harness.h" +struct in6_addr in6addr_v4mapped_any = { + .s6_addr = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 255, 255, + 0, 0, 0, 0 + } +}; + +struct in6_addr in6addr_v4mapped_loopback = { + .s6_addr = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 255, 255, + 127, 0, 0, 1 + } +}; + FIXTURE(bind_wildcard) { struct sockaddr_in addr4; struct sockaddr_in6 addr6; - int expected_errno; }; FIXTURE_VARIANT(bind_wildcard) { const __u32 addr4_const; const struct in6_addr *addr6_const; + int expected_errno; }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any) { .addr4_const = INADDR_ANY, .addr6_const = &in6addr_any, + .expected_errno = EADDRINUSE, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local) { .addr4_const = INADDR_ANY, .addr6_const = &in6addr_loopback, + .expected_errno = 0, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_any) +{ + .addr4_const = INADDR_ANY, + .addr6_const = &in6addr_v4mapped_any, + .expected_errno = EADDRINUSE, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_local) +{ + .addr4_const = INADDR_ANY, + .addr6_const = &in6addr_v4mapped_loopback, + .expected_errno = EADDRINUSE, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any) { .addr4_const = INADDR_LOOPBACK, .addr6_const = &in6addr_any, + .expected_errno = EADDRINUSE, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local) { .addr4_const = INADDR_LOOPBACK, .addr6_const = &in6addr_loopback, + .expected_errno = 0, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_any) +{ + .addr4_const = INADDR_LOOPBACK, + .addr6_const = &in6addr_v4mapped_any, + .expected_errno = EADDRINUSE, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_local) +{ + .addr4_const = INADDR_LOOPBACK, + .addr6_const = &in6addr_v4mapped_loopback, + .expected_errno = EADDRINUSE, }; FIXTURE_SETUP(bind_wildcard) @@ -52,11 +102,6 @@ FIXTURE_SETUP(bind_wildcard) self->addr6.sin6_family = AF_INET6; self->addr6.sin6_port = htons(0); self->addr6.sin6_addr = *variant->addr6_const; - - if (variant->addr6_const == &in6addr_any) - self->expected_errno = EADDRINUSE; - else - self->expected_errno = 0; } FIXTURE_TEARDOWN(bind_wildcard) @@ -65,6 +110,7 @@ FIXTURE_TEARDOWN(bind_wildcard) void bind_sockets(struct __test_metadata *_metadata, FIXTURE_DATA(bind_wildcard) *self, + int expected_errno, struct sockaddr *addr1, socklen_t addrlen1, struct sockaddr *addr2, socklen_t addrlen2) { @@ -86,9 +132,9 @@ void bind_sockets(struct __test_metadata *_metadata, ASSERT_GT(fd[1], 0); ret = bind(fd[1], addr2, addrlen2); - if (self->expected_errno) { + if (expected_errno) { ASSERT_EQ(ret, -1); - ASSERT_EQ(errno, self->expected_errno); + ASSERT_EQ(errno, expected_errno); } else { ASSERT_EQ(ret, 0); } @@ -99,14 +145,14 @@ void bind_sockets(struct __test_metadata *_metadata, TEST_F(bind_wildcard, v4_v6) { - bind_sockets(_metadata, self, - (struct sockaddr *)&self->addr4, sizeof(self->addr6), + bind_sockets(_metadata, self, variant->expected_errno, + (struct sockaddr *)&self->addr4, sizeof(self->addr4), (struct sockaddr *)&self->addr6, sizeof(self->addr6)); } TEST_F(bind_wildcard, v6_v4) { - bind_sockets(_metadata, self, + bind_sockets(_metadata, self, variant->expected_errno, (struct sockaddr *)&self->addr6, sizeof(self->addr6), (struct sockaddr *)&self->addr4, sizeof(self->addr4)); } diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh index d328af4a149c..e7d2a530618a 100755 --- a/tools/testing/selftests/net/fib_tests.sh +++ b/tools/testing/selftests/net/fib_tests.sh @@ -12,7 +12,8 @@ ksft_skip=4 TESTS="unregister down carrier nexthop suppress ipv6_notify ipv4_notify \ ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics \ ipv4_route_metrics ipv4_route_v6_gw rp_filter ipv4_del_addr \ - ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test" + ipv6_del_addr ipv4_mangle ipv6_mangle ipv4_bcast_neigh fib6_gc_test \ + ipv4_mpath_list ipv6_mpath_list" VERBOSE=0 PAUSE_ON_FAIL=no @@ -2352,6 +2353,156 @@ ipv4_bcast_neigh_test() cleanup } +mpath_dep_check() +{ + if [ ! -x "$(command -v mausezahn)" ]; then + echo "mausezahn command not found. Skipping test" + return 1 + fi + + if [ ! -x "$(command -v jq)" ]; then + echo "jq command not found. Skipping test" + return 1 + fi + + if [ ! -x "$(command -v bc)" ]; then + echo "bc command not found. Skipping test" + return 1 + fi + + if [ ! -x "$(command -v perf)" ]; then + echo "perf command not found. Skipping test" + return 1 + fi + + perf list fib:* | grep -q fib_table_lookup + if [ $? -ne 0 ]; then + echo "IPv4 FIB tracepoint not found. Skipping test" + return 1 + fi + + perf list fib6:* | grep -q fib6_table_lookup + if [ $? -ne 0 ]; then + echo "IPv6 FIB tracepoint not found. Skipping test" + return 1 + fi + + return 0 +} + +link_stats_get() +{ + local ns=$1; shift + local dev=$1; shift + local dir=$1; shift + local stat=$1; shift + + ip -n $ns -j -s link show dev $dev \ + | jq '.[]["stats64"]["'$dir'"]["'$stat'"]' +} + +list_rcv_eval() +{ + local file=$1; shift + local expected=$1; shift + + local count=$(tail -n 1 $file | jq '.["counter-value"] | tonumber | floor') + local ratio=$(echo "scale=2; $count / $expected" | bc -l) + local res=$(echo "$ratio >= 0.95" | bc) + [[ $res -eq 1 ]] + log_test $? 0 "Multipath route hit ratio ($ratio)" +} + +ipv4_mpath_list_test() +{ + echo + echo "IPv4 multipath list receive tests" + + mpath_dep_check || return 1 + + route_setup + + set -e + run_cmd "ip netns exec ns1 ethtool -K veth1 tcp-segmentation-offload off" + + run_cmd "ip netns exec ns2 bash -c \"echo 20000 > /sys/class/net/veth2/gro_flush_timeout\"" + run_cmd "ip netns exec ns2 bash -c \"echo 1 > /sys/class/net/veth2/napi_defer_hard_irqs\"" + run_cmd "ip netns exec ns2 ethtool -K veth2 generic-receive-offload on" + run_cmd "ip -n ns2 link add name nh1 up type dummy" + run_cmd "ip -n ns2 link add name nh2 up type dummy" + run_cmd "ip -n ns2 address add 172.16.201.1/24 dev nh1" + run_cmd "ip -n ns2 address add 172.16.202.1/24 dev nh2" + run_cmd "ip -n ns2 neigh add 172.16.201.2 lladdr 00:11:22:33:44:55 nud perm dev nh1" + run_cmd "ip -n ns2 neigh add 172.16.202.2 lladdr 00:aa:bb:cc:dd:ee nud perm dev nh2" + run_cmd "ip -n ns2 route add 203.0.113.0/24 + nexthop via 172.16.201.2 nexthop via 172.16.202.2" + run_cmd "ip netns exec ns2 sysctl -qw net.ipv4.fib_multipath_hash_policy=1" + set +e + + local dmac=$(ip -n ns2 -j link show dev veth2 | jq -r '.[]["address"]') + local tmp_file=$(mktemp) + local cmd="ip netns exec ns1 mausezahn veth1 -a own -b $dmac + -A 172.16.101.1 -B 203.0.113.1 -t udp 'sp=12345,dp=0-65535' -q" + + # Packets forwarded in a list using a multipath route must not reuse a + # cached result so that a flow always hits the same nexthop. In other + # words, the FIB lookup tracepoint needs to be triggered for every + # packet. + local t0_rx_pkts=$(link_stats_get ns2 veth2 rx packets) + run_cmd "perf stat -e fib:fib_table_lookup --filter 'err == 0' -j -o $tmp_file -- $cmd" + local t1_rx_pkts=$(link_stats_get ns2 veth2 rx packets) + local diff=$(echo $t1_rx_pkts - $t0_rx_pkts | bc -l) + list_rcv_eval $tmp_file $diff + + rm $tmp_file + route_cleanup +} + +ipv6_mpath_list_test() +{ + echo + echo "IPv6 multipath list receive tests" + + mpath_dep_check || return 1 + + route_setup + + set -e + run_cmd "ip netns exec ns1 ethtool -K veth1 tcp-segmentation-offload off" + + run_cmd "ip netns exec ns2 bash -c \"echo 20000 > /sys/class/net/veth2/gro_flush_timeout\"" + run_cmd "ip netns exec ns2 bash -c \"echo 1 > /sys/class/net/veth2/napi_defer_hard_irqs\"" + run_cmd "ip netns exec ns2 ethtool -K veth2 generic-receive-offload on" + run_cmd "ip -n ns2 link add name nh1 up type dummy" + run_cmd "ip -n ns2 link add name nh2 up type dummy" + run_cmd "ip -n ns2 -6 address add 2001:db8:201::1/64 dev nh1" + run_cmd "ip -n ns2 -6 address add 2001:db8:202::1/64 dev nh2" + run_cmd "ip -n ns2 -6 neigh add 2001:db8:201::2 lladdr 00:11:22:33:44:55 nud perm dev nh1" + run_cmd "ip -n ns2 -6 neigh add 2001:db8:202::2 lladdr 00:aa:bb:cc:dd:ee nud perm dev nh2" + run_cmd "ip -n ns2 -6 route add 2001:db8:301::/64 + nexthop via 2001:db8:201::2 nexthop via 2001:db8:202::2" + run_cmd "ip netns exec ns2 sysctl -qw net.ipv6.fib_multipath_hash_policy=1" + set +e + + local dmac=$(ip -n ns2 -j link show dev veth2 | jq -r '.[]["address"]') + local tmp_file=$(mktemp) + local cmd="ip netns exec ns1 mausezahn -6 veth1 -a own -b $dmac + -A 2001:db8:101::1 -B 2001:db8:301::1 -t udp 'sp=12345,dp=0-65535' -q" + + # Packets forwarded in a list using a multipath route must not reuse a + # cached result so that a flow always hits the same nexthop. In other + # words, the FIB lookup tracepoint needs to be triggered for every + # packet. + local t0_rx_pkts=$(link_stats_get ns2 veth2 rx packets) + run_cmd "perf stat -e fib6:fib6_table_lookup --filter 'err == 0' -j -o $tmp_file -- $cmd" + local t1_rx_pkts=$(link_stats_get ns2 veth2 rx packets) + local diff=$(echo $t1_rx_pkts - $t0_rx_pkts | bc -l) + list_rcv_eval $tmp_file $diff + + rm $tmp_file + route_cleanup +} + ################################################################################ # usage @@ -2433,6 +2584,8 @@ do ipv6_mangle) ipv6_mangle_test;; ipv4_bcast_neigh) ipv4_bcast_neigh_test;; fib6_gc_test|ipv6_gc) fib6_gc_test;; + ipv4_mpath_list) ipv4_mpath_list_test;; + ipv6_mpath_list) ipv6_mpath_list_test;; help) echo "Test names: $TESTS"; exit 0;; esac diff --git a/tools/testing/selftests/net/hsr/hsr_ping.sh b/tools/testing/selftests/net/hsr/hsr_ping.sh index df9143538708..1c6457e54625 100755 --- a/tools/testing/selftests/net/hsr/hsr_ping.sh +++ b/tools/testing/selftests/net/hsr/hsr_ping.sh @@ -41,61 +41,6 @@ cleanup() done } -ip -Version > /dev/null 2>&1 -if [ $? -ne 0 ];then - echo "SKIP: Could not run test without ip tool" - exit $ksft_skip -fi - -trap cleanup EXIT - -for i in "$ns1" "$ns2" "$ns3" ;do - ip netns add $i || exit $ksft_skip - ip -net $i link set lo up -done - -echo "INFO: preparing interfaces." -# Three HSR nodes. Each node has one link to each of its neighbour, two links in total. -# -# ns1eth1 ----- ns2eth1 -# hsr1 hsr2 -# ns1eth2 ns2eth2 -# | | -# ns3eth1 ns3eth2 -# \ / -# hsr3 -# -# Interfaces -ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2" -ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3" -ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2" - -# HSRv0. -ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version 0 proto 0 -ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version 0 proto 0 -ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version 0 proto 0 - -# IP for HSR -ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1 -ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad -ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2 -ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad -ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3 -ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad - -# All Links up -ip -net "$ns1" link set ns1eth1 up -ip -net "$ns1" link set ns1eth2 up -ip -net "$ns1" link set hsr1 up - -ip -net "$ns2" link set ns2eth1 up -ip -net "$ns2" link set ns2eth2 up -ip -net "$ns2" link set hsr2 up - -ip -net "$ns3" link set ns3eth1 up -ip -net "$ns3" link set ns3eth2 up -ip -net "$ns3" link set hsr3 up - # $1: IP address is_v6() { @@ -164,93 +109,168 @@ stop_if_error() fi } - -echo "INFO: Initial validation ping." -# Each node has to be able each one. -do_ping "$ns1" 100.64.0.2 -do_ping "$ns2" 100.64.0.1 -do_ping "$ns3" 100.64.0.1 -stop_if_error "Initial validation failed." - -do_ping "$ns1" 100.64.0.3 -do_ping "$ns2" 100.64.0.3 -do_ping "$ns3" 100.64.0.2 - -do_ping "$ns1" dead:beef:1::2 -do_ping "$ns1" dead:beef:1::3 -do_ping "$ns2" dead:beef:1::1 -do_ping "$ns2" dead:beef:1::2 -do_ping "$ns3" dead:beef:1::1 -do_ping "$ns3" dead:beef:1::2 - -stop_if_error "Initial validation failed." +do_complete_ping_test() +{ + echo "INFO: Initial validation ping." + # Each node has to be able each one. + do_ping "$ns1" 100.64.0.2 + do_ping "$ns2" 100.64.0.1 + do_ping "$ns3" 100.64.0.1 + stop_if_error "Initial validation failed." + + do_ping "$ns1" 100.64.0.3 + do_ping "$ns2" 100.64.0.3 + do_ping "$ns3" 100.64.0.2 + + do_ping "$ns1" dead:beef:1::2 + do_ping "$ns1" dead:beef:1::3 + do_ping "$ns2" dead:beef:1::1 + do_ping "$ns2" dead:beef:1::2 + do_ping "$ns3" dead:beef:1::1 + do_ping "$ns3" dead:beef:1::2 + + stop_if_error "Initial validation failed." # Wait until supervisor all supervision frames have been processed and the node # entries have been merged. Otherwise duplicate frames will be observed which is # valid at this stage. -WAIT=5 -while [ ${WAIT} -gt 0 ] -do - grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table - if [ $? -ne 0 ] - then - break - fi - sleep 1 - let WAIT = WAIT - 1 -done + WAIT=5 + while [ ${WAIT} -gt 0 ] + do + grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table + if [ $? -ne 0 ] + then + break + fi + sleep 1 + let "WAIT = WAIT - 1" + done # Just a safety delay in case the above check didn't handle it. -sleep 1 + sleep 1 + + echo "INFO: Longer ping test." + do_ping_long "$ns1" 100.64.0.2 + do_ping_long "$ns1" dead:beef:1::2 + do_ping_long "$ns1" 100.64.0.3 + do_ping_long "$ns1" dead:beef:1::3 -echo "INFO: Longer ping test." -do_ping_long "$ns1" 100.64.0.2 -do_ping_long "$ns1" dead:beef:1::2 -do_ping_long "$ns1" 100.64.0.3 -do_ping_long "$ns1" dead:beef:1::3 + stop_if_error "Longer ping test failed." -stop_if_error "Longer ping test failed." + do_ping_long "$ns2" 100.64.0.1 + do_ping_long "$ns2" dead:beef:1::1 + do_ping_long "$ns2" 100.64.0.3 + do_ping_long "$ns2" dead:beef:1::2 + stop_if_error "Longer ping test failed." -do_ping_long "$ns2" 100.64.0.1 -do_ping_long "$ns2" dead:beef:1::1 -do_ping_long "$ns2" 100.64.0.3 -do_ping_long "$ns2" dead:beef:1::2 -stop_if_error "Longer ping test failed." + do_ping_long "$ns3" 100.64.0.1 + do_ping_long "$ns3" dead:beef:1::1 + do_ping_long "$ns3" 100.64.0.2 + do_ping_long "$ns3" dead:beef:1::2 + stop_if_error "Longer ping test failed." -do_ping_long "$ns3" 100.64.0.1 -do_ping_long "$ns3" dead:beef:1::1 -do_ping_long "$ns3" 100.64.0.2 -do_ping_long "$ns3" dead:beef:1::2 -stop_if_error "Longer ping test failed." + echo "INFO: Cutting one link." + do_ping_long "$ns1" 100.64.0.3 & -echo "INFO: Cutting one link." -do_ping_long "$ns1" 100.64.0.3 & + sleep 3 + ip -net "$ns3" link set ns3eth1 down + wait -sleep 3 -ip -net "$ns3" link set ns3eth1 down -wait + ip -net "$ns3" link set ns3eth1 up -ip -net "$ns3" link set ns3eth1 up + stop_if_error "Failed with one link down." -stop_if_error "Failed with one link down." + echo "INFO: Delay the link and drop a few packages." + tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms + tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25% -echo "INFO: Delay the link and drop a few packages." -tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms -tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25% + do_ping_long "$ns1" 100.64.0.2 + do_ping_long "$ns1" 100.64.0.3 -do_ping_long "$ns1" 100.64.0.2 -do_ping_long "$ns1" 100.64.0.3 + stop_if_error "Failed with delay and packetloss." -stop_if_error "Failed with delay and packetloss." + do_ping_long "$ns2" 100.64.0.1 + do_ping_long "$ns2" 100.64.0.3 -do_ping_long "$ns2" 100.64.0.1 -do_ping_long "$ns2" 100.64.0.3 + stop_if_error "Failed with delay and packetloss." -stop_if_error "Failed with delay and packetloss." + do_ping_long "$ns3" 100.64.0.1 + do_ping_long "$ns3" 100.64.0.2 + stop_if_error "Failed with delay and packetloss." + + echo "INFO: All good." +} + +setup_hsr_interfaces() +{ + local HSRv="$1" + + echo "INFO: preparing interfaces for HSRv${HSRv}." +# Three HSR nodes. Each node has one link to each of its neighbour, two links in total. +# +# ns1eth1 ----- ns2eth1 +# hsr1 hsr2 +# ns1eth2 ns2eth2 +# | | +# ns3eth1 ns3eth2 +# \ / +# hsr3 +# + # Interfaces + ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2" + ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3" + ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2" + + # HSRv0/1 + ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version $HSRv proto 0 + ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version $HSRv proto 0 + ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version $HSRv proto 0 + + # IP for HSR + ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1 + ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad + ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2 + ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad + ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3 + ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad + + # All Links up + ip -net "$ns1" link set ns1eth1 up + ip -net "$ns1" link set ns1eth2 up + ip -net "$ns1" link set hsr1 up + + ip -net "$ns2" link set ns2eth1 up + ip -net "$ns2" link set ns2eth2 up + ip -net "$ns2" link set hsr2 up + + ip -net "$ns3" link set ns3eth1 up + ip -net "$ns3" link set ns3eth2 up + ip -net "$ns3" link set hsr3 up +} + +ip -Version > /dev/null 2>&1 +if [ $? -ne 0 ];then + echo "SKIP: Could not run test without ip tool" + exit $ksft_skip +fi + +trap cleanup EXIT + +for i in "$ns1" "$ns2" "$ns3" ;do + ip netns add $i || exit $ksft_skip + ip -net $i link set lo up +done + +setup_hsr_interfaces 0 +do_complete_ping_test +cleanup + +for i in "$ns1" "$ns2" "$ns3" ;do + ip netns add $i || exit $ksft_skip + ip -net $i link set lo up +done -do_ping_long "$ns3" 100.64.0.1 -do_ping_long "$ns3" 100.64.0.2 -stop_if_error "Failed with delay and packetloss." +setup_hsr_interfaces 1 +do_complete_ping_test -echo "INFO: All good." exit $ret diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index b1fc8afd072d..61a2a1988ce6 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -716,7 +716,7 @@ run_test_transparent() # the required infrastructure in MPTCP sockopt code. To support TOS, the # following function has been exported (T). Not great but better than # checking for a specific kernel version. - if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then + if ! mptcp_lib_kallsyms_has "T ip_sock_set_tos$"; then echo "INFO: ${msg} not supported by the kernel: SKIP" mptcp_lib_result_skip "${TEST_GROUP}" return diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 488f4964365e..5f2b3f6c0d74 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -31,6 +31,9 @@ ALL_TESTS=" " devdummy="test-dummy0" +VERBOSE=0 +PAUSE=no +PAUSE_ON_FAIL=no # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 @@ -51,35 +54,102 @@ check_fail() fi } +run_cmd_common() +{ + local cmd="$*" + local out + if [ "$VERBOSE" = "1" ]; then + echo "COMMAND: ${cmd}" + fi + out=$($cmd 2>&1) + rc=$? + if [ "$VERBOSE" = "1" -a -n "$out" ]; then + echo " $out" + fi + return $rc +} + +run_cmd() { + run_cmd_common "$@" + rc=$? + check_err $rc + return $rc +} +run_cmd_fail() +{ + run_cmd_common "$@" + rc=$? + check_fail $rc + return $rc +} + +run_cmd_grep_common() +{ + local find="$1"; shift + local cmd="$*" + local out + if [ "$VERBOSE" = "1" ]; then + echo "COMMAND: ${cmd} 2>&1 | grep -q '${find}'" + fi + out=$($cmd 2>&1 | grep -q "${find}" 2>&1) + return $? +} + +run_cmd_grep() { + run_cmd_grep_common "$@" + rc=$? + check_err $rc + return $rc +} + +run_cmd_grep_fail() +{ + run_cmd_grep_common "$@" + rc=$? + check_fail $rc + return $rc +} + +end_test() +{ + echo "$*" + [ "${VERBOSE}" = "1" ] && echo + + if [[ $ret -ne 0 ]] && [[ "${PAUSE_ON_FAIL}" = "yes" ]]; then + echo "Hit enter to continue" + read a + fi; + + if [ "${PAUSE}" = "yes" ]; then + echo "Hit enter to continue" + read a + fi + +} + + kci_add_dummy() { - ip link add name "$devdummy" type dummy - check_err $? - ip link set "$devdummy" up - check_err $? + run_cmd ip link add name "$devdummy" type dummy + run_cmd ip link set "$devdummy" up } kci_del_dummy() { - ip link del dev "$devdummy" - check_err $? + run_cmd ip link del dev "$devdummy" } kci_test_netconf() { dev="$1" r=$ret - - ip netconf show dev "$dev" > /dev/null - check_err $? - + run_cmd ip netconf show dev "$dev" for f in 4 6; do - ip -$f netconf show dev "$dev" > /dev/null - check_err $? + run_cmd ip -$f netconf show dev "$dev" done if [ $ret -ne 0 ] ;then - echo "FAIL: ip netconf show $dev" + end_test "FAIL: ip netconf show $dev" test $r -eq 0 && ret=0 return 1 fi @@ -92,43 +162,27 @@ kci_test_bridge() vlandev="testbr-vlan1" local ret=0 - ip link add name "$devbr" type bridge - check_err $? - - ip link set dev "$devdummy" master "$devbr" - check_err $? - - ip link set "$devbr" up - check_err $? - - ip link add link "$devbr" name "$vlandev" type vlan id 1 - check_err $? - ip addr add dev "$vlandev" 10.200.7.23/30 - check_err $? - ip -6 addr add dev "$vlandev" dead:42::1234/64 - check_err $? - ip -d link > /dev/null - check_err $? - ip r s t all > /dev/null - check_err $? + run_cmd ip link add name "$devbr" type bridge + run_cmd ip link set dev "$devdummy" master "$devbr" + run_cmd ip link set "$devbr" up + run_cmd ip link add link "$devbr" name "$vlandev" type vlan id 1 + run_cmd ip addr add dev "$vlandev" 10.200.7.23/30 + run_cmd ip -6 addr add dev "$vlandev" dead:42::1234/64 + run_cmd ip -d link + run_cmd ip r s t all for name in "$devbr" "$vlandev" "$devdummy" ; do kci_test_netconf "$name" done - - ip -6 addr del dev "$vlandev" dead:42::1234/64 - check_err $? - - ip link del dev "$vlandev" - check_err $? - ip link del dev "$devbr" - check_err $? + run_cmd ip -6 addr del dev "$vlandev" dead:42::1234/64 + run_cmd ip link del dev "$vlandev" + run_cmd ip link del dev "$devbr" if [ $ret -ne 0 ];then - echo "FAIL: bridge setup" + end_test "FAIL: bridge setup" return 1 fi - echo "PASS: bridge setup" + end_test "PASS: bridge setup" } @@ -139,34 +193,23 @@ kci_test_gre() loc=10.0.0.1 local ret=0 - ip tunnel add $gredev mode gre remote $rem local $loc ttl 1 - check_err $? - ip link set $gredev up - check_err $? - ip addr add 10.23.7.10 dev $gredev - check_err $? - ip route add 10.23.8.0/30 dev $gredev - check_err $? - ip addr add dev "$devdummy" 10.23.7.11/24 - check_err $? - ip link > /dev/null - check_err $? - ip addr > /dev/null - check_err $? + run_cmd ip tunnel add $gredev mode gre remote $rem local $loc ttl 1 + run_cmd ip link set $gredev up + run_cmd ip addr add 10.23.7.10 dev $gredev + run_cmd ip route add 10.23.8.0/30 dev $gredev + run_cmd ip addr add dev "$devdummy" 10.23.7.11/24 + run_cmd ip link + run_cmd ip addr kci_test_netconf "$gredev" - - ip addr del dev "$devdummy" 10.23.7.11/24 - check_err $? - - ip link del $gredev - check_err $? + run_cmd ip addr del dev "$devdummy" 10.23.7.11/24 + run_cmd ip link del $gredev if [ $ret -ne 0 ];then - echo "FAIL: gre tunnel endpoint" + end_test "FAIL: gre tunnel endpoint" return 1 fi - echo "PASS: gre tunnel endpoint" + end_test "PASS: gre tunnel endpoint" } # tc uses rtnetlink too, for full tc testing @@ -176,56 +219,40 @@ kci_test_tc() dev=lo local ret=0 - tc qdisc add dev "$dev" root handle 1: htb - check_err $? - tc class add dev "$dev" parent 1: classid 1:10 htb rate 1mbit - check_err $? - tc filter add dev "$dev" parent 1:0 prio 5 handle ffe: protocol ip u32 divisor 256 - check_err $? - tc filter add dev "$dev" parent 1:0 prio 5 handle ffd: protocol ip u32 divisor 256 - check_err $? - tc filter add dev "$dev" parent 1:0 prio 5 handle ffc: protocol ip u32 divisor 256 - check_err $? - tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 ht ffe:2: match ip src 10.0.0.3 flowid 1:10 - check_err $? - tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:2 u32 ht ffe:2: match ip src 10.0.0.2 flowid 1:10 - check_err $? - tc filter show dev "$dev" parent 1:0 > /dev/null - check_err $? - tc filter del dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 - check_err $? - tc filter show dev "$dev" parent 1:0 > /dev/null - check_err $? - tc qdisc del dev "$dev" root handle 1: htb - check_err $? + run_cmd tc qdisc add dev "$dev" root handle 1: htb + run_cmd tc class add dev "$dev" parent 1: classid 1:10 htb rate 1mbit + run_cmd tc filter add dev "$dev" parent 1:0 prio 5 handle ffe: protocol ip u32 divisor 256 + run_cmd tc filter add dev "$dev" parent 1:0 prio 5 handle ffd: protocol ip u32 divisor 256 + run_cmd tc filter add dev "$dev" parent 1:0 prio 5 handle ffc: protocol ip u32 divisor 256 + run_cmd tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 ht ffe:2: match ip src 10.0.0.3 flowid 1:10 + run_cmd tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:2 u32 ht ffe:2: match ip src 10.0.0.2 flowid 1:10 + run_cmd tc filter show dev "$dev" parent 1:0 + run_cmd tc filter del dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 + run_cmd tc filter show dev "$dev" parent 1:0 + run_cmd tc qdisc del dev "$dev" root handle 1: htb if [ $ret -ne 0 ];then - echo "FAIL: tc htb hierarchy" + end_test "FAIL: tc htb hierarchy" return 1 fi - echo "PASS: tc htb hierarchy" + end_test "PASS: tc htb hierarchy" } kci_test_polrouting() { local ret=0 - ip rule add fwmark 1 lookup 100 - check_err $? - ip route add local 0.0.0.0/0 dev lo table 100 - check_err $? - ip r s t all > /dev/null - check_err $? - ip rule del fwmark 1 lookup 100 - check_err $? - ip route del local 0.0.0.0/0 dev lo table 100 - check_err $? + run_cmd ip rule add fwmark 1 lookup 100 + run_cmd ip route add local 0.0.0.0/0 dev lo table 100 + run_cmd ip r s t all + run_cmd ip rule del fwmark 1 lookup 100 + run_cmd ip route del local 0.0.0.0/0 dev lo table 100 if [ $ret -ne 0 ];then - echo "FAIL: policy route test" + end_test "FAIL: policy route test" return 1 fi - echo "PASS: policy routing" + end_test "PASS: policy routing" } kci_test_route_get() @@ -233,65 +260,51 @@ kci_test_route_get() local hash_policy=$(sysctl -n net.ipv4.fib_multipath_hash_policy) local ret=0 - - ip route get 127.0.0.1 > /dev/null - check_err $? - ip route get 127.0.0.1 dev "$devdummy" > /dev/null - check_err $? - ip route get ::1 > /dev/null - check_err $? - ip route get fe80::1 dev "$devdummy" > /dev/null - check_err $? - ip route get 127.0.0.1 from 127.0.0.1 oif lo tos 0x10 mark 0x1 > /dev/null - check_err $? - ip route get ::1 from ::1 iif lo oif lo tos 0x10 mark 0x1 > /dev/null - check_err $? - ip addr add dev "$devdummy" 10.23.7.11/24 - check_err $? - ip route get 10.23.7.11 from 10.23.7.12 iif "$devdummy" > /dev/null - check_err $? - ip route add 10.23.8.0/24 \ + run_cmd ip route get 127.0.0.1 + run_cmd ip route get 127.0.0.1 dev "$devdummy" + run_cmd ip route get ::1 + run_cmd ip route get fe80::1 dev "$devdummy" + run_cmd ip route get 127.0.0.1 from 127.0.0.1 oif lo tos 0x10 mark 0x1 + run_cmd ip route get ::1 from ::1 iif lo oif lo tos 0x10 mark 0x1 + run_cmd ip addr add dev "$devdummy" 10.23.7.11/24 + run_cmd ip route get 10.23.7.11 from 10.23.7.12 iif "$devdummy" + run_cmd ip route add 10.23.8.0/24 \ nexthop via 10.23.7.13 dev "$devdummy" \ nexthop via 10.23.7.14 dev "$devdummy" - check_err $? + sysctl -wq net.ipv4.fib_multipath_hash_policy=0 - ip route get 10.23.8.11 > /dev/null - check_err $? + run_cmd ip route get 10.23.8.11 sysctl -wq net.ipv4.fib_multipath_hash_policy=1 - ip route get 10.23.8.11 > /dev/null - check_err $? + run_cmd ip route get 10.23.8.11 sysctl -wq net.ipv4.fib_multipath_hash_policy="$hash_policy" - ip route del 10.23.8.0/24 - check_err $? - ip addr del dev "$devdummy" 10.23.7.11/24 - check_err $? + run_cmd ip route del 10.23.8.0/24 + run_cmd ip addr del dev "$devdummy" 10.23.7.11/24 + if [ $ret -ne 0 ];then - echo "FAIL: route get" + end_test "FAIL: route get" return 1 fi - echo "PASS: route get" + end_test "PASS: route get" } kci_test_addrlft() { for i in $(seq 10 100) ;do lft=$(((RANDOM%3) + 1)) - ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1)) - check_err $? + run_cmd ip addr add 10.23.11.$i/32 dev "$devdummy" preferred_lft $lft valid_lft $((lft+1)) done sleep 5 - - ip addr show dev "$devdummy" | grep "10.23.11." + run_cmd_grep "10.23.11." ip addr show dev "$devdummy" if [ $? -eq 0 ]; then - echo "FAIL: preferred_lft addresses remaining" check_err 1 + end_test "FAIL: preferred_lft addresses remaining" return fi - echo "PASS: preferred_lft addresses have expired" + end_test "PASS: preferred_lft addresses have expired" } kci_test_promote_secondaries() @@ -310,27 +323,17 @@ kci_test_promote_secondaries() [ $promote -eq 0 ] && sysctl -q net.ipv4.conf.$devdummy.promote_secondaries=0 - echo "PASS: promote_secondaries complete" + end_test "PASS: promote_secondaries complete" } kci_test_addrlabel() { local ret=0 - - ip addrlabel add prefix dead::/64 dev lo label 1 - check_err $? - - ip addrlabel list |grep -q "prefix dead::/64 dev lo label 1" - check_err $? - - ip addrlabel del prefix dead::/64 dev lo label 1 2> /dev/null - check_err $? - - ip addrlabel add prefix dead::/64 label 1 2> /dev/null - check_err $? - - ip addrlabel del prefix dead::/64 label 1 2> /dev/null - check_err $? + run_cmd ip addrlabel add prefix dead::/64 dev lo label 1 + run_cmd_grep "prefix dead::/64 dev lo label 1" ip addrlabel list + run_cmd ip addrlabel del prefix dead::/64 dev lo label 1 + run_cmd ip addrlabel add prefix dead::/64 label 1 + run_cmd ip addrlabel del prefix dead::/64 label 1 # concurrent add/delete for i in $(seq 1 1000); do @@ -346,11 +349,11 @@ kci_test_addrlabel() ip addrlabel del prefix 1c3::/64 label 12345 2>/dev/null if [ $ret -ne 0 ];then - echo "FAIL: ipv6 addrlabel" + end_test "FAIL: ipv6 addrlabel" return 1 fi - echo "PASS: ipv6 addrlabel" + end_test "PASS: ipv6 addrlabel" } kci_test_ifalias() @@ -358,35 +361,28 @@ kci_test_ifalias() local ret=0 namewant=$(uuidgen) syspathname="/sys/class/net/$devdummy/ifalias" - - ip link set dev "$devdummy" alias "$namewant" - check_err $? + run_cmd ip link set dev "$devdummy" alias "$namewant" if [ $ret -ne 0 ]; then - echo "FAIL: cannot set interface alias of $devdummy to $namewant" + end_test "FAIL: cannot set interface alias of $devdummy to $namewant" return 1 fi - - ip link show "$devdummy" | grep -q "alias $namewant" - check_err $? + run_cmd_grep "alias $namewant" ip link show "$devdummy" if [ -r "$syspathname" ] ; then read namehave < "$syspathname" if [ "$namewant" != "$namehave" ]; then - echo "FAIL: did set ifalias $namewant but got $namehave" + end_test "FAIL: did set ifalias $namewant but got $namehave" return 1 fi namewant=$(uuidgen) echo "$namewant" > "$syspathname" - ip link show "$devdummy" | grep -q "alias $namewant" - check_err $? + run_cmd_grep "alias $namewant" ip link show "$devdummy" # sysfs interface allows to delete alias again echo "" > "$syspathname" - - ip link show "$devdummy" | grep -q "alias $namewant" - check_fail $? + run_cmd_grep_fail "alias $namewant" ip link show "$devdummy" for i in $(seq 1 100); do uuidgen > "$syspathname" & @@ -395,57 +391,48 @@ kci_test_ifalias() wait # re-add the alias -- kernel should free mem when dummy dev is removed - ip link set dev "$devdummy" alias "$namewant" - check_err $? + run_cmd ip link set dev "$devdummy" alias "$namewant" + fi if [ $ret -ne 0 ]; then - echo "FAIL: set interface alias $devdummy to $namewant" + end_test "FAIL: set interface alias $devdummy to $namewant" return 1 fi - echo "PASS: set ifalias $namewant for $devdummy" + end_test "PASS: set ifalias $namewant for $devdummy" } kci_test_vrf() { vrfname="test-vrf" local ret=0 - - ip link show type vrf 2>/dev/null + run_cmd ip link show type vrf if [ $? -ne 0 ]; then - echo "SKIP: vrf: iproute2 too old" + end_test "SKIP: vrf: iproute2 too old" return $ksft_skip fi - - ip link add "$vrfname" type vrf table 10 - check_err $? + run_cmd ip link add "$vrfname" type vrf table 10 if [ $ret -ne 0 ];then - echo "FAIL: can't add vrf interface, skipping test" + end_test "FAIL: can't add vrf interface, skipping test" return 0 fi - - ip -br link show type vrf | grep -q "$vrfname" - check_err $? + run_cmd_grep "$vrfname" ip -br link show type vrf if [ $ret -ne 0 ];then - echo "FAIL: created vrf device not found" + end_test "FAIL: created vrf device not found" return 1 fi - ip link set dev "$vrfname" up - check_err $? - - ip link set dev "$devdummy" master "$vrfname" - check_err $? - ip link del dev "$vrfname" - check_err $? + run_cmd ip link set dev "$vrfname" up + run_cmd ip link set dev "$devdummy" master "$vrfname" + run_cmd ip link del dev "$vrfname" if [ $ret -ne 0 ];then - echo "FAIL: vrf" + end_test "FAIL: vrf" return 1 fi - echo "PASS: vrf" + end_test "PASS: vrf" } kci_test_encap_vxlan() @@ -454,84 +441,44 @@ kci_test_encap_vxlan() vxlan="test-vxlan0" vlan="test-vlan0" testns="$1" - - ip -netns "$testns" link add "$vxlan" type vxlan id 42 group 239.1.1.1 \ - dev "$devdummy" dstport 4789 2>/dev/null + run_cmd ip -netns "$testns" link add "$vxlan" type vxlan id 42 group 239.1.1.1 \ + dev "$devdummy" dstport 4789 if [ $? -ne 0 ]; then - echo "FAIL: can't add vxlan interface, skipping test" + end_test "FAIL: can't add vxlan interface, skipping test" return 0 fi - check_err $? - ip -netns "$testns" addr add 10.2.11.49/24 dev "$vxlan" - check_err $? - - ip -netns "$testns" link set up dev "$vxlan" - check_err $? - - ip -netns "$testns" link add link "$vxlan" name "$vlan" type vlan id 1 - check_err $? + run_cmd ip -netns "$testns" addr add 10.2.11.49/24 dev "$vxlan" + run_cmd ip -netns "$testns" link set up dev "$vxlan" + run_cmd ip -netns "$testns" link add link "$vxlan" name "$vlan" type vlan id 1 # changelink testcases - ip -netns "$testns" link set dev "$vxlan" type vxlan vni 43 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan group ffe5::5 dev "$devdummy" 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan ttl inherit 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan ttl 64 - check_err $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan nolearning - check_err $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan proxy 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan norsc 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan l2miss 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan l3miss 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan external 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan udpcsum 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan udp6zerocsumtx 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan udp6zerocsumrx 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan remcsumtx 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan remcsumrx 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan gbp 2>/dev/null - check_fail $? - - ip -netns "$testns" link set dev "$vxlan" type vxlan gpe 2>/dev/null - check_fail $? - - ip -netns "$testns" link del "$vxlan" - check_err $? + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan vni 43 + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan group ffe5::5 dev "$devdummy" + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan ttl inherit + + run_cmd ip -netns "$testns" link set dev "$vxlan" type vxlan ttl 64 + run_cmd ip -netns "$testns" link set dev "$vxlan" type vxlan nolearning + + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan proxy + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan norsc + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan l2miss + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan l3miss + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan external + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan udpcsum + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan udp6zerocsumtx + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan udp6zerocsumrx + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan remcsumtx + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan remcsumrx + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan gbp + run_cmd_fail ip -netns "$testns" link set dev "$vxlan" type vxlan gpe + run_cmd ip -netns "$testns" link del "$vxlan" if [ $ret -ne 0 ]; then - echo "FAIL: vxlan" + end_test "FAIL: vxlan" return 1 fi - echo "PASS: vxlan" + end_test "PASS: vxlan" } kci_test_encap_fou() @@ -539,39 +486,32 @@ kci_test_encap_fou() local ret=0 name="test-fou" testns="$1" - - ip fou help 2>&1 |grep -q 'Usage: ip fou' + run_cmd_grep 'Usage: ip fou' ip fou help if [ $? -ne 0 ];then - echo "SKIP: fou: iproute2 too old" + end_test "SKIP: fou: iproute2 too old" return $ksft_skip fi if ! /sbin/modprobe -q -n fou; then - echo "SKIP: module fou is not found" + end_test "SKIP: module fou is not found" return $ksft_skip fi /sbin/modprobe -q fou - ip -netns "$testns" fou add port 7777 ipproto 47 2>/dev/null + + run_cmd ip -netns "$testns" fou add port 7777 ipproto 47 if [ $? -ne 0 ];then - echo "FAIL: can't add fou port 7777, skipping test" + end_test "FAIL: can't add fou port 7777, skipping test" return 1 fi - - ip -netns "$testns" fou add port 8888 ipproto 4 - check_err $? - - ip -netns "$testns" fou del port 9999 2>/dev/null - check_fail $? - - ip -netns "$testns" fou del port 7777 - check_err $? - + run_cmd ip -netns "$testns" fou add port 8888 ipproto 4 + run_cmd_fail ip -netns "$testns" fou del port 9999 + run_cmd ip -netns "$testns" fou del port 7777 if [ $ret -ne 0 ]; then - echo "FAIL: fou" + end_test "FAIL: fou"s return 1 fi - echo "PASS: fou" + end_test "PASS: fou" } # test various encap methods, use netns to avoid unwanted interference @@ -579,25 +519,16 @@ kci_test_encap() { testns="testns" local ret=0 - - ip netns add "$testns" + run_cmd ip netns add "$testns" if [ $? -ne 0 ]; then - echo "SKIP encap tests: cannot add net namespace $testns" + end_test "SKIP encap tests: cannot add net namespace $testns" return $ksft_skip fi - - ip -netns "$testns" link set lo up - check_err $? - - ip -netns "$testns" link add name "$devdummy" type dummy - check_err $? - ip -netns "$testns" link set "$devdummy" up - check_err $? - - kci_test_encap_vxlan "$testns" - check_err $? - kci_test_encap_fou "$testns" - check_err $? + run_cmd ip -netns "$testns" link set lo up + run_cmd ip -netns "$testns" link add name "$devdummy" type dummy + run_cmd ip -netns "$testns" link set "$devdummy" up + run_cmd kci_test_encap_vxlan "$testns" + run_cmd kci_test_encap_fou "$testns" ip netns del "$testns" return $ret @@ -607,41 +538,28 @@ kci_test_macsec() { msname="test_macsec0" local ret=0 - - ip macsec help 2>&1 | grep -q "^Usage: ip macsec" + run_cmd_grep "^Usage: ip macsec" ip macsec help if [ $? -ne 0 ]; then - echo "SKIP: macsec: iproute2 too old" + end_test "SKIP: macsec: iproute2 too old" return $ksft_skip fi - - ip link add link "$devdummy" "$msname" type macsec port 42 encrypt on - check_err $? + run_cmd ip link add link "$devdummy" "$msname" type macsec port 42 encrypt on if [ $ret -ne 0 ];then - echo "FAIL: can't add macsec interface, skipping test" + end_test "FAIL: can't add macsec interface, skipping test" return 1 fi - - ip macsec add "$msname" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012 - check_err $? - - ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" - check_err $? - - ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef - check_err $? - - ip macsec show > /dev/null - check_err $? - - ip link del dev "$msname" - check_err $? + run_cmd ip macsec add "$msname" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012 + run_cmd ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" + run_cmd ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on key 00 0123456789abcdef0123456789abcdef + run_cmd ip macsec show + run_cmd ip link del dev "$msname" if [ $ret -ne 0 ];then - echo "FAIL: macsec" + end_test "FAIL: macsec" return 1 fi - echo "PASS: macsec" + end_test "PASS: macsec" } kci_test_macsec_offload() @@ -650,19 +568,18 @@ kci_test_macsec_offload() sysfsnet=/sys/bus/netdevsim/devices/netdevsim0/net/ probed=false local ret=0 - - ip macsec help 2>&1 | grep -q "^Usage: ip macsec" + run_cmd_grep "^Usage: ip macsec" ip macsec help if [ $? -ne 0 ]; then - echo "SKIP: macsec: iproute2 too old" + end_test "SKIP: macsec: iproute2 too old" return $ksft_skip fi # setup netdevsim since dummydev doesn't have offload support if [ ! -w /sys/bus/netdevsim/new_device ] ; then - modprobe -q netdevsim - check_err $? + run_cmd modprobe -q netdevsim + if [ $ret -ne 0 ]; then - echo "SKIP: macsec_offload can't load netdevsim" + end_test "SKIP: macsec_offload can't load netdevsim" return $ksft_skip fi probed=true @@ -675,43 +592,25 @@ kci_test_macsec_offload() ip link set $dev up if [ ! -d $sysfsd ] ; then - echo "FAIL: macsec_offload can't create device $dev" + end_test "FAIL: macsec_offload can't create device $dev" return 1 fi - - ethtool -k $dev | grep -q 'macsec-hw-offload: on' + run_cmd_grep 'macsec-hw-offload: on' ethtool -k $dev if [ $? -eq 1 ] ; then - echo "FAIL: macsec_offload netdevsim doesn't support MACsec offload" + end_test "FAIL: macsec_offload netdevsim doesn't support MACsec offload" return 1 fi - - ip link add link $dev kci_macsec1 type macsec port 4 offload mac - check_err $? - - ip link add link $dev kci_macsec2 type macsec address "aa:bb:cc:dd:ee:ff" port 5 offload mac - check_err $? - - ip link add link $dev kci_macsec3 type macsec sci abbacdde01020304 offload mac - check_err $? - - ip link add link $dev kci_macsec4 type macsec port 8 offload mac 2> /dev/null - check_fail $? + run_cmd ip link add link $dev kci_macsec1 type macsec port 4 offload mac + run_cmd ip link add link $dev kci_macsec2 type macsec address "aa:bb:cc:dd:ee:ff" port 5 offload mac + run_cmd ip link add link $dev kci_macsec3 type macsec sci abbacdde01020304 offload mac + run_cmd_fail ip link add link $dev kci_macsec4 type macsec port 8 offload mac msname=kci_macsec1 - - ip macsec add "$msname" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012 - check_err $? - - ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" - check_err $? - - ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on \ + run_cmd ip macsec add "$msname" tx sa 0 pn 1024 on key 01 12345678901234567890123456789012 + run_cmd ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" + run_cmd ip macsec add "$msname" rx port 1234 address "1c:ed:de:ad:be:ef" sa 0 pn 1 on \ key 00 0123456789abcdef0123456789abcdef - check_err $? - - ip macsec add "$msname" rx port 1235 address "1c:ed:de:ad:be:ef" 2> /dev/null - check_fail $? - + run_cmd_fail ip macsec add "$msname" rx port 1235 address "1c:ed:de:ad:be:ef" # clean up any leftovers for msdev in kci_macsec{1,2,3,4} ; do ip link del $msdev 2> /dev/null @@ -720,10 +619,10 @@ kci_test_macsec_offload() $probed && rmmod netdevsim if [ $ret -ne 0 ]; then - echo "FAIL: macsec_offload" + end_test "FAIL: macsec_offload" return 1 fi - echo "PASS: macsec_offload" + end_test "PASS: macsec_offload" } #------------------------------------------------------------------- @@ -755,8 +654,7 @@ kci_test_ipsec() ip addr add $srcip dev $devdummy # flush to be sure there's nothing configured - ip x s flush ; ip x p flush - check_err $? + run_cmd ip x s flush ; ip x p flush # start the monitor in the background tmpfile=`mktemp /var/run/ipsectestXXX` @@ -764,72 +662,57 @@ kci_test_ipsec() sleep 0.2 ipsecid="proto esp src $srcip dst $dstip spi 0x07" - ip x s add $ipsecid \ + run_cmd ip x s add $ipsecid \ mode transport reqid 0x07 replay-window 32 \ $algo sel src $srcip/24 dst $dstip/24 - check_err $? - lines=`ip x s list | grep $srcip | grep $dstip | wc -l` - test $lines -eq 2 - check_err $? - ip x s count | grep -q "SAD count 1" - check_err $? + lines=`ip x s list | grep $srcip | grep $dstip | wc -l` + run_cmd test $lines -eq 2 + run_cmd_grep "SAD count 1" ip x s count lines=`ip x s get $ipsecid | grep $srcip | grep $dstip | wc -l` - test $lines -eq 2 - check_err $? - - ip x s delete $ipsecid - check_err $? + run_cmd test $lines -eq 2 + run_cmd ip x s delete $ipsecid lines=`ip x s list | wc -l` - test $lines -eq 0 - check_err $? + run_cmd test $lines -eq 0 ipsecsel="dir out src $srcip/24 dst $dstip/24" - ip x p add $ipsecsel \ + run_cmd ip x p add $ipsecsel \ tmpl proto esp src $srcip dst $dstip \ spi 0x07 mode transport reqid 0x07 - check_err $? + lines=`ip x p list | grep $srcip | grep $dstip | wc -l` - test $lines -eq 2 - check_err $? + run_cmd test $lines -eq 2 - ip x p count | grep -q "SPD IN 0 OUT 1 FWD 0" - check_err $? + run_cmd_grep "SPD IN 0 OUT 1 FWD 0" ip x p count lines=`ip x p get $ipsecsel | grep $srcip | grep $dstip | wc -l` - test $lines -eq 2 - check_err $? + run_cmd test $lines -eq 2 - ip x p delete $ipsecsel - check_err $? + run_cmd ip x p delete $ipsecsel lines=`ip x p list | wc -l` - test $lines -eq 0 - check_err $? + run_cmd test $lines -eq 0 # check the monitor results kill $mpid lines=`wc -l $tmpfile | cut "-d " -f1` - test $lines -eq 20 - check_err $? + run_cmd test $lines -eq 20 rm -rf $tmpfile # clean up any leftovers - ip x s flush - check_err $? - ip x p flush - check_err $? + run_cmd ip x s flush + run_cmd ip x p flush ip addr del $srcip/32 dev $devdummy if [ $ret -ne 0 ]; then - echo "FAIL: ipsec" + end_test "FAIL: ipsec" return 1 fi - echo "PASS: ipsec" + end_test "PASS: ipsec" } #------------------------------------------------------------------- @@ -857,10 +740,9 @@ kci_test_ipsec_offload() # setup netdevsim since dummydev doesn't have offload support if [ ! -w /sys/bus/netdevsim/new_device ] ; then - modprobe -q netdevsim - check_err $? + run_cmd modprobe -q netdevsim if [ $ret -ne 0 ]; then - echo "SKIP: ipsec_offload can't load netdevsim" + end_test "SKIP: ipsec_offload can't load netdevsim" return $ksft_skip fi probed=true @@ -874,11 +756,11 @@ kci_test_ipsec_offload() ip addr add $srcip dev $dev ip link set $dev up if [ ! -d $sysfsd ] ; then - echo "FAIL: ipsec_offload can't create device $dev" + end_test "FAIL: ipsec_offload can't create device $dev" return 1 fi if [ ! -f $sysfsf ] ; then - echo "FAIL: ipsec_offload netdevsim doesn't support IPsec offload" + end_test "FAIL: ipsec_offload netdevsim doesn't support IPsec offload" return 1 fi @@ -886,40 +768,39 @@ kci_test_ipsec_offload() ip x s flush ; ip x p flush # create offloaded SAs, both in and out - ip x p add dir out src $srcip/24 dst $dstip/24 \ + run_cmd ip x p add dir out src $srcip/24 dst $dstip/24 \ tmpl proto esp src $srcip dst $dstip spi 9 \ mode transport reqid 42 - check_err $? - ip x p add dir in src $dstip/24 dst $srcip/24 \ + + run_cmd ip x p add dir in src $dstip/24 dst $srcip/24 \ tmpl proto esp src $dstip dst $srcip spi 9 \ mode transport reqid 42 - check_err $? - ip x s add proto esp src $srcip dst $dstip spi 9 \ + run_cmd ip x s add proto esp src $srcip dst $dstip spi 9 \ mode transport reqid 42 $algo sel src $srcip/24 dst $dstip/24 \ offload dev $dev dir out - check_err $? - ip x s add proto esp src $dstip dst $srcip spi 9 \ + + run_cmd ip x s add proto esp src $dstip dst $srcip spi 9 \ mode transport reqid 42 $algo sel src $dstip/24 dst $srcip/24 \ offload dev $dev dir in - check_err $? + if [ $ret -ne 0 ]; then - echo "FAIL: ipsec_offload can't create SA" + end_test "FAIL: ipsec_offload can't create SA" return 1 fi # does offload show up in ip output lines=`ip x s list | grep -c "crypto offload parameters: dev $dev dir"` if [ $lines -ne 2 ] ; then - echo "FAIL: ipsec_offload SA offload missing from list output" check_err 1 + end_test "FAIL: ipsec_offload SA offload missing from list output" fi # use ping to exercise the Tx path ping -I $dev -c 3 -W 1 -i 0 $dstip >/dev/null # does driver have correct offload info - diff $sysfsf - << EOF + run_cmd diff $sysfsf - << EOF SA count=2 tx=3 sa[0] tx ipaddr=0x00000000 00000000 00000000 00000000 sa[0] spi=0x00000009 proto=0x32 salt=0x61626364 crypt=1 @@ -929,7 +810,7 @@ sa[1] spi=0x00000009 proto=0x32 salt=0x61626364 crypt=1 sa[1] key=0x34333231 38373635 32313039 36353433 EOF if [ $? -ne 0 ] ; then - echo "FAIL: ipsec_offload incorrect driver data" + end_test "FAIL: ipsec_offload incorrect driver data" check_err 1 fi @@ -938,8 +819,8 @@ EOF ip x p flush lines=`grep -c "SA count=0" $sysfsf` if [ $lines -ne 1 ] ; then - echo "FAIL: ipsec_offload SA not removed from driver" check_err 1 + end_test "FAIL: ipsec_offload SA not removed from driver" fi # clean up any leftovers @@ -947,10 +828,10 @@ EOF $probed && rmmod netdevsim if [ $ret -ne 0 ]; then - echo "FAIL: ipsec_offload" + end_test "FAIL: ipsec_offload" return 1 fi - echo "PASS: ipsec_offload" + end_test "PASS: ipsec_offload" } kci_test_gretap() @@ -959,46 +840,38 @@ kci_test_gretap() DEV_NS=gretap00 local ret=0 - ip netns add "$testns" + run_cmd ip netns add "$testns" if [ $? -ne 0 ]; then - echo "SKIP gretap tests: cannot add net namespace $testns" + end_test "SKIP gretap tests: cannot add net namespace $testns" return $ksft_skip fi - ip link help gretap 2>&1 | grep -q "^Usage:" + run_cmd_grep "^Usage:" ip link help gretap if [ $? -ne 0 ];then - echo "SKIP: gretap: iproute2 too old" + end_test "SKIP: gretap: iproute2 too old" ip netns del "$testns" return $ksft_skip fi # test native tunnel - ip -netns "$testns" link add dev "$DEV_NS" type gretap seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type gretap seq \ key 102 local 172.16.1.100 remote 172.16.1.200 - check_err $? - - ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 + run_cmd ip -netns "$testns" link set dev $DEV_NS ups + run_cmd ip -netns "$testns" link del "$DEV_NS" # test external mode - ip -netns "$testns" link add dev "$DEV_NS" type gretap external - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type gretap external + run_cmd ip -netns "$testns" link del "$DEV_NS" if [ $ret -ne 0 ]; then - echo "FAIL: gretap" + end_test "FAIL: gretap" ip netns del "$testns" return 1 fi - echo "PASS: gretap" + end_test "PASS: gretap" ip netns del "$testns" } @@ -1009,46 +882,38 @@ kci_test_ip6gretap() DEV_NS=ip6gretap00 local ret=0 - ip netns add "$testns" + run_cmd ip netns add "$testns" if [ $? -ne 0 ]; then - echo "SKIP ip6gretap tests: cannot add net namespace $testns" + end_test "SKIP ip6gretap tests: cannot add net namespace $testns" return $ksft_skip fi - ip link help ip6gretap 2>&1 | grep -q "^Usage:" + run_cmd_grep "^Usage:" ip link help ip6gretap if [ $? -ne 0 ];then - echo "SKIP: ip6gretap: iproute2 too old" + end_test "SKIP: ip6gretap: iproute2 too old" ip netns del "$testns" return $ksft_skip fi # test native tunnel - ip -netns "$testns" link add dev "$DEV_NS" type ip6gretap seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type ip6gretap seq \ key 102 local fc00:100::1 remote fc00:100::2 - check_err $? - ip -netns "$testns" addr add dev "$DEV_NS" fc00:200::1/96 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" fc00:200::1/96 + run_cmd ip -netns "$testns" link set dev $DEV_NS up + run_cmd ip -netns "$testns" link del "$DEV_NS" # test external mode - ip -netns "$testns" link add dev "$DEV_NS" type ip6gretap external - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type ip6gretap external + run_cmd ip -netns "$testns" link del "$DEV_NS" if [ $ret -ne 0 ]; then - echo "FAIL: ip6gretap" + end_test "FAIL: ip6gretap" ip netns del "$testns" return 1 fi - echo "PASS: ip6gretap" + end_test "PASS: ip6gretap" ip netns del "$testns" } @@ -1058,62 +923,47 @@ kci_test_erspan() testns="testns" DEV_NS=erspan00 local ret=0 - - ip link help erspan 2>&1 | grep -q "^Usage:" + run_cmd_grep "^Usage:" ip link help erspan if [ $? -ne 0 ];then - echo "SKIP: erspan: iproute2 too old" + end_test "SKIP: erspan: iproute2 too old" return $ksft_skip fi - - ip netns add "$testns" + run_cmd ip netns add "$testns" if [ $? -ne 0 ]; then - echo "SKIP erspan tests: cannot add net namespace $testns" + end_test "SKIP erspan tests: cannot add net namespace $testns" return $ksft_skip fi # test native tunnel erspan v1 - ip -netns "$testns" link add dev "$DEV_NS" type erspan seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type erspan seq \ key 102 local 172.16.1.100 remote 172.16.1.200 \ erspan_ver 1 erspan 488 - check_err $? - ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 + run_cmd ip -netns "$testns" link set dev $DEV_NS up + run_cmd ip -netns "$testns" link del "$DEV_NS" # test native tunnel erspan v2 - ip -netns "$testns" link add dev "$DEV_NS" type erspan seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type erspan seq \ key 102 local 172.16.1.100 remote 172.16.1.200 \ erspan_ver 2 erspan_dir ingress erspan_hwid 7 - check_err $? - - ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 + run_cmd ip -netns "$testns" link set dev $DEV_NS up + run_cmd ip -netns "$testns" link del "$DEV_NS" # test external mode - ip -netns "$testns" link add dev "$DEV_NS" type erspan external - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type erspan external + run_cmd ip -netns "$testns" link del "$DEV_NS" if [ $ret -ne 0 ]; then - echo "FAIL: erspan" + end_test "FAIL: erspan" ip netns del "$testns" return 1 fi - echo "PASS: erspan" + end_test "PASS: erspan" ip netns del "$testns" } @@ -1123,63 +973,49 @@ kci_test_ip6erspan() testns="testns" DEV_NS=ip6erspan00 local ret=0 - - ip link help ip6erspan 2>&1 | grep -q "^Usage:" + run_cmd_grep "^Usage:" ip link help ip6erspan if [ $? -ne 0 ];then - echo "SKIP: ip6erspan: iproute2 too old" + end_test "SKIP: ip6erspan: iproute2 too old" return $ksft_skip fi - - ip netns add "$testns" + run_cmd ip netns add "$testns" if [ $? -ne 0 ]; then - echo "SKIP ip6erspan tests: cannot add net namespace $testns" + end_test "SKIP ip6erspan tests: cannot add net namespace $testns" return $ksft_skip fi # test native tunnel ip6erspan v1 - ip -netns "$testns" link add dev "$DEV_NS" type ip6erspan seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type ip6erspan seq \ key 102 local fc00:100::1 remote fc00:100::2 \ erspan_ver 1 erspan 488 - check_err $? - ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 + run_cmd ip -netns "$testns" link set dev $DEV_NS up + run_cmd ip -netns "$testns" link del "$DEV_NS" # test native tunnel ip6erspan v2 - ip -netns "$testns" link add dev "$DEV_NS" type ip6erspan seq \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" type ip6erspan seq \ key 102 local fc00:100::1 remote fc00:100::2 \ erspan_ver 2 erspan_dir ingress erspan_hwid 7 - check_err $? - ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - check_err $? - ip -netns "$testns" link set dev $DEV_NS up - check_err $? - - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 + run_cmd ip -netns "$testns" link set dev $DEV_NS up + run_cmd ip -netns "$testns" link del "$DEV_NS" # test external mode - ip -netns "$testns" link add dev "$DEV_NS" \ + run_cmd ip -netns "$testns" link add dev "$DEV_NS" \ type ip6erspan external - check_err $? - ip -netns "$testns" link del "$DEV_NS" - check_err $? + run_cmd ip -netns "$testns" link del "$DEV_NS" if [ $ret -ne 0 ]; then - echo "FAIL: ip6erspan" + end_test "FAIL: ip6erspan" ip netns del "$testns" return 1 fi - echo "PASS: ip6erspan" + end_test "PASS: ip6erspan" ip netns del "$testns" } @@ -1195,45 +1031,35 @@ kci_test_fdb_get() dstip="10.0.2.3" local ret=0 - bridge fdb help 2>&1 |grep -q 'bridge fdb get' + run_cmd_grep 'bridge fdb get' bridge fdb help if [ $? -ne 0 ];then - echo "SKIP: fdb get tests: iproute2 too old" + end_test "SKIP: fdb get tests: iproute2 too old" return $ksft_skip fi - ip netns add testns + run_cmd ip netns add testns if [ $? -ne 0 ]; then - echo "SKIP fdb get tests: cannot add net namespace $testns" + end_test "SKIP fdb get tests: cannot add net namespace $testns" return $ksft_skip fi - - $IP link add "$vxlandev" type vxlan id 10 local $localip \ - dstport 4789 2>/dev/null - check_err $? - $IP link add name "$brdev" type bridge &>/dev/null - check_err $? - $IP link set dev "$vxlandev" master "$brdev" &>/dev/null - check_err $? - $BRIDGE fdb add $test_mac dev "$vxlandev" master &>/dev/null - check_err $? - $BRIDGE fdb add $test_mac dev "$vxlandev" dst $dstip self &>/dev/null - check_err $? - - $BRIDGE fdb get $test_mac brport "$vxlandev" 2>/dev/null | grep -q "dev $vxlandev master $brdev" - check_err $? - $BRIDGE fdb get $test_mac br "$brdev" 2>/dev/null | grep -q "dev $vxlandev master $brdev" - check_err $? - $BRIDGE fdb get $test_mac dev "$vxlandev" self 2>/dev/null | grep -q "dev $vxlandev dst $dstip" - check_err $? + run_cmd $IP link add "$vxlandev" type vxlan id 10 local $localip \ + dstport 4789 + run_cmd $IP link add name "$brdev" type bridge + run_cmd $IP link set dev "$vxlandev" master "$brdev" + run_cmd $BRIDGE fdb add $test_mac dev "$vxlandev" master + run_cmd $BRIDGE fdb add $test_mac dev "$vxlandev" dst $dstip self + run_cmd_grep "dev $vxlandev master $brdev" $BRIDGE fdb get $test_mac brport "$vxlandev" + run_cmd_grep "dev $vxlandev master $brdev" $BRIDGE fdb get $test_mac br "$brdev" + run_cmd_grep "dev $vxlandev dst $dstip" $BRIDGE fdb get $test_mac dev "$vxlandev" self ip netns del testns &>/dev/null if [ $ret -ne 0 ]; then - echo "FAIL: bridge fdb get" + end_test "FAIL: bridge fdb get" return 1 fi - echo "PASS: bridge fdb get" + end_test "PASS: bridge fdb get" } kci_test_neigh_get() @@ -1243,50 +1069,38 @@ kci_test_neigh_get() dstip6=dead::2 local ret=0 - ip neigh help 2>&1 |grep -q 'ip neigh get' + run_cmd_grep 'ip neigh get' ip neigh help if [ $? -ne 0 ];then - echo "SKIP: fdb get tests: iproute2 too old" + end_test "SKIP: fdb get tests: iproute2 too old" return $ksft_skip fi # ipv4 - ip neigh add $dstip lladdr $dstmac dev "$devdummy" > /dev/null - check_err $? - ip neigh get $dstip dev "$devdummy" 2> /dev/null | grep -q "$dstmac" - check_err $? - ip neigh del $dstip lladdr $dstmac dev "$devdummy" > /dev/null - check_err $? + run_cmd ip neigh add $dstip lladdr $dstmac dev "$devdummy" + run_cmd_grep "$dstmac" ip neigh get $dstip dev "$devdummy" + run_cmd ip neigh del $dstip lladdr $dstmac dev "$devdummy" # ipv4 proxy - ip neigh add proxy $dstip dev "$devdummy" > /dev/null - check_err $? - ip neigh get proxy $dstip dev "$devdummy" 2>/dev/null | grep -q "$dstip" - check_err $? - ip neigh del proxy $dstip dev "$devdummy" > /dev/null - check_err $? + run_cmd ip neigh add proxy $dstip dev "$devdummy" + run_cmd_grep "$dstip" ip neigh get proxy $dstip dev "$devdummy" + run_cmd ip neigh del proxy $dstip dev "$devdummy" # ipv6 - ip neigh add $dstip6 lladdr $dstmac dev "$devdummy" > /dev/null - check_err $? - ip neigh get $dstip6 dev "$devdummy" 2> /dev/null | grep -q "$dstmac" - check_err $? - ip neigh del $dstip6 lladdr $dstmac dev "$devdummy" > /dev/null - check_err $? + run_cmd ip neigh add $dstip6 lladdr $dstmac dev "$devdummy" + run_cmd_grep "$dstmac" ip neigh get $dstip6 dev "$devdummy" + run_cmd ip neigh del $dstip6 lladdr $dstmac dev "$devdummy" # ipv6 proxy - ip neigh add proxy $dstip6 dev "$devdummy" > /dev/null - check_err $? - ip neigh get proxy $dstip6 dev "$devdummy" 2>/dev/null | grep -q "$dstip6" - check_err $? - ip neigh del proxy $dstip6 dev "$devdummy" > /dev/null - check_err $? + run_cmd ip neigh add proxy $dstip6 dev "$devdummy" + run_cmd_grep "$dstip6" ip neigh get proxy $dstip6 dev "$devdummy" + run_cmd ip neigh del proxy $dstip6 dev "$devdummy" if [ $ret -ne 0 ];then - echo "FAIL: neigh get" + end_test "FAIL: neigh get" return 1 fi - echo "PASS: neigh get" + end_test "PASS: neigh get" } kci_test_bridge_parent_id() @@ -1296,10 +1110,9 @@ kci_test_bridge_parent_id() probed=false if [ ! -w /sys/bus/netdevsim/new_device ] ; then - modprobe -q netdevsim - check_err $? + run_cmd modprobe -q netdevsim if [ $ret -ne 0 ]; then - echo "SKIP: bridge_parent_id can't load netdevsim" + end_test "SKIP: bridge_parent_id can't load netdevsim" return $ksft_skip fi probed=true @@ -1312,13 +1125,11 @@ kci_test_bridge_parent_id() udevadm settle dev10=`ls ${sysfsnet}10/net/` dev20=`ls ${sysfsnet}20/net/` - - ip link add name test-bond0 type bond mode 802.3ad - ip link set dev $dev10 master test-bond0 - ip link set dev $dev20 master test-bond0 - ip link add name test-br0 type bridge - ip link set dev test-bond0 master test-br0 - check_err $? + run_cmd ip link add name test-bond0 type bond mode 802.3ad + run_cmd ip link set dev $dev10 master test-bond0 + run_cmd ip link set dev $dev20 master test-bond0 + run_cmd ip link add name test-br0 type bridge + run_cmd ip link set dev test-bond0 master test-br0 # clean up any leftovers ip link del dev test-br0 @@ -1328,10 +1139,10 @@ kci_test_bridge_parent_id() $probed && rmmod netdevsim if [ $ret -ne 0 ]; then - echo "FAIL: bridge_parent_id" + end_test "FAIL: bridge_parent_id" return 1 fi - echo "PASS: bridge_parent_id" + end_test "PASS: bridge_parent_id" } address_get_proto() @@ -1409,10 +1220,10 @@ do_test_address_proto() ip address del dev "$devdummy" "$addr3" if [ $ret -ne 0 ]; then - echo "FAIL: address proto $what" + end_test "FAIL: address proto $what" return 1 fi - echo "PASS: address proto $what" + end_test "PASS: address proto $what" } kci_test_address_proto() @@ -1435,7 +1246,7 @@ kci_test_rtnl() kci_add_dummy if [ $ret -ne 0 ];then - echo "FAIL: cannot add dummy interface" + end_test "FAIL: cannot add dummy interface" return 1 fi @@ -1455,31 +1266,39 @@ usage: ${0##*/} OPTS -t <test> Test(s) to run (default: all) (options: $(echo $ALL_TESTS)) + -v Verbose mode (show commands and output) + -P Pause after every test + -p Pause after every failing test before cleanup (for debugging) EOF } #check for needed privileges if [ "$(id -u)" -ne 0 ];then - echo "SKIP: Need root privileges" + end_test "SKIP: Need root privileges" exit $ksft_skip fi for x in ip tc;do $x -Version 2>/dev/null >/dev/null if [ $? -ne 0 ];then - echo "SKIP: Could not run test without the $x tool" + end_test "SKIP: Could not run test without the $x tool" exit $ksft_skip fi done -while getopts t:h o; do +while getopts t:hvpP o; do case $o in t) TESTS=$OPTARG;; + v) VERBOSE=1;; + p) PAUSE_ON_FAIL=yes;; + P) PAUSE=yes;; h) usage; exit 0;; *) usage; exit 1;; esac done +[ $PAUSE = "yes" ] && PAUSE_ON_FAIL="no" + kci_test_rtnl exit $? diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 297d972558fb..464853a7f982 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -613,11 +613,11 @@ TEST_F(tls, sendmsg_large) msg.msg_iov = &vec; msg.msg_iovlen = 1; - EXPECT_EQ(sendmsg(self->cfd, &msg, 0), send_len); + EXPECT_EQ(sendmsg(self->fd, &msg, 0), send_len); } while (recvs++ < sends) { - EXPECT_NE(recv(self->fd, mem, send_len, 0), -1); + EXPECT_NE(recv(self->cfd, mem, send_len, 0), -1); } free(mem); @@ -646,9 +646,9 @@ TEST_F(tls, sendmsg_multiple) msg.msg_iov = vec; msg.msg_iovlen = iov_len; - EXPECT_EQ(sendmsg(self->cfd, &msg, 0), total_len); + EXPECT_EQ(sendmsg(self->fd, &msg, 0), total_len); buf = malloc(total_len); - EXPECT_NE(recv(self->fd, buf, total_len, 0), -1); + EXPECT_NE(recv(self->cfd, buf, total_len, 0), -1); for (i = 0; i < iov_len; i++) { EXPECT_EQ(memcmp(test_strs[i], buf + len_cmp, strlen(test_strs[i])), diff --git a/tools/testing/selftests/netfilter/.gitignore b/tools/testing/selftests/netfilter/.gitignore index 4cb887b57413..4b2928e1c19d 100644 --- a/tools/testing/selftests/netfilter/.gitignore +++ b/tools/testing/selftests/netfilter/.gitignore @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only nf-queue connect_close +audit_logread diff --git a/tools/testing/selftests/netfilter/Makefile b/tools/testing/selftests/netfilter/Makefile index 3686bfa6c58d..ef90aca4cc96 100644 --- a/tools/testing/selftests/netfilter/Makefile +++ b/tools/testing/selftests/netfilter/Makefile @@ -6,13 +6,14 @@ TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \ nft_concat_range.sh nft_conntrack_helper.sh \ nft_queue.sh nft_meta.sh nf_nat_edemux.sh \ ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \ - conntrack_vrf.sh nft_synproxy.sh rpath.sh + conntrack_vrf.sh nft_synproxy.sh rpath.sh nft_audit.sh \ + conntrack_sctp_collision.sh HOSTPKG_CONFIG := pkg-config CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null) LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) -TEST_GEN_FILES = nf-queue connect_close +TEST_GEN_FILES = nf-queue connect_close audit_logread sctp_collision include ../lib.mk diff --git a/tools/testing/selftests/netfilter/audit_logread.c b/tools/testing/selftests/netfilter/audit_logread.c new file mode 100644 index 000000000000..a0a880fc2d9d --- /dev/null +++ b/tools/testing/selftests/netfilter/audit_logread.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> +#include <linux/audit.h> +#include <linux/netlink.h> + +static int fd; + +#define MAX_AUDIT_MESSAGE_LENGTH 8970 +struct audit_message { + struct nlmsghdr nlh; + union { + struct audit_status s; + char data[MAX_AUDIT_MESSAGE_LENGTH]; + } u; +}; + +int audit_recv(int fd, struct audit_message *rep) +{ + struct sockaddr_nl addr; + socklen_t addrlen = sizeof(addr); + int ret; + + do { + ret = recvfrom(fd, rep, sizeof(*rep), 0, + (struct sockaddr *)&addr, &addrlen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0 || + addrlen != sizeof(addr) || + addr.nl_pid != 0 || + rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */ + return -1; + + return ret; +} + +int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val) +{ + static int seq = 0; + struct audit_message msg = { + .nlh = { + .nlmsg_len = NLMSG_SPACE(sizeof(msg.u.s)), + .nlmsg_type = type, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .nlmsg_seq = ++seq, + }, + .u.s = { + .mask = key, + .enabled = key == AUDIT_STATUS_ENABLED ? val : 0, + .pid = key == AUDIT_STATUS_PID ? val : 0, + } + }; + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + int ret; + + do { + ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0, + (struct sockaddr *)&addr, sizeof(addr)); + } while (ret < 0 && errno == EINTR); + + if (ret != (int)msg.nlh.nlmsg_len) + return -1; + return 0; +} + +int audit_set(int fd, uint32_t key, uint32_t val) +{ + struct audit_message rep = { 0 }; + int ret; + + ret = audit_send(fd, AUDIT_SET, key, val); + if (ret) + return ret; + + ret = audit_recv(fd, &rep); + if (ret < 0) + return ret; + return 0; +} + +int readlog(int fd) +{ + struct audit_message rep = { 0 }; + int ret = audit_recv(fd, &rep); + const char *sep = ""; + char *k, *v; + + if (ret < 0) + return ret; + + if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG) + return 0; + + /* skip the initial "audit(...): " part */ + strtok(rep.u.data, " "); + + while ((k = strtok(NULL, "="))) { + v = strtok(NULL, " "); + + /* these vary and/or are uninteresting, ignore */ + if (!strcmp(k, "pid") || + !strcmp(k, "comm") || + !strcmp(k, "subj")) + continue; + + /* strip the varying sequence number */ + if (!strcmp(k, "table")) + *strchrnul(v, ':') = '\0'; + + printf("%s%s=%s", sep, k, v); + sep = " "; + } + if (*sep) { + printf("\n"); + fflush(stdout); + } + return 0; +} + +void cleanup(int sig) +{ + audit_set(fd, AUDIT_STATUS_ENABLED, 0); + close(fd); + if (sig) + exit(0); +} + +int main(int argc, char **argv) +{ + struct sigaction act = { + .sa_handler = cleanup, + }; + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); + if (fd < 0) { + perror("Can't open netlink socket"); + return -1; + } + + if (sigaction(SIGTERM, &act, NULL) < 0 || + sigaction(SIGINT, &act, NULL) < 0) { + perror("Can't set signal handler"); + close(fd); + return -1; + } + + audit_set(fd, AUDIT_STATUS_ENABLED, 1); + audit_set(fd, AUDIT_STATUS_PID, getpid()); + + while (1) + readlog(fd); +} diff --git a/tools/testing/selftests/netfilter/config b/tools/testing/selftests/netfilter/config index 4faf2ce021d9..7c42b1b2c69b 100644 --- a/tools/testing/selftests/netfilter/config +++ b/tools/testing/selftests/netfilter/config @@ -6,3 +6,4 @@ CONFIG_NFT_REDIR=m CONFIG_NFT_MASQ=m CONFIG_NFT_FLOW_OFFLOAD=m CONFIG_NF_CT_NETLINK=m +CONFIG_AUDIT=y diff --git a/tools/testing/selftests/netfilter/conntrack_sctp_collision.sh b/tools/testing/selftests/netfilter/conntrack_sctp_collision.sh new file mode 100755 index 000000000000..a924e595cfd8 --- /dev/null +++ b/tools/testing/selftests/netfilter/conntrack_sctp_collision.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Testing For SCTP COLLISION SCENARIO as Below: +# +# 14:35:47.655279 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT] [init tag: 2017837359] +# 14:35:48.353250 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT] [init tag: 1187206187] +# 14:35:48.353275 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [INIT ACK] [init tag: 2017837359] +# 14:35:48.353283 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [COOKIE ECHO] +# 14:35:48.353977 IP CLIENT_IP.PORT > SERVER_IP.PORT: sctp (1) [COOKIE ACK] +# 14:35:48.855335 IP SERVER_IP.PORT > CLIENT_IP.PORT: sctp (1) [INIT ACK] [init tag: 164579970] +# +# TOPO: SERVER_NS (link0)<--->(link1) ROUTER_NS (link2)<--->(link3) CLIENT_NS + +CLIENT_NS=$(mktemp -u client-XXXXXXXX) +CLIENT_IP="198.51.200.1" +CLIENT_PORT=1234 + +SERVER_NS=$(mktemp -u server-XXXXXXXX) +SERVER_IP="198.51.100.1" +SERVER_PORT=1234 + +ROUTER_NS=$(mktemp -u router-XXXXXXXX) +CLIENT_GW="198.51.200.2" +SERVER_GW="198.51.100.2" + +# setup the topo +setup() { + ip net add $CLIENT_NS + ip net add $SERVER_NS + ip net add $ROUTER_NS + ip -n $SERVER_NS link add link0 type veth peer name link1 netns $ROUTER_NS + ip -n $CLIENT_NS link add link3 type veth peer name link2 netns $ROUTER_NS + + ip -n $SERVER_NS link set link0 up + ip -n $SERVER_NS addr add $SERVER_IP/24 dev link0 + ip -n $SERVER_NS route add $CLIENT_IP dev link0 via $SERVER_GW + + ip -n $ROUTER_NS link set link1 up + ip -n $ROUTER_NS link set link2 up + ip -n $ROUTER_NS addr add $SERVER_GW/24 dev link1 + ip -n $ROUTER_NS addr add $CLIENT_GW/24 dev link2 + ip net exec $ROUTER_NS sysctl -wq net.ipv4.ip_forward=1 + + ip -n $CLIENT_NS link set link3 up + ip -n $CLIENT_NS addr add $CLIENT_IP/24 dev link3 + ip -n $CLIENT_NS route add $SERVER_IP dev link3 via $CLIENT_GW + + # simulate the delay on OVS upcall by setting up a delay for INIT_ACK with + # tc on $SERVER_NS side + tc -n $SERVER_NS qdisc add dev link0 root handle 1: htb + tc -n $SERVER_NS class add dev link0 parent 1: classid 1:1 htb rate 100mbit + tc -n $SERVER_NS filter add dev link0 parent 1: protocol ip u32 match ip protocol 132 \ + 0xff match u8 2 0xff at 32 flowid 1:1 + tc -n $SERVER_NS qdisc add dev link0 parent 1:1 handle 10: netem delay 1200ms + + # simulate the ctstate check on OVS nf_conntrack + ip net exec $ROUTER_NS iptables -A FORWARD -m state --state INVALID,UNTRACKED -j DROP + ip net exec $ROUTER_NS iptables -A INPUT -p sctp -j DROP + + # use a smaller number for assoc's max_retrans to reproduce the issue + modprobe sctp + ip net exec $CLIENT_NS sysctl -wq net.sctp.association_max_retrans=3 +} + +cleanup() { + ip net exec $CLIENT_NS pkill sctp_collision 2>&1 >/dev/null + ip net exec $SERVER_NS pkill sctp_collision 2>&1 >/dev/null + ip net del "$CLIENT_NS" + ip net del "$SERVER_NS" + ip net del "$ROUTER_NS" +} + +do_test() { + ip net exec $SERVER_NS ./sctp_collision server \ + $SERVER_IP $SERVER_PORT $CLIENT_IP $CLIENT_PORT & + ip net exec $CLIENT_NS ./sctp_collision client \ + $CLIENT_IP $CLIENT_PORT $SERVER_IP $SERVER_PORT +} + +# NOTE: one way to work around the issue is set a smaller hb_interval +# ip net exec $CLIENT_NS sysctl -wq net.sctp.hb_interval=3500 + +# run the test case +trap cleanup EXIT +setup && \ +echo "Test for SCTP Collision in nf_conntrack:" && \ +do_test && echo "PASS!" +exit $? diff --git a/tools/testing/selftests/netfilter/nf_nat_edemux.sh b/tools/testing/selftests/netfilter/nf_nat_edemux.sh index 1092bbcb1fba..a1aa8f4a5828 100755 --- a/tools/testing/selftests/netfilter/nf_nat_edemux.sh +++ b/tools/testing/selftests/netfilter/nf_nat_edemux.sh @@ -11,16 +11,18 @@ ret=0 sfx=$(mktemp -u "XXXXXXXX") ns1="ns1-$sfx" ns2="ns2-$sfx" +socatpid=0 cleanup() { + [ $socatpid -gt 0 ] && kill $socatpid ip netns del $ns1 ip netns del $ns2 } -iperf3 -v > /dev/null 2>&1 +socat -h > /dev/null 2>&1 if [ $? -ne 0 ];then - echo "SKIP: Could not run test without iperf3" + echo "SKIP: Could not run test without socat" exit $ksft_skip fi @@ -60,8 +62,8 @@ ip netns exec $ns2 ip link set up dev veth2 ip netns exec $ns2 ip addr add 192.168.1.2/24 dev veth2 # Create a server in one namespace -ip netns exec $ns1 iperf3 -s > /dev/null 2>&1 & -iperfs=$! +ip netns exec $ns1 socat -u TCP-LISTEN:5201,fork OPEN:/dev/null,wronly=1 & +socatpid=$! # Restrict source port to just one so we don't have to exhaust # all others. @@ -83,17 +85,43 @@ sleep 1 # ip daddr:dport will be rewritten to 192.168.1.1 5201 # NAT must reallocate source port 10000 because # 192.168.1.2:10000 -> 192.168.1.1:5201 is already in use -echo test | ip netns exec $ns2 socat -t 3 -u STDIN TCP:10.96.0.1:443 >/dev/null +echo test | ip netns exec $ns2 socat -t 3 -u STDIN TCP:10.96.0.1:443,connect-timeout=3 >/dev/null ret=$? -kill $iperfs - # Check socat can connect to 10.96.0.1:443 (aka 192.168.1.1:5201). if [ $ret -eq 0 ]; then echo "PASS: socat can connect via NAT'd address" else echo "FAIL: socat cannot connect via NAT'd address" - exit 1 fi -exit 0 +# check sport clashres. +ip netns exec $ns1 iptables -t nat -A PREROUTING -p tcp --dport 5202 -j REDIRECT --to-ports 5201 +ip netns exec $ns1 iptables -t nat -A PREROUTING -p tcp --dport 5203 -j REDIRECT --to-ports 5201 + +sleep 5 | ip netns exec $ns2 socat -t 5 -u STDIN TCP:192.168.1.1:5202,connect-timeout=5 >/dev/null & +cpid1=$! +sleep 1 + +# if connect succeeds, client closes instantly due to EOF on stdin. +# if connect hangs, it will time out after 5s. +echo | ip netns exec $ns2 socat -t 3 -u STDIN TCP:192.168.1.1:5203,connect-timeout=5 >/dev/null & +cpid2=$! + +time_then=$(date +%s) +wait $cpid2 +rv=$? +time_now=$(date +%s) + +# Check how much time has elapsed, expectation is for +# 'cpid2' to connect and then exit (and no connect delay). +delta=$((time_now - time_then)) + +if [ $delta -lt 2 -a $rv -eq 0 ]; then + echo "PASS: could connect to service via redirected ports" +else + echo "FAIL: socat cannot connect to service via redirect ($delta seconds elapsed, returned $rv)" + ret=1 +fi + +exit $ret diff --git a/tools/testing/selftests/netfilter/nft_audit.sh b/tools/testing/selftests/netfilter/nft_audit.sh new file mode 100755 index 000000000000..bb34329e02a7 --- /dev/null +++ b/tools/testing/selftests/netfilter/nft_audit.sh @@ -0,0 +1,193 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Check that audit logs generated for nft commands are as expected. + +SKIP_RC=4 +RC=0 + +nft --version >/dev/null 2>&1 || { + echo "SKIP: missing nft tool" + exit $SKIP_RC +} + +logfile=$(mktemp) +rulefile=$(mktemp) +echo "logging into $logfile" +./audit_logread >"$logfile" & +logread_pid=$! +trap 'kill $logread_pid; rm -f $logfile $rulefile' EXIT +exec 3<"$logfile" + +do_test() { # (cmd, log) + echo -n "testing for cmd: $1 ... " + cat <&3 >/dev/null + $1 >/dev/null || exit 1 + sleep 0.1 + res=$(diff -a -u <(echo "$2") - <&3) + [ $? -eq 0 ] && { echo "OK"; return; } + echo "FAIL" + grep -v '^\(---\|+++\|@@\)' <<< "$res" + ((RC--)) +} + +nft flush ruleset + +# adding tables, chains and rules + +for table in t1 t2; do + do_test "nft add table $table" \ + "table=$table family=2 entries=1 op=nft_register_table" + + do_test "nft add chain $table c1" \ + "table=$table family=2 entries=1 op=nft_register_chain" + + do_test "nft add chain $table c2; add chain $table c3" \ + "table=$table family=2 entries=2 op=nft_register_chain" + + cmd="add rule $table c1 counter" + + do_test "nft $cmd" \ + "table=$table family=2 entries=1 op=nft_register_rule" + + do_test "nft $cmd; $cmd" \ + "table=$table family=2 entries=2 op=nft_register_rule" + + cmd="" + sep="" + for chain in c2 c3; do + for i in {1..3}; do + cmd+="$sep add rule $table $chain counter" + sep=";" + done + done + do_test "nft $cmd" \ + "table=$table family=2 entries=6 op=nft_register_rule" +done + +for ((i = 0; i < 500; i++)); do + echo "add rule t2 c3 counter accept comment \"rule $i\"" +done >$rulefile +do_test "nft -f $rulefile" \ +'table=t2 family=2 entries=500 op=nft_register_rule' + +# adding sets and elements + +settype='type inet_service; counter' +setelem='{ 22, 80, 443 }' +setblock="{ $settype; elements = $setelem; }" +do_test "nft add set t1 s $setblock" \ +"table=t1 family=2 entries=4 op=nft_register_set" + +do_test "nft add set t1 s2 $setblock; add set t1 s3 { $settype; }" \ +"table=t1 family=2 entries=5 op=nft_register_set" + +do_test "nft add element t1 s3 $setelem" \ +"table=t1 family=2 entries=3 op=nft_register_setelem" + +# adding counters + +do_test 'nft add counter t1 c1' \ +'table=t1 family=2 entries=1 op=nft_register_obj' + +do_test 'nft add counter t2 c1; add counter t2 c2' \ +'table=t2 family=2 entries=2 op=nft_register_obj' + +# adding/updating quotas + +do_test 'nft add quota t1 q1 { 10 bytes }' \ +'table=t1 family=2 entries=1 op=nft_register_obj' + +do_test 'nft add quota t2 q1 { 10 bytes }; add quota t2 q2 { 10 bytes }' \ +'table=t2 family=2 entries=2 op=nft_register_obj' + +# changing the quota value triggers obj update path +do_test 'nft add quota t1 q1 { 20 bytes }' \ +'table=t1 family=2 entries=1 op=nft_register_obj' + +# resetting rules + +do_test 'nft reset rules t1 c2' \ +'table=t1 family=2 entries=3 op=nft_reset_rule' + +do_test 'nft reset rules table t1' \ +'table=t1 family=2 entries=3 op=nft_reset_rule +table=t1 family=2 entries=3 op=nft_reset_rule +table=t1 family=2 entries=3 op=nft_reset_rule' + +do_test 'nft reset rules t2 c3' \ +'table=t2 family=2 entries=189 op=nft_reset_rule +table=t2 family=2 entries=188 op=nft_reset_rule +table=t2 family=2 entries=126 op=nft_reset_rule' + +do_test 'nft reset rules t2' \ +'table=t2 family=2 entries=3 op=nft_reset_rule +table=t2 family=2 entries=3 op=nft_reset_rule +table=t2 family=2 entries=186 op=nft_reset_rule +table=t2 family=2 entries=188 op=nft_reset_rule +table=t2 family=2 entries=129 op=nft_reset_rule' + +do_test 'nft reset rules' \ +'table=t1 family=2 entries=3 op=nft_reset_rule +table=t1 family=2 entries=3 op=nft_reset_rule +table=t1 family=2 entries=3 op=nft_reset_rule +table=t2 family=2 entries=3 op=nft_reset_rule +table=t2 family=2 entries=3 op=nft_reset_rule +table=t2 family=2 entries=180 op=nft_reset_rule +table=t2 family=2 entries=188 op=nft_reset_rule +table=t2 family=2 entries=135 op=nft_reset_rule' + +# resetting sets and elements + +elem=(22 ,80 ,443) +relem="" +for i in {1..3}; do + relem+="${elem[((i - 1))]}" + do_test "nft reset element t1 s { $relem }" \ + "table=t1 family=2 entries=$i op=nft_reset_setelem" +done + +do_test 'nft reset set t1 s' \ +'table=t1 family=2 entries=3 op=nft_reset_setelem' + +# deleting rules + +readarray -t handles < <(nft -a list chain t1 c1 | \ + sed -n 's/.*counter.* handle \(.*\)$/\1/p') + +do_test "nft delete rule t1 c1 handle ${handles[0]}" \ +'table=t1 family=2 entries=1 op=nft_unregister_rule' + +cmd='delete rule t1 c1 handle' +do_test "nft $cmd ${handles[1]}; $cmd ${handles[2]}" \ +'table=t1 family=2 entries=2 op=nft_unregister_rule' + +do_test 'nft flush chain t1 c2' \ +'table=t1 family=2 entries=3 op=nft_unregister_rule' + +do_test 'nft flush table t2' \ +'table=t2 family=2 entries=509 op=nft_unregister_rule' + +# deleting chains + +do_test 'nft delete chain t2 c2' \ +'table=t2 family=2 entries=1 op=nft_unregister_chain' + +# deleting sets and elements + +do_test 'nft delete element t1 s { 22 }' \ +'table=t1 family=2 entries=1 op=nft_unregister_setelem' + +do_test 'nft delete element t1 s { 80, 443 }' \ +'table=t1 family=2 entries=2 op=nft_unregister_setelem' + +do_test 'nft flush set t1 s2' \ +'table=t1 family=2 entries=3 op=nft_unregister_setelem' + +do_test 'nft delete set t1 s2' \ +'table=t1 family=2 entries=1 op=nft_unregister_set' + +do_test 'nft delete set t1 s3' \ +'table=t1 family=2 entries=1 op=nft_unregister_set' + +exit $RC diff --git a/tools/testing/selftests/netfilter/sctp_collision.c b/tools/testing/selftests/netfilter/sctp_collision.c new file mode 100644 index 000000000000..21bb1cfd8a85 --- /dev/null +++ b/tools/testing/selftests/netfilter/sctp_collision.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> + +int main(int argc, char *argv[]) +{ + struct sockaddr_in saddr = {}, daddr = {}; + int sd, ret, len = sizeof(daddr); + struct timeval tv = {25, 0}; + char buf[] = "hello"; + + if (argc != 6 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { + printf("%s <server|client> <LOCAL_IP> <LOCAL_PORT> <REMOTE_IP> <REMOTE_PORT>\n", + argv[0]); + return -1; + } + + sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + if (sd < 0) { + printf("Failed to create sd\n"); + return -1; + } + + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(argv[2]); + saddr.sin_port = htons(atoi(argv[3])); + + ret = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + printf("Failed to bind to address\n"); + goto out; + } + + ret = listen(sd, 5); + if (ret < 0) { + printf("Failed to listen on port\n"); + goto out; + } + + daddr.sin_family = AF_INET; + daddr.sin_addr.s_addr = inet_addr(argv[4]); + daddr.sin_port = htons(atoi(argv[5])); + + /* make test shorter than 25s */ + ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + if (ret < 0) { + printf("Failed to setsockopt SO_RCVTIMEO\n"); + goto out; + } + + if (!strcmp(argv[1], "server")) { + sleep(1); /* wait a bit for client's INIT */ + ret = connect(sd, (struct sockaddr *)&daddr, len); + if (ret < 0) { + printf("Failed to connect to peer\n"); + goto out; + } + ret = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&daddr, &len); + if (ret < 0) { + printf("Failed to recv msg %d\n", ret); + goto out; + } + ret = sendto(sd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&daddr, len); + if (ret < 0) { + printf("Failed to send msg %d\n", ret); + goto out; + } + printf("Server: sent! %d\n", ret); + } + + if (!strcmp(argv[1], "client")) { + usleep(300000); /* wait a bit for server's listening */ + ret = connect(sd, (struct sockaddr *)&daddr, len); + if (ret < 0) { + printf("Failed to connect to peer\n"); + goto out; + } + sleep(1); /* wait a bit for server's delayed INIT_ACK to reproduce the issue */ + ret = sendto(sd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&daddr, len); + if (ret < 0) { + printf("Failed to send msg %d\n", ret); + goto out; + } + ret = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&daddr, &len); + if (ret < 0) { + printf("Failed to recv msg %d\n", ret); + goto out; + } + printf("Client: rcvd! %d\n", ret); + } + ret = 0; +out: + close(sd); + return ret; +} diff --git a/tools/testing/selftests/openat2/Makefile b/tools/testing/selftests/openat2/Makefile index 843ba56d8e49..254d676a2689 100644 --- a/tools/testing/selftests/openat2/Makefile +++ b/tools/testing/selftests/openat2/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later -CFLAGS += -Wall -O2 -g -fsanitize=address -fsanitize=undefined +CFLAGS += -Wall -O2 -g -fsanitize=address -fsanitize=undefined -static-libasan TEST_GEN_PROGS := openat2_test resolve_test rename_attack_test include ../lib.mk diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 49f2ad1793fd..7ea42fa02eab 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile @@ -59,12 +59,11 @@ override define INSTALL_RULE done; endef -override define EMIT_TESTS +emit_tests: +@for TARGET in $(SUB_DIRS); do \ BUILD_TARGET=$(OUTPUT)/$$TARGET; \ - $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests;\ + $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET $@;\ done; -endef override define CLEAN +@for TARGET in $(SUB_DIRS); do \ @@ -77,4 +76,4 @@ endef tags: find . -name '*.c' -o -name '*.h' | xargs ctags -.PHONY: tags $(SUB_DIRS) +.PHONY: tags $(SUB_DIRS) emit_tests diff --git a/tools/testing/selftests/powerpc/copyloops/asm/export.h b/tools/testing/selftests/powerpc/copyloops/linux/export.h index e6b80d5fbd14..e6b80d5fbd14 100644 --- a/tools/testing/selftests/powerpc/copyloops/asm/export.h +++ b/tools/testing/selftests/powerpc/copyloops/linux/export.h diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index 0ad4f12b3d43..5876220d8ff2 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c @@ -24,7 +24,7 @@ /* Setting timeout to -1 disables the alarm */ static uint64_t timeout = 120; -int run_test(int (test_function)(void), char *name) +int run_test(int (test_function)(void), const char *name) { bool terminated; int rc, status; @@ -101,7 +101,7 @@ void test_harness_set_timeout(uint64_t time) timeout = time; } -int test_harness(int (test_function)(void), char *name) +int test_harness(int (test_function)(void), const char *name) { int rc; diff --git a/tools/testing/selftests/powerpc/include/subunit.h b/tools/testing/selftests/powerpc/include/subunit.h index 068d55fdf80f..b0bb774617c9 100644 --- a/tools/testing/selftests/powerpc/include/subunit.h +++ b/tools/testing/selftests/powerpc/include/subunit.h @@ -6,37 +6,37 @@ #ifndef _SELFTESTS_POWERPC_SUBUNIT_H #define _SELFTESTS_POWERPC_SUBUNIT_H -static inline void test_start(char *name) +static inline void test_start(const char *name) { printf("test: %s\n", name); } -static inline void test_failure_detail(char *name, char *detail) +static inline void test_failure_detail(const char *name, const char *detail) { printf("failure: %s [%s]\n", name, detail); } -static inline void test_failure(char *name) +static inline void test_failure(const char *name) { printf("failure: %s\n", name); } -static inline void test_error(char *name) +static inline void test_error(const char *name) { printf("error: %s\n", name); } -static inline void test_skip(char *name) +static inline void test_skip(const char *name) { printf("skip: %s\n", name); } -static inline void test_success(char *name) +static inline void test_success(const char *name) { printf("success: %s\n", name); } -static inline void test_finish(char *name, int status) +static inline void test_finish(const char *name, int status) { if (status) test_failure(name); @@ -44,7 +44,7 @@ static inline void test_finish(char *name, int status) test_success(name); } -static inline void test_set_git_version(char *value) +static inline void test_set_git_version(const char *value) { printf("tags: git_version:%s\n", value); } diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 36c30c611457..66d7b2368dd4 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -32,7 +32,7 @@ typedef uint16_t u16; typedef uint8_t u8; void test_harness_set_timeout(uint64_t time); -int test_harness(int (test_function)(void), char *name); +int test_harness(int (test_function)(void), const char *name); int read_auxv(char *buf, ssize_t buf_size); void *find_auxv_entry(int type, char *auxv); diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 4e1a294eec35..0df1a3afc5e2 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore @@ -1,15 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-only +bad_accesses +exec_prot hugetlb_vs_thp_test -subpage_prot -tempfile -prot_sao -segv_errors -wild_bctr large_vm_fork_separation -bad_accesses -tlbie_test +large_vm_gpr_corruption pkey_exec_prot pkey_siginfo +prot_sao +segv_errors stack_expansion_ldst stack_expansion_signal -large_vm_gpr_corruption +subpage_prot +tempfile +tlbie_test +wild_bctr diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile index 2b95e44d20ff..a284fa874a9f 100644 --- a/tools/testing/selftests/powerpc/pmu/Makefile +++ b/tools/testing/selftests/powerpc/pmu/Makefile @@ -30,13 +30,14 @@ override define RUN_TESTS +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests endef -DEFAULT_EMIT_TESTS := $(EMIT_TESTS) -override define EMIT_TESTS - $(DEFAULT_EMIT_TESTS) +emit_tests: + for TEST in $(TEST_GEN_PROGS); do \ + BASENAME_TEST=`basename $$TEST`; \ + echo "$(COLLECTION):$$BASENAME_TEST"; \ + done +TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests +TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests -endef DEFAULT_INSTALL_RULE := $(INSTALL_RULE) override define INSTALL_RULE @@ -64,4 +65,4 @@ sampling_tests: event_code_tests: TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all -.PHONY: all run_tests ebb sampling_tests event_code_tests +.PHONY: all run_tests ebb sampling_tests event_code_tests emit_tests diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile index cbeeaeae8837..1b39b86849da 100644 --- a/tools/testing/selftests/powerpc/ptrace/Makefile +++ b/tools/testing/selftests/powerpc/ptrace/Makefile @@ -36,6 +36,7 @@ $(TM_TESTS): CFLAGS += -I../tm -mhtm CFLAGS += $(KHDR_INCLUDES) -fno-pie $(OUTPUT)/ptrace-gpr: ptrace-gpr.S +$(OUTPUT)/ptrace-perf-hwbreak: ptrace-perf-asm.S $(OUTPUT)/ptrace-pkey $(OUTPUT)/core-pkey: LDLIBS += -pthread $(TEST_GEN_PROGS): ../harness.c ../utils.c ../lib/reg.S diff --git a/tools/testing/selftests/powerpc/ptrace/child.h b/tools/testing/selftests/powerpc/ptrace/child.h index d7275b7b33dc..df62ff0735f7 100644 --- a/tools/testing/selftests/powerpc/ptrace/child.h +++ b/tools/testing/selftests/powerpc/ptrace/child.h @@ -48,12 +48,12 @@ struct child_sync { } \ } while (0) -#define PARENT_SKIP_IF_UNSUPPORTED(x, sync) \ +#define PARENT_SKIP_IF_UNSUPPORTED(x, sync, msg) \ do { \ if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \ (sync)->parent_gave_up = true; \ prod_child(sync); \ - SKIP_IF(1); \ + SKIP_IF_MSG(1, msg); \ } \ } while (0) diff --git a/tools/testing/selftests/powerpc/ptrace/core-pkey.c b/tools/testing/selftests/powerpc/ptrace/core-pkey.c index f6f8596ce8e1..f6da4cb30cd6 100644 --- a/tools/testing/selftests/powerpc/ptrace/core-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/core-pkey.c @@ -266,7 +266,7 @@ static int parent(struct shared_info *info, pid_t pid) * to the child. */ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); - PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); + PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported"); PARENT_FAIL_IF(ret, &info->child_sync); info->amr = regs[0]; diff --git a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c index f75739bbad28..e374c6b7ace6 100644 --- a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c +++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c @@ -884,7 +884,7 @@ static int perf_hwbreak(void) { srand ( time(NULL) ); - SKIP_IF(!perf_breakpoint_supported()); + SKIP_IF_MSG(!perf_breakpoint_supported(), "Perf breakpoints not supported"); return runtest(); } diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c index 1345e9b9af0f..75d30d61ab0e 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c @@ -64,26 +64,26 @@ static bool dawr_present(struct ppc_debug_info *dbginfo) static void write_var(int len) { - __u8 *pcvar; - __u16 *psvar; - __u32 *pivar; - __u64 *plvar; + volatile __u8 *pcvar; + volatile __u16 *psvar; + volatile __u32 *pivar; + volatile __u64 *plvar; switch (len) { case 1: - pcvar = (__u8 *)&glvar; + pcvar = (volatile __u8 *)&glvar; *pcvar = 0xff; break; case 2: - psvar = (__u16 *)&glvar; + psvar = (volatile __u16 *)&glvar; *psvar = 0xffff; break; case 4: - pivar = (__u32 *)&glvar; + pivar = (volatile __u32 *)&glvar; *pivar = 0xffffffff; break; case 8: - plvar = (__u64 *)&glvar; + plvar = (volatile __u64 *)&glvar; *plvar = 0xffffffffffffffffLL; break; } @@ -98,16 +98,16 @@ static void read_var(int len) switch (len) { case 1: - cvar = (__u8)glvar; + cvar = (volatile __u8)glvar; break; case 2: - svar = (__u16)glvar; + svar = (volatile __u16)glvar; break; case 4: - ivar = (__u32)glvar; + ivar = (volatile __u32)glvar; break; case 8: - lvar = (__u64)glvar; + lvar = (volatile __u64)glvar; break; } } @@ -603,7 +603,7 @@ static int ptrace_hwbreak(void) wait(NULL); get_dbginfo(child_pid, &dbginfo); - SKIP_IF(dbginfo.num_data_bps == 0); + SKIP_IF_MSG(dbginfo.num_data_bps == 0, "No data breakpoints present"); dawr = dawr_present(&dbginfo); run_tests(child_pid, &dbginfo, dawr); diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-perf-asm.S b/tools/testing/selftests/powerpc/ptrace/ptrace-perf-asm.S new file mode 100644 index 000000000000..9aa2e58f3189 --- /dev/null +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-perf-asm.S @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <ppc-asm.h> + +.global same_watch_addr_load +.global same_watch_addr_trap + +FUNC_START(same_watch_addr_child) + nop +same_watch_addr_load: + ld 0,0(3) + nop +same_watch_addr_trap: + trap + blr +FUNC_END(same_watch_addr_child) + + +.global perf_then_ptrace_load1 +.global perf_then_ptrace_load2 +.global perf_then_ptrace_trap + +FUNC_START(perf_then_ptrace_child) + nop +perf_then_ptrace_load1: + ld 0,0(3) +perf_then_ptrace_load2: + ld 0,0(4) + nop +perf_then_ptrace_trap: + trap + blr +FUNC_END(perf_then_ptrace_child) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c index 3344e74a97b4..a0a0b9bb5854 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c @@ -1,659 +1,445 @@ // SPDX-License-Identifier: GPL-2.0+ -#include <stdio.h> -#include <string.h> -#include <signal.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <linux/hw_breakpoint.h> -#include <linux/perf_event.h> + #include <asm/unistd.h> -#include <sys/ptrace.h> +#include <linux/hw_breakpoint.h> +#include <linux/ptrace.h> +#include <memory.h> +#include <stdlib.h> #include <sys/wait.h> -#include "ptrace.h" -char data[16]; +#include "utils.h" -/* Overlapping address range */ -volatile __u64 *ptrace_data1 = (__u64 *)&data[0]; -volatile __u64 *perf_data1 = (__u64 *)&data[4]; +/* + * Child subroutine that performs a load on the address, then traps + */ +void same_watch_addr_child(unsigned long *addr); -/* Non-overlapping address range */ -volatile __u64 *ptrace_data2 = (__u64 *)&data[0]; -volatile __u64 *perf_data2 = (__u64 *)&data[8]; +/* Address of the ld instruction in same_watch_addr_child() */ +extern char same_watch_addr_load[]; -static unsigned long pid_max_addr(void) -{ - FILE *fp; - char *line, *c; - char addr[100]; - size_t len = 0; - - fp = fopen("/proc/kallsyms", "r"); - if (!fp) { - printf("Failed to read /proc/kallsyms. Exiting..\n"); - exit(EXIT_FAILURE); - } +/* Address of the end trap instruction in same_watch_addr_child() */ +extern char same_watch_addr_trap[]; - while (getline(&line, &len, fp) != -1) { - if (!strstr(line, "pid_max") || strstr(line, "pid_max_max") || - strstr(line, "pid_max_min")) - continue; +/* + * Child subroutine that performs a load on the first address, then a load on + * the second address (with no instructions separating this from the first + * load), then traps. + */ +void perf_then_ptrace_child(unsigned long *first_addr, unsigned long *second_addr); - strncpy(addr, line, len < 100 ? len : 100); - c = strchr(addr, ' '); - *c = '\0'; - return strtoul(addr, &c, 16); - } - fclose(fp); - printf("Could not find pix_max. Exiting..\n"); - exit(EXIT_FAILURE); - return -1; -} +/* Address of the first ld instruction in perf_then_ptrace_child() */ +extern char perf_then_ptrace_load1[]; -static void perf_user_event_attr_set(struct perf_event_attr *attr, __u64 addr, __u64 len) -{ - memset(attr, 0, sizeof(struct perf_event_attr)); - attr->type = PERF_TYPE_BREAKPOINT; - attr->size = sizeof(struct perf_event_attr); - attr->bp_type = HW_BREAKPOINT_R; - attr->bp_addr = addr; - attr->bp_len = len; - attr->exclude_kernel = 1; - attr->exclude_hv = 1; -} +/* Address of the second ld instruction in perf_then_ptrace_child() */ +extern char perf_then_ptrace_load2[]; -static void perf_kernel_event_attr_set(struct perf_event_attr *attr) +/* Address of the end trap instruction in perf_then_ptrace_child() */ +extern char perf_then_ptrace_trap[]; + +static inline long sys_ptrace(long request, pid_t pid, unsigned long addr, unsigned long data) { - memset(attr, 0, sizeof(struct perf_event_attr)); - attr->type = PERF_TYPE_BREAKPOINT; - attr->size = sizeof(struct perf_event_attr); - attr->bp_type = HW_BREAKPOINT_R; - attr->bp_addr = pid_max_addr(); - attr->bp_len = sizeof(unsigned long); - attr->exclude_user = 1; - attr->exclude_hv = 1; + return syscall(__NR_ptrace, request, pid, addr, data); } -static int perf_cpu_event_open(int cpu, __u64 addr, __u64 len) +static long ptrace_traceme(void) { - struct perf_event_attr attr; - - perf_user_event_attr_set(&attr, addr, len); - return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0); + return sys_ptrace(PTRACE_TRACEME, 0, 0, 0); } -static int perf_thread_event_open(pid_t child_pid, __u64 addr, __u64 len) +static long ptrace_getregs(pid_t pid, struct pt_regs *result) { - struct perf_event_attr attr; - - perf_user_event_attr_set(&attr, addr, len); - return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0); + return sys_ptrace(PTRACE_GETREGS, pid, 0, (unsigned long)result); } -static int perf_thread_cpu_event_open(pid_t child_pid, int cpu, __u64 addr, __u64 len) +static long ptrace_setregs(pid_t pid, struct pt_regs *result) { - struct perf_event_attr attr; - - perf_user_event_attr_set(&attr, addr, len); - return syscall(__NR_perf_event_open, &attr, child_pid, cpu, -1, 0); + return sys_ptrace(PTRACE_SETREGS, pid, 0, (unsigned long)result); } -static int perf_thread_kernel_event_open(pid_t child_pid) +static long ptrace_cont(pid_t pid, long signal) { - struct perf_event_attr attr; - - perf_kernel_event_attr_set(&attr); - return syscall(__NR_perf_event_open, &attr, child_pid, -1, -1, 0); + return sys_ptrace(PTRACE_CONT, pid, 0, signal); } -static int perf_cpu_kernel_event_open(int cpu) +static long ptrace_singlestep(pid_t pid, long signal) { - struct perf_event_attr attr; - - perf_kernel_event_attr_set(&attr); - return syscall(__NR_perf_event_open, &attr, -1, cpu, -1, 0); + return sys_ptrace(PTRACE_SINGLESTEP, pid, 0, signal); } -static int child(void) +static long ppc_ptrace_gethwdbginfo(pid_t pid, struct ppc_debug_info *dbginfo) { - int ret; - - ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); - if (ret) { - printf("Error: PTRACE_TRACEME failed\n"); - return 0; - } - kill(getpid(), SIGUSR1); /* --> parent (SIGUSR1) */ - - return 0; + return sys_ptrace(PPC_PTRACE_GETHWDBGINFO, pid, 0, (unsigned long)dbginfo); } -static void ptrace_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type, - __u64 addr, int len) +static long ppc_ptrace_sethwdbg(pid_t pid, struct ppc_hw_breakpoint *bp_info) { - info->version = 1; - info->trigger_type = type; - info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; - info->addr = addr; - info->addr2 = addr + len; - info->condition_value = 0; - if (!len) - info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; - else - info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; + return sys_ptrace(PPC_PTRACE_SETHWDEBUG, pid, 0, (unsigned long)bp_info); } -static int ptrace_open(pid_t child_pid, __u64 wp_addr, int len) +static long ppc_ptrace_delhwdbg(pid_t pid, int bp_id) { - struct ppc_hw_breakpoint info; - - ptrace_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); - return ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info); + return sys_ptrace(PPC_PTRACE_DELHWDEBUG, pid, 0L, bp_id); } -static int test1(pid_t child_pid) +static long ptrace_getreg_pc(pid_t pid, void **pc) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by ptrace) - * if (existing cpu event by perf) - * if (addr range overlaps) - * fail; - */ + struct pt_regs regs; + long err; - perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1)); - if (perf_fd < 0) - return -1; + err = ptrace_getregs(pid, ®s); + if (err) + return err; - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd > 0 || errno != ENOSPC) - ret = -1; + *pc = (void *)regs.nip; - close(perf_fd); - return ret; + return 0; } -static int test2(pid_t child_pid) +static long ptrace_setreg_pc(pid_t pid, void *pc) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by ptrace) - * if (existing cpu event by perf) - * if (addr range does not overlaps) - * allow; - */ + struct pt_regs regs; + long err; - perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2)); - if (perf_fd < 0) - return -1; - - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2)); - if (ptrace_fd < 0) { - ret = -1; - goto perf_close; - } - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - -perf_close: - close(perf_fd); - return ret; -} + err = ptrace_getregs(pid, ®s); + if (err) + return err; -static int test3(pid_t child_pid) -{ - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by ptrace) - * if (existing thread event by perf on the same thread) - * if (addr range overlaps) - * fail; - */ - perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1, - sizeof(*perf_data1)); - if (perf_fd < 0) - return -1; + regs.nip = (unsigned long)pc; - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd > 0 || errno != ENOSPC) - ret = -1; + err = ptrace_setregs(pid, ®s); + if (err) + return err; - close(perf_fd); - return ret; + return 0; } -static int test4(pid_t child_pid) +static int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, + int group_fd, unsigned long flags) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by ptrace) - * if (existing thread event by perf on the same thread) - * if (addr range does not overlaps) - * fail; - */ - perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2, - sizeof(*perf_data2)); - if (perf_fd < 0) - return -1; - - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2)); - if (ptrace_fd < 0) { - ret = -1; - goto perf_close; - } - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - -perf_close: - close(perf_fd); - return ret; + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } -static int test5(pid_t child_pid) +static void perf_user_event_attr_set(struct perf_event_attr *attr, void *addr, u64 len) { - int perf_fd; - int ptrace_fd; - int cpid; - int ret = 0; - - /* Test: - * if (new per thread event by ptrace) - * if (existing thread event by perf on the different thread) - * allow; - */ - cpid = fork(); - if (!cpid) { - /* Temporary Child */ - pause(); - exit(EXIT_SUCCESS); - } - - perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1)); - if (perf_fd < 0) { - ret = -1; - goto kill_child; - } - - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) { - ret = -1; - goto perf_close; - } + memset(attr, 0, sizeof(struct perf_event_attr)); - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); -perf_close: - close(perf_fd); -kill_child: - kill(cpid, SIGINT); - return ret; + attr->type = PERF_TYPE_BREAKPOINT; + attr->size = sizeof(struct perf_event_attr); + attr->bp_type = HW_BREAKPOINT_R; + attr->bp_addr = (u64)addr; + attr->bp_len = len; + attr->exclude_kernel = 1; + attr->exclude_hv = 1; } -static int test6(pid_t child_pid) +static int perf_watchpoint_open(pid_t child_pid, void *addr, u64 len) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread kernel event by perf) - * if (existing thread event by ptrace on the same thread) - * allow; - * -- OR -- - * if (new per cpu kernel event by perf) - * if (existing thread event by ptrace) - * allow; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; - - perf_fd = perf_thread_kernel_event_open(child_pid); - if (perf_fd < 0) { - ret = -1; - goto ptrace_close; - } - close(perf_fd); - - perf_fd = perf_cpu_kernel_event_open(0); - if (perf_fd < 0) { - ret = -1; - goto ptrace_close; - } - close(perf_fd); + struct perf_event_attr attr; -ptrace_close: - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; + perf_user_event_attr_set(&attr, addr, len); + return perf_event_open(&attr, child_pid, -1, -1, 0); } -static int test7(pid_t child_pid) +static int perf_read_counter(int perf_fd, u64 *count) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range overlaps) - * fail; + /* + * A perf counter is retrieved by the read() syscall. It contains + * the current count as 8 bytes that are interpreted as a u64 */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; + ssize_t len = read(perf_fd, count, sizeof(*count)); - perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data1, - sizeof(*perf_data1)); - if (perf_fd > 0 || errno != ENOSPC) - ret = -1; + if (len != sizeof(*count)) + return -1; - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; + return 0; } -static int test8(pid_t child_pid) +static void ppc_ptrace_init_breakpoint(struct ppc_hw_breakpoint *info, + int type, void *addr, int len) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range does not overlaps) - * allow; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2)); - if (ptrace_fd < 0) - return -1; - - perf_fd = perf_thread_event_open(child_pid, (__u64)perf_data2, - sizeof(*perf_data2)); - if (perf_fd < 0) { - ret = -1; - goto ptrace_close; - } - close(perf_fd); - -ptrace_close: - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; + info->version = 1; + info->trigger_type = type; + info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + info->addr = (u64)addr; + info->addr2 = (u64)addr + len; + info->condition_value = 0; + if (!len) + info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; + else + info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; } -static int test9(pid_t child_pid) +/* + * Checks if we can place at least 2 watchpoints on the child process + */ +static int check_watchpoints(pid_t pid) { - int perf_fd; - int ptrace_fd; - int cpid; - int ret = 0; - - /* Test: - * if (new per thread event by perf) - * if (existing thread event by ptrace on the other thread) - * allow; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; - - cpid = fork(); - if (!cpid) { - /* Temporary Child */ - pause(); - exit(EXIT_SUCCESS); - } + struct ppc_debug_info dbginfo; - perf_fd = perf_thread_event_open(cpid, (__u64)perf_data1, sizeof(*perf_data1)); - if (perf_fd < 0) { - ret = -1; - goto kill_child; - } - close(perf_fd); + FAIL_IF_MSG(ppc_ptrace_gethwdbginfo(pid, &dbginfo), "PPC_PTRACE_GETHWDBGINFO failed"); + SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)"); -kill_child: - kill(cpid, SIGINT); - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; + return 0; } -static int test10(pid_t child_pid) +/* + * Wrapper around a plain fork() call that sets up the child for + * ptrace-ing. Both the parent and child return from this, though + * the child is stopped until ptrace_cont(pid) is run by the parent. + */ +static int ptrace_fork_child(pid_t *pid) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per cpu event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range overlaps) - * fail; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; + int status; - perf_fd = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1)); - if (perf_fd > 0 || errno != ENOSPC) - ret = -1; - - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; -} + *pid = fork(); -static int test11(pid_t child_pid) -{ - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per cpu event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range does not overlap) - * allow; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2)); - if (ptrace_fd < 0) - return -1; + if (*pid < 0) + FAIL_IF_MSG(1, "Failed to fork child"); - perf_fd = perf_cpu_event_open(0, (__u64)perf_data2, sizeof(*perf_data2)); - if (perf_fd < 0) { - ret = -1; - goto ptrace_close; + if (!*pid) { + FAIL_IF_EXIT_MSG(ptrace_traceme(), "PTRACE_TRACEME failed"); + FAIL_IF_EXIT_MSG(raise(SIGSTOP), "Child failed to raise SIGSTOP"); + } else { + /* Synchronise on child SIGSTOP */ + FAIL_IF_MSG(waitpid(*pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); } - close(perf_fd); -ptrace_close: - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; + return 0; } -static int test12(pid_t child_pid) +/* + * Tests the interaction between ptrace and perf watching the same data. + * + * We expect ptrace to take 'priority', as it is has before-execute + * semantics. + * + * The perf counter should not be incremented yet because perf has after-execute + * semantics. E.g., if ptrace changes the child PC, we don't even execute the + * instruction at all. + * + * When the child is stopped for ptrace, we test both continue and single step. + * Both should increment the perf counter. We also test changing the PC somewhere + * different and stepping, which should not increment the perf counter. + */ +int same_watch_addr_test(void) { - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread and per cpu event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range overlaps) - * fail; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; + struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */ + int bp_id; /* Breakpoint handle of ptrace watchpoint */ + int perf_fd; /* File descriptor of perf performance counter */ + u64 perf_count; /* Most recently fetched perf performance counter value */ + pid_t pid; /* PID of child process */ + void *pc; /* Most recently fetched child PC value */ + int status; /* Stop status of child after waitpid */ + unsigned long value; /* Dummy value to be read/written to by child */ + int err; + + err = ptrace_fork_child(&pid); + if (err) + return err; + + if (!pid) { + same_watch_addr_child(&value); + exit(1); + } - perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data1, sizeof(*perf_data1)); - if (perf_fd > 0 || errno != ENOSPC) - ret = -1; + err = check_watchpoints(pid); + if (err) + return err; - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; -} + /* Place a perf watchpoint counter on value */ + perf_fd = perf_watchpoint_open(pid, &value, sizeof(value)); + FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter"); -static int test13(pid_t child_pid) -{ - int perf_fd; - int ptrace_fd; - int ret = 0; - - /* Test: - * if (new per thread and per cpu event by perf) - * if (existing thread event by ptrace on the same thread) - * if (addr range does not overlap) - * allow; - */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data2, sizeof(*ptrace_data2)); - if (ptrace_fd < 0) - return -1; + /* Place a ptrace watchpoint on value */ + ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, &value, sizeof(value)); + bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); + FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); - perf_fd = perf_thread_cpu_event_open(child_pid, 0, (__u64)perf_data2, sizeof(*perf_data2)); - if (perf_fd < 0) { - ret = -1; - goto ptrace_close; - } - close(perf_fd); + /* Let the child run. It should stop on the ptrace watchpoint */ + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); -ptrace_close: - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; -} + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction"); -static int test14(pid_t child_pid) -{ - int perf_fd; - int ptrace_fd; - int cpid; - int ret = 0; - - /* Test: - * if (new per thread and per cpu event by perf) - * if (existing thread event by ptrace on the other thread) - * allow; + /* + * We stopped before executing the load, so perf should not have + * recorded any events yet */ - ptrace_fd = ptrace_open(child_pid, (__u64)ptrace_data1, sizeof(*ptrace_data1)); - if (ptrace_fd < 0) - return -1; - - cpid = fork(); - if (!cpid) { - /* Temporary Child */ - pause(); - exit(EXIT_SUCCESS); - } - - perf_fd = perf_thread_cpu_event_open(cpid, 0, (__u64)perf_data1, - sizeof(*perf_data1)); - if (perf_fd < 0) { - ret = -1; - goto kill_child; - } - close(perf_fd); - -kill_child: - kill(cpid, SIGINT); - ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, ptrace_fd); - return ret; -} + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 0, "perf recorded unexpected event"); + + /* Single stepping over the load should increment the perf counter */ + FAIL_IF_MSG(ptrace_singlestep(pid, 0), "Failed to single step child"); + + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_load + 4, "Failed to single step load instruction"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 1, "perf counter did not increment"); + + /* + * Set up a ptrace watchpoint on the value again and trigger it. + * The perf counter should not have incremented because we do not + * execute the load yet. + */ + FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint"); + bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); + FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); + FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); + + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load trap"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 1, "perf counter should not have changed"); + + /* Continuing over the load should increment the perf counter */ + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); + + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 2, "perf counter did not increment"); + + /* + * If we set the child PC back to the load instruction, then continue, + * we should reach the end trap (because ptrace is one-shot) and have + * another perf event. + */ + FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); + + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 3, "perf counter did not increment"); + + /* + * If we set the child PC back to the load instruction, set a ptrace + * watchpoint on the load, then continue, we should immediately get + * the ptrace trap without incrementing the perf counter + */ + FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint"); + bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); + FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); + FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC"); + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); + + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed"); + + /* + * If we change the PC while stopped on the load instruction, we should + * not increment the perf counter (because ptrace is before-execute, + * perf is after-execute). + */ + FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load + 4), "Failed to set child PC"); + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); -static int do_test(const char *msg, int (*fun)(pid_t arg), pid_t arg) -{ - int ret; + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap"); + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed"); - ret = fun(arg); - if (ret) - printf("%s: Error\n", msg); - else - printf("%s: Ok\n", msg); - return ret; -} + /* Clean up child */ + FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child"); -char *desc[14] = { - "perf cpu event -> ptrace thread event (Overlapping)", - "perf cpu event -> ptrace thread event (Non-overlapping)", - "perf thread event -> ptrace same thread event (Overlapping)", - "perf thread event -> ptrace same thread event (Non-overlapping)", - "perf thread event -> ptrace other thread event", - "ptrace thread event -> perf kernel event", - "ptrace thread event -> perf same thread event (Overlapping)", - "ptrace thread event -> perf same thread event (Non-overlapping)", - "ptrace thread event -> perf other thread event", - "ptrace thread event -> perf cpu event (Overlapping)", - "ptrace thread event -> perf cpu event (Non-overlapping)", - "ptrace thread event -> perf same thread & cpu event (Overlapping)", - "ptrace thread event -> perf same thread & cpu event (Non-overlapping)", - "ptrace thread event -> perf other thread & cpu event", -}; - -static int test(pid_t child_pid) -{ - int ret = TEST_PASS; - - ret |= do_test(desc[0], test1, child_pid); - ret |= do_test(desc[1], test2, child_pid); - ret |= do_test(desc[2], test3, child_pid); - ret |= do_test(desc[3], test4, child_pid); - ret |= do_test(desc[4], test5, child_pid); - ret |= do_test(desc[5], test6, child_pid); - ret |= do_test(desc[6], test7, child_pid); - ret |= do_test(desc[7], test8, child_pid); - ret |= do_test(desc[8], test9, child_pid); - ret |= do_test(desc[9], test10, child_pid); - ret |= do_test(desc[10], test11, child_pid); - ret |= do_test(desc[11], test12, child_pid); - ret |= do_test(desc[12], test13, child_pid); - ret |= do_test(desc[13], test14, child_pid); - - return ret; + return 0; } -static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo) +/* + * Tests the interaction between ptrace and perf when: + * 1. perf watches a value + * 2. ptrace watches a different value + * 3. The perf value is read, then the ptrace value is read immediately after + * + * A breakpoint implementation may accidentally misattribute/skip one of + * the ptrace or perf handlers, as interrupt based work is done after perf + * and before ptrace. + * + * We expect the perf counter to increment before the ptrace watchpoint + * triggers. + */ +int perf_then_ptrace_test(void) { - if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) { - perror("Can't get breakpoint info"); - exit(-1); + struct ppc_hw_breakpoint bp_info; /* ptrace breakpoint info */ + int bp_id; /* Breakpoint handle of ptrace watchpoint */ + int perf_fd; /* File descriptor of perf performance counter */ + u64 perf_count; /* Most recently fetched perf performance counter value */ + pid_t pid; /* PID of child process */ + void *pc; /* Most recently fetched child PC value */ + int status; /* Stop status of child after waitpid */ + unsigned long perf_value; /* Dummy value to be watched by perf */ + unsigned long ptrace_value; /* Dummy value to be watched by ptrace */ + int err; + + err = ptrace_fork_child(&pid); + if (err) + return err; + + /* + * If we are the child, run a subroutine that reads the perf value, + * then reads the ptrace value with consecutive load instructions + */ + if (!pid) { + perf_then_ptrace_child(&perf_value, &ptrace_value); + exit(0); } -} -static int ptrace_perf_hwbreak(void) -{ - int ret; - pid_t child_pid; - struct ppc_debug_info dbginfo; + err = check_watchpoints(pid); + if (err) + return err; - child_pid = fork(); - if (!child_pid) - return child(); + /* Place a perf watchpoint counter */ + perf_fd = perf_watchpoint_open(pid, &perf_value, sizeof(perf_value)); + FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter"); - /* parent */ - wait(NULL); /* <-- child (SIGUSR1) */ + /* Place a ptrace watchpoint */ + ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, + &ptrace_value, sizeof(ptrace_value)); + bp_id = ppc_ptrace_sethwdbg(pid, &bp_info); + FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint"); - get_dbginfo(child_pid, &dbginfo); - SKIP_IF(dbginfo.num_data_bps <= 1); + /* Let the child run. It should stop on the ptrace watchpoint */ + FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child"); - ret = perf_cpu_event_open(0, (__u64)perf_data1, sizeof(*perf_data1)); - SKIP_IF(ret < 0); - close(ret); + FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child"); + FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped"); + FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC"); + FAIL_IF_MSG(pc != perf_then_ptrace_load2, "Child did not stop on ptrace load"); - ret = test(child_pid); + /* perf should have recorded the first load */ + FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter"); + FAIL_IF_MSG(perf_count != 1, "perf counter did not increment"); - ptrace(PTRACE_CONT, child_pid, NULL, 0); - return ret; + /* Clean up child */ + FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child"); + + return 0; } int main(int argc, char *argv[]) { - return test_harness(ptrace_perf_hwbreak, "ptrace-perf-hwbreak"); + int err = 0; + + err |= test_harness(same_watch_addr_test, "same_watch_addr"); + err |= test_harness(perf_then_ptrace_test, "perf_then_ptrace"); + + return err; } diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c index bc454f899124..d89474377f11 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-pkey.c @@ -192,7 +192,7 @@ static int parent(struct shared_info *info, pid_t pid) * to the child. */ ret = ptrace_read_regs(pid, NT_PPC_PKEY, regs, 3); - PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync); + PARENT_SKIP_IF_UNSUPPORTED(ret, &info->child_sync, "PKEYs not supported"); PARENT_FAIL_IF(ret, &info->child_sync); info->amr1 = info->amr2 = regs[0]; diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c index 4436ca9d3caf..14726c77a6ce 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tar.c @@ -79,7 +79,7 @@ int ptrace_tar(void) int ret, status; // TAR was added in v2.07 - SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); + SKIP_IF_MSG(!have_hwcap2(PPC_FEATURE2_ARCH_2_07), "TAR requires ISA 2.07 compatible hardware"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); pid = fork(); diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c index 5dc152b162df..7c70d62587c2 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-gpr.c @@ -112,8 +112,8 @@ int ptrace_tm_gpr(void) pid_t pid; int ret, status; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); pid = fork(); if (pid < 0) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c index 458cc1a70ccf..6c17ed099969 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-gpr.c @@ -118,8 +118,8 @@ int ptrace_tm_spd_gpr(void) pid_t pid; int ret, status; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); pid = fork(); if (pid < 0) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c index e112a34fbe59..afd8dc2e2097 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c @@ -128,8 +128,8 @@ int ptrace_tm_spd_tar(void) pid_t pid; int ret, status; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); pid = fork(); if (pid == 0) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c index 40133d49fe39..14d2fac8f237 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c @@ -128,8 +128,8 @@ int ptrace_tm_spd_vsx(void) pid_t pid; int ret, status, i; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 3, 0777|IPC_CREAT); for (i = 0; i < 128; i++) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c index 880ba6a29a48..e64cdb04cecf 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spr.c @@ -113,8 +113,8 @@ int ptrace_tm_spr(void) pid_t pid; int ret, status; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(struct shared), 0777|IPC_CREAT); shm_id1 = shmget(IPC_PRIVATE, sizeof(int), 0777|IPC_CREAT); pid = fork(); diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c index d0db6df0f0ea..3963d4b0429f 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c @@ -116,8 +116,8 @@ int ptrace_tm_tar(void) pid_t pid; int ret, status; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); pid = fork(); if (pid == 0) diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c index 4f05ce4fd282..8c925d734a72 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c @@ -112,8 +112,8 @@ int ptrace_tm_vsx(void) pid_t pid; int ret, status, i; - SKIP_IF(!have_htm()); - SKIP_IF(htm_is_synthetic()); + SKIP_IF_MSG(!have_htm(), "Don't have transactional memory"); + SKIP_IF_MSG(htm_is_synthetic(), "Transactional memory is synthetic"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); for (i = 0; i < 128; i++) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c index cb9875f764ca..11bc624574fe 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-vsx.c @@ -61,7 +61,7 @@ int ptrace_vsx(void) pid_t pid; int ret, status, i; - SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_VSX)); + SKIP_IF_MSG(!have_hwcap(PPC_FEATURE_HAS_VSX), "Don't have VSX"); shm_id = shmget(IPC_PRIVATE, sizeof(int) * 2, 0777|IPC_CREAT); diff --git a/tools/testing/selftests/powerpc/stringloops/asm/export.h b/tools/testing/selftests/powerpc/stringloops/linux/export.h index 2d14a9b4248c..2d14a9b4248c 100644 --- a/tools/testing/selftests/powerpc/stringloops/asm/export.h +++ b/tools/testing/selftests/powerpc/stringloops/linux/export.h diff --git a/tools/testing/selftests/powerpc/vphn/asm/lppaca.h b/tools/testing/selftests/powerpc/vphn/asm/lppaca.h deleted file mode 120000 index 942b1d00999c..000000000000 --- a/tools/testing/selftests/powerpc/vphn/asm/lppaca.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../arch/powerpc/include/asm/lppaca.h
\ No newline at end of file diff --git a/tools/testing/selftests/powerpc/vphn/asm/vphn.h b/tools/testing/selftests/powerpc/vphn/asm/vphn.h new file mode 120000 index 000000000000..3a0b2a00171c --- /dev/null +++ b/tools/testing/selftests/powerpc/vphn/asm/vphn.h @@ -0,0 +1 @@ +../../../../../../arch/powerpc/include/asm/vphn.h
\ No newline at end of file diff --git a/tools/testing/selftests/proc/proc-empty-vm.c b/tools/testing/selftests/proc/proc-empty-vm.c index 7588428b8fcd..ee71ce52cb6a 100644 --- a/tools/testing/selftests/proc/proc-empty-vm.c +++ b/tools/testing/selftests/proc/proc-empty-vm.c @@ -1,3 +1,4 @@ +#if defined __amd64__ || defined __i386__ /* * Copyright (c) 2022 Alexey Dobriyan <adobriyan@gmail.com> * @@ -37,6 +38,10 @@ #include <sys/wait.h> #include <unistd.h> +#ifdef __amd64__ +#define TEST_VSYSCALL +#endif + /* * 0: vsyscall VMA doesn't exist vsyscall=none * 1: vsyscall VMA is --xp vsyscall=xonly @@ -77,7 +82,7 @@ static const char proc_pid_smaps_vsyscall_1[] = "Swap: 0 kB\n" "SwapPss: 0 kB\n" "Locked: 0 kB\n" -"THPeligible: 0\n" +"THPeligible: 0\n" /* * "ProtectionKey:" field is conditional. It is possible to check it as well, * but I don't have such machine. @@ -107,7 +112,7 @@ static const char proc_pid_smaps_vsyscall_2[] = "Swap: 0 kB\n" "SwapPss: 0 kB\n" "Locked: 0 kB\n" -"THPeligible: 0\n" +"THPeligible: 0\n" /* * "ProtectionKey:" field is conditional. It is possible to check it as well, * but I'm too tired. @@ -119,6 +124,7 @@ static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___) _exit(EXIT_FAILURE); } +#ifdef TEST_VSYSCALL static void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___) { _exit(g_vsyscall); @@ -170,6 +176,7 @@ static void vsyscall(void) exit(1); } } +#endif static int test_proc_pid_maps(pid_t pid) { @@ -260,6 +267,7 @@ static const char g_smaps_rollup[] = "Private_Dirty: 0 kB\n" "Referenced: 0 kB\n" "Anonymous: 0 kB\n" +"KSM: 0 kB\n" "LazyFree: 0 kB\n" "AnonHugePages: 0 kB\n" "ShmemPmdMapped: 0 kB\n" @@ -299,7 +307,9 @@ int main(void) { int rv = EXIT_SUCCESS; +#ifdef TEST_VSYSCALL vsyscall(); +#endif switch (g_vsyscall) { case 0: @@ -346,6 +356,14 @@ int main(void) #ifdef __amd64__ munmap(NULL, ((size_t)1 << 47) - 4096); +#elif defined __i386__ + { + size_t len; + + for (len = -4096;; len -= 4096) { + munmap(NULL, len); + } + } #else #error "implement 'unmap everything'" #endif @@ -386,3 +404,9 @@ int main(void) return rv; } +#else +int main(void) +{ + return 4; +} +#endif diff --git a/tools/testing/selftests/riscv/Makefile b/tools/testing/selftests/riscv/Makefile index f4b3d5c9af5b..4a9ff515a3a0 100644 --- a/tools/testing/selftests/riscv/Makefile +++ b/tools/testing/selftests/riscv/Makefile @@ -5,7 +5,7 @@ ARCH ?= $(shell uname -m 2>/dev/null || echo not) ifneq (,$(filter $(ARCH),riscv)) -RISCV_SUBTARGETS ?= hwprobe vector +RISCV_SUBTARGETS ?= hwprobe vector mm else RISCV_SUBTARGETS := endif diff --git a/tools/testing/selftests/riscv/mm/.gitignore b/tools/testing/selftests/riscv/mm/.gitignore new file mode 100644 index 000000000000..5c2c57cb950c --- /dev/null +++ b/tools/testing/selftests/riscv/mm/.gitignore @@ -0,0 +1,2 @@ +mmap_bottomup +mmap_default diff --git a/tools/testing/selftests/riscv/mm/Makefile b/tools/testing/selftests/riscv/mm/Makefile new file mode 100644 index 000000000000..11e0f0568923 --- /dev/null +++ b/tools/testing/selftests/riscv/mm/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 ARM Limited +# Originally tools/testing/arm64/abi/Makefile + +# Additional include paths needed by kselftest.h and local headers +CFLAGS += -D_GNU_SOURCE -std=gnu99 -I. + +TEST_GEN_FILES := testcases/mmap_default testcases/mmap_bottomup + +TEST_PROGS := testcases/run_mmap.sh + +include ../../lib.mk + +$(OUTPUT)/mm: testcases/mmap_default.c testcases/mmap_bottomup.c testcases/mmap_tests.h + $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/mm/testcases/mmap_bottomup.c b/tools/testing/selftests/riscv/mm/testcases/mmap_bottomup.c new file mode 100644 index 000000000000..b29379f7e478 --- /dev/null +++ b/tools/testing/selftests/riscv/mm/testcases/mmap_bottomup.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <sys/mman.h> +#include <testcases/mmap_test.h> + +#include "../../kselftest_harness.h" + +TEST(infinite_rlimit) +{ +// Only works on 64 bit +#if __riscv_xlen == 64 + struct addresses mmap_addresses; + + EXPECT_EQ(BOTTOM_UP, memory_layout()); + + do_mmaps(&mmap_addresses); + + EXPECT_NE(MAP_FAILED, mmap_addresses.no_hint); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_37_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_38_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_46_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_47_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_55_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_56_addr); + + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.no_hint); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_37_addr); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_38_addr); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_46_addr); + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_47_addr); + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_55_addr); + EXPECT_GT(1UL << 56, (unsigned long)mmap_addresses.on_56_addr); +#endif +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/riscv/mm/testcases/mmap_default.c b/tools/testing/selftests/riscv/mm/testcases/mmap_default.c new file mode 100644 index 000000000000..d1accb91b726 --- /dev/null +++ b/tools/testing/selftests/riscv/mm/testcases/mmap_default.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <sys/mman.h> +#include <testcases/mmap_test.h> + +#include "../../kselftest_harness.h" + +TEST(default_rlimit) +{ +// Only works on 64 bit +#if __riscv_xlen == 64 + struct addresses mmap_addresses; + + EXPECT_EQ(TOP_DOWN, memory_layout()); + + do_mmaps(&mmap_addresses); + + EXPECT_NE(MAP_FAILED, mmap_addresses.no_hint); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_37_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_38_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_46_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_47_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_55_addr); + EXPECT_NE(MAP_FAILED, mmap_addresses.on_56_addr); + + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.no_hint); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_37_addr); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_38_addr); + EXPECT_GT(1UL << 38, (unsigned long)mmap_addresses.on_46_addr); + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_47_addr); + EXPECT_GT(1UL << 47, (unsigned long)mmap_addresses.on_55_addr); + EXPECT_GT(1UL << 56, (unsigned long)mmap_addresses.on_56_addr); +#endif +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/riscv/mm/testcases/mmap_test.h b/tools/testing/selftests/riscv/mm/testcases/mmap_test.h new file mode 100644 index 000000000000..9b8434f62f57 --- /dev/null +++ b/tools/testing/selftests/riscv/mm/testcases/mmap_test.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _TESTCASES_MMAP_TEST_H +#define _TESTCASES_MMAP_TEST_H +#include <sys/mman.h> +#include <sys/resource.h> +#include <stddef.h> + +#define TOP_DOWN 0 +#define BOTTOM_UP 1 + +struct addresses { + int *no_hint; + int *on_37_addr; + int *on_38_addr; + int *on_46_addr; + int *on_47_addr; + int *on_55_addr; + int *on_56_addr; +}; + +static inline void do_mmaps(struct addresses *mmap_addresses) +{ + /* + * Place all of the hint addresses on the boundaries of mmap + * sv39, sv48, sv57 + * User addresses end at 1<<38, 1<<47, 1<<56 respectively + */ + void *on_37_bits = (void *)(1UL << 37); + void *on_38_bits = (void *)(1UL << 38); + void *on_46_bits = (void *)(1UL << 46); + void *on_47_bits = (void *)(1UL << 47); + void *on_55_bits = (void *)(1UL << 55); + void *on_56_bits = (void *)(1UL << 56); + + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + + mmap_addresses->no_hint = + mmap(NULL, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_37_addr = + mmap(on_37_bits, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_38_addr = + mmap(on_38_bits, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_46_addr = + mmap(on_46_bits, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_47_addr = + mmap(on_47_bits, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_55_addr = + mmap(on_55_bits, 5 * sizeof(int), prot, flags, 0, 0); + mmap_addresses->on_56_addr = + mmap(on_56_bits, 5 * sizeof(int), prot, flags, 0, 0); +} + +static inline int memory_layout(void) +{ + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + + void *value1 = mmap(NULL, sizeof(int), prot, flags, 0, 0); + void *value2 = mmap(NULL, sizeof(int), prot, flags, 0, 0); + + return value2 > value1; +} +#endif /* _TESTCASES_MMAP_TEST_H */ diff --git a/tools/testing/selftests/riscv/mm/testcases/run_mmap.sh b/tools/testing/selftests/riscv/mm/testcases/run_mmap.sh new file mode 100755 index 000000000000..ca5ad7c48bad --- /dev/null +++ b/tools/testing/selftests/riscv/mm/testcases/run_mmap.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +original_stack_limit=$(ulimit -s) + +./mmap_default + +# Force mmap_bottomup to be ran with bottomup memory due to +# the unlimited stack +ulimit -s unlimited +./mmap_bottomup +ulimit -s $original_stack_limit diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README index b0954c873e2f..be7b00799b3e 100644 --- a/tools/testing/selftests/tc-testing/README +++ b/tools/testing/selftests/tc-testing/README @@ -9,8 +9,7 @@ execute them inside a network namespace dedicated to the task. REQUIREMENTS ------------ -* Minimum Python version of 3.4. Earlier 3.X versions may work but are not - guaranteed. +* Minimum Python version of 3.8. * The kernel must have network namespace support if using nsPlugin @@ -96,6 +95,15 @@ the stdout with a regular expression. Each of the commands in any stage will run in a shell instance. +Each test is an atomic unit. A test that for whatever reason spans multiple test +definitions is a bug. + +A test that runs inside a namespace (requires "nsPlugin") will run in parallel +with other tests. + +Tests that use netdevsim or don't run inside a namespace run serially with regards +to each other. + USER-DEFINED CONSTANTS ---------------------- @@ -116,59 +124,6 @@ COMMAND LINE ARGUMENTS Run tdc.py -h to see the full list of available arguments. -usage: tdc.py [-h] [-p PATH] [-D DIR [DIR ...]] [-f FILE [FILE ...]] - [-c [CATG [CATG ...]]] [-e ID [ID ...]] [-l] [-s] [-i] [-v] [-N] - [-d DEVICE] [-P] [-n] [-V] - -Linux TC unit tests - -optional arguments: - -h, --help show this help message and exit - -p PATH, --path PATH The full path to the tc executable to use - -v, --verbose Show the commands that are being run - -N, --notap Suppress tap results for command under test - -d DEVICE, --device DEVICE - Execute test cases that use a physical device, where - DEVICE is its name. (If not defined, tests that require - a physical device will be skipped) - -P, --pause Pause execution just before post-suite stage - -selection: - select which test cases: files plus directories; filtered by categories - plus testids - - -D DIR [DIR ...], --directory DIR [DIR ...] - Collect tests from the specified directory(ies) - (default [tc-tests]) - -f FILE [FILE ...], --file FILE [FILE ...] - Run tests from the specified file(s) - -c [CATG [CATG ...]], --category [CATG [CATG ...]] - Run tests only from the specified category/ies, or if - no category/ies is/are specified, list known - categories. - -e ID [ID ...], --execute ID [ID ...] - Execute the specified test cases with specified IDs - -action: - select action to perform on selected test cases - - -l, --list List all test cases, or those only within the - specified category - -s, --show Display the selected test cases - -i, --id Generate ID numbers for new test cases - -netns: - options for nsPlugin (run commands in net namespace) - - -N, --no-namespace - Do not run commands in a network namespace. - -valgrind: - options for valgrindPlugin (run command under test under Valgrind) - - -V, --valgrind Run commands under valgrind - - PLUGIN ARCHITECTURE ------------------- diff --git a/tools/testing/selftests/tc-testing/TdcPlugin.py b/tools/testing/selftests/tc-testing/TdcPlugin.py index 79f3ca8617c9..aae85ce4f776 100644 --- a/tools/testing/selftests/tc-testing/TdcPlugin.py +++ b/tools/testing/selftests/tc-testing/TdcPlugin.py @@ -5,10 +5,10 @@ class TdcPlugin: super().__init__() print(' -- {}.__init__'.format(self.sub_class)) - def pre_suite(self, testcount, testidlist): + def pre_suite(self, testcount, testlist): '''run commands before test_runner goes into a test loop''' self.testcount = testcount - self.testidlist = testidlist + self.testlist = testlist if self.args.verbose > 1: print(' -- {}.pre_suite'.format(self.sub_class)) diff --git a/tools/testing/selftests/tc-testing/TdcResults.py b/tools/testing/selftests/tc-testing/TdcResults.py index 1e4d95fdf8d0..e56817b97f08 100644 --- a/tools/testing/selftests/tc-testing/TdcResults.py +++ b/tools/testing/selftests/tc-testing/TdcResults.py @@ -59,7 +59,8 @@ class TestResult: return self.steps class TestSuiteReport(): - _testsuite = [] + def __init__(self): + self._testsuite = [] def add_resultdata(self, result_data): if isinstance(result_data, TestResult): diff --git a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py index 9539cffa9e5e..b62429b0fcdb 100644 --- a/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py +++ b/tools/testing/selftests/tc-testing/plugin-lib/nsPlugin.py @@ -3,35 +3,96 @@ import signal from string import Template import subprocess import time +from multiprocessing import Pool +from functools import cached_property from TdcPlugin import TdcPlugin from tdc_config import * +def prepare_suite(obj, test): + original = obj.args.NAMES + + if 'skip' in test and test['skip'] == 'yes': + return + + if 'nsPlugin' not in test['plugins']: + return + + shadow = {} + shadow['IP'] = original['IP'] + shadow['TC'] = original['TC'] + shadow['NS'] = '{}-{}'.format(original['NS'], test['random']) + shadow['DEV0'] = '{}id{}'.format(original['DEV0'], test['id']) + shadow['DEV1'] = '{}id{}'.format(original['DEV1'], test['id']) + shadow['DUMMY'] = '{}id{}'.format(original['DUMMY'], test['id']) + shadow['DEV2'] = original['DEV2'] + obj.args.NAMES = shadow + + if obj.args.namespace: + obj._ns_create() + else: + obj._ports_create() + + # Make sure the netns is visible in the fs + while True: + obj._proc_check() + try: + ns = obj.args.NAMES['NS'] + f = open('/run/netns/{}'.format(ns)) + f.close() + break + except: + time.sleep(0.1) + continue + + obj.args.NAMES = original + class SubPlugin(TdcPlugin): def __init__(self): self.sub_class = 'ns/SubPlugin' super().__init__() - def pre_suite(self, testcount, testidlist): - '''run commands before test_runner goes into a test loop''' - super().pre_suite(testcount, testidlist) + def pre_suite(self, testcount, testlist): + from itertools import cycle - if self.args.namespace: - self._ns_create() - else: - self._ports_create() + super().pre_suite(testcount, testlist) - def post_suite(self, index): - '''run commands after test_runner goes into a test loop''' - super().post_suite(index) + print("Setting up namespaces and devices...") + + with Pool(self.args.mp) as p: + it = zip(cycle([self]), testlist) + p.starmap(prepare_suite, it) + + def pre_case(self, caseinfo, test_skip): if self.args.verbose: - print('{}.post_suite'.format(self.sub_class)) + print('{}.pre_case'.format(self.sub_class)) + + if test_skip: + return + + + def post_case(self): + if self.args.verbose: + print('{}.post_case'.format(self.sub_class)) if self.args.namespace: self._ns_destroy() else: self._ports_destroy() + def post_suite(self, index): + if self.args.verbose: + print('{}.post_suite'.format(self.sub_class)) + + # Make sure we don't leak resources + for f in os.listdir('/run/netns/'): + cmd = self._replace_keywords("$IP netns del {}".format(f)) + + if self.args.verbose > 3: + print('_exec_cmd: command "{}"'.format(cmd)) + + subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + def add_args(self, parser): super().add_args(parser) self.argparser_group = self.argparser.add_argument_group( @@ -77,18 +138,43 @@ class SubPlugin(TdcPlugin): print('adjust_command: return command [{}]'.format(command)) return command - def _ports_create(self): - cmd = '$IP link add $DEV0 type veth peer name $DEV1' - self._exec_cmd('pre', cmd) - cmd = '$IP link set $DEV0 up' - self._exec_cmd('pre', cmd) + def _ports_create_cmds(self): + cmds = [] + + cmds.append(self._replace_keywords('link add $DEV0 type veth peer name $DEV1')) + cmds.append(self._replace_keywords('link set $DEV0 up')) + cmds.append(self._replace_keywords('link add $DUMMY type dummy')) if not self.args.namespace: - cmd = '$IP link set $DEV1 up' - self._exec_cmd('pre', cmd) + cmds.append(self._replace_keywords('link set $DEV1 up')) + + return cmds + + def _ports_create(self): + self._exec_cmd_batched('pre', self._ports_create_cmds()) + + def _ports_destroy_cmd(self): + return self._replace_keywords('link del $DEV0') def _ports_destroy(self): - cmd = '$IP link del $DEV0' - self._exec_cmd('post', cmd) + self._exec_cmd('post', self._ports_destroy_cmd()) + + def _ns_create_cmds(self): + cmds = [] + + if self.args.namespace: + ns = self.args.NAMES['NS'] + + cmds.append(self._replace_keywords('netns add {}'.format(ns))) + cmds.append(self._replace_keywords('link set $DEV1 netns {}'.format(ns))) + cmds.append(self._replace_keywords('link set $DUMMY netns {}'.format(ns))) + cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV1 up'.format(ns))) + cmds.append(self._replace_keywords('netns exec {} $IP link set $DUMMY up'.format(ns))) + + if self.args.device: + cmds.append(self._replace_keywords('link set $DEV2 netns {}'.format(ns))) + cmds.append(self._replace_keywords('netns exec {} $IP link set $DEV2 up'.format(ns))) + + return cmds def _ns_create(self): ''' @@ -96,18 +182,10 @@ class SubPlugin(TdcPlugin): the required network devices for it. ''' self._ports_create() - if self.args.namespace: - cmd = '$IP netns add {}'.format(self.args.NAMES['NS']) - self._exec_cmd('pre', cmd) - cmd = '$IP link set $DEV1 netns {}'.format(self.args.NAMES['NS']) - self._exec_cmd('pre', cmd) - cmd = '$IP -n {} link set $DEV1 up'.format(self.args.NAMES['NS']) - self._exec_cmd('pre', cmd) - if self.args.device: - cmd = '$IP link set $DEV2 netns {}'.format(self.args.NAMES['NS']) - self._exec_cmd('pre', cmd) - cmd = '$IP -n {} link set $DEV2 up'.format(self.args.NAMES['NS']) - self._exec_cmd('pre', cmd) + self._exec_cmd_batched('pre', self._ns_create_cmds()) + + def _ns_destroy_cmd(self): + return self._replace_keywords('netns delete {}'.format(self.args.NAMES['NS'])) def _ns_destroy(self): ''' @@ -115,35 +193,49 @@ class SubPlugin(TdcPlugin): devices as well) ''' if self.args.namespace: - cmd = '$IP netns delete {}'.format(self.args.NAMES['NS']) - self._exec_cmd('post', cmd) + self._exec_cmd('post', self._ns_destroy_cmd()) + self._ports_destroy() + + @cached_property + def _proc(self): + ip = self._replace_keywords("$IP -b -") + proc = subprocess.Popen(ip, + shell=True, + stdin=subprocess.PIPE, + env=ENVIR) + + return proc + + def _proc_check(self): + proc = self._proc + + proc.poll() + + if proc.returncode is not None and proc.returncode != 0: + raise RuntimeError("iproute2 exited with an error code") def _exec_cmd(self, stage, command): ''' Perform any required modifications on an executable command, then run it in a subprocess and return the results. ''' - if '$' in command: - command = self._replace_keywords(command) - self.adjust_command(stage, command) - if self.args.verbose: + if self.args.verbose > 3: print('_exec_cmd: command "{}"'.format(command)) - proc = subprocess.Popen(command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=ENVIR) - (rawout, serr) = proc.communicate() - if proc.returncode != 0 and len(serr) > 0: - foutput = serr.decode("utf-8") - else: - foutput = rawout.decode("utf-8") + proc = self._proc + + proc.stdin.write((command + '\n').encode()) + proc.stdin.flush() + + if self.args.verbose > 3: + print('_exec_cmd proc: {}'.format(proc)) + + self._proc_check() - proc.stdout.close() - proc.stderr.close() - return proc, foutput + def _exec_cmd_batched(self, stage, commands): + for cmd in commands: + self._exec_cmd(stage, cmd) def _replace_keywords(self, cmd): """ diff --git a/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py index e36775bd4d12..8762c0f4a095 100644 --- a/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py +++ b/tools/testing/selftests/tc-testing/plugin-lib/rootPlugin.py @@ -10,9 +10,9 @@ class SubPlugin(TdcPlugin): self.sub_class = 'root/SubPlugin' super().__init__() - def pre_suite(self, testcount, testidlist): + def pre_suite(self, testcount, testlist): # run commands before test_runner goes into a test loop - super().pre_suite(testcount, testidlist) + super().pre_suite(testcount, testlist) if os.geteuid(): print('This script must be run with root privileges', file=sys.stderr) diff --git a/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py b/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py index 4bb866575ea1..c6f61649c430 100644 --- a/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py +++ b/tools/testing/selftests/tc-testing/plugin-lib/valgrindPlugin.py @@ -25,9 +25,10 @@ class SubPlugin(TdcPlugin): self._tsr = TestSuiteReport() super().__init__() - def pre_suite(self, testcount, testidlist): + def pre_suite(self, testcount, testist): '''run commands before test_runner goes into a test loop''' - super().pre_suite(testcount, testidlist) + self.testidlist = [tidx['id'] for tidx in testlist] + super().pre_suite(testcount, testlist) if self.args.verbose > 1: print('{}.pre_suite'.format(self.sub_class)) if self.args.valgrind: diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json index 0de2f79ea329..3d0f9310bde4 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/connmark.json @@ -6,6 +6,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -30,6 +33,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -54,6 +60,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -78,6 +87,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -102,6 +114,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -126,6 +141,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -150,6 +168,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -174,6 +195,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -198,6 +222,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -222,6 +249,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -246,6 +276,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -271,6 +304,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -295,6 +331,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -320,6 +359,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", @@ -345,6 +387,9 @@ "actions", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action connmark", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json index 072febf25f55..56e11136d0f6 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/csum.json @@ -6,6 +6,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -30,6 +33,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -54,6 +60,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -78,6 +87,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -102,6 +114,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -126,6 +141,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -150,6 +168,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -174,6 +195,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -198,6 +222,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -222,6 +249,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -246,6 +276,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -270,6 +303,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -294,6 +330,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -318,6 +357,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -342,6 +384,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -366,6 +411,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -390,6 +438,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -414,6 +465,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -438,6 +492,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -461,6 +518,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -485,6 +545,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -508,6 +571,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", @@ -533,6 +599,9 @@ "actions", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action csum", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json index bd843ab00a58..7d07c55bb668 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ct.json @@ -6,6 +6,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -30,6 +33,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -54,6 +60,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -78,6 +87,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -102,6 +114,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -126,6 +141,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -150,6 +168,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -174,6 +195,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -198,6 +222,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -222,6 +249,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -246,6 +276,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -270,6 +303,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -294,6 +330,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -318,6 +357,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -342,6 +384,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -366,6 +411,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -390,6 +438,9 @@ "actions", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ct", @@ -415,6 +466,9 @@ "ct", "scapy" ], + "plugins": { + "requires": "nsPlugin" + }, "plugins": { "requires": [ "nsPlugin", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ctinfo.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ctinfo.json index d9710c067eb7..bb54d71241a0 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ctinfo.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ctinfo.json @@ -6,6 +6,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action ctinfo", @@ -30,6 +33,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -54,6 +60,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action ctinfo", @@ -78,6 +87,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action ctinfo", @@ -102,6 +114,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -132,6 +147,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -162,6 +180,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -192,6 +213,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action ctinfo", @@ -219,6 +243,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -246,6 +273,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -271,6 +301,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", @@ -295,6 +328,9 @@ "actions", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ctinfo", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json index c652e8c1157d..0fcd52742939 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gact.json @@ -6,6 +6,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -30,6 +33,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -54,6 +60,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -78,6 +87,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -102,6 +114,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -126,6 +141,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -150,6 +168,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -175,6 +196,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -199,6 +223,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -223,6 +250,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -252,6 +282,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC actions add action reclassify index 101", "$TC actions add action reclassify index 102", @@ -273,6 +306,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -298,6 +334,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -323,6 +362,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -348,6 +390,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -373,6 +418,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -398,6 +446,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -422,6 +473,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -448,6 +502,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -473,6 +530,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -497,6 +557,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -521,6 +584,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -544,6 +610,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -568,6 +637,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", @@ -593,6 +665,9 @@ "actions", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gact", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/gate.json b/tools/testing/selftests/tc-testing/tc-tests/actions/gate.json index e16a4963fdd2..db645c22ad7b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/gate.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/gate.json @@ -6,6 +6,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action gate", @@ -30,6 +33,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -54,6 +60,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action gate", @@ -78,6 +87,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action gate", @@ -102,6 +114,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -132,6 +147,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -162,6 +180,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -192,6 +213,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action gate", @@ -219,6 +243,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -246,6 +273,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -271,6 +301,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", @@ -295,6 +328,9 @@ "actions", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action gate", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json b/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json index 459bcad35810..f587a32e44c4 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/ife.json @@ -6,6 +6,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -30,6 +33,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -54,6 +60,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -78,6 +87,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -102,6 +114,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -126,6 +141,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -150,6 +168,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -174,6 +195,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -196,6 +220,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -220,6 +247,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -244,6 +274,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -268,6 +301,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -292,6 +328,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -316,6 +355,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -340,6 +382,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -364,6 +409,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -386,6 +434,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -410,6 +461,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -434,6 +488,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -458,6 +515,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -482,6 +542,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -506,6 +569,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -530,6 +596,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -554,6 +623,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -578,6 +650,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -600,6 +675,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -624,6 +702,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -648,6 +729,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -672,6 +756,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -696,6 +783,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -720,6 +810,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -744,6 +837,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -768,6 +864,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -792,6 +891,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -816,6 +918,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -840,6 +945,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -864,6 +972,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -888,6 +999,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -912,6 +1026,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -934,6 +1051,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -956,6 +1076,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -980,6 +1103,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1002,6 +1128,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1024,6 +1153,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1046,6 +1178,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1068,6 +1203,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1093,6 +1231,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", @@ -1118,6 +1259,9 @@ "actions", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action ife", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json index 12a2fe0e1472..b53d12909962 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json @@ -6,6 +6,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -30,6 +33,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -55,6 +61,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -81,6 +90,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -105,6 +117,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -129,6 +144,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -153,6 +171,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -178,6 +199,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -202,6 +226,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -226,6 +253,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -250,6 +280,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -274,6 +307,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -298,6 +334,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -322,6 +361,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -346,6 +388,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -370,6 +415,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -392,6 +440,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -417,6 +468,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -442,6 +496,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -467,6 +524,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -491,6 +551,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -514,6 +577,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -538,6 +604,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", @@ -561,6 +630,9 @@ "actions", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mirred", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json index 866f0efd0859..b1c5dd27a70d 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mpls.json @@ -6,6 +6,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -30,6 +33,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -54,6 +60,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -78,6 +87,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -102,6 +114,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -126,6 +141,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -150,6 +168,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -174,6 +195,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -198,6 +222,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -222,6 +249,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -244,6 +274,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -266,6 +299,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -288,6 +324,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -310,6 +349,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -332,6 +374,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -356,6 +401,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -380,6 +428,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -404,6 +455,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -426,6 +480,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -448,6 +505,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -470,6 +530,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -492,6 +555,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -514,6 +580,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -538,6 +607,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -562,6 +634,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -586,6 +661,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -610,6 +688,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -634,6 +715,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -656,6 +740,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -678,6 +765,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -700,6 +790,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -722,6 +815,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -744,6 +840,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -768,6 +867,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -792,6 +894,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -814,6 +919,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -836,6 +944,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -860,6 +971,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -884,6 +998,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -906,6 +1023,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -930,6 +1050,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -954,6 +1077,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -978,6 +1104,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1002,6 +1131,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1024,6 +1156,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1046,6 +1181,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1070,6 +1208,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1094,6 +1235,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1116,6 +1260,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1138,6 +1285,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1163,6 +1313,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1188,6 +1341,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", @@ -1211,6 +1367,9 @@ "actions", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action mpls", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json b/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json index 0a3c491edbc5..ee2792998c89 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/nat.json @@ -6,6 +6,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -30,6 +33,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -54,6 +60,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -78,6 +87,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -102,6 +114,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -126,6 +141,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -150,6 +168,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -174,6 +195,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -203,6 +227,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -232,6 +259,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -261,6 +291,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -285,6 +318,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -309,6 +345,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -333,6 +372,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -357,6 +399,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -381,6 +426,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -405,6 +453,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -429,6 +480,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -453,6 +507,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -477,6 +534,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -501,6 +561,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -525,6 +588,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -549,6 +615,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -573,6 +642,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -597,6 +669,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -622,6 +697,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", @@ -647,6 +725,9 @@ "actions", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action nat", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json index 72cdc3c800a5..37c410332174 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json @@ -6,6 +6,9 @@ "actions", "pedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -30,6 +33,9 @@ "actions", "pedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -56,6 +62,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -81,6 +90,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -106,6 +118,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -131,6 +146,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -156,6 +174,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -206,6 +227,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -231,6 +255,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -256,6 +283,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -281,6 +311,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -306,6 +339,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -331,6 +367,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -356,6 +395,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -381,6 +423,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -406,6 +451,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -431,6 +479,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -456,6 +507,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -481,6 +535,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -506,6 +563,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -531,6 +591,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -556,6 +619,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -581,6 +647,9 @@ "pedit", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -606,6 +675,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -631,6 +703,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -656,6 +731,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -681,6 +759,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -706,6 +787,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -731,6 +815,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -756,6 +843,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -779,6 +869,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -804,6 +897,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -829,6 +925,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -854,6 +953,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -879,6 +981,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -904,6 +1009,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -929,6 +1037,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -954,6 +1065,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -979,6 +1093,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1004,6 +1121,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1029,6 +1149,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1054,6 +1177,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1079,6 +1205,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1104,6 +1233,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1129,6 +1261,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1154,6 +1289,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1179,6 +1317,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1204,6 +1345,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1229,6 +1373,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1254,6 +1401,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1279,6 +1429,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1304,6 +1457,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1329,6 +1485,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1354,6 +1513,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1379,6 +1541,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1404,6 +1569,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1429,6 +1597,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1454,6 +1625,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1479,6 +1653,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1504,6 +1681,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1554,6 +1734,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1579,6 +1762,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1629,6 +1815,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1654,6 +1843,9 @@ "pedit", "layered_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1680,6 +1872,9 @@ "layered_op", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", @@ -1706,6 +1901,9 @@ "layered_op", "raw_op" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action pedit", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json index b7205a069534..dd8109768f8f 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/police.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/police.json @@ -6,6 +6,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -30,6 +33,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -55,6 +61,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -79,6 +88,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -103,6 +115,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -127,6 +142,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -151,6 +169,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -175,6 +196,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -199,6 +223,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -223,6 +250,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -247,6 +277,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -271,6 +304,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -295,6 +331,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -319,6 +358,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -343,6 +385,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -367,6 +412,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -391,6 +439,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -415,6 +466,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -439,6 +493,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -463,6 +520,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -488,6 +548,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -520,6 +583,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -545,6 +611,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -577,6 +646,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC actions add action police rate 1mbit burst 100k index 1", "$TC actions add action police rate 2mbit burst 200k index 2", @@ -603,6 +675,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -627,6 +702,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -651,6 +729,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -675,6 +756,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -699,6 +783,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -723,6 +810,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -747,6 +837,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -772,6 +865,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -796,6 +892,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", @@ -820,6 +919,9 @@ "actions", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action police", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/sample.json b/tools/testing/selftests/tc-testing/tc-tests/actions/sample.json index 148d8bcb8606..af35e2f30a95 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/sample.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/sample.json @@ -6,6 +6,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -30,6 +33,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -54,6 +60,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -78,6 +87,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -102,6 +114,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -126,6 +141,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -150,6 +168,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -174,6 +195,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -196,6 +220,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -218,6 +245,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -240,6 +270,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -262,6 +295,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -284,6 +320,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -308,6 +347,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -332,6 +374,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -356,6 +401,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -380,6 +428,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -402,6 +453,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -424,6 +478,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -446,6 +503,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -468,6 +528,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -492,6 +555,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -516,6 +582,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -541,6 +610,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -566,6 +638,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -591,6 +666,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -616,6 +694,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -641,6 +722,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", @@ -666,6 +750,9 @@ "actions", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action sample", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json b/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json index e0c5f060ccb9..ac960e70dc9b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/simple.json @@ -6,6 +6,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -30,6 +33,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -54,6 +60,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -79,6 +88,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -106,6 +118,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -131,6 +146,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -158,6 +176,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -183,6 +204,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", @@ -213,6 +237,9 @@ "actions", "simple" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action simple", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json index 9cdd2e31ac2c..27ba0f72e904 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbedit.json @@ -6,6 +6,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -30,6 +33,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -54,6 +60,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -76,6 +85,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -100,6 +112,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -124,6 +139,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -146,6 +164,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -168,6 +189,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -193,6 +217,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -217,6 +244,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -241,6 +271,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -265,6 +298,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -289,6 +325,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -313,6 +352,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -337,6 +379,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -361,6 +406,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -385,6 +433,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -409,6 +460,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -433,6 +487,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -457,6 +514,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -481,6 +541,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -505,6 +568,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -529,6 +595,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -557,6 +626,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -581,6 +653,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -603,6 +678,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -628,6 +706,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC actions add action skbedit mark 500", "$TC actions add action skbedit mark 501", @@ -653,6 +734,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -678,6 +762,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", @@ -702,6 +789,9 @@ "actions", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbedit", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json index 742f2290973e..33ed7a8099e2 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/skbmod.json @@ -6,6 +6,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -30,6 +33,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -54,6 +60,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -78,6 +87,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -102,6 +114,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -126,6 +141,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -150,6 +168,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -174,6 +195,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -198,6 +222,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -222,6 +249,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -246,6 +276,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -270,6 +303,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -294,6 +330,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -323,6 +362,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -352,6 +394,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -377,6 +422,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC actions add action skbmod set etype 0x0001", "$TC actions add action skbmod set etype 0x0011", @@ -400,6 +448,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", @@ -425,6 +476,9 @@ "actions", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action skbmod", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json index b5b47fbf6c00..0b6f0b5aeaad 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tunnel_key.json @@ -6,6 +6,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -30,6 +33,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -59,6 +65,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -88,6 +97,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -117,6 +129,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -146,6 +161,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -175,6 +193,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -204,6 +225,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -228,6 +252,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -252,6 +279,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -281,6 +311,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -305,6 +338,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -334,6 +370,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -358,6 +397,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -387,6 +429,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -411,6 +456,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -435,6 +483,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -459,6 +510,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -488,6 +542,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -517,6 +574,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -541,6 +601,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -565,6 +628,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -594,6 +660,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -618,6 +687,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -642,6 +714,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -666,6 +741,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -690,6 +768,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -714,6 +795,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -738,6 +822,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -762,6 +849,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -786,6 +876,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -811,6 +904,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -836,6 +932,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -864,6 +963,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -892,6 +994,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -917,6 +1022,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -941,6 +1049,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -966,6 +1077,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action tunnel_key", @@ -991,6 +1105,9 @@ "actions", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "dependsOn": "$TC actions add action tunnel_key help 2>&1 | grep -q nofrag", "setup": [ [ diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json index 2aad4caa8581..e5fe8762978a 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/vlan.json @@ -6,6 +6,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -30,6 +33,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -54,6 +60,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -78,6 +87,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -102,6 +114,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -126,6 +141,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -150,6 +168,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -174,6 +195,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -196,6 +220,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -220,6 +247,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -242,6 +272,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -264,6 +297,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -286,6 +322,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -310,6 +349,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -334,6 +376,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -358,6 +403,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -382,6 +430,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -406,6 +457,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -430,6 +484,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -452,6 +509,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -476,6 +536,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -500,6 +563,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -524,6 +590,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -549,6 +618,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -574,6 +646,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -599,6 +674,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -624,6 +702,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -647,6 +728,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -670,6 +754,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -696,6 +783,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -720,6 +810,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -745,6 +838,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -769,6 +865,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -792,6 +891,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -816,6 +918,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", @@ -839,6 +944,9 @@ "actions", "vlan" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action vlan", diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/xt.json b/tools/testing/selftests/tc-testing/tc-tests/actions/xt.json index c9f002aea6d4..1a92e8898fec 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/xt.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/xt.json @@ -6,6 +6,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -30,6 +33,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -60,6 +66,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -90,6 +99,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -120,6 +132,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC action flush action xt", @@ -147,6 +162,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -174,6 +192,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", @@ -199,6 +220,9 @@ "actions", "xt" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ [ "$TC actions flush action xt", diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/bpf.json b/tools/testing/selftests/tc-testing/tc-tests/filters/bpf.json index 1f0cae474db2..013fb983bc3f 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/bpf.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/bpf.json @@ -51,7 +51,10 @@ "bpf-filter" ], "plugins": { - "requires": "buildebpfPlugin" + "requires": [ + "buildebpfPlugin", + "nsPlugin" + ] }, "setup": [ "$TC qdisc add dev $DEV1 ingress" @@ -73,7 +76,10 @@ "bpf-filter" ], "plugins": { - "requires": "buildebpfPlugin" + "requires": [ + "buildebpfPlugin", + "nsPlugin" + ] }, "setup": [ "$TC qdisc add dev $DEV1 ingress" diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json index 5272049566d6..a9b071e1354b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/fw.json @@ -53,111 +53,6 @@ "plugins": { "requires": "nsPlugin" }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, - "plugins": { - "requires": "nsPlugin" - }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -173,14 +68,15 @@ { "id": "c591", "name": "Add fw filter with action ok by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet. Remove this when the behaviour is fixed.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact ok index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -189,9 +85,7 @@ "matchPattern": "handle 0x1.*gact action pass.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -201,6 +95,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -216,14 +113,15 @@ { "id": "38b3", "name": "Add fw filter with action continue by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet. Remove this when the behaviour is fixed.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact continue index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -232,9 +130,7 @@ "matchPattern": "handle 0x1.*gact action continue.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -244,6 +140,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -259,14 +158,15 @@ { "id": "6753", "name": "Add fw filter with action pipe by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact pipe index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -275,9 +175,7 @@ "matchPattern": "handle 0x1.*gact action pipe.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -287,6 +185,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -302,14 +203,15 @@ { "id": "6dc6", "name": "Add fw filter with action drop by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact drop index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -318,9 +220,7 @@ "matchPattern": "handle 0x1.*gact action drop.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -330,6 +230,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -345,14 +248,15 @@ { "id": "3bc2", "name": "Add fw filter with action reclassify by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact reclassify index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -361,9 +265,7 @@ "matchPattern": "handle 0x1.*gact action reclassify.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -373,6 +275,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -388,14 +293,15 @@ { "id": "36f7", "name": "Add fw filter with action jump 10 by reference", - "__comment": "We add sleep here because action might have not been deleted by workqueue just yet.", "category": [ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress", - "/bin/sleep 1", "$TC actions add action gact jump 10 index 1" ], "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 prio 1 fw action gact index 1", @@ -404,9 +310,7 @@ "matchPattern": "handle 0x1.*gact action jump 10.*index 1 ref 2 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DEV1 ingress", - "/bin/sleep 1", - "$TC actions del action gact index 1" + "$TC qdisc del dev $DEV1 ingress" ] }, { @@ -416,6 +320,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -435,6 +342,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -454,6 +364,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -473,6 +386,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -492,6 +408,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -511,6 +430,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -530,6 +452,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -549,6 +474,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -568,6 +496,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -587,6 +518,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -606,6 +540,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -625,6 +562,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -644,6 +584,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -663,6 +606,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -682,6 +628,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -701,6 +650,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -720,6 +672,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -739,6 +694,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -758,6 +716,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -777,6 +738,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -796,6 +760,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -815,6 +782,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -834,6 +804,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -853,6 +826,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -872,6 +848,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -891,6 +870,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -910,6 +892,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -929,6 +914,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -948,6 +936,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -967,6 +958,9 @@ "filter", "fw" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ "$TC qdisc add dev $DEV1 ingress" ], @@ -1096,7 +1090,6 @@ { "id": "0e99", "name": "Del single fw filter x1", - "__comment__": "First of two tests to check that one filter is there and the other isn't", "category": [ "filter", "fw" @@ -1121,7 +1114,6 @@ { "id": "f54c", "name": "Del single fw filter x2", - "__comment__": "Second of two tests to check that one filter is there and the other isn't", "category": [ "filter", "fw" @@ -1351,5 +1343,54 @@ "teardown": [ "$TC qdisc del dev $DEV1 ingress" ] + }, + { + "id": "e470", + "name": "Try to delete class referenced by fw after a replace", + "category": [ + "filter", + "fw" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 parent root handle 10: drr", + "$TC class add dev $DEV1 parent root classid 1 drr", + "$TC filter add dev $DEV1 parent 10: handle 1 prio 1 fw classid 10:1 action ok", + "$TC filter replace dev $DEV1 parent 10: handle 1 prio 1 fw classid 10:1 action drop" + ], + "cmdUnderTest": "$TC class delete dev $DEV1 parent 10: classid 10:1", + "expExitCode": "2", + "verifyCmd": "$TC class show dev $DEV1", + "matchPattern": "class drr 10:1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 parent root drr" + ] + }, + { + "id": "ec1a", + "name": "Replace fw classid with nil", + "category": [ + "filter", + "fw" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 parent root handle 10: drr", + "$TC class add dev $DEV1 parent root classid 1 drr", + "$TC filter add dev $DEV1 parent 10: handle 1 prio 1 fw classid 10:1 action ok" + ], + "cmdUnderTest": "$TC filter replace dev $DEV1 parent 10: handle 1 prio 1 fw action drop", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent 10:", + "matchPattern": "fw chain 0 handle 0x1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 parent root drr" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json index 2df68017dfb8..afa1b9b0c856 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json @@ -6,8 +6,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ip matchall action ok", @@ -16,8 +18,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -27,8 +28,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol ip matchall action ok", @@ -37,8 +40,7 @@ "matchPattern": "^filter parent 1: protocol ip pref 1 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -48,8 +50,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall action drop", @@ -58,8 +62,7 @@ "matchPattern": "^filter parent ffff: protocol ipv6 pref 1 matchall.*handle 0x1.*gact action drop.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -69,8 +72,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol ipv6 matchall action drop", @@ -79,8 +84,7 @@ "matchPattern": "^filter parent 1: protocol ipv6 pref 1 matchall.*handle 0x1.*gact action drop.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -90,8 +94,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 65535 protocol ipv4 matchall action pass", @@ -100,8 +106,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 65535 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -111,8 +116,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 65535 protocol ipv4 matchall action pass", @@ -121,8 +128,7 @@ "matchPattern": "^filter parent 1: protocol ip pref 65535 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -132,8 +138,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 655355 protocol ipv4 matchall action pass", @@ -142,8 +150,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 655355 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -153,8 +160,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 655355 protocol ipv4 matchall action pass", @@ -163,8 +172,7 @@ "matchPattern": "^filter parent 1: protocol ip pref 655355 matchall.*handle 0x1.*gact action pass.*ref 1 bind 1", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -174,8 +182,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0xffffffff prio 1 protocol all matchall action continue", @@ -184,8 +194,7 @@ "matchPattern": "^filter parent ffff: protocol all pref 1 matchall.*handle 0xffffffff.*gact action continue.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -195,8 +204,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0xffffffff prio 1 protocol all matchall action continue", @@ -205,8 +216,7 @@ "matchPattern": "^filter parent 1: protocol all pref 1 matchall.*handle 0xffffffff.*gact action continue.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -216,8 +226,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall skip_hw action reclassify", @@ -226,8 +238,7 @@ "matchPattern": "^filter parent ffff: protocol all pref 1 matchall.*handle 0x1.*skip_hw.*not_in_hw.*gact action reclassify.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -237,8 +248,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent 1: handle 0x1 prio 1 protocol all matchall skip_hw action reclassify", @@ -247,8 +260,7 @@ "matchPattern": "^filter parent 1: protocol all pref 1 matchall.*handle 0x1.*skip_hw.*not_in_hw.*gact action reclassify.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: prio" ] }, { @@ -258,8 +270,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 1:1 action pass", @@ -268,8 +282,7 @@ "matchPattern": "^filter parent ffff: protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 1:1.*gact action pass.*ref 1 bind 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -279,8 +292,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 6789defg action pass", @@ -289,8 +304,7 @@ "matchPattern": "^filter protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 6789defg.*gact action pass.*ref 1 bind 1", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -300,8 +314,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv6 matchall classid 1:2 action pass" ], @@ -311,8 +327,7 @@ "matchPattern": "^filter protocol ipv6 pref 1 matchall.*handle 0x1.*flowid 1:2.*gact action pass.*ref 1 bind 1", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -322,8 +337,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall classid 1:2 action pass", "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol all matchall classid 1:3 action pass", @@ -336,8 +353,7 @@ "matchPattern": "^filter protocol all pref.*matchall.*handle.*flowid.*gact action pass", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -347,8 +363,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all matchall classid 1:2 action pass", "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol all matchall classid 1:3 action pass", @@ -361,8 +379,7 @@ "matchPattern": "^filter protocol all pref 2 matchall.*handle 0x2 flowid 1:2.*gact action pass", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -372,8 +389,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol all chain 1 matchall classid 1:1 action pass", "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv4 chain 2 matchall classid 1:3 action continue" @@ -384,8 +403,7 @@ "matchPattern": "^filter protocol all pref 1 matchall chain 1 handle 0x1 flowid 1:1.*gact action pass", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -395,8 +413,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions flush action police", "$TC actions add action police rate 1mbit burst 100k index 199 skip_hw" @@ -408,7 +428,6 @@ "matchCount": "0", "teardown": [ "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", "$TC actions del action police index 199" ] }, @@ -419,8 +438,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions flush action police", "$TC actions add action police rate 1mbit burst 100k index 199" @@ -432,7 +453,6 @@ "matchCount": "0", "teardown": [ "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", "$TC actions del action police index 199" ] }, @@ -443,8 +463,10 @@ "filter", "matchall" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions flush action police", "$TC actions add action police rate 1mbit burst 100k index 199" @@ -456,7 +478,6 @@ "matchCount": "0", "teardown": [ "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", "$TC actions del action police index 199" ] } diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json index 1f6f19f02997..8d8de8f65aef 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/route.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/route.json @@ -177,5 +177,30 @@ "teardown": [ "$TC qdisc del dev $DEV1 ingress" ] + }, + { + "id": "b042", + "name": "Try to delete class referenced by route after a replace", + "category": [ + "filter", + "route" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 parent root handle 10: drr", + "$TC class add dev $DEV1 parent root classid 1 drr", + "$TC filter add dev $DEV1 parent 10: prio 1 route from 10 classid 10:1 action ok", + "$TC filter replace dev $DEV1 parent 10: prio 1 route from 5 classid 10:1 action drop" + ], + "cmdUnderTest": "$TC class delete dev $DEV1 parent 10: classid 10:1", + "expExitCode": "2", + "verifyCmd": "$TC class show dev $DEV1", + "matchPattern": "class drr 10:1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 parent root drr" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json index bd64a4bf11ab..ddc7c355be0a 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json @@ -247,5 +247,30 @@ "teardown": [ "$TC qdisc del dev $DEV1 ingress" ] + }, + { + "id": "0c37", + "name": "Try to delete class referenced by u32 after a replace", + "category": [ + "filter", + "u32" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DEV1 parent root handle 10: drr", + "$TC class add dev $DEV1 parent root classid 1 drr", + "$TC filter add dev $DEV1 parent 10: prio 1 u32 match icmp type 1 0xff classid 10:1 action ok", + "$TC filter replace dev $DEV1 parent 10: prio 1 u32 match icmp type 1 0xff classid 10:1 action drop" + ], + "cmdUnderTest": "$TC class delete dev $DEV1 parent 10: classid 10:1", + "expExitCode": "2", + "verifyCmd": "$TC class show dev $DEV1", + "matchPattern": "class drr 10:1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DEV1 parent root drr" + ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/actions.json b/tools/testing/selftests/tc-testing/tc-tests/infra/actions.json index 16f3a83605e4..1ba96c467754 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/actions.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/actions.json @@ -6,8 +6,10 @@ "infra", "pedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC action add action pedit munge offset 0 u8 clear index 1" ], @@ -17,9 +19,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action pedit" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -29,8 +29,10 @@ "infra", "mpls" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC action add action mpls pop protocol ipv4 index 1" ], @@ -40,9 +42,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action mpls" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -52,8 +52,10 @@ "infra", "bpf" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC action add action bpf bytecode '4,40 0 0 12,21 0 1 2048,6 0 0 262144,6 0 0 0' index 1" ], @@ -63,9 +65,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action bpf" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -75,8 +75,10 @@ "infra", "connmark" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action connmark" ], @@ -86,9 +88,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action connmark" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -98,8 +98,10 @@ "infra", "csum" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action csum ip4h index 1" ], @@ -109,9 +111,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action csum" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -121,8 +121,10 @@ "infra", "ct" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action ct index 1" ], @@ -132,9 +134,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action ct" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -144,8 +144,10 @@ "infra", "ctinfo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC action add action ctinfo index 1" ], @@ -155,9 +157,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action ctinfo" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -167,8 +167,10 @@ "infra", "gact" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action pass index 1" ], @@ -178,9 +180,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action gact" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -190,8 +190,10 @@ "infra", "gate" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC action add action gate priority 1 sched-entry close 100000000ns index 1" ], @@ -201,9 +203,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action gate" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -213,8 +213,10 @@ "infra", "ife" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action ife encode allow mark pass index 1" ], @@ -224,9 +226,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action ife" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -236,8 +236,10 @@ "infra", "mirred" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action mirred egress mirror index 1 dev lo" ], @@ -247,9 +249,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action mirred" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -259,8 +259,10 @@ "infra", "nat" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action nat ingress 192.168.1.1 200.200.200.1" ], @@ -270,9 +272,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action nat" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -282,8 +282,10 @@ "infra", "police" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action police rate 1kbit burst 10k index 1" ], @@ -293,9 +295,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action police" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -305,8 +305,10 @@ "infra", "sample" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action sample rate 10 group 1 index 1" ], @@ -316,9 +318,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action sample" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -328,8 +328,10 @@ "infra", "skbedit" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action skbedit mark 1" ], @@ -339,9 +341,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action skbedit" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -351,8 +351,10 @@ "infra", "skbmod" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action skbmod set dmac 11:22:33:44:55:66 index 1" ], @@ -362,9 +364,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action skbmod" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -374,8 +374,10 @@ "infra", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action tunnel_key set src_ip 10.10.10.1 dst_ip 20.20.20.2 id 1 index 1" ], @@ -385,9 +387,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action tunnel_key" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -397,8 +397,10 @@ "infra", "tunnel_key" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC actions add action vlan pop pipe index 1" ], @@ -408,9 +410,7 @@ "matchPattern": "^filter parent ffff: protocol ip pref 1 matchall.*handle 0x1.*", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy", - "$TC actions flush action vlan" + "$TC qdisc del dev $DUMMY ingress" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json index c4c778e83da2..8d10042b489b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/filter.json @@ -1,13 +1,15 @@ [ { "id": "c2b4", - "name": "soft lockup alarm will be not generated after delete the prio 0 filter of the chain", + "name": "Soft lockup alarm will be not generated after delete the prio 0 filter of the chain", "category": [ "filter", "chain" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: htb default 1", "$TC chain add dev $DUMMY", "$TC filter del dev $DUMMY chain 0 parent 1: prio 0" @@ -18,8 +20,7 @@ "matchPattern": "chain parent 1: chain 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY root handle 1: htb default 1", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY root handle 1: htb default 1" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake.json index 1134b72d281d..c4c5f7ba0e0f 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cake.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake bandwidth 1000", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth 1Kbit diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake autorate-ingress", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited autorate-ingress diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake rtt 200", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 200us raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake besteffort", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited besteffort triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake diffserv8", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv8 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake diffserv4", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv4 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake flowblind", "expExitCode": "0", @@ -179,8 +164,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 flowblind nonat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -194,7 +178,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake dsthost nat", "expExitCode": "0", @@ -202,8 +185,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 dsthost nat nowash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -217,7 +199,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake hosts wash", "expExitCode": "0", @@ -225,8 +206,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 hosts nonat wash no-ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -240,7 +220,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake flowblind no-split-gso", "expExitCode": "0", @@ -248,8 +227,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 flowblind nonat nowash no-ack-filter no-split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -263,7 +241,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake dual-srchost ack-filter", "expExitCode": "0", @@ -271,8 +248,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 dual-srchost nonat nowash ack-filter split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -286,7 +262,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake dual-dsthost ack-filter-aggressive", "expExitCode": "0", @@ -294,8 +269,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 dual-dsthost nonat nowash ack-filter-aggressive split-gso rtt 100ms raw overhead", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -309,7 +283,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake memlimit 10000 ptm", "expExitCode": "0", @@ -317,8 +290,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw ptm overhead 0 memlimit 10000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -332,7 +304,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake fwmark 8 atm", "expExitCode": "0", @@ -340,8 +311,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw atm overhead 0 fwmark 0x8", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -355,7 +325,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake overhead 128 mpu 256", "expExitCode": "0", @@ -363,8 +332,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 256", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -378,7 +346,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake conservative ingress", "expExitCode": "0", @@ -386,8 +353,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash ingress no-ack-filter split-gso rtt 100ms atm overhead 48", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -401,7 +367,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cake conservative ingress" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -410,7 +375,6 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash ingress no-ack-filter split-gso rtt 100ms atm overhead 48", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -424,7 +388,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cake overhead 128 mpu 256" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root cake mpu 128", @@ -433,8 +396,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 128", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -448,7 +410,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cake overhead 128 mpu 256" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root cake mpu 128", @@ -457,8 +418,7 @@ "matchPattern": "qdisc cake 1: root refcnt [0-9]+ bandwidth unlimited diffserv3 triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms noatm overhead 128 mpu 128", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -472,7 +432,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cake", "expExitCode": "0", @@ -480,8 +439,7 @@ "matchPattern": "class cake", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cbs.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cbs.json index a46bf5ff8277..33ea986176d9 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cbs.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/cbs.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 0 idleslope 0 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs hicredit 64", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 64 locredit 0 sendslope 0 idleslope 0 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs locredit 10", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 10 sendslope 0 idleslope 0 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs sendslope 888", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 888 idleslope 0 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs idleslope 666", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 0 idleslope 666 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs hicredit 10 locredit 75 sendslope 2 idleslope 666", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 10 locredit 75 sendslope 2 idleslope 666 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cbs idleslope 666" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root cbs sendslope 10", @@ -157,8 +144,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 10 idleslope 0 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -172,7 +158,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cbs idleslope 666" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root cbs idleslope 1", @@ -181,8 +166,7 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 0 idleslope 1 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -196,7 +180,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root cbs idleslope 666" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -205,7 +188,6 @@ "matchPattern": "qdisc cbs 1: root refcnt [0-9]+ hicredit 0 locredit 0 sendslope 0 idleslope 1 offload 0.*qdisc pfifo 0: parent 1: limit 1000p", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -219,7 +201,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root cbs", "expExitCode": "0", @@ -227,8 +208,7 @@ "matchPattern": "class cbs 1:[0-9]+ parent 1:", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/choke.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/choke.json index 31b7775d25fc..d46e5e2c9430 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/choke.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/choke.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 83p max 250p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 min 100", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 100p max 250p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 max 900", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min.*max 900p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 ecn", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 83p max 250p ecn", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 burst 100", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 83p max 250p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -134,7 +123,6 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 83p max 250p", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 min 100", @@ -157,8 +144,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 100p max 250p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -172,7 +158,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root choke limit 1000 bandwidth 10000 min 100", @@ -181,8 +166,7 @@ "matchPattern": "qdisc choke 1: root refcnt [0-9]+ limit 1000p min 100p max 250p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json index ea38099d48e5..e9469ee71e6f 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/codel.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel limit 1500", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1500p target 5ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel target 100ms", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 100ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel interval 20ms", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 20ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel ecn", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 100ms ecn", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root codel ce_threshold 20ms", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms ce_threshold 20ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root codel" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -157,7 +144,6 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 1000p target 5ms interval 100ms", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root codel" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root codel limit 5000", @@ -180,8 +165,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 5000p target 5ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -195,7 +179,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root codel" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root codel limit 100", @@ -204,8 +187,7 @@ "matchPattern": "qdisc codel 1: root refcnt [0-9]+ limit 100p target 5ms interval 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/drr.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/drr.json index 486a425b3c1c..7126ec3485cb 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/drr.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/drr.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root drr", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc drr 1: root refcnt [0-9]+", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root drr" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -42,7 +39,6 @@ "matchPattern": "qdisc drr 1: root refcnt [0-9]+", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root drr", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "class drr 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/etf.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/etf.json index 0046d44bcd93..2c73ee47bf58 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/etf.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/etf.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root etf clockid CLOCK_TAI", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc etf 1: root refcnt [0-9]+ clockid TAI delta 0 offload off deadline_mode off skip_sock_check off", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root etf delta 100 clockid CLOCK_TAI", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc etf 1: root refcnt [0-9]+ clockid TAI delta 100 offload off deadline_mode off skip_sock_check off", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root etf clockid CLOCK_TAI deadline_mode", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc etf 1: root refcnt [0-9]+ clockid TAI delta 0 offload off deadline_mode on skip_sock_check off", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root etf clockid CLOCK_TAI skip_sock_check", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc etf 1: root refcnt [0-9]+ clockid TAI delta 0 offload off deadline_mode off skip_sock_check on", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root etf clockid CLOCK_TAI" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -111,7 +102,6 @@ "matchPattern": "qdisc etf 1: root refcnt [0-9]+ clockid TAI delta 0 offload off deadline_mode off skip_sock_check off", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json index 180593010675..a5d94cdec605 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ets.json @@ -6,8 +6,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2", "expExitCode": "0", @@ -15,8 +17,7 @@ "matchPattern": "qdisc ets 1: root .* bands 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -26,8 +27,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 900 800 700", "expExitCode": "0", @@ -35,8 +38,7 @@ "matchPattern": "qdisc ets 1: root .*bands 4 quanta 1000 900 800 700", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -46,8 +48,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3", "expExitCode": "0", @@ -55,8 +59,7 @@ "matchPattern": "qdisc ets 1: root .*bands 3 strict 3", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -66,8 +69,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 quanta 1000 900 800 700", "expExitCode": "0", @@ -75,8 +80,7 @@ "matchPattern": "qdisc ets 1: root .*bands 4 quanta 1000 900 800 700 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -86,8 +90,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 3", "expExitCode": "0", @@ -95,8 +101,7 @@ "matchPattern": "qdisc ets 1: root .*bands 3 strict 3 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -106,8 +111,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3 quanta 1500 750", "expExitCode": "0", @@ -115,8 +122,7 @@ "matchPattern": "qdisc ets 1: root .*bands 5 strict 3 quanta 1500 750 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -126,8 +132,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 0 quanta 1500 750", "expExitCode": "0", @@ -135,8 +143,7 @@ "matchPattern": "qdisc ets 1: root .*bands 2 quanta 1500 750 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -146,8 +153,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 strict 3 quanta 1500 750", "expExitCode": "0", @@ -155,8 +164,7 @@ "matchPattern": "qdisc ets 1: root .*bands 5 .*strict 3 quanta 1500 750 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -166,8 +174,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 quanta 1000", "expExitCode": "0", @@ -175,8 +185,7 @@ "matchPattern": "qdisc ets 1: root .*bands 2 .*quanta 1000 [1-9][0-9]* priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -186,8 +195,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 1", "expExitCode": "0", @@ -195,8 +206,7 @@ "matchPattern": "qdisc ets 1: root .*bands 3 strict 1 quanta ([1-9][0-9]* ){2}priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -206,8 +216,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 3 strict 1 quanta 1000", "expExitCode": "0", @@ -215,8 +227,7 @@ "matchPattern": "qdisc ets 1: root .*bands 3 strict 1 quanta 1000 [1-9][0-9]* priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -226,8 +237,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 16", "expExitCode": "0", @@ -235,8 +248,7 @@ "matchPattern": "qdisc ets 1: root .* bands 16", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -246,8 +258,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 17", "expExitCode": "1", @@ -255,7 +269,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -265,8 +278,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 17", "expExitCode": "1", @@ -274,7 +289,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -284,8 +298,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", "expExitCode": "0", @@ -293,8 +309,7 @@ "matchPattern": "qdisc ets 1: root .* bands 16", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -304,8 +319,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17", "expExitCode": "2", @@ -313,7 +330,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -323,8 +339,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 8 quanta 1 2 3 4 5 6 7 8", "expExitCode": "0", @@ -332,8 +350,7 @@ "matchPattern": "qdisc ets 1: root .* bands 16", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -343,8 +360,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 9 quanta 1 2 3 4 5 6 7 8", "expExitCode": "2", @@ -352,7 +371,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -362,8 +380,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "expExitCode": "0", @@ -371,8 +391,7 @@ "matchPattern": "qdisc ets 1: root .*priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -382,8 +401,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000 4000 5000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "expExitCode": "0", @@ -391,8 +412,7 @@ "matchPattern": "qdisc ets 1: root .*quanta 1000 2000 3000 4000 5000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -402,8 +422,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "expExitCode": "0", @@ -411,8 +433,7 @@ "matchPattern": "qdisc ets 1: root .*bands 5 strict 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -422,8 +443,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 2 quanta 1000 2000 3000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "expExitCode": "0", @@ -431,8 +454,7 @@ "matchPattern": "qdisc ets 1: root .*strict 2 quanta 1000 2000 3000 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -442,8 +464,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", "expExitCode": "0", @@ -451,8 +475,7 @@ "matchPattern": "class ets 1:1 root quantum 4000", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -462,8 +485,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", "expExitCode": "0", @@ -471,8 +496,7 @@ "matchPattern": "class ets 1:2 root quantum 3000", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -482,8 +506,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 4000 3000 2000", "expExitCode": "0", @@ -491,8 +517,7 @@ "matchPattern": "class ets 1:3 root quantum 2000", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -502,8 +527,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 3", "expExitCode": "0", @@ -511,8 +538,7 @@ "matchPattern": "class ets 1:1 root $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -522,8 +548,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 quanta 1000 2000 3000", "expExitCode": "1", @@ -531,7 +559,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -541,8 +568,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 strict 3", "expExitCode": "1", @@ -550,7 +579,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -560,8 +588,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 2 quanta 1000 2000 3000", "expExitCode": "1", @@ -569,7 +599,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -579,8 +608,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 5 priomap 0 0 1 0 1 2 0 1 2 3 0 1 2 3 4 0 1 2", "expExitCode": "1", @@ -588,7 +619,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -598,8 +628,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 2 priomap 0 1 2", "expExitCode": "1", @@ -607,7 +639,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -617,8 +648,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 500 priomap 0 1 2", "expExitCode": "1", @@ -626,7 +659,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -636,8 +668,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 2 priomap 0 1 2", "expExitCode": "1", @@ -645,7 +679,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -655,8 +688,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets strict 1 quanta 1000 500 priomap 0 1 2 3", "expExitCode": "1", @@ -664,7 +699,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -674,8 +708,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 1 quanta 1000 500 priomap 0 1 2 3", "expExitCode": "0", @@ -683,7 +719,6 @@ "matchPattern": "qdisc ets", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -693,8 +728,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 strict 1 quanta 1000 500 priomap 0 1 2 3 4", "expExitCode": "1", @@ -702,7 +739,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -712,8 +748,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4 priomap 0 0 0 0", "expExitCode": "0", @@ -721,7 +759,6 @@ "matchPattern": "qdisc ets .*priomap 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -731,8 +768,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 4", "expExitCode": "0", @@ -740,7 +779,6 @@ "matchPattern": "qdisc ets .*priomap 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -750,8 +788,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 0", "expExitCode": "1", @@ -759,7 +799,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -769,8 +808,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets bands 17", "expExitCode": "1", @@ -778,7 +819,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -788,8 +828,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets", "expExitCode": "1", @@ -797,7 +839,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -807,8 +848,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 0 800 700", "expExitCode": "1", @@ -816,7 +859,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -826,8 +868,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta 0", "expExitCode": "1", @@ -835,7 +879,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -845,8 +888,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root ets quanta", "expExitCode": "255", @@ -854,7 +899,6 @@ "matchPattern": "qdisc ets", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -864,8 +908,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000" ], "cmdUnderTest": "$TC class change dev $DUMMY classid 1:1 ets quantum 1500", @@ -874,7 +920,6 @@ "matchPattern": "qdisc ets 1: root .*quanta 1500 2000 3000 priomap ", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -884,8 +929,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root ets quanta 1000 2000 3000" ], "cmdUnderTest": "$TC class change dev $DUMMY classid 1:1 ets", @@ -894,7 +941,6 @@ "matchPattern": "qdisc ets 1: root .*quanta 1000 2000 3000 priomap ", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -904,8 +950,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root ets strict 5" ], "cmdUnderTest": "$TC class change dev $DUMMY classid 1:2 ets quantum 1500", @@ -914,7 +962,6 @@ "matchPattern": "qdisc ets .*bands 5 .*strict 5", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -924,8 +971,10 @@ "qdisc", "ets" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root ets strict 5" ], "cmdUnderTest": "$TC class change dev $DUMMY classid 1:2 ets", @@ -934,7 +983,6 @@ "matchPattern": "qdisc ets .*bands 5 .*strict 5", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json index 5ecd93b4c473..ae3d286a32b2 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fifo.json @@ -2,13 +2,14 @@ { "id": "a519", "name": "Add bfifo qdisc with system default parameters on egress", - "__comment": "When omitted, queue size in bfifo is calculated as: txqueuelen * (MTU + LinkLayerHdrSize), where LinkLayerHdrSize=14 for Ethernet", "category": [ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo", "expExitCode": "0", @@ -16,20 +17,20 @@ "matchPattern": "qdisc bfifo 1: root.*limit [0-9]+b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root bfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root bfifo" ] }, { "id": "585c", "name": "Add pfifo qdisc with system default parameters on egress", - "__comment": "When omitted, queue size in pfifo is defaulted to the interface's txqueuelen value.", "category": [ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo", "expExitCode": "0", @@ -37,8 +38,7 @@ "matchPattern": "qdisc pfifo 1: root.*limit [0-9]+p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root pfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root pfifo" ] }, { @@ -48,8 +48,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle ffff: bfifo", "expExitCode": "0", @@ -57,8 +59,7 @@ "matchPattern": "qdisc bfifo ffff: root.*limit [0-9]+b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle ffff: root bfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle ffff: root bfifo" ] }, { @@ -68,8 +69,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo limit 3000b", "expExitCode": "0", @@ -77,8 +80,7 @@ "matchPattern": "qdisc bfifo 1: root.*limit 3000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root bfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root bfifo" ] }, { @@ -88,8 +90,11 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY txqueuelen 3000 type dummy || /bin/true" + "$IP link set dev $DUMMY txqueuelen 3000" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo limit 3000", "expExitCode": "0", @@ -97,8 +102,7 @@ "matchPattern": "qdisc pfifo 1: root.*limit 3000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root pfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root pfifo" ] }, { @@ -108,8 +112,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 10000: bfifo", "expExitCode": "255", @@ -117,7 +123,6 @@ "matchPattern": "qdisc bfifo 10000: root.*limit [0-9]+b", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -127,8 +132,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo foorbar", "expExitCode": "1", @@ -136,7 +143,6 @@ "matchPattern": "qdisc bfifo 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -146,8 +152,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo foorbar", "expExitCode": "1", @@ -155,7 +163,6 @@ "matchPattern": "qdisc pfifo 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -165,9 +172,11 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link del dev $DUMMY type dummy || /bin/true", - "$IP link add dev $DUMMY txqueuelen 1000 type dummy", + "$IP link set dev $DUMMY txqueuelen 1000", "$TC qdisc add dev $DUMMY handle 1: root bfifo" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root bfifo limit 3000b", @@ -176,8 +185,7 @@ "matchPattern": "qdisc bfifo 1: root.*limit 3000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root bfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root bfifo" ] }, { @@ -187,9 +195,11 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link del dev $DUMMY type dummy || /bin/true", - "$IP link add dev $DUMMY txqueuelen 1000 type dummy", + "$IP link set dev $DUMMY txqueuelen 1000", "$TC qdisc add dev $DUMMY handle 1: root pfifo" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root pfifo limit 30", @@ -198,8 +208,7 @@ "matchPattern": "qdisc pfifo 1: root.*limit 30p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root pfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root pfifo" ] }, { @@ -209,8 +218,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo limit foo-bar", "expExitCode": "1", @@ -218,7 +229,6 @@ "matchPattern": "qdisc bfifo 1: root.*limit foo-bar", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -228,8 +238,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root bfifo" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root bfifo", @@ -238,8 +250,7 @@ "matchPattern": "qdisc bfifo 1: root", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root bfifo", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root bfifo" ] }, { @@ -249,8 +260,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY root handle 1: bfifo", "expExitCode": "2", @@ -258,7 +271,6 @@ "matchPattern": "qdisc bfifo 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -268,8 +280,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 123^ bfifo limit 100b", "expExitCode": "255", @@ -277,7 +291,6 @@ "matchPattern": "qdisc bfifo 123 root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -287,8 +300,10 @@ "qdisc", "fifo" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: bfifo", "$TC qdisc del dev $DUMMY root handle 1: bfifo" ], @@ -298,7 +313,6 @@ "matchPattern": "qdisc bfifo 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq.json index 3593fb8f79ad..be293e7c6d18 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq limit 3000", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 3000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq flow_limit 300", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 300p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq quantum 9000", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p buckets.*orphan_mask 1023 quantum 9000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq initial_quantum 900000", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p buckets.*initial_quantum 900000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq initial_quantum 0x80000000", "expExitCode": "2", @@ -133,7 +122,6 @@ "matchPattern": "qdisc fq 1: root.*initial_quantum 2048Mb", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -147,7 +135,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq maxrate 100000", "expExitCode": "0", @@ -155,8 +142,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p buckets.*maxrate 100Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -170,7 +156,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq nopacing", "expExitCode": "0", @@ -178,8 +163,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*nopacing", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -193,7 +177,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq refill_delay 100ms", "expExitCode": "0", @@ -201,8 +184,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*refill_delay 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -216,7 +198,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq low_rate_threshold 10000", "expExitCode": "0", @@ -224,8 +205,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*low_rate_threshold 10Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -239,7 +219,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq orphan_mask 255", "expExitCode": "0", @@ -247,8 +226,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*orphan_mask 255", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -262,7 +240,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq timer_slack 100", "expExitCode": "0", @@ -270,8 +247,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*timer_slack 100ns", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -285,7 +261,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq ce_threshold 100", "expExitCode": "0", @@ -293,8 +268,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -308,7 +282,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq horizon 100", "expExitCode": "0", @@ -316,8 +289,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*horizon 100us", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -331,7 +303,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq horizon_cap", "expExitCode": "0", @@ -339,8 +310,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p flow_limit 100p.*horizon_cap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -354,7 +324,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -363,7 +332,6 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 10000p", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -377,7 +345,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root fq limit 5000", @@ -386,8 +353,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 5000p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -401,7 +367,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root fq limit 100", @@ -410,8 +375,7 @@ "matchPattern": "qdisc fq 1: root refcnt [0-9]+ limit 100p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json index a65266357a9a..9774b1e8801b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_codel.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel limit 1000", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 1000p flows 1024 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel memory_limit 100000", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms interval 100ms memory_limit 100000b ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel target 2000", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 2ms interval 100ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel interval 5000", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms interval 5ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel quantum 9000", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum 9000 target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel noecn", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms interval 100ms memory_limit 32Mb drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel ce_threshold 1024000", "expExitCode": "0", @@ -179,8 +164,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms ce_threshold 1.02s interval 100ms memory_limit 32Mb ecn drop_batch 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -194,7 +178,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel drop_batch 100", "expExitCode": "0", @@ -202,8 +185,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 10240p flows 1024 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 100", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -217,7 +199,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel limit 1000 flows 256 drop_batch 100", "expExitCode": "0", @@ -225,8 +206,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 1000p flows 256 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 100", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -240,7 +220,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq_codel limit 1000 flows 256 drop_batch 100" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root fq_codel noecn", @@ -249,8 +228,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 1000p flows 256 quantum.*target 5ms interval 100ms memory_limit 32Mb drop_batch 100", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -264,7 +242,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq_codel limit 1000 flows 256 drop_batch 100" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root fq_codel limit 2000", @@ -273,8 +250,7 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 2000p flows 256 quantum.*target 5ms interval 100ms memory_limit 32Mb ecn drop_batch 100", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -288,7 +264,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root fq_codel limit 1000 flows 256 drop_batch 100" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -297,7 +272,6 @@ "matchPattern": "qdisc fq_codel 1: root refcnt [0-9]+ limit 1000p flows 256 quantum.*target 5ms interval 100ms memory_limit 32Mb noecn drop_batch 100", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -311,7 +285,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_codel", "expExitCode": "0", @@ -319,8 +292,7 @@ "matchPattern": "class fq_codel 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json index 773c5027553d..d012d88d67fe 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json @@ -6,8 +6,10 @@ "qdisc", "fq_pie" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root fq_pie flows 65536", "expExitCode": "0", @@ -15,7 +17,6 @@ "matchPattern": "qdisc fq_pie 1: root refcnt 2 limit 10240p flows 65536", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/gred.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/gred.json index 013c8ee037a4..df07fe318de9 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/gred.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/gred.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 1", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 1 grio", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 1.*grio", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 1 limit 1000", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 1 limit 1000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 2 ecn", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 2.*ecn", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 2 harddrop", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 2.*harddrop", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 1" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root gred limit 60KB min 15K max 25K burst 64 avpkt 1500 bandwidth 10Mbit DP 1 probability 0.1", @@ -134,8 +123,7 @@ "matchPattern": "qdisc gred 1: root refcnt [0-9]+ vqs 10 default 1 limit.*vq 1 prio [0-9]+ limit 60Kb min 15Kb max 25Kb", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -149,7 +137,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root gred setup vqs 10 default 1", "expExitCode": "0", @@ -157,8 +144,7 @@ "matchPattern": "class gred 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json index af27b2c20e17..0ddb8e1b4369 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hfsc.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hfsc", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc hfsc 1: root refcnt [0-9]+", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hfsc default 11" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 hfsc sc rate 20000 ul rate 10000", @@ -42,8 +39,7 @@ "matchPattern": "class hfsc 1:1 parent 1: sc m1 0bit d 0us m2 20Kbit ul m1 0bit d 0us m2 10Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -57,7 +53,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hfsc default 11" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 hfsc sc umax 1540 dmax 5ms rate 10000 ul rate 10000", @@ -66,8 +61,7 @@ "matchPattern": "class hfsc 1:1 parent 1: sc m1 2464Kbit d 5ms m2 10Kbit ul m1 0bit d 0us m2 10Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -81,7 +75,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hfsc default 11" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 hfsc rt rate 20000 ls rate 10000", @@ -90,8 +83,7 @@ "matchPattern": "class hfsc 1:1 parent 1: rt m1 0bit d 0us m2 20Kbit ls m1 0bit d 0us m2 10Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -105,7 +97,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hfsc default 11" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 hfsc rt umax 1540 dmax 5ms rate 10000 ls rate 10000", @@ -114,8 +105,7 @@ "matchPattern": "class hfsc 1:1 parent 1: rt m1 2464Kbit d 5ms m2 10Kbit ls m1 0bit d 0us m2 10Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -129,7 +119,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hfsc default 11" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -138,7 +127,6 @@ "matchPattern": "qdisc hfsc 1: root refcnt [0-9]+", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -152,7 +140,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hfsc", "expExitCode": "0", @@ -160,8 +147,7 @@ "matchPattern": "class hfsc 1: root", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf.json index 949f6e5de902..dbef5474b26b 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/hhf.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf limit 1500", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+ limit 1500p.*hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf quantum 9000", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*quantum 9000b hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf reset_timeout 100ms", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048 reset_timeout 100ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf admit_bytes 100000", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048 reset_timeout 40ms admit_bytes 100000b evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf evict_timeout 0.5s", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 500ms non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf non_hh_weight 10", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+.*hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 10", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root hhf" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root hhf limit 1500", @@ -180,8 +165,7 @@ "matchPattern": "qdisc hhf 1: root refcnt [0-9]+ limit 1500p.*hh_limit 2048 reset_timeout 40ms admit_bytes 128Kb evict_timeout 1s non_hh_weight 2", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -195,7 +179,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root hhf", "expExitCode": "0", @@ -203,8 +186,7 @@ "matchPattern": "class hhf 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/htb.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/htb.json index 9529899482e0..cab745f9a83c 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/htb.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/htb.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root htb", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc htb 1: root refcnt [0-9]+ r2q 10 default 0 direct_packets_stat.*direct_qlen", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root htb default 10", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc htb 1: root refcnt [0-9]+ r2q 10 default 0x10 direct_packets_stat.* direct_qlen", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root htb r2q 5", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc htb 1: root refcnt [0-9]+ r2q 5 default 0 direct_packets_stat.*direct_qlen", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root htb direct_qlen 1024", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc htb 1: root refcnt [0-9]+ r2q 10 default 0 direct_packets_stat.*direct_qlen 1024", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20kbit burst 1000", @@ -111,8 +102,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 20Kbit burst 1000b cburst 1600b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -126,7 +116,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit mpu 64", @@ -135,8 +124,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 20Kbit burst 1600b cburst 1600b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -150,7 +138,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit prio 1", @@ -159,8 +146,7 @@ "matchPattern": "class htb 1:1 root prio 1 rate 20Kbit ceil 20Kbit burst 1600b cburst 1600b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -174,7 +160,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit ceil 10Kbit", @@ -183,8 +168,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 10Kbit burst 1600b cburst 1600b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -198,7 +182,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit cburst 2000", @@ -207,8 +190,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 20Kbit burst 1600b cburst 2000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -222,7 +204,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit mtu 2048", @@ -231,8 +212,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 20Kbit burst 2Kb cburst 2Kb", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -246,7 +226,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 20Kbit quantum 2048", @@ -255,8 +234,7 @@ "matchPattern": "class htb 1:1 root prio 0 rate 20Kbit ceil 20Kbit burst 1600b cburst 1600b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -270,7 +248,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root htb r2q 5" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -279,7 +256,6 @@ "matchPattern": "qdisc htb 1: root refcnt [0-9]+", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json index 11d33362408c..57bddc1212d8 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/ingress.json @@ -7,16 +7,17 @@ "ingress" ], "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], + "plugins": { + "requires": "nsPlugin" + }, "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress", "expExitCode": "0", "verifyCmd": "$TC qdisc show dev $DUMMY", "matchPattern": "qdisc ingress ffff:", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -26,8 +27,10 @@ "qdisc", "ingress" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress foorbar", "expExitCode": "1", @@ -35,7 +38,6 @@ "matchPattern": "qdisc ingress ffff:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -45,8 +47,10 @@ "qdisc", "ingress" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress", @@ -55,8 +59,7 @@ "matchPattern": "qdisc ingress ffff:", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] }, { @@ -66,8 +69,10 @@ "qdisc", "ingress" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY ingress", "expExitCode": "2", @@ -75,7 +80,6 @@ "matchPattern": "qdisc ingress ffff:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -85,8 +89,10 @@ "qdisc", "ingress" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY ingress", "$TC qdisc del dev $DUMMY ingress" ], @@ -96,7 +102,6 @@ "matchPattern": "qdisc ingress ffff:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -106,8 +111,10 @@ "qdisc", "ingress" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY ingress", "expExitCode": "0", @@ -115,8 +122,7 @@ "matchPattern": "class ingress", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY ingress", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY ingress" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json index 7e41f548f8e8..3c4444961488 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ limit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem limit 200", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ limit 200", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal corrupt 1%", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms 10ms corrupt 1%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal duplicate 1%", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms 10ms duplicate 1%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution pareto loss 1%", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms 10ms loss 1%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution paretonormal loss state 1", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms 10ms loss state p13 1% p31 99% p32 0% p23 100% p14 0%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem loss gemodel 1%", "expExitCode": "0", @@ -179,8 +164,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*loss gemodel p 1%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -194,7 +178,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms reorder 2% gap 100", "expExitCode": "0", @@ -202,8 +185,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*reorder 2%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -217,7 +199,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem rate 20000", "expExitCode": "0", @@ -225,8 +206,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*rate 20Kbit", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -240,7 +220,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem slot 10 200 packets 2000 bytes 9000", "expExitCode": "0", @@ -248,8 +227,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*slot 10ns 200ns packets 2000 bytes 9000", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -263,7 +241,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem slot distribution pareto 1ms 0.1ms", "expExitCode": "0", @@ -271,8 +248,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*slot distribution 1ms 100us", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -286,7 +262,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal loss 1%" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal loss 2%", @@ -295,8 +270,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*loss 2%", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -310,7 +284,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal loss 1%" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root netem delay 200ms 10ms", @@ -319,8 +292,7 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 200ms 10ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -334,7 +306,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root netem delay 100ms 10ms distribution normal" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -343,7 +314,6 @@ "matchPattern": "qdisc netem 1: root refcnt [0-9]+ .*delay 100ms 10ms", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -357,7 +327,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root netem", "expExitCode": "0", @@ -365,8 +334,7 @@ "matchPattern": "class netem 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json index ab53238f4c5a..30da27fe8806 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/pfifo_fast.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc pfifo_fast 1: root refcnt [0-9]+ bands 3 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "Sent.*bytes.*pkt \\(dropped.*overlimits.*requeues .*\\)", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 2: root pfifo_fast", @@ -65,8 +60,7 @@ "matchPattern": "qdisc pfifo_fast 2: root refcnt [0-9]+ bands 3 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 2: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 2: root" ] }, { @@ -80,7 +74,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -89,7 +82,6 @@ "matchPattern": "qdisc pfifo_fast 1: root refcnt [0-9]+ bands 3 priomap", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -103,7 +95,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root pfifo_fast" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 2: root", @@ -112,8 +103,7 @@ "matchPattern": "qdisc pfifo_fast 1: root refcnt [0-9]+ bands 3 priomap", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/plug.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/plug.json index 6454518af178..6ec7e0a01265 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/plug.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/plug.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root plug", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root plug block", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root plug release", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root plug release_indefinite", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root plug limit 100", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root plug" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -134,7 +123,6 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root plug" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root plug limit 1000", @@ -157,8 +144,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -172,7 +158,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root plug" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root plug limit 1000", @@ -181,8 +166,7 @@ "matchPattern": "qdisc plug 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json index 8186de2f0dcf..69abf041c799 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/prio.json @@ -6,8 +6,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio", "expExitCode": "0", @@ -15,8 +17,7 @@ "matchPattern": "qdisc prio 1: root", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root prio" ] }, { @@ -26,8 +27,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle ffff: prio", "expExitCode": "0", @@ -35,7 +38,6 @@ "matchPattern": "qdisc prio ffff: root", "matchCount": "1", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -45,8 +47,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 10000: prio", "expExitCode": "255", @@ -54,7 +58,6 @@ "matchPattern": "qdisc prio 10000: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -64,8 +67,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio foorbar", "expExitCode": "1", @@ -73,7 +78,6 @@ "matchPattern": "qdisc prio 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -83,8 +87,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0", "expExitCode": "0", @@ -92,8 +98,7 @@ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root prio" ] }, { @@ -103,8 +108,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0 1 1", "expExitCode": "1", @@ -112,7 +119,6 @@ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 3 3 0 0 1 2 3 0 0 0 0 0 1 1", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -122,8 +128,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 4 priomap 1 1 2 2 7 5 0 0 1 2 3 0 0 0 0 0", "expExitCode": "1", @@ -131,7 +139,6 @@ "matchPattern": "qdisc prio 1: root.*bands 4 priomap.*1 1 2 2 7 5 0 0 1 2 3 0 0 0 0 0", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -141,8 +148,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 1 priomap 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "expExitCode": "2", @@ -150,7 +159,6 @@ "matchPattern": "qdisc prio 1: root.*bands 1 priomap.*0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -160,8 +168,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio bands 1024 priomap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", "expExitCode": "2", @@ -169,7 +179,6 @@ "matchPattern": "qdisc prio 1: root.*bands 1024 priomap.*1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -179,8 +188,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root prio" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root prio bands 8 priomap 1 1 2 2 3 3 4 4 5 5 6 6 7 7 0 0", @@ -189,8 +200,7 @@ "matchPattern": "qdisc prio 1: root.*bands 8 priomap.*1 1 2 2 3 3 4 4 5 5 6 6 7 7 0 0", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root prio" ] }, { @@ -200,8 +210,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root prio" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio", @@ -210,8 +222,7 @@ "matchPattern": "qdisc prio 1: root", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root prio" ] }, { @@ -221,8 +232,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY root handle 1: prio", "expExitCode": "2", @@ -230,7 +243,6 @@ "matchPattern": "qdisc prio 1: root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -240,8 +252,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY root handle 123^ prio", "expExitCode": "255", @@ -249,7 +263,6 @@ "matchPattern": "qdisc prio 123 root", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -259,8 +272,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY root handle 1: prio", "$TC qdisc del dev $DUMMY root handle 1: prio" ], @@ -270,7 +285,6 @@ "matchPattern": "qdisc ingress ffff:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -280,8 +294,10 @@ "qdisc", "prio" ], + "plugins": { + "requires": "nsPlugin" + }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root prio", "expExitCode": "0", @@ -289,8 +305,7 @@ "matchPattern": "class prio 1:[0-9]+ parent 1:", "matchCount": "3", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root prio", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root prio" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/qfq.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/qfq.json index 976dffda4654..c95643929841 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/qfq.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/qfq.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root qfq", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc qfq 1: root refcnt [0-9]+", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 100", @@ -42,8 +39,7 @@ "matchPattern": "class qfq 1:1 root weight 100 maxpkt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -57,7 +53,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 9999", @@ -66,8 +61,7 @@ "matchPattern": "class qfq 1:1 root weight 9999 maxpkt", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -81,7 +75,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 qfq maxpkt 2000", @@ -90,8 +83,7 @@ "matchPattern": "class qfq 1:1 root weight 1 maxpkt 2000", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -105,7 +97,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 qfq maxpkt 128", @@ -114,8 +105,7 @@ "matchPattern": "class qfq 1:1 root weight 1 maxpkt 128", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -129,7 +119,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], "cmdUnderTest": "$TC class add dev $DUMMY parent 1: classid 1:1 qfq maxpkt 99999", @@ -138,8 +127,7 @@ "matchPattern": "class qfq 1:1 root weight 1 maxpkt 99999", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -153,7 +141,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq", "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 100" ], @@ -163,8 +150,7 @@ "matchPattern": "class qfq 1:[0-9]+ root weight [0-9]+00 maxpkt", "matchCount": "2", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -178,7 +164,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq", "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 100" ], @@ -188,7 +173,6 @@ "matchPattern": "qdisc qfq 1: root refcnt [0-9]+", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -202,7 +186,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root qfq", "expExitCode": "0", @@ -210,8 +193,7 @@ "matchPattern": "class qfq 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -225,7 +207,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$IP link set dev $DUMMY mtu 2147483647 || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], @@ -235,7 +216,6 @@ "matchPattern": "class qfq 1:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -249,7 +229,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$IP link set dev $DUMMY mtu 256 || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root qfq" ], @@ -259,7 +238,6 @@ "matchPattern": "class qfq 1:", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -277,7 +255,6 @@ ] }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$IP link set dev $DUMMY up || /bin/true", "$TC qdisc add dev $DUMMY handle 1: stab mtu 2048 tsize 512 mpu 0 overhead 999999999 linklayer ethernet root qfq", "$TC class add dev $DUMMY parent 1: classid 1:1 qfq weight 100", diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/red.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/red.json index 4b3e449857f2..eec73fda6c80 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/red.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/red.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red adaptive limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb adaptive $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red ecn limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb ecn $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red ecn adaptive limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb ecn adaptive $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red ecn harddrop limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb ecn harddrop $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red ecn nodrop limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb ecn nodrop $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red nodrop limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "2", @@ -156,7 +143,6 @@ "matchPattern": "qdisc red", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" ] }, { @@ -170,7 +156,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red ecn harddrop nodrop limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -178,8 +163,7 @@ "matchPattern": "qdisc red 1: root .* limit 1Mb min 100Kb max 300Kb ecn harddrop nodrop $", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -193,7 +177,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root red limit 1M avpkt 1500 min 100K max 300K", "expExitCode": "0", @@ -201,8 +184,7 @@ "matchPattern": "class red 1:[0-9]+ parent 1:", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json index e21c7f22c6d4..aa7914c441ea 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfb.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 60s", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb rehash 60", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 60ms db 60s", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb db 100", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb limit 100", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc sfb 1: root refcnt [0-9]+ rehash 600s db 60s limit 100p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb max 100", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*max 100p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb target 100", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*target 100p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb increment 0.1", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*increment 0.1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb decrement 0.1", "expExitCode": "0", @@ -179,8 +164,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*decrement 0.1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -194,7 +178,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb penalty_rate 4000", "expExitCode": "0", @@ -202,8 +185,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*penalty_rate 4000pps", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -217,7 +199,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb penalty_burst 64", "expExitCode": "0", @@ -225,8 +206,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 600s db 60s.*penalty_burst 64p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -240,7 +220,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root sfb penalty_burst 64" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root sfb rehash 100", @@ -249,8 +228,7 @@ "matchPattern": "qdisc sfb 1: root refcnt 2 rehash 100ms db 60s", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -264,7 +242,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfb", "expExitCode": "0", @@ -272,8 +249,7 @@ "matchPattern": "class sfb 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json index b6be718a174a..16d51936b385 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/sfq.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum.*depth 127 divisor 1024", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq limit 8", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 8p", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq perturb 10", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "depth 127 divisor 1024 perturb 10sec", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq quantum 9000", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum 9000b depth 127 divisor 1024", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq divisor 512", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum 1514b depth 127 divisor 512", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq flows 20", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc sfq 1: root refcnt", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq depth 64", "expExitCode": "0", @@ -156,8 +143,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum 1514b depth 64 divisor 1024", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -171,7 +157,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq headdrop", "expExitCode": "0", @@ -179,8 +164,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum 1514b depth 127 headdrop divisor 1024", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -194,7 +178,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq redflowlimit 100000 min 8000 max 60000 probability 0.20 ecn headdrop", "expExitCode": "0", @@ -202,8 +185,7 @@ "matchPattern": "qdisc sfq 1: root refcnt [0-9]+ limit 127p quantum 1514b depth 127 headdrop divisor 1024 ewma 6 min 8000b max 60000b probability 0.2 ecn", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -217,7 +199,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root sfq", "expExitCode": "0", @@ -225,8 +206,7 @@ "matchPattern": "class sfq 1:", "matchCount": "0", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/skbprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/skbprio.json index 5766045c9d33..076d1d69a3a4 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/skbprio.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/skbprio.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root skbprio", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc skbprio 1: root refcnt [0-9]+ limit 64", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root skbprio limit 1", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc skbprio 1: root refcnt [0-9]+ limit 1", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root skbprio" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root skbprio limit 32", @@ -65,8 +60,7 @@ "matchPattern": "qdisc skbprio 1: root refcnt [0-9]+ limit 32", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -80,7 +74,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root skbprio", "expExitCode": "0", @@ -88,8 +81,7 @@ "matchPattern": "class skbprio 1:", "matchCount": "64", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/tbf.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/tbf.json index a4b3dfe51ff5..547a44910041 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/tbf.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/tbf.json @@ -10,7 +10,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 10000", "expExitCode": "0", @@ -18,8 +17,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 10Kbit burst 1500b limit 1000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -33,7 +31,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 mtu 2048", "expExitCode": "0", @@ -41,8 +38,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1500b limit 1000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -56,7 +52,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 mtu 1510 peakrate 30000", "expExitCode": "0", @@ -64,8 +59,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1500b peakrate 30Kbit minburst.*limit 1000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -79,7 +73,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf burst 1500 rate 20000 latency 100ms", "expExitCode": "0", @@ -87,8 +80,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1500b lat 100ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -102,7 +94,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 overhead 300", "expExitCode": "0", @@ -110,8 +101,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1800b limit 1000b overhead 300", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -125,7 +115,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 linklayer atm", "expExitCode": "0", @@ -133,8 +122,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1696b limit 1000b linklayer atm", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -148,7 +136,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 linklayer atm" ], "cmdUnderTest": "$TC qdisc replace dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 20000 linklayer ethernet", @@ -157,8 +144,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1500b limit 1000b", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -172,7 +158,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", "$TC qdisc add dev $DUMMY handle 1: root tbf burst 1500 rate 20000 latency 10ms" ], "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: root tbf burst 1500 rate 20000 latency 200ms", @@ -181,8 +166,7 @@ "matchPattern": "qdisc tbf 1: root refcnt [0-9]+ rate 20Kbit burst 1500b lat 200ms", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] }, { @@ -196,7 +180,6 @@ "requires": "nsPlugin" }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root tbf limit 1000 burst 1500 rate 10000", "expExitCode": "0", @@ -204,8 +187,7 @@ "matchPattern": "class tbf.*parent 1:", "matchCount": "1", "teardown": [ - "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$TC qdisc del dev $DUMMY handle 1: root" ] } ] diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json index 0082be0e93ac..e5cc31f265f8 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/teql.json @@ -6,11 +6,8 @@ "qdisc", "teql" ], - "plugins": { - "requires": "nsPlugin" - }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" + "$IP link add dev $DUMMY type dummy" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root teql0", "expExitCode": "0", @@ -19,7 +16,7 @@ "matchCount": "1", "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$IP link del dev $DUMMY" ] }, { @@ -29,13 +26,10 @@ "qdisc", "teql" ], - "plugins": { - "requires": "nsPlugin" - }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", - "echo \"1 1 4\" > /sys/bus/netdevsim/new_device", - "$TC qdisc add dev $ETH root handle 1: teql0" + "$IP link add dev $DUMMY type dummy", + "$IP link add dev $ETH type dummy", + "$TC qdisc add dev $ETH handle 1: root teql0" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root teql0", "expExitCode": "0", @@ -44,8 +38,8 @@ "matchCount": "1", "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root", - "echo \"1\" > /sys/bus/netdevsim/del_device", - "$IP link del dev $DUMMY type dummy" + "$IP link del dev $DUMMY", + "$IP link del dev $ETH" ] }, { @@ -55,11 +49,8 @@ "qdisc", "teql" ], - "plugins": { - "requires": "nsPlugin" - }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true", + "$IP link add dev $DUMMY type dummy", "$TC qdisc add dev $DUMMY handle 1: root teql0" ], "cmdUnderTest": "$TC qdisc del dev $DUMMY handle 1: root", @@ -68,7 +59,7 @@ "matchPattern": "qdisc teql0 1: root refcnt", "matchCount": "0", "teardown": [ - "$IP link del dev $DUMMY type dummy" + "$IP link del dev $DUMMY" ] }, { @@ -78,11 +69,8 @@ "qdisc", "teql" ], - "plugins": { - "requires": "nsPlugin" - }, "setup": [ - "$IP link add dev $DUMMY type dummy || /bin/true" + "$IP link add dev $DUMMY type dummy" ], "cmdUnderTest": "$TC qdisc add dev $DUMMY handle 1: root teql0", "expExitCode": "0", @@ -91,7 +79,7 @@ "matchCount": "1", "teardown": [ "$TC qdisc del dev $DUMMY handle 1: root", - "$IP link del dev $DUMMY type dummy" + "$IP link del dev $DUMMY" ] } ] diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py index b98256f38447..a6718192aff3 100755 --- a/tools/testing/selftests/tc-testing/tdc.py +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -16,6 +16,8 @@ import json import subprocess import time import traceback +import random +from multiprocessing import Pool from collections import OrderedDict from string import Template @@ -38,12 +40,11 @@ class PluginMgrTestFail(Exception): class PluginMgr: def __init__(self, argparser): super().__init__() - self.plugins = {} + self.plugins = set() self.plugin_instances = [] self.failed_plugins = {} self.argparser = argparser - # TODO, put plugins in order plugindir = os.getenv('TDC_PLUGIN_DIR', './plugins') for dirpath, dirnames, filenames in os.walk(plugindir): for fn in filenames: @@ -53,32 +54,43 @@ class PluginMgr: not fn.startswith('.#')): mn = fn[0:-3] foo = importlib.import_module('plugins.' + mn) - self.plugins[mn] = foo - self.plugin_instances.append(foo.SubPlugin()) + self.plugins.add(mn) + self.plugin_instances[mn] = foo.SubPlugin() def load_plugin(self, pgdir, pgname): pgname = pgname[0:-3] + self.plugins.add(pgname) + foo = importlib.import_module('{}.{}'.format(pgdir, pgname)) - self.plugins[pgname] = foo - self.plugin_instances.append(foo.SubPlugin()) - self.plugin_instances[-1].check_args(self.args, None) + + # nsPlugin must always be the first one + if pgname == "nsPlugin": + self.plugin_instances.insert(0, (pgname, foo.SubPlugin())) + self.plugin_instances[0][1].check_args(self.args, None) + else: + self.plugin_instances.append((pgname, foo.SubPlugin())) + self.plugin_instances[-1][1].check_args(self.args, None) def get_required_plugins(self, testlist): ''' Get all required plugins from the list of test cases and return all unique items. ''' - reqs = [] + reqs = set() for t in testlist: try: if 'requires' in t['plugins']: if isinstance(t['plugins']['requires'], list): - reqs.extend(t['plugins']['requires']) + reqs.update(set(t['plugins']['requires'])) else: - reqs.append(t['plugins']['requires']) + reqs.add(t['plugins']['requires']) + t['plugins'] = t['plugins']['requires'] + else: + t['plugins'] = [] except KeyError: + t['plugins'] = [] continue - reqs = get_unique_item(reqs) + return reqs def load_required_plugins(self, reqs, parser, args, remaining): @@ -115,15 +127,17 @@ class PluginMgr: return args def call_pre_suite(self, testcount, testidlist): - for pgn_inst in self.plugin_instances: + for (_, pgn_inst) in self.plugin_instances: pgn_inst.pre_suite(testcount, testidlist) def call_post_suite(self, index): - for pgn_inst in reversed(self.plugin_instances): + for (_, pgn_inst) in reversed(self.plugin_instances): pgn_inst.post_suite(index) def call_pre_case(self, caseinfo, *, test_skip=False): - for pgn_inst in self.plugin_instances: + for (pgn, pgn_inst) in self.plugin_instances: + if pgn not in caseinfo['plugins']: + continue try: pgn_inst.pre_case(caseinfo, test_skip) except Exception as ee: @@ -133,29 +147,37 @@ class PluginMgr: print('testid is {}'.format(caseinfo['id'])) raise - def call_post_case(self): - for pgn_inst in reversed(self.plugin_instances): + def call_post_case(self, caseinfo): + for (pgn, pgn_inst) in reversed(self.plugin_instances): + if pgn not in caseinfo['plugins']: + continue pgn_inst.post_case() - def call_pre_execute(self): - for pgn_inst in self.plugin_instances: + def call_pre_execute(self, caseinfo): + for (pgn, pgn_inst) in self.plugin_instances: + if pgn not in caseinfo['plugins']: + continue pgn_inst.pre_execute() - def call_post_execute(self): - for pgn_inst in reversed(self.plugin_instances): + def call_post_execute(self, caseinfo): + for (pgn, pgn_inst) in reversed(self.plugin_instances): + if pgn not in caseinfo['plugins']: + continue pgn_inst.post_execute() def call_add_args(self, parser): - for pgn_inst in self.plugin_instances: + for (pgn, pgn_inst) in self.plugin_instances: parser = pgn_inst.add_args(parser) return parser def call_check_args(self, args, remaining): - for pgn_inst in self.plugin_instances: + for (pgn, pgn_inst) in self.plugin_instances: pgn_inst.check_args(args, remaining) - def call_adjust_command(self, stage, command): - for pgn_inst in self.plugin_instances: + def call_adjust_command(self, caseinfo, stage, command): + for (pgn, pgn_inst) in self.plugin_instances: + if pgn not in caseinfo['plugins']: + continue command = pgn_inst.adjust_command(stage, command) return command @@ -177,7 +199,7 @@ def replace_keywords(cmd): return subcmd -def exec_cmd(args, pm, stage, command): +def exec_cmd(caseinfo, args, pm, stage, command): """ Perform any required modifications on an executable command, then run it in a subprocess and return the results. @@ -187,9 +209,10 @@ def exec_cmd(args, pm, stage, command): if '$' in command: command = replace_keywords(command) - command = pm.call_adjust_command(stage, command) + command = pm.call_adjust_command(caseinfo, stage, command) if args.verbose > 0: print('command "{}"'.format(command)) + proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, @@ -211,7 +234,7 @@ def exec_cmd(args, pm, stage, command): return proc, foutput -def prepare_env(args, pm, stage, prefix, cmdlist, output = None): +def prepare_env(caseinfo, args, pm, stage, prefix, cmdlist, output = None): """ Execute the setup/teardown commands for a test case. Optionally terminate test execution if the command fails. @@ -229,7 +252,7 @@ def prepare_env(args, pm, stage, prefix, cmdlist, output = None): if not cmd: continue - (proc, foutput) = exec_cmd(args, pm, stage, cmd) + (proc, foutput) = exec_cmd(caseinfo, args, pm, stage, cmd) if proc and (proc.returncode not in exit_codes): print('', file=sys.stderr) @@ -352,6 +375,10 @@ def find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None): def run_one_test(pm, args, index, tidx): global NAMES + ns = NAMES['NS'] + dev0 = NAMES['DEV0'] + dev1 = NAMES['DEV1'] + dummy = NAMES['DUMMY'] result = True tresult = "" tap = "" @@ -366,38 +393,42 @@ def run_one_test(pm, args, index, tidx): res.set_result(ResultState.skip) res.set_errormsg('Test case designated as skipped.') pm.call_pre_case(tidx, test_skip=True) - pm.call_post_execute() + pm.call_post_execute(tidx) return res if 'dependsOn' in tidx: if (args.verbose > 0): print('probe command for test skip') - (p, procout) = exec_cmd(args, pm, 'execute', tidx['dependsOn']) + (p, procout) = exec_cmd(tidx, args, pm, 'execute', tidx['dependsOn']) if p: if (p.returncode != 0): res = TestResult(tidx['id'], tidx['name']) res.set_result(ResultState.skip) res.set_errormsg('probe command: test skipped.') pm.call_pre_case(tidx, test_skip=True) - pm.call_post_execute() + pm.call_post_execute(tidx) return res # populate NAMES with TESTID for this test NAMES['TESTID'] = tidx['id'] + NAMES['NS'] = '{}-{}'.format(NAMES['NS'], tidx['random']) + NAMES['DEV0'] = '{}id{}'.format(NAMES['DEV0'], tidx['id']) + NAMES['DEV1'] = '{}id{}'.format(NAMES['DEV1'], tidx['id']) + NAMES['DUMMY'] = '{}id{}'.format(NAMES['DUMMY'], tidx['id']) pm.call_pre_case(tidx) - prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"]) + prepare_env(tidx, args, pm, 'setup', "-----> prepare stage", tidx["setup"]) if (args.verbose > 0): print('-----> execute stage') - pm.call_pre_execute() - (p, procout) = exec_cmd(args, pm, 'execute', tidx["cmdUnderTest"]) + pm.call_pre_execute(tidx) + (p, procout) = exec_cmd(tidx, args, pm, 'execute', tidx["cmdUnderTest"]) if p: exit_code = p.returncode else: exit_code = None - pm.call_post_execute() + pm.call_post_execute(tidx) if (exit_code is None or exit_code != int(tidx["expExitCode"])): print("exit: {!r}".format(exit_code)) @@ -409,7 +440,7 @@ def run_one_test(pm, args, index, tidx): else: if args.verbose > 0: print('-----> verify stage') - (p, procout) = exec_cmd(args, pm, 'verify', tidx["verifyCmd"]) + (p, procout) = exec_cmd(tidx, args, pm, 'verify', tidx["verifyCmd"]) if procout: if 'matchJSON' in tidx: verify_by_json(procout, res, tidx, args, pm) @@ -431,15 +462,49 @@ def run_one_test(pm, args, index, tidx): else: res.set_result(ResultState.success) - prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) - pm.call_post_case() + prepare_env(tidx, args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) + pm.call_post_case(tidx) index += 1 # remove TESTID from NAMES del(NAMES['TESTID']) + + # Restore names + NAMES['NS'] = ns + NAMES['DEV0'] = dev0 + NAMES['DEV1'] = dev1 + NAMES['DUMMY'] = dummy + return res +def prepare_run(pm, args, testlist): + tcount = len(testlist) + emergency_exit = False + emergency_exit_message = '' + + try: + pm.call_pre_suite(tcount, testlist) + except Exception as ee: + ex_type, ex, ex_tb = sys.exc_info() + print('Exception {} {} (caught in pre_suite).'. + format(ex_type, ex)) + traceback.print_tb(ex_tb) + emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex) + emergency_exit = True + + if emergency_exit: + pm.call_post_suite(1) + return emergency_exit_message + + if args.verbose: + print('give test rig 2 seconds to stabilize') + + time.sleep(2) + +def purge_run(pm, index): + pm.call_post_suite(index) + def test_runner(pm, args, filtered_tests): """ Driver function for the unit tests. @@ -455,28 +520,9 @@ def test_runner(pm, args, filtered_tests): tap = '' badtest = None stage = None - emergency_exit = False - emergency_exit_message = '' tsr = TestSuiteReport() - try: - pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist]) - except Exception as ee: - ex_type, ex, ex_tb = sys.exc_info() - print('Exception {} {} (caught in pre_suite).'. - format(ex_type, ex)) - traceback.print_tb(ex_tb) - emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex) - emergency_exit = True - stage = 'pre-SUITE' - - if emergency_exit: - pm.call_post_suite(index) - return emergency_exit_message - if args.verbose > 1: - print('give test rig 2 seconds to stabilize') - time.sleep(2) for tidx in testlist: if "flower" in tidx["category"] and args.device == None: errmsg = "Tests using the DEV2 variable must define the name of a " @@ -539,7 +585,68 @@ def test_runner(pm, args, filtered_tests): if input(sys.stdin): print('got something on stdin') - pm.call_post_suite(index) + return (index, tsr) + +def mp_bins(alltests): + serial = [] + parallel = [] + + for test in alltests: + if 'nsPlugin' not in test['plugins']: + serial.append(test) + else: + # We can only create one netdevsim device at a time + if 'netdevsim/new_device' in str(test['setup']): + serial.append(test) + else: + parallel.append(test) + + return (serial, parallel) + +def __mp_runner(tests): + (_, tsr) = test_runner(mp_pm, mp_args, tests) + return tsr._testsuite + +def test_runner_mp(pm, args, alltests): + prepare_run(pm, args, alltests) + + (serial, parallel) = mp_bins(alltests) + + batches = [parallel[n : n + 32] for n in range(0, len(parallel), 32)] + batches.insert(0, serial) + + print("Executing {} tests in parallel and {} in serial".format(len(parallel), len(serial))) + print("Using {} batches".format(len(batches))) + + # We can't pickle these objects so workaround them + global mp_pm + mp_pm = pm + + global mp_args + mp_args = args + + with Pool(args.mp) as p: + pres = p.map(__mp_runner, batches) + + tsr = TestSuiteReport() + for trs in pres: + for res in trs: + tsr.add_resultdata(res) + + # Passing an index is not useful in MP + purge_run(pm, None) + + return tsr + +def test_runner_serial(pm, args, alltests): + prepare_run(pm, args, alltests) + + if args.verbose: + print("Executing {} tests in serial".format(len(alltests))) + + (index, tsr) = test_runner(pm, args, alltests) + + purge_run(pm, index) return tsr @@ -568,12 +675,15 @@ def load_from_file(filename): k['filename'] = filename return testlist +def identity(string): + return string def args_parse(): """ Create the argument parser. """ parser = argparse.ArgumentParser(description='Linux TC unit tests') + parser.register('type', None, identity) return parser @@ -631,6 +741,9 @@ def set_args(parser): parser.add_argument( '-P', '--pause', action='store_true', help='Pause execution just before post-suite stage') + parser.add_argument( + '-J', '--multiprocess', type=int, default=1, dest='mp', + help='Run tests in parallel whenever possible') return parser @@ -661,7 +774,6 @@ def get_id_list(alltests): """ return [x["id"] for x in alltests] - def check_case_id(alltests): """ Check for duplicate test case IDs. @@ -683,7 +795,6 @@ def generate_case_ids(alltests): If a test case has a blank ID field, generate a random hex ID for it and then write the test cases back to disk. """ - import random for c in alltests: if (c["id"] == ""): while True: @@ -742,6 +853,9 @@ def filter_tests_by_category(args, testlist): return answer +def set_random(alltests): + for tidx in alltests: + tidx['random'] = random.getrandbits(32) def get_test_cases(args): """ @@ -840,6 +954,8 @@ def set_operation_mode(pm, parser, args, remaining): list_test_cases(alltests) exit(0) + set_random(alltests) + exit_code = 0 # KSFT_PASS if len(alltests): req_plugins = pm.get_required_plugins(alltests) @@ -848,7 +964,12 @@ def set_operation_mode(pm, parser, args, remaining): except PluginDependencyException as pde: print('The following plugins were not found:') print('{}'.format(pde.missing_pg)) - catresults = test_runner(pm, args, alltests) + + if args.mp > 1: + catresults = test_runner_mp(pm, args, alltests) + else: + catresults = test_runner_serial(pm, args, alltests) + if catresults.count_failures() != 0: exit_code = 1 # KSFT_FAIL if args.format == 'none': @@ -883,6 +1004,13 @@ def main(): Start of execution; set up argument parser and get the arguments, and start operations. """ + import resource + + if sys.version_info.major < 3 or sys.version_info.minor < 8: + sys.exit("tdc requires at least python 3.8") + + resource.setrlimit(resource.RLIMIT_NOFILE, (1048576, 1048576)) + parser = args_parse() parser = set_args(parser) pm = PluginMgr(parser) diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c index 5125c42efe65..8202f1327c39 100644 --- a/tools/testing/selftests/user_events/abi_test.c +++ b/tools/testing/selftests/user_events/abi_test.c @@ -19,6 +19,7 @@ #include <asm/unistd.h> #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable"; @@ -90,14 +91,18 @@ static int reg_disable(long *enable, int bit) FIXTURE(user) { long check; + bool umount; }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return, self->umount); + change_event(false); self->check = 0; } FIXTURE_TEARDOWN(user) { + USER_EVENT_FIXTURE_TEARDOWN(self->umount); } TEST_F(user, enablement) { diff --git a/tools/testing/selftests/user_events/config b/tools/testing/selftests/user_events/config new file mode 100644 index 000000000000..64f7a9a90cec --- /dev/null +++ b/tools/testing/selftests/user_events/config @@ -0,0 +1 @@ +CONFIG_USER_EVENTS=y diff --git a/tools/testing/selftests/user_events/dyn_test.c b/tools/testing/selftests/user_events/dyn_test.c index 91a4444ad42b..a85980190bea 100644 --- a/tools/testing/selftests/user_events/dyn_test.c +++ b/tools/testing/selftests/user_events/dyn_test.c @@ -15,6 +15,7 @@ #include <unistd.h> #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *abi_file = "/sys/kernel/tracing/user_events_data"; const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable"; @@ -143,12 +144,16 @@ do { \ FIXTURE(user) { int check; + bool umount; }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return, self->umount); } FIXTURE_TEARDOWN(user) { + USER_EVENT_FIXTURE_TEARDOWN(self->umount); + wait_for_delete(); } diff --git a/tools/testing/selftests/user_events/ftrace_test.c b/tools/testing/selftests/user_events/ftrace_test.c index 5beb0aef1d81..dcd7509fe2e0 100644 --- a/tools/testing/selftests/user_events/ftrace_test.c +++ b/tools/testing/selftests/user_events/ftrace_test.c @@ -16,6 +16,7 @@ #include <unistd.h> #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *status_file = "/sys/kernel/tracing/user_events_status"; @@ -203,9 +204,12 @@ FIXTURE(user) { int data_fd; int enable_fd; int check; + bool umount; }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return, self->umount); + self->status_fd = open(status_file, O_RDONLY); ASSERT_NE(-1, self->status_fd); @@ -216,6 +220,8 @@ FIXTURE_SETUP(user) { } FIXTURE_TEARDOWN(user) { + USER_EVENT_FIXTURE_TEARDOWN(self->umount); + close(self->status_fd); close(self->data_fd); diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c index 8b09be566fa2..5288e768b207 100644 --- a/tools/testing/selftests/user_events/perf_test.c +++ b/tools/testing/selftests/user_events/perf_test.c @@ -17,6 +17,7 @@ #include <asm/unistd.h> #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id"; @@ -110,14 +111,19 @@ static int clear(int *check) FIXTURE(user) { int data_fd; int check; + bool umount; }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return, self->umount); + self->data_fd = open(data_file, O_RDWR); ASSERT_NE(-1, self->data_fd); } FIXTURE_TEARDOWN(user) { + USER_EVENT_FIXTURE_TEARDOWN(self->umount); + close(self->data_fd); if (clear(&self->check) != 0) diff --git a/tools/testing/selftests/user_events/user_events_selftests.h b/tools/testing/selftests/user_events/user_events_selftests.h new file mode 100644 index 000000000000..e1c3c063c031 --- /dev/null +++ b/tools/testing/selftests/user_events/user_events_selftests.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _USER_EVENTS_SELFTESTS_H +#define _USER_EVENTS_SELFTESTS_H + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <unistd.h> +#include <errno.h> + +#include "../kselftest.h" + +static inline void tracefs_unmount(void) +{ + umount("/sys/kernel/tracing"); +} + +static inline bool tracefs_enabled(char **message, bool *fail, bool *umount) +{ + struct stat buf; + int ret; + + *message = ""; + *fail = false; + *umount = false; + + /* Ensure tracefs is installed */ + ret = stat("/sys/kernel/tracing", &buf); + + if (ret == -1) { + *message = "Tracefs is not installed"; + return false; + } + + /* Ensure mounted tracefs */ + ret = stat("/sys/kernel/tracing/README", &buf); + + if (ret == -1 && errno == ENOENT) { + if (mount(NULL, "/sys/kernel/tracing", "tracefs", 0, NULL) != 0) { + *message = "Cannot mount tracefs"; + *fail = true; + return false; + } + + *umount = true; + + ret = stat("/sys/kernel/tracing/README", &buf); + } + + if (ret == -1) { + *message = "Cannot access tracefs"; + *fail = true; + return false; + } + + return true; +} + +static inline bool user_events_enabled(char **message, bool *fail, bool *umount) +{ + struct stat buf; + int ret; + + *message = ""; + *fail = false; + *umount = false; + + if (getuid() != 0) { + *message = "Must be run as root"; + *fail = true; + return false; + } + + if (!tracefs_enabled(message, fail, umount)) + return false; + + /* Ensure user_events is installed */ + ret = stat("/sys/kernel/tracing/user_events_data", &buf); + + if (ret == -1) { + switch (errno) { + case ENOENT: + *message = "user_events is not installed"; + return false; + + default: + *message = "Cannot access user_events_data"; + *fail = true; + return false; + } + } + + return true; +} + +#define USER_EVENT_FIXTURE_SETUP(statement, umount) do { \ + char *message; \ + bool fail; \ + if (!user_events_enabled(&message, &fail, &(umount))) { \ + if (fail) { \ + TH_LOG("Setup failed due to: %s", message); \ + ASSERT_FALSE(fail); \ + } \ + SKIP(statement, "Skipping due to: %s", message); \ + } \ +} while (0) + +#define USER_EVENT_FIXTURE_TEARDOWN(umount) do { \ + if ((umount)) \ + tracefs_unmount(); \ +} while (0) + +#endif /* _USER_EVENTS_SELFTESTS_H */ diff --git a/tools/testing/selftests/wireguard/qemu/kernel.config b/tools/testing/selftests/wireguard/qemu/kernel.config index 6327c9c400e0..507555714b1d 100644 --- a/tools/testing/selftests/wireguard/qemu/kernel.config +++ b/tools/testing/selftests/wireguard/qemu/kernel.config @@ -41,7 +41,6 @@ CONFIG_KALLSYMS=y CONFIG_BUG=y CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y CONFIG_JUMP_LABEL=y -CONFIG_EMBEDDED=n CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_SHMEM=y diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 598135d3162b..7e8c937627dd 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -18,7 +18,7 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \ - corrupt_xstate_header amx lam + corrupt_xstate_header amx lam test_shadow_stack # Some selftests require 32bit support enabled also on 64bit systems TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall diff --git a/tools/testing/selftests/x86/test_shadow_stack.c b/tools/testing/selftests/x86/test_shadow_stack.c new file mode 100644 index 000000000000..757e6527f67e --- /dev/null +++ b/tools/testing/selftests/x86/test_shadow_stack.c @@ -0,0 +1,884 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program test's basic kernel shadow stack support. It enables shadow + * stack manual via the arch_prctl(), instead of relying on glibc. It's + * Makefile doesn't compile with shadow stack support, so it doesn't rely on + * any particular glibc. As a result it can't do any operations that require + * special glibc shadow stack support (longjmp(), swapcontext(), etc). Just + * stick to the basics and hope the compiler doesn't do anything strange. + */ + +#define _GNU_SOURCE + +#include <sys/syscall.h> +#include <asm/mman.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdbool.h> +#include <x86intrin.h> +#include <asm/prctl.h> +#include <sys/prctl.h> +#include <stdint.h> +#include <signal.h> +#include <pthread.h> +#include <sys/ioctl.h> +#include <linux/userfaultfd.h> +#include <setjmp.h> +#include <sys/ptrace.h> +#include <sys/signal.h> +#include <linux/elf.h> + +/* + * Define the ABI defines if needed, so people can run the tests + * without building the headers. + */ +#ifndef __NR_map_shadow_stack +#define __NR_map_shadow_stack 453 + +#define SHADOW_STACK_SET_TOKEN (1ULL << 0) + +#define ARCH_SHSTK_ENABLE 0x5001 +#define ARCH_SHSTK_DISABLE 0x5002 +#define ARCH_SHSTK_LOCK 0x5003 +#define ARCH_SHSTK_UNLOCK 0x5004 +#define ARCH_SHSTK_STATUS 0x5005 + +#define ARCH_SHSTK_SHSTK (1ULL << 0) +#define ARCH_SHSTK_WRSS (1ULL << 1) + +#define NT_X86_SHSTK 0x204 +#endif + +#define SS_SIZE 0x200000 +#define PAGE_SIZE 0x1000 + +#if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5) +int main(int argc, char *argv[]) +{ + printf("[SKIP]\tCompiler does not support CET.\n"); + return 0; +} +#else +void write_shstk(unsigned long *addr, unsigned long val) +{ + asm volatile("wrssq %[val], (%[addr])\n" + : "=m" (addr) + : [addr] "r" (addr), [val] "r" (val)); +} + +static inline unsigned long __attribute__((always_inline)) get_ssp(void) +{ + unsigned long ret = 0; + + asm volatile("xor %0, %0; rdsspq %0" : "=r" (ret)); + return ret; +} + +/* + * For use in inline enablement of shadow stack. + * + * The program can't return from the point where shadow stack gets enabled + * because there will be no address on the shadow stack. So it can't use + * syscall() for enablement, since it is a function. + * + * Based on code from nolibc.h. Keep a copy here because this can't pull in all + * of nolibc.h. + */ +#define ARCH_PRCTL(arg1, arg2) \ +({ \ + long _ret; \ + register long _num asm("eax") = __NR_arch_prctl; \ + register long _arg1 asm("rdi") = (long)(arg1); \ + register long _arg2 asm("rsi") = (long)(arg2); \ + \ + asm volatile ( \ + "syscall\n" \ + : "=a"(_ret) \ + : "r"(_arg1), "r"(_arg2), \ + "0"(_num) \ + : "rcx", "r11", "memory", "cc" \ + ); \ + _ret; \ +}) + +void *create_shstk(void *addr) +{ + return (void *)syscall(__NR_map_shadow_stack, addr, SS_SIZE, SHADOW_STACK_SET_TOKEN); +} + +void *create_normal_mem(void *addr) +{ + return mmap(addr, SS_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); +} + +void free_shstk(void *shstk) +{ + munmap(shstk, SS_SIZE); +} + +int reset_shstk(void *shstk) +{ + return madvise(shstk, SS_SIZE, MADV_DONTNEED); +} + +void try_shstk(unsigned long new_ssp) +{ + unsigned long ssp; + + printf("[INFO]\tnew_ssp = %lx, *new_ssp = %lx\n", + new_ssp, *((unsigned long *)new_ssp)); + + ssp = get_ssp(); + printf("[INFO]\tchanging ssp from %lx to %lx\n", ssp, new_ssp); + + asm volatile("rstorssp (%0)\n":: "r" (new_ssp)); + asm volatile("saveprevssp"); + printf("[INFO]\tssp is now %lx\n", get_ssp()); + + /* Switch back to original shadow stack */ + ssp -= 8; + asm volatile("rstorssp (%0)\n":: "r" (ssp)); + asm volatile("saveprevssp"); +} + +int test_shstk_pivot(void) +{ + void *shstk = create_shstk(0); + + if (shstk == MAP_FAILED) { + printf("[FAIL]\tError creating shadow stack: %d\n", errno); + return 1; + } + try_shstk((unsigned long)shstk + SS_SIZE - 8); + free_shstk(shstk); + + printf("[OK]\tShadow stack pivot\n"); + return 0; +} + +int test_shstk_faults(void) +{ + unsigned long *shstk = create_shstk(0); + + /* Read shadow stack, test if it's zero to not get read optimized out */ + if (*shstk != 0) + goto err; + + /* Wrss memory that was already read. */ + write_shstk(shstk, 1); + if (*shstk != 1) + goto err; + + /* Page out memory, so we can wrss it again. */ + if (reset_shstk((void *)shstk)) + goto err; + + write_shstk(shstk, 1); + if (*shstk != 1) + goto err; + + printf("[OK]\tShadow stack faults\n"); + return 0; + +err: + return 1; +} + +unsigned long saved_ssp; +unsigned long saved_ssp_val; +volatile bool segv_triggered; + +void __attribute__((noinline)) violate_ss(void) +{ + saved_ssp = get_ssp(); + saved_ssp_val = *(unsigned long *)saved_ssp; + + /* Corrupt shadow stack */ + printf("[INFO]\tCorrupting shadow stack\n"); + write_shstk((void *)saved_ssp, 0); +} + +void segv_handler(int signum, siginfo_t *si, void *uc) +{ + printf("[INFO]\tGenerated shadow stack violation successfully\n"); + + segv_triggered = true; + + /* Fix shadow stack */ + write_shstk((void *)saved_ssp, saved_ssp_val); +} + +int test_shstk_violation(void) +{ + struct sigaction sa = {}; + + sa.sa_sigaction = segv_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + segv_triggered = false; + + /* Make sure segv_triggered is set before violate_ss() */ + asm volatile("" : : : "memory"); + + violate_ss(); + + signal(SIGSEGV, SIG_DFL); + + printf("[OK]\tShadow stack violation test\n"); + + return !segv_triggered; +} + +/* Gup test state */ +#define MAGIC_VAL 0x12345678 +bool is_shstk_access; +void *shstk_ptr; +int fd; + +void reset_test_shstk(void *addr) +{ + if (shstk_ptr) + free_shstk(shstk_ptr); + shstk_ptr = create_shstk(addr); +} + +void test_access_fix_handler(int signum, siginfo_t *si, void *uc) +{ + printf("[INFO]\tViolation from %s\n", is_shstk_access ? "shstk access" : "normal write"); + + segv_triggered = true; + + /* Fix shadow stack */ + if (is_shstk_access) { + reset_test_shstk(shstk_ptr); + return; + } + + free_shstk(shstk_ptr); + create_normal_mem(shstk_ptr); +} + +bool test_shstk_access(void *ptr) +{ + is_shstk_access = true; + segv_triggered = false; + write_shstk(ptr, MAGIC_VAL); + + asm volatile("" : : : "memory"); + + return segv_triggered; +} + +bool test_write_access(void *ptr) +{ + is_shstk_access = false; + segv_triggered = false; + *(unsigned long *)ptr = MAGIC_VAL; + + asm volatile("" : : : "memory"); + + return segv_triggered; +} + +bool gup_write(void *ptr) +{ + unsigned long val; + + lseek(fd, (unsigned long)ptr, SEEK_SET); + if (write(fd, &val, sizeof(val)) < 0) + return 1; + + return 0; +} + +bool gup_read(void *ptr) +{ + unsigned long val; + + lseek(fd, (unsigned long)ptr, SEEK_SET); + if (read(fd, &val, sizeof(val)) < 0) + return 1; + + return 0; +} + +int test_gup(void) +{ + struct sigaction sa = {}; + int status; + pid_t pid; + + sa.sa_sigaction = test_access_fix_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + segv_triggered = false; + + fd = open("/proc/self/mem", O_RDWR); + if (fd == -1) + return 1; + + reset_test_shstk(0); + if (gup_read(shstk_ptr)) + return 1; + if (test_shstk_access(shstk_ptr)) + return 1; + printf("[INFO]\tGup read -> shstk access success\n"); + + reset_test_shstk(0); + if (gup_write(shstk_ptr)) + return 1; + if (test_shstk_access(shstk_ptr)) + return 1; + printf("[INFO]\tGup write -> shstk access success\n"); + + reset_test_shstk(0); + if (gup_read(shstk_ptr)) + return 1; + if (!test_write_access(shstk_ptr)) + return 1; + printf("[INFO]\tGup read -> write access success\n"); + + reset_test_shstk(0); + if (gup_write(shstk_ptr)) + return 1; + if (!test_write_access(shstk_ptr)) + return 1; + printf("[INFO]\tGup write -> write access success\n"); + + close(fd); + + /* COW/gup test */ + reset_test_shstk(0); + pid = fork(); + if (!pid) { + fd = open("/proc/self/mem", O_RDWR); + if (fd == -1) + exit(1); + + if (gup_write(shstk_ptr)) { + close(fd); + exit(1); + } + close(fd); + exit(0); + } + waitpid(pid, &status, 0); + if (WEXITSTATUS(status)) { + printf("[FAIL]\tWrite in child failed\n"); + return 1; + } + if (*(unsigned long *)shstk_ptr == MAGIC_VAL) { + printf("[FAIL]\tWrite in child wrote through to shared memory\n"); + return 1; + } + + printf("[INFO]\tCow gup write -> write access success\n"); + + free_shstk(shstk_ptr); + + signal(SIGSEGV, SIG_DFL); + + printf("[OK]\tShadow gup test\n"); + + return 0; +} + +int test_mprotect(void) +{ + struct sigaction sa = {}; + + sa.sa_sigaction = test_access_fix_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + segv_triggered = false; + + /* mprotect a shadow stack as read only */ + reset_test_shstk(0); + if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { + printf("[FAIL]\tmprotect(PROT_READ) failed\n"); + return 1; + } + + /* try to wrss it and fail */ + if (!test_shstk_access(shstk_ptr)) { + printf("[FAIL]\tShadow stack access to read-only memory succeeded\n"); + return 1; + } + + /* + * The shadow stack was reset above to resolve the fault, make the new one + * read-only. + */ + if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { + printf("[FAIL]\tmprotect(PROT_READ) failed\n"); + return 1; + } + + /* then back to writable */ + if (mprotect(shstk_ptr, SS_SIZE, PROT_WRITE | PROT_READ) < 0) { + printf("[FAIL]\tmprotect(PROT_WRITE) failed\n"); + return 1; + } + + /* then wrss to it and succeed */ + if (test_shstk_access(shstk_ptr)) { + printf("[FAIL]\tShadow stack access to mprotect() writable memory failed\n"); + return 1; + } + + free_shstk(shstk_ptr); + + signal(SIGSEGV, SIG_DFL); + + printf("[OK]\tmprotect() test\n"); + + return 0; +} + +char zero[4096]; + +static void *uffd_thread(void *arg) +{ + struct uffdio_copy req; + int uffd = *(int *)arg; + struct uffd_msg msg; + int ret; + + while (1) { + ret = read(uffd, &msg, sizeof(msg)); + if (ret > 0) + break; + else if (errno == EAGAIN) + continue; + return (void *)1; + } + + req.dst = msg.arg.pagefault.address; + req.src = (__u64)zero; + req.len = 4096; + req.mode = 0; + + if (ioctl(uffd, UFFDIO_COPY, &req)) + return (void *)1; + + return (void *)0; +} + +int test_userfaultfd(void) +{ + struct uffdio_register uffdio_register; + struct uffdio_api uffdio_api; + struct sigaction sa = {}; + pthread_t thread; + void *res; + int uffd; + + sa.sa_sigaction = test_access_fix_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + if (uffd < 0) { + printf("[SKIP]\tUserfaultfd unavailable.\n"); + return 0; + } + + reset_test_shstk(0); + + uffdio_api.api = UFFD_API; + uffdio_api.features = 0; + if (ioctl(uffd, UFFDIO_API, &uffdio_api)) + goto err; + + uffdio_register.range.start = (__u64)shstk_ptr; + uffdio_register.range.len = 4096; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) + goto err; + + if (pthread_create(&thread, NULL, &uffd_thread, &uffd)) + goto err; + + reset_shstk(shstk_ptr); + test_shstk_access(shstk_ptr); + + if (pthread_join(thread, &res)) + goto err; + + if (test_shstk_access(shstk_ptr)) + goto err; + + free_shstk(shstk_ptr); + + signal(SIGSEGV, SIG_DFL); + + if (!res) + printf("[OK]\tUserfaultfd test\n"); + return !!res; +err: + free_shstk(shstk_ptr); + close(uffd); + signal(SIGSEGV, SIG_DFL); + return 1; +} + +/* Simple linked list for keeping track of mappings in test_guard_gap() */ +struct node { + struct node *next; + void *mapping; +}; + +/* + * This tests whether mmap will place other mappings in a shadow stack's guard + * gap. The steps are: + * 1. Finds an empty place by mapping and unmapping something. + * 2. Map a shadow stack in the middle of the known empty area. + * 3. Map a bunch of PAGE_SIZE mappings. These will use the search down + * direction, filling any gaps until it encounters the shadow stack's + * guard gap. + * 4. When a mapping lands below the shadow stack from step 2, then all + * of the above gaps are filled. The search down algorithm will have + * looked at the shadow stack gaps. + * 5. See if it landed in the gap. + */ +int test_guard_gap(void) +{ + void *free_area, *shstk, *test_map = (void *)0xFFFFFFFFFFFFFFFF; + struct node *head = NULL, *cur; + + free_area = mmap(0, SS_SIZE * 3, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + munmap(free_area, SS_SIZE * 3); + + shstk = create_shstk(free_area + SS_SIZE); + if (shstk == MAP_FAILED) + return 1; + + while (test_map > shstk) { + test_map = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (test_map == MAP_FAILED) + return 1; + cur = malloc(sizeof(*cur)); + cur->mapping = test_map; + + cur->next = head; + head = cur; + } + + while (head) { + cur = head; + head = cur->next; + munmap(cur->mapping, PAGE_SIZE); + free(cur); + } + + free_shstk(shstk); + + if (shstk - test_map - PAGE_SIZE != PAGE_SIZE) + return 1; + + printf("[OK]\tGuard gap test\n"); + + return 0; +} + +/* + * Too complicated to pull it out of the 32 bit header, but also get the + * 64 bit one needed above. Just define a copy here. + */ +#define __NR_compat_sigaction 67 + +/* + * Call 32 bit signal handler to get 32 bit signals ABI. Make sure + * to push the registers that will get clobbered. + */ +int sigaction32(int signum, const struct sigaction *restrict act, + struct sigaction *restrict oldact) +{ + register long syscall_reg asm("eax") = __NR_compat_sigaction; + register long signum_reg asm("ebx") = signum; + register long act_reg asm("ecx") = (long)act; + register long oldact_reg asm("edx") = (long)oldact; + int ret = 0; + + asm volatile ("int $0x80;" + : "=a"(ret), "=m"(oldact) + : "r"(syscall_reg), "r"(signum_reg), "r"(act_reg), + "r"(oldact_reg) + : "r8", "r9", "r10", "r11" + ); + + return ret; +} + +sigjmp_buf jmp_buffer; + +void segv_gp_handler(int signum, siginfo_t *si, void *uc) +{ + segv_triggered = true; + + /* + * To work with old glibc, this can't rely on siglongjmp working with + * shadow stack enabled, so disable shadow stack before siglongjmp(). + */ + ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK); + siglongjmp(jmp_buffer, -1); +} + +/* + * Transition to 32 bit mode and check that a #GP triggers a segfault. + */ +int test_32bit(void) +{ + struct sigaction sa = {}; + struct sigaction *sa32; + + /* Create sigaction in 32 bit address range */ + sa32 = mmap(0, 4096, PROT_READ | PROT_WRITE, + MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + sa32->sa_flags = SA_SIGINFO; + + sa.sa_sigaction = segv_gp_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + + segv_triggered = false; + + /* Make sure segv_triggered is set before triggering the #GP */ + asm volatile("" : : : "memory"); + + /* + * Set handler to somewhere in 32 bit address space + */ + sa32->sa_handler = (void *)sa32; + if (sigaction32(SIGUSR1, sa32, NULL)) + return 1; + + if (!sigsetjmp(jmp_buffer, 1)) + raise(SIGUSR1); + + if (segv_triggered) + printf("[OK]\t32 bit test\n"); + + return !segv_triggered; +} + +void segv_handler_ptrace(int signum, siginfo_t *si, void *uc) +{ + /* The SSP adjustment caused a segfault. */ + exit(0); +} + +int test_ptrace(void) +{ + unsigned long saved_ssp, ssp = 0; + struct sigaction sa= {}; + struct iovec iov; + int status; + int pid; + + iov.iov_base = &ssp; + iov.iov_len = sizeof(ssp); + + pid = fork(); + if (!pid) { + ssp = get_ssp(); + + sa.sa_sigaction = segv_handler_ptrace; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &sa, NULL)) + return 1; + + ptrace(PTRACE_TRACEME, NULL, NULL, NULL); + /* + * The parent will tweak the SSP and return from this function + * will #CP. + */ + raise(SIGTRAP); + + exit(1); + } + + while (waitpid(pid, &status, 0) != -1 && WSTOPSIG(status) != SIGTRAP); + + if (ptrace(PTRACE_GETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tFailed to PTRACE_GETREGS\n"); + goto out_kill; + } + + if (!ssp) { + printf("[INFO]\tPtrace child SSP was 0\n"); + goto out_kill; + } + + saved_ssp = ssp; + + iov.iov_len = 0; + if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tToo small size accepted via PTRACE_SETREGS\n"); + goto out_kill; + } + + iov.iov_len = sizeof(ssp) + 1; + if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tToo large size accepted via PTRACE_SETREGS\n"); + goto out_kill; + } + + ssp += 1; + if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tUnaligned SSP written via PTRACE_SETREGS\n"); + goto out_kill; + } + + ssp = 0xFFFFFFFFFFFF0000; + if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tKernel range SSP written via PTRACE_SETREGS\n"); + goto out_kill; + } + + /* + * Tweak the SSP so the child with #CP when it resumes and returns + * from raise() + */ + ssp = saved_ssp + 8; + iov.iov_len = sizeof(ssp); + if (ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { + printf("[INFO]\tFailed to PTRACE_SETREGS\n"); + goto out_kill; + } + + if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) { + printf("[INFO]\tFailed to PTRACE_DETACH\n"); + goto out_kill; + } + + waitpid(pid, &status, 0); + if (WEXITSTATUS(status)) + return 1; + + printf("[OK]\tPtrace test\n"); + return 0; + +out_kill: + kill(pid, SIGKILL); + return 1; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { + printf("[SKIP]\tCould not enable Shadow stack\n"); + return 1; + } + + if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { + ret = 1; + printf("[FAIL]\tDisabling shadow stack failed\n"); + } + + if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { + printf("[SKIP]\tCould not re-enable Shadow stack\n"); + return 1; + } + + if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_WRSS)) { + printf("[SKIP]\tCould not enable WRSS\n"); + ret = 1; + goto out; + } + + /* Should have succeeded if here, but this is a test, so double check. */ + if (!get_ssp()) { + printf("[FAIL]\tShadow stack disabled\n"); + return 1; + } + + if (test_shstk_pivot()) { + ret = 1; + printf("[FAIL]\tShadow stack pivot\n"); + goto out; + } + + if (test_shstk_faults()) { + ret = 1; + printf("[FAIL]\tShadow stack fault test\n"); + goto out; + } + + if (test_shstk_violation()) { + ret = 1; + printf("[FAIL]\tShadow stack violation test\n"); + goto out; + } + + if (test_gup()) { + ret = 1; + printf("[FAIL]\tShadow shadow stack gup\n"); + goto out; + } + + if (test_mprotect()) { + ret = 1; + printf("[FAIL]\tShadow shadow mprotect test\n"); + goto out; + } + + if (test_userfaultfd()) { + ret = 1; + printf("[FAIL]\tUserfaultfd test\n"); + goto out; + } + + if (test_guard_gap()) { + ret = 1; + printf("[FAIL]\tGuard gap test\n"); + goto out; + } + + if (test_ptrace()) { + ret = 1; + printf("[FAIL]\tptrace test\n"); + } + + if (test_32bit()) { + ret = 1; + printf("[FAIL]\t32 bit test\n"); + goto out; + } + + return ret; + +out: + /* + * Disable shadow stack before the function returns, or there will be a + * shadow stack violation. + */ + if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { + ret = 1; + printf("[FAIL]\tDisabling shadow stack failed\n"); + } + + return ret; +} +#endif diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 01b636d3039a..6779d5008b27 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -211,102 +211,138 @@ int vsock_seqpacket_accept(unsigned int cid, unsigned int port, return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET); } -/* Transmit one byte and check the return value. +/* Transmit bytes from a buffer and check the return value. * * expected_ret: * <0 Negative errno (for testing errors) * 0 End-of-file - * 1 Success + * >0 Success (bytes successfully written) */ -void send_byte(int fd, int expected_ret, int flags) +void send_buf(int fd, const void *buf, size_t len, int flags, + ssize_t expected_ret) { - const uint8_t byte = 'A'; - ssize_t nwritten; + ssize_t nwritten = 0; + ssize_t ret; timeout_begin(TIMEOUT); do { - nwritten = send(fd, &byte, sizeof(byte), flags); - timeout_check("write"); - } while (nwritten < 0 && errno == EINTR); + ret = send(fd, buf + nwritten, len - nwritten, flags); + timeout_check("send"); + + if (ret == 0 || (ret < 0 && errno != EINTR)) + break; + + nwritten += ret; + } while (nwritten < len); timeout_end(); if (expected_ret < 0) { - if (nwritten != -1) { - fprintf(stderr, "bogus send(2) return value %zd\n", - nwritten); + if (ret != -1) { + fprintf(stderr, "bogus send(2) return value %zd (expected %zd)\n", + ret, expected_ret); exit(EXIT_FAILURE); } if (errno != -expected_ret) { - perror("write"); + perror("send"); exit(EXIT_FAILURE); } return; } - if (nwritten < 0) { - perror("write"); + if (ret < 0) { + perror("send"); exit(EXIT_FAILURE); } - if (nwritten == 0) { - if (expected_ret == 0) - return; - fprintf(stderr, "unexpected EOF while sending byte\n"); - exit(EXIT_FAILURE); - } - if (nwritten != sizeof(byte)) { - fprintf(stderr, "bogus send(2) return value %zd\n", nwritten); + if (nwritten != expected_ret) { + if (ret == 0) + fprintf(stderr, "unexpected EOF while sending bytes\n"); + + fprintf(stderr, "bogus send(2) bytes written %zd (expected %zd)\n", + nwritten, expected_ret); exit(EXIT_FAILURE); } } -/* Receive one byte and check the return value. +/* Receive bytes in a buffer and check the return value. * * expected_ret: * <0 Negative errno (for testing errors) * 0 End-of-file - * 1 Success + * >0 Success (bytes successfully read) */ -void recv_byte(int fd, int expected_ret, int flags) +void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) { - uint8_t byte; - ssize_t nread; + ssize_t nread = 0; + ssize_t ret; timeout_begin(TIMEOUT); do { - nread = recv(fd, &byte, sizeof(byte), flags); - timeout_check("read"); - } while (nread < 0 && errno == EINTR); + ret = recv(fd, buf + nread, len - nread, flags); + timeout_check("recv"); + + if (ret == 0 || (ret < 0 && errno != EINTR)) + break; + + nread += ret; + } while (nread < len); timeout_end(); if (expected_ret < 0) { - if (nread != -1) { - fprintf(stderr, "bogus recv(2) return value %zd\n", - nread); + if (ret != -1) { + fprintf(stderr, "bogus recv(2) return value %zd (expected %zd)\n", + ret, expected_ret); exit(EXIT_FAILURE); } if (errno != -expected_ret) { - perror("read"); + perror("recv"); exit(EXIT_FAILURE); } return; } - if (nread < 0) { - perror("read"); + if (ret < 0) { + perror("recv"); exit(EXIT_FAILURE); } - if (nread == 0) { - if (expected_ret == 0) - return; - fprintf(stderr, "unexpected EOF while receiving byte\n"); - exit(EXIT_FAILURE); - } - if (nread != sizeof(byte)) { - fprintf(stderr, "bogus recv(2) return value %zd\n", nread); + if (nread != expected_ret) { + if (ret == 0) + fprintf(stderr, "unexpected EOF while receiving bytes\n"); + + fprintf(stderr, "bogus recv(2) bytes read %zd (expected %zd)\n", + nread, expected_ret); exit(EXIT_FAILURE); } +} + +/* Transmit one byte and check the return value. + * + * expected_ret: + * <0 Negative errno (for testing errors) + * 0 End-of-file + * 1 Success + */ +void send_byte(int fd, int expected_ret, int flags) +{ + const uint8_t byte = 'A'; + + send_buf(fd, &byte, sizeof(byte), flags, expected_ret); +} + +/* Receive one byte and check the return value. + * + * expected_ret: + * <0 Negative errno (for testing errors) + * 0 End-of-file + * 1 Success + */ +void recv_byte(int fd, int expected_ret, int flags) +{ + uint8_t byte; + + recv_buf(fd, &byte, sizeof(byte), flags, expected_ret); + if (byte != 'A') { fprintf(stderr, "unexpected byte read %c\n", byte); exit(EXIT_FAILURE); diff --git a/tools/testing/vsock/util.h b/tools/testing/vsock/util.h index fb99208a95ea..e5407677ce05 100644 --- a/tools/testing/vsock/util.h +++ b/tools/testing/vsock/util.h @@ -42,6 +42,9 @@ int vsock_stream_accept(unsigned int cid, unsigned int port, int vsock_seqpacket_accept(unsigned int cid, unsigned int port, struct sockaddr_vm *clientaddrp); void vsock_wait_remote_close(int fd); +void send_buf(int fd, const void *buf, size_t len, int flags, + ssize_t expected_ret); +void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret); void send_byte(int fd, int expected_ret, int flags); void recv_byte(int fd, int expected_ret, int flags); void run_tests(const struct test_case *test_cases, diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 90718c2fd4ea..da4cb819a183 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -19,6 +19,7 @@ #include <time.h> #include <sys/mman.h> #include <poll.h> +#include <signal.h> #include "timeout.h" #include "control.h" @@ -261,7 +262,6 @@ static void test_msg_peek_client(const struct test_opts *opts, bool seqpacket) { unsigned char buf[MSG_PEEK_BUF_LEN]; - ssize_t send_size; int fd; int i; @@ -280,17 +280,7 @@ static void test_msg_peek_client(const struct test_opts *opts, control_expectln("SRVREADY"); - send_size = send(fd, buf, sizeof(buf), 0); - - if (send_size < 0) { - perror("send"); - exit(EXIT_FAILURE); - } - - if (send_size != sizeof(buf)) { - fprintf(stderr, "Invalid send size %zi\n", send_size); - exit(EXIT_FAILURE); - } + send_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); close(fd); } @@ -301,7 +291,6 @@ static void test_msg_peek_server(const struct test_opts *opts, unsigned char buf_half[MSG_PEEK_BUF_LEN / 2]; unsigned char buf_normal[MSG_PEEK_BUF_LEN]; unsigned char buf_peek[MSG_PEEK_BUF_LEN]; - ssize_t res; int fd; if (seqpacket) @@ -315,34 +304,16 @@ static void test_msg_peek_server(const struct test_opts *opts, } /* Peek from empty socket. */ - res = recv(fd, buf_peek, sizeof(buf_peek), MSG_PEEK | MSG_DONTWAIT); - if (res != -1) { - fprintf(stderr, "expected recv(2) failure, got %zi\n", res); - exit(EXIT_FAILURE); - } - - if (errno != EAGAIN) { - perror("EAGAIN expected"); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK | MSG_DONTWAIT, + -EAGAIN); control_writeln("SRVREADY"); /* Peek part of data. */ - res = recv(fd, buf_half, sizeof(buf_half), MSG_PEEK); - if (res != sizeof(buf_half)) { - fprintf(stderr, "recv(2) + MSG_PEEK, expected %zu, got %zi\n", - sizeof(buf_half), res); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf_half, sizeof(buf_half), MSG_PEEK, sizeof(buf_half)); /* Peek whole data. */ - res = recv(fd, buf_peek, sizeof(buf_peek), MSG_PEEK); - if (res != sizeof(buf_peek)) { - fprintf(stderr, "recv(2) + MSG_PEEK, expected %zu, got %zi\n", - sizeof(buf_peek), res); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek)); /* Compare partial and full peek. */ if (memcmp(buf_half, buf_peek, sizeof(buf_half))) { @@ -355,22 +326,11 @@ static void test_msg_peek_server(const struct test_opts *opts, * so check it with MSG_PEEK. We must get length * of the message. */ - res = recv(fd, buf_half, sizeof(buf_half), MSG_PEEK | - MSG_TRUNC); - if (res != sizeof(buf_peek)) { - fprintf(stderr, - "recv(2) + MSG_PEEK | MSG_TRUNC, exp %zu, got %zi\n", - sizeof(buf_half), res); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf_half, sizeof(buf_half), MSG_PEEK | MSG_TRUNC, + sizeof(buf_peek)); } - res = recv(fd, buf_normal, sizeof(buf_normal), 0); - if (res != sizeof(buf_normal)) { - fprintf(stderr, "recv(2), expected %zu, got %zi\n", - sizeof(buf_normal), res); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf_normal, sizeof(buf_normal), 0, sizeof(buf_normal)); /* Compare full peek and normal read. */ if (memcmp(buf_peek, buf_normal, sizeof(buf_peek))) { @@ -415,7 +375,6 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) msg_count = SOCK_BUF_SIZE / MAX_MSG_SIZE; for (int i = 0; i < msg_count; i++) { - ssize_t send_size; size_t buf_size; int flags; void *buf; @@ -443,17 +402,7 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) flags = 0; } - send_size = send(fd, buf, buf_size, flags); - - if (send_size < 0) { - perror("send"); - exit(EXIT_FAILURE); - } - - if (send_size != buf_size) { - fprintf(stderr, "Invalid send size\n"); - exit(EXIT_FAILURE); - } + send_buf(fd, buf, buf_size, flags, buf_size); /* * Hash sum is computed at both client and server in @@ -554,10 +503,7 @@ static void test_seqpacket_msg_trunc_client(const struct test_opts *opts) exit(EXIT_FAILURE); } - if (send(fd, buf, sizeof(buf), 0) != sizeof(buf)) { - perror("send failed"); - exit(EXIT_FAILURE); - } + send_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); control_writeln("SENDDONE"); close(fd); @@ -679,7 +625,6 @@ static void test_seqpacket_timeout_server(const struct test_opts *opts) static void test_seqpacket_bigmsg_client(const struct test_opts *opts) { unsigned long sock_buf_size; - ssize_t send_size; socklen_t len; void *data; int fd; @@ -706,18 +651,7 @@ static void test_seqpacket_bigmsg_client(const struct test_opts *opts) exit(EXIT_FAILURE); } - send_size = send(fd, data, sock_buf_size, 0); - if (send_size != -1) { - fprintf(stderr, "expected 'send(2)' failure, got %zi\n", - send_size); - exit(EXIT_FAILURE); - } - - if (errno != EMSGSIZE) { - fprintf(stderr, "expected EMSGSIZE in 'errno', got %i\n", - errno); - exit(EXIT_FAILURE); - } + send_buf(fd, data, sock_buf_size, 0, -EMSGSIZE); control_writeln("CLISENT"); @@ -771,15 +705,9 @@ static void test_seqpacket_invalid_rec_buffer_client(const struct test_opts *opt memset(buf1, BUF_PATTERN_1, buf_size); memset(buf2, BUF_PATTERN_2, buf_size); - if (send(fd, buf1, buf_size, 0) != buf_size) { - perror("send failed"); - exit(EXIT_FAILURE); - } + send_buf(fd, buf1, buf_size, 0, buf_size); - if (send(fd, buf2, buf_size, 0) != buf_size) { - perror("send failed"); - exit(EXIT_FAILURE); - } + send_buf(fd, buf2, buf_size, 0, buf_size); close(fd); } @@ -900,7 +828,6 @@ static void test_stream_poll_rcvlowat_client(const struct test_opts *opts) unsigned long lowat_val = RCVLOWAT_BUF_SIZE; char buf[RCVLOWAT_BUF_SIZE]; struct pollfd fds; - ssize_t read_res; short poll_flags; int fd; @@ -955,12 +882,7 @@ static void test_stream_poll_rcvlowat_client(const struct test_opts *opts) /* Use MSG_DONTWAIT, if call is going to wait, EAGAIN * will be returned. */ - read_res = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); - if (read_res != RCVLOWAT_BUF_SIZE) { - fprintf(stderr, "Unexpected recv result %zi\n", - read_res); - exit(EXIT_FAILURE); - } + recv_buf(fd, buf, sizeof(buf), MSG_DONTWAIT, RCVLOWAT_BUF_SIZE); control_writeln("POLLDONE"); @@ -972,7 +894,7 @@ static void test_stream_poll_rcvlowat_client(const struct test_opts *opts) static void test_inv_buf_client(const struct test_opts *opts, bool stream) { unsigned char data[INV_BUF_TEST_DATA_LEN] = {0}; - ssize_t ret; + ssize_t expected_ret; int fd; if (stream) @@ -988,39 +910,18 @@ static void test_inv_buf_client(const struct test_opts *opts, bool stream) control_expectln("SENDDONE"); /* Use invalid buffer here. */ - ret = recv(fd, NULL, sizeof(data), 0); - if (ret != -1) { - fprintf(stderr, "expected recv(2) failure, got %zi\n", ret); - exit(EXIT_FAILURE); - } - - if (errno != EFAULT) { - fprintf(stderr, "unexpected recv(2) errno %d\n", errno); - exit(EXIT_FAILURE); - } - - ret = recv(fd, data, sizeof(data), MSG_DONTWAIT); + recv_buf(fd, NULL, sizeof(data), 0, -EFAULT); if (stream) { /* For SOCK_STREAM we must continue reading. */ - if (ret != sizeof(data)) { - fprintf(stderr, "expected recv(2) success, got %zi\n", ret); - exit(EXIT_FAILURE); - } - /* Don't check errno in case of success. */ + expected_ret = sizeof(data); } else { /* For SOCK_SEQPACKET socket's queue must be empty. */ - if (ret != -1) { - fprintf(stderr, "expected recv(2) failure, got %zi\n", ret); - exit(EXIT_FAILURE); - } - - if (errno != EAGAIN) { - fprintf(stderr, "unexpected recv(2) errno %d\n", errno); - exit(EXIT_FAILURE); - } + expected_ret = -EAGAIN; } + recv_buf(fd, data, sizeof(data), MSG_DONTWAIT, expected_ret); + control_writeln("DONE"); close(fd); @@ -1029,7 +930,6 @@ static void test_inv_buf_client(const struct test_opts *opts, bool stream) static void test_inv_buf_server(const struct test_opts *opts, bool stream) { unsigned char data[INV_BUF_TEST_DATA_LEN] = {0}; - ssize_t res; int fd; if (stream) @@ -1042,11 +942,7 @@ static void test_inv_buf_server(const struct test_opts *opts, bool stream) exit(EXIT_FAILURE); } - res = send(fd, data, sizeof(data), 0); - if (res != sizeof(data)) { - fprintf(stderr, "unexpected send(2) result %zi\n", res); - exit(EXIT_FAILURE); - } + send_buf(fd, data, sizeof(data), 0, sizeof(data)); control_writeln("SENDDONE"); @@ -1080,7 +976,6 @@ static void test_seqpacket_inv_buf_server(const struct test_opts *opts) static void test_stream_virtio_skb_merge_client(const struct test_opts *opts) { - ssize_t res; int fd; fd = vsock_stream_connect(opts->peer_cid, 1234); @@ -1090,22 +985,14 @@ static void test_stream_virtio_skb_merge_client(const struct test_opts *opts) } /* Send first skbuff. */ - res = send(fd, HELLO_STR, strlen(HELLO_STR), 0); - if (res != strlen(HELLO_STR)) { - fprintf(stderr, "unexpected send(2) result %zi\n", res); - exit(EXIT_FAILURE); - } + send_buf(fd, HELLO_STR, strlen(HELLO_STR), 0, strlen(HELLO_STR)); control_writeln("SEND0"); /* Peer reads part of first skbuff. */ control_expectln("REPLY0"); /* Send second skbuff, it will be appended to the first. */ - res = send(fd, WORLD_STR, strlen(WORLD_STR), 0); - if (res != strlen(WORLD_STR)) { - fprintf(stderr, "unexpected send(2) result %zi\n", res); - exit(EXIT_FAILURE); - } + send_buf(fd, WORLD_STR, strlen(WORLD_STR), 0, strlen(WORLD_STR)); control_writeln("SEND1"); /* Peer reads merged skbuff packet. */ @@ -1116,8 +1003,8 @@ static void test_stream_virtio_skb_merge_client(const struct test_opts *opts) static void test_stream_virtio_skb_merge_server(const struct test_opts *opts) { + size_t read = 0, to_read; unsigned char buf[64]; - ssize_t res; int fd; fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); @@ -1129,26 +1016,21 @@ static void test_stream_virtio_skb_merge_server(const struct test_opts *opts) control_expectln("SEND0"); /* Read skbuff partially. */ - res = recv(fd, buf, 2, 0); - if (res != 2) { - fprintf(stderr, "expected recv(2) returns 2 bytes, got %zi\n", res); - exit(EXIT_FAILURE); - } + to_read = 2; + recv_buf(fd, buf + read, to_read, 0, to_read); + read += to_read; control_writeln("REPLY0"); control_expectln("SEND1"); - res = recv(fd, buf + 2, sizeof(buf) - 2, 0); - if (res != 8) { - fprintf(stderr, "expected recv(2) returns 8 bytes, got %zi\n", res); - exit(EXIT_FAILURE); - } + /* Read the rest of both buffers */ + to_read = strlen(HELLO_STR WORLD_STR) - read; + recv_buf(fd, buf + read, to_read, 0, to_read); + read += to_read; - res = recv(fd, buf, sizeof(buf) - 8 - 2, MSG_DONTWAIT); - if (res != -1) { - fprintf(stderr, "expected recv(2) failure, got %zi\n", res); - exit(EXIT_FAILURE); - } + /* No more bytes should be there */ + to_read = sizeof(buf) - read; + recv_buf(fd, buf + read, to_read, MSG_DONTWAIT, -EAGAIN); if (memcmp(buf, HELLO_STR WORLD_STR, strlen(HELLO_STR WORLD_STR))) { fprintf(stderr, "pattern mismatch\n"); @@ -1170,6 +1052,133 @@ static void test_seqpacket_msg_peek_server(const struct test_opts *opts) return test_msg_peek_server(opts, true); } +static sig_atomic_t have_sigpipe; + +static void sigpipe(int signo) +{ + have_sigpipe = 1; +} + +static void test_stream_check_sigpipe(int fd) +{ + ssize_t res; + + have_sigpipe = 0; + + res = send(fd, "A", 1, 0); + if (res != -1) { + fprintf(stderr, "expected send(2) failure, got %zi\n", res); + exit(EXIT_FAILURE); + } + + if (!have_sigpipe) { + fprintf(stderr, "SIGPIPE expected\n"); + exit(EXIT_FAILURE); + } + + have_sigpipe = 0; + + res = send(fd, "A", 1, MSG_NOSIGNAL); + if (res != -1) { + fprintf(stderr, "expected send(2) failure, got %zi\n", res); + exit(EXIT_FAILURE); + } + + if (have_sigpipe) { + fprintf(stderr, "SIGPIPE not expected\n"); + exit(EXIT_FAILURE); + } +} + +static void test_stream_shutwr_client(const struct test_opts *opts) +{ + int fd; + + struct sigaction act = { + .sa_handler = sigpipe, + }; + + sigaction(SIGPIPE, &act, NULL); + + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + if (shutdown(fd, SHUT_WR)) { + perror("shutdown"); + exit(EXIT_FAILURE); + } + + test_stream_check_sigpipe(fd); + + control_writeln("CLIENTDONE"); + + close(fd); +} + +static void test_stream_shutwr_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + control_expectln("CLIENTDONE"); + + close(fd); +} + +static void test_stream_shutrd_client(const struct test_opts *opts) +{ + int fd; + + struct sigaction act = { + .sa_handler = sigpipe, + }; + + sigaction(SIGPIPE, &act, NULL); + + fd = vsock_stream_connect(opts->peer_cid, 1234); + if (fd < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + control_expectln("SHUTRDDONE"); + + test_stream_check_sigpipe(fd); + + control_writeln("CLIENTDONE"); + + close(fd); +} + +static void test_stream_shutrd_server(const struct test_opts *opts) +{ + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + if (shutdown(fd, SHUT_RD)) { + perror("shutdown"); + exit(EXIT_FAILURE); + } + + control_writeln("SHUTRDDONE"); + control_expectln("CLIENTDONE"); + + close(fd); +} + static struct test_case test_cases[] = { { .name = "SOCK_STREAM connection reset", @@ -1250,6 +1259,16 @@ static struct test_case test_cases[] = { .run_client = test_seqpacket_msg_peek_client, .run_server = test_seqpacket_msg_peek_server, }, + { + .name = "SOCK_STREAM SHUT_WR", + .run_client = test_stream_shutwr_client, + .run_server = test_stream_shutwr_server, + }, + { + .name = "SOCK_STREAM SHUT_RD", + .run_client = test_stream_shutrd_client, + .run_server = test_stream_shutrd_server, + }, {}, }; diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c index e0ffe69c271c..7093fd5333be 100644 --- a/tools/tracing/rtla/src/timerlat_aa.c +++ b/tools/tracing/rtla/src/timerlat_aa.c @@ -159,6 +159,7 @@ static int timerlat_aa_irq_latency(struct timerlat_aa_data *taa_data, taa_data->thread_nmi_sum = 0; taa_data->thread_irq_sum = 0; taa_data->thread_softirq_sum = 0; + taa_data->thread_thread_sum = 0; taa_data->thread_blocking_duration = 0; taa_data->timer_irq_start_time = 0; taa_data->timer_irq_duration = 0; @@ -337,7 +338,23 @@ static int timerlat_aa_irq_handler(struct trace_seq *s, struct tep_record *recor taa_data->timer_irq_start_time = start; taa_data->timer_irq_duration = duration; - taa_data->timer_irq_start_delay = taa_data->timer_irq_start_time - expected_start; + /* + * We are dealing with two different clock sources: the + * external clock source that timerlat uses as a reference + * and the clock used by the tracer. There are also two + * moments: the time reading the clock and the timer in + * which the event is placed in the buffer (the trace + * event timestamp). If the processor is slow or there + * is some hardware noise, the difference between the + * timestamp and the external clock read can be longer + * than the IRQ handler delay, resulting in a negative + * time. If so, set IRQ start delay as 0. In the end, + * it is less relevant than the noise. + */ + if (expected_start < taa_data->timer_irq_start_time) + taa_data->timer_irq_start_delay = taa_data->timer_irq_start_time - expected_start; + else + taa_data->timer_irq_start_delay = 0; /* * not exit from idle. @@ -528,7 +545,7 @@ static int timerlat_aa_kworker_start_handler(struct trace_seq *s, struct tep_rec static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu, int irq_thresh, int thread_thresh) { - unsigned long long exp_irq_ts; + long long exp_irq_ts; int total; int irq; @@ -545,12 +562,15 @@ static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu, /* * Expected IRQ arrival time using the trace clock as the base. + * + * TODO: Add a list of previous IRQ, and then run the list backwards. */ exp_irq_ts = taa_data->timer_irq_start_time - taa_data->timer_irq_start_delay; - - if (exp_irq_ts < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration) - printf(" Previous IRQ interference: \t\t up to %9.2f us\n", - ns_to_usf(taa_data->prev_irq_duration)); + if (exp_irq_ts < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration) { + if (taa_data->prev_irq_timstamp < taa_data->timer_irq_start_time) + printf(" Previous IRQ interference: \t\t up to %9.2f us\n", + ns_to_usf(taa_data->prev_irq_duration)); + } /* * The delay that the IRQ suffered before starting. diff --git a/tools/tracing/rtla/src/timerlat_u.c b/tools/tracing/rtla/src/timerlat_u.c index 05e310696dd5..01dbf9a6b5a5 100644 --- a/tools/tracing/rtla/src/timerlat_u.c +++ b/tools/tracing/rtla/src/timerlat_u.c @@ -45,7 +45,7 @@ static int timerlat_u_main(int cpu, struct timerlat_u_params *params) retval = sched_setaffinity(gettid(), sizeof(set), &set); if (retval == -1) { - err_msg("Error setting user thread affinity\n"); + debug_msg("Error setting user thread affinity %d, is the CPU online?\n", cpu); exit(1); } @@ -193,7 +193,9 @@ void *timerlat_u_dispatcher(void *data) procs_count--; } } - break; + + if (!procs_count) + break; } sleep(1); diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py new file mode 100644 index 000000000000..d0df5833f2c1 --- /dev/null +++ b/tools/workqueue/wq_dump.py @@ -0,0 +1,177 @@ +#!/usr/bin/env drgn +# +# Copyright (C) 2023 Tejun Heo <tj@kernel.org> +# Copyright (C) 2023 Meta Platforms, Inc. and affiliates. + +desc = """ +This is a drgn script to show the current workqueue configuration. For more +info on drgn, visit https://github.com/osandov/drgn. + +Affinity Scopes +=============== + +Shows the CPUs that can be used for unbound workqueues and how they will be +grouped by each available affinity type. For each type: + + nr_pods number of CPU pods in the affinity type + pod_cpus CPUs in each pod + pod_node NUMA node for memory allocation for each pod + cpu_pod pod that each CPU is associated to + +Worker Pools +============ + +Lists all worker pools indexed by their ID. For each pool: + + ref number of pool_workqueue's associated with this pool + nice nice value of the worker threads in the pool + idle number of idle workers + workers number of all workers + cpu CPU the pool is associated with (per-cpu pool) + cpus CPUs the workers in the pool can run on (unbound pool) + +Workqueue CPU -> pool +===================== + +Lists all workqueues along with their type and worker pool association. For +each workqueue: + + NAME TYPE[,FLAGS] POOL_ID... + + NAME name of the workqueue + TYPE percpu, unbound or ordered + FLAGS S: strict affinity scope + POOL_ID worker pool ID associated with each possible CPU +""" + +import sys + +import drgn +from drgn.helpers.linux.list import list_for_each_entry,list_empty +from drgn.helpers.linux.percpu import per_cpu_ptr +from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu +from drgn.helpers.linux.idr import idr_for_each + +import argparse +parser = argparse.ArgumentParser(description=desc, + formatter_class=argparse.RawTextHelpFormatter) +args = parser.parse_args() + +def err(s): + print(s, file=sys.stderr, flush=True) + sys.exit(1) + +def cpumask_str(cpumask): + output = "" + base = 0 + v = 0 + for cpu in for_each_cpu(cpumask[0]): + while cpu - base >= 32: + output += f'{hex(v)} ' + base += 32 + v = 0 + v |= 1 << (cpu - base) + if v > 0: + output += f'{v:08x}' + return output.strip() + +worker_pool_idr = prog['worker_pool_idr'] +workqueues = prog['workqueues'] +wq_unbound_cpumask = prog['wq_unbound_cpumask'] +wq_pod_types = prog['wq_pod_types'] +wq_affn_dfl = prog['wq_affn_dfl'] +wq_affn_names = prog['wq_affn_names'] + +WQ_UNBOUND = prog['WQ_UNBOUND'] +WQ_ORDERED = prog['__WQ_ORDERED'] +WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] + +WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] +WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] +WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] +WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] +WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] + +print('Affinity Scopes') +print('===============') + +print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') + +def print_pod_type(pt): + print(f' nr_pods {pt.nr_pods.value_()}') + + print(' pod_cpus', end='') + for pod in range(pt.nr_pods): + print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') + print('') + + print(' pod_node', end='') + for pod in range(pt.nr_pods): + print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') + print('') + + print(f' cpu_pod ', end='') + for cpu in for_each_possible_cpu(prog): + print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') + print('') + +for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: + print('') + print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}') + print_pod_type(wq_pod_types[affn]) + +print('') +print('Worker Pools') +print('============') + +max_pool_id_len = 0 +max_ref_len = 0 +for pi, pool in idr_for_each(worker_pool_idr): + pool = drgn.Object(prog, 'struct worker_pool', address=pool) + max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) + max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) + +for pi, pool in idr_for_each(worker_pool_idr): + pool = drgn.Object(prog, 'struct worker_pool', address=pool) + print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='') + print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') + if pool.cpu >= 0: + print(f'cpu={pool.cpu.value_():3}', end='') + else: + print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') + print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='') + if pool.attrs.affn_strict: + print(' strict', end='') + print('') + +print('') +print('Workqueue CPU -> pool') +print('=====================') + +print('[ workqueue \ type CPU', end='') +for cpu in for_each_possible_cpu(prog): + print(f' {cpu:{max_pool_id_len}}', end='') +print(' dfl]') + +for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): + print(f'{wq.name.string_().decode()[-24:]:24}', end='') + if wq.flags & WQ_UNBOUND: + if wq.flags & WQ_ORDERED: + print(' ordered ', end='') + else: + print(' unbound', end='') + if wq.unbound_attrs.affn_strict: + print(',S ', end='') + else: + print(' ', end='') + else: + print(' percpu ', end='') + + for cpu in for_each_possible_cpu(prog): + pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() + field_len = max(len(str(cpu)), max_pool_id_len) + print(f' {pool_id:{field_len}}', end='') + + if wq.flags & WQ_UNBOUND: + print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') + print('') diff --git a/tools/workqueue/wq_monitor.py b/tools/workqueue/wq_monitor.py index 6e258d123e8c..a8856a9c45dc 100644 --- a/tools/workqueue/wq_monitor.py +++ b/tools/workqueue/wq_monitor.py @@ -20,8 +20,11 @@ https://github.com/osandov/drgn. and got excluded from concurrency management to avoid stalling other work items. - CMwake The number of concurrency-management wake-ups while executing a - work item of the workqueue. + CMW/RPR For per-cpu workqueues, the number of concurrency-management + wake-ups while executing a work item of the workqueue. For + unbound workqueues, the number of times a worker was repatriated + to its affinity scope after being migrated to an off-scope CPU by + the scheduler. mayday The number of times the rescuer was requested while waiting for new worker creation. @@ -65,6 +68,7 @@ PWQ_STAT_COMPLETED = prog['PWQ_STAT_COMPLETED'] # work items completed exec PWQ_STAT_CPU_TIME = prog['PWQ_STAT_CPU_TIME'] # total CPU time consumed PWQ_STAT_CPU_INTENSIVE = prog['PWQ_STAT_CPU_INTENSIVE'] # wq_cpu_intensive_thresh_us violations PWQ_STAT_CM_WAKEUP = prog['PWQ_STAT_CM_WAKEUP'] # concurrency-management worker wakeups +PWQ_STAT_REPATRIATED = prog['PWQ_STAT_REPATRIATED'] # unbound workers brought back into scope PWQ_STAT_MAYDAY = prog['PWQ_STAT_MAYDAY'] # maydays to rescuer PWQ_STAT_RESCUED = prog['PWQ_STAT_RESCUED'] # linked work items executed by rescuer PWQ_NR_STATS = prog['PWQ_NR_STATS'] @@ -89,22 +93,25 @@ class WqStats: 'cpu_time' : self.stats[PWQ_STAT_CPU_TIME], 'cpu_intensive' : self.stats[PWQ_STAT_CPU_INTENSIVE], 'cm_wakeup' : self.stats[PWQ_STAT_CM_WAKEUP], + 'repatriated' : self.stats[PWQ_STAT_REPATRIATED], 'mayday' : self.stats[PWQ_STAT_MAYDAY], 'rescued' : self.stats[PWQ_STAT_RESCUED], } def table_header_str(): return f'{"":>24} {"total":>8} {"infl":>5} {"CPUtime":>8} '\ - f'{"CPUitsv":>7} {"CMwake":>7} {"mayday":>7} {"rescued":>7}' + f'{"CPUitsv":>7} {"CMW/RPR":>7} {"mayday":>7} {"rescued":>7}' def table_row_str(self): cpu_intensive = '-' - cm_wakeup = '-' + cmw_rpr = '-' mayday = '-' rescued = '-' - if not self.unbound: + if self.unbound: + cmw_rpr = str(self.stats[PWQ_STAT_REPATRIATED]); + else: cpu_intensive = str(self.stats[PWQ_STAT_CPU_INTENSIVE]) - cm_wakeup = str(self.stats[PWQ_STAT_CM_WAKEUP]) + cmw_rpr = str(self.stats[PWQ_STAT_CM_WAKEUP]) if self.mem_reclaim: mayday = str(self.stats[PWQ_STAT_MAYDAY]) @@ -115,7 +122,7 @@ class WqStats: f'{max(self.stats[PWQ_STAT_STARTED] - self.stats[PWQ_STAT_COMPLETED], 0):5} ' \ f'{self.stats[PWQ_STAT_CPU_TIME] / 1000000:8.1f} ' \ f'{cpu_intensive:>7} ' \ - f'{cm_wakeup:>7} ' \ + f'{cmw_rpr:>7} ' \ f'{mayday:>7} ' \ f'{rescued:>7} ' return out.rstrip(':') |