diff options
Diffstat (limited to 'tools')
390 files changed, 14343 insertions, 2326 deletions
diff --git a/tools/arch/arm/include/uapi/asm/kvm.h b/tools/arch/arm/include/uapi/asm/kvm.h index a2b3eb313a25..af05f8e0903e 100644 --- a/tools/arch/arm/include/uapi/asm/kvm.h +++ b/tools/arch/arm/include/uapi/asm/kvm.h @@ -84,6 +84,15 @@ struct kvm_regs { #define KVM_VGIC_V2_DIST_SIZE 0x1000 #define KVM_VGIC_V2_CPU_SIZE 0x2000 +/* Supported VGICv3 address types */ +#define KVM_VGIC_V3_ADDR_TYPE_DIST 2 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 +#define KVM_VGIC_ITS_ADDR_TYPE 4 + +#define KVM_VGIC_V3_DIST_SIZE SZ_64K +#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) +#define KVM_VGIC_V3_ITS_SIZE (2 * SZ_64K) + #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ #define KVM_ARM_VCPU_PSCI_0_2 1 /* CPU uses PSCI v0.2 */ diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h index c93cf35ce379..3603b6f51b11 100644 --- a/tools/arch/powerpc/include/uapi/asm/kvm.h +++ b/tools/arch/powerpc/include/uapi/asm/kvm.h @@ -573,6 +573,10 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_SPRG9 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xba) #define KVM_REG_PPC_DBSR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbb) +/* POWER9 registers */ +#define KVM_REG_PPC_TIDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbc) +#define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd) + /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs */ @@ -596,6 +600,7 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67) #define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68) #define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69) +#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a) /* PPC64 eXternal Interrupt Controller Specification */ #define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */ diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index cddd5d06e1cb..293149a1c6a1 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -105,6 +105,7 @@ #define X86_FEATURE_AMD_DCM ( 3*32+27) /* multi-node processor */ #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ +#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ @@ -188,10 +189,14 @@ #define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */ #define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */ +#define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */ +#define X86_FEATURE_CAT_L2 ( 7*32+ 5) /* Cache Allocation Technology L2 */ +#define X86_FEATURE_CDP_L3 ( 7*32+ 6) /* Code and Data Prioritization L3 */ #define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */ #define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */ +#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */ #define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */ #define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */ #define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */ @@ -220,11 +225,13 @@ #define X86_FEATURE_RTM ( 9*32+11) /* Restricted Transactional Memory */ #define X86_FEATURE_CQM ( 9*32+12) /* Cache QoS Monitoring */ #define X86_FEATURE_MPX ( 9*32+14) /* Memory Protection Extension */ +#define X86_FEATURE_RDT_A ( 9*32+15) /* Resource Director Technology Allocation */ #define X86_FEATURE_AVX512F ( 9*32+16) /* AVX-512 Foundation */ #define X86_FEATURE_AVX512DQ ( 9*32+17) /* AVX-512 DQ (Double/Quad granular) Instructions */ #define X86_FEATURE_RDSEED ( 9*32+18) /* The RDSEED instruction */ #define X86_FEATURE_ADX ( 9*32+19) /* The ADCX and ADOX instructions */ #define X86_FEATURE_SMAP ( 9*32+20) /* Supervisor Mode Access Prevention */ +#define X86_FEATURE_AVX512IFMA ( 9*32+21) /* AVX-512 Integer Fused Multiply-Add instructions */ #define X86_FEATURE_CLFLUSHOPT ( 9*32+23) /* CLFLUSHOPT instruction */ #define X86_FEATURE_CLWB ( 9*32+24) /* CLWB instruction */ #define X86_FEATURE_AVX512PF ( 9*32+26) /* AVX-512 Prefetch */ @@ -278,8 +285,11 @@ #define X86_FEATURE_AVIC (15*32+13) /* Virtual Interrupt Controller */ /* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx), word 16 */ +#define X86_FEATURE_AVX512VBMI (16*32+ 1) /* AVX512 Vector Bit Manipulation instructions*/ #define X86_FEATURE_PKU (16*32+ 3) /* Protection Keys for Userspace */ #define X86_FEATURE_OSPKE (16*32+ 4) /* OS Protection Keys Enable */ +#define X86_FEATURE_AVX512_VPOPCNTDQ (16*32+14) /* POPCNT for vectors of DW/QW */ +#define X86_FEATURE_RDPID (16*32+ 22) /* RDPID instruction */ /* AMD-defined CPU features, CPUID level 0x80000007 (ebx), word 17 */ #define X86_FEATURE_OVERFLOW_RECOV (17*32+0) /* MCA overflow recovery support */ @@ -310,4 +320,6 @@ #define X86_BUG_NULL_SEG X86_BUG(10) /* Nulling a selector preserves the base */ #define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */ #define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */ +#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */ + #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/tools/arch/x86/include/uapi/asm/vmx.h b/tools/arch/x86/include/uapi/asm/vmx.h index 37fee272618f..14458658e988 100644 --- a/tools/arch/x86/include/uapi/asm/vmx.h +++ b/tools/arch/x86/include/uapi/asm/vmx.h @@ -65,6 +65,8 @@ #define EXIT_REASON_TPR_BELOW_THRESHOLD 43 #define EXIT_REASON_APIC_ACCESS 44 #define EXIT_REASON_EOI_INDUCED 45 +#define EXIT_REASON_GDTR_IDTR 46 +#define EXIT_REASON_LDTR_TR 47 #define EXIT_REASON_EPT_VIOLATION 48 #define EXIT_REASON_EPT_MISCONFIG 49 #define EXIT_REASON_INVEPT 50 @@ -113,6 +115,8 @@ { EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \ { EXIT_REASON_TPR_BELOW_THRESHOLD, "TPR_BELOW_THRESHOLD" }, \ { EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \ + { EXIT_REASON_GDTR_IDTR, "GDTR_IDTR" }, \ + { EXIT_REASON_LDTR_TR, "LDTR_TR" }, \ { EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \ { EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \ { EXIT_REASON_INVEPT, "INVEPT" }, \ @@ -129,6 +133,7 @@ { EXIT_REASON_XRSTORS, "XRSTORS" } #define VMX_ABORT_SAVE_GUEST_MSR_FAIL 1 +#define VMX_ABORT_LOAD_HOST_PDPTE_FAIL 2 #define VMX_ABORT_LOAD_HOST_MSR_FAIL 4 #endif /* _UAPIVMX_H */ diff --git a/tools/build/Makefile b/tools/build/Makefile index aaf7ed329a45..477f00eda591 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -35,8 +35,8 @@ all: $(OUTPUT)fixdep clean: $(call QUIET_CLEAN, fixdep) - $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete - $(Q)rm -f fixdep + $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete + $(Q)rm -f $(OUTPUT)fixdep $(OUTPUT)fixdep-in.o: FORCE $(Q)$(MAKE) $(build)=fixdep diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index 99c0ccd2f176..e279a71c650d 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build @@ -19,6 +19,16 @@ else Q=@ endif +ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 +ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) + quiet=silent_ +endif +else # make-3.8x +ifneq ($(filter s% -s%,$(MAKEFLAGS)),) + quiet=silent_ +endif +endif + build-dir := $(srctree)/tools/build # Define $(fixdep) for dep-cmd function diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index ad22e4e7bc59..d360f39a445b 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include @@ -3,4 +3,7 @@ build := -f $(srctree)/tools/build/Makefile.build dir=. obj fixdep: $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep +fixdep-clean: + $(Q)$(MAKE) -C $(srctree)/tools/build clean + .PHONY: fixdep diff --git a/tools/gpio/.gitignore b/tools/gpio/.gitignore new file mode 100644 index 000000000000..9e9dd4b681b2 --- /dev/null +++ b/tools/gpio/.gitignore @@ -0,0 +1,4 @@ +gpio-event-mon +gpio-hammer +lsgpio + diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c index f1eab587dfea..4bcb234c0fca 100644 --- a/tools/gpio/gpio-hammer.c +++ b/tools/gpio/gpio-hammer.c @@ -38,7 +38,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines, memset(&data.values, 0, sizeof(data.values)); ret = gpiotools_request_linehandle(device_name, lines, nlines, GPIOHANDLE_REQUEST_OUTPUT, &data, - "gpio-hammler"); + "gpio-hammer"); if (ret < 0) goto exit_error; else diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index d9b7e0f306c6..b61245e1181d 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -57,6 +57,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_RESISTANCE] = "resistance", [IIO_PH] = "ph", [IIO_UVINDEX] = "uvindex", + [IIO_GRAVITY] = "gravity", }; static const char * const iio_ev_type_text[] = { @@ -149,6 +150,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_RESISTANCE: case IIO_PH: case IIO_UVINDEX: + case IIO_GRAVITY: break; default: return false; diff --git a/tools/include/asm-generic/bitops/atomic.h b/tools/include/asm-generic/bitops/atomic.h index 18663f59d72f..68b8c1516c5a 100644 --- a/tools/include/asm-generic/bitops/atomic.h +++ b/tools/include/asm-generic/bitops/atomic.h @@ -20,4 +20,7 @@ static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) (((unsigned long *)addr)[nr / __BITS_PER_LONG])) != 0; } +#define __set_bit(nr, addr) set_bit(nr, addr) +#define __clear_bit(nr, addr) clear_bit(nr, addr) + #endif /* _TOOLS_LINUX_ASM_GENERIC_BITOPS_ATOMIC_H_ */ diff --git a/tools/include/asm/bug.h b/tools/include/asm/bug.h index beda1a884b50..4790f047a89c 100644 --- a/tools/include/asm/bug.h +++ b/tools/include/asm/bug.h @@ -12,6 +12,14 @@ unlikely(__ret_warn_on); \ }) +#define WARN_ON(condition) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + __WARN_printf("assertion failed at %s:%d\n", \ + __FILE__, __LINE__); \ + unlikely(__ret_warn_on); \ +}) + #define WARN_ON_ONCE(condition) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \ diff --git a/tools/include/linux/bitmap.h b/tools/include/linux/bitmap.h index eef41d500e9e..e8b9f518e36b 100644 --- a/tools/include/linux/bitmap.h +++ b/tools/include/linux/bitmap.h @@ -4,6 +4,7 @@ #include <string.h> #include <linux/bitops.h> #include <stdlib.h> +#include <linux/kernel.h> #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h index fc446343ff41..1aecad369af5 100644 --- a/tools/include/linux/bitops.h +++ b/tools/include/linux/bitops.h @@ -2,7 +2,6 @@ #define _TOOLS_LINUX_BITOPS_H_ #include <asm/types.h> -#include <linux/kernel.h> #include <linux/compiler.h> #ifndef __WORDSIZE diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h new file mode 100644 index 000000000000..48af2f10a42d --- /dev/null +++ b/tools/include/linux/compiler-gcc.h @@ -0,0 +1,14 @@ +#ifndef _TOOLS_LINUX_COMPILER_H_ +#error "Please don't include <linux/compiler-gcc.h> directly, include <linux/compiler.h> instead." +#endif + +/* + * Common definitions for all gcc versions go here. + */ +#define GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION >= 70000 && !defined(__CHECKER__) +# define __fallthrough __attribute__ ((fallthrough)) +#endif diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index e33fc1df3935..8de163b17c0d 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h @@ -1,6 +1,10 @@ #ifndef _TOOLS_LINUX_COMPILER_H_ #define _TOOLS_LINUX_COMPILER_H_ +#ifdef __GNUC__ +#include <linux/compiler-gcc.h> +#endif + /* Optimization barrier */ /* The "volatile" is due to gcc bugs */ #define barrier() __asm__ __volatile__("": : :"memory") @@ -21,6 +25,8 @@ #endif #define __user +#define __rcu +#define __read_mostly #ifndef __attribute_const__ # define __attribute_const__ @@ -50,6 +56,8 @@ # define unlikely(x) __builtin_expect(!!(x), 0) #endif +#define uninitialized_var(x) x = *(&(x)) + #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) #include <linux/types.h> @@ -126,4 +134,9 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s #define WRITE_ONCE(x, val) \ ({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; }) + +#ifndef __fallthrough +# define __fallthrough +#endif + #endif /* _TOOLS_LINUX_COMPILER_H */ diff --git a/tools/include/linux/filter.h b/tools/include/linux/filter.h index 122153b16ea4..390d7c9685fd 100644 --- a/tools/include/linux/filter.h +++ b/tools/include/linux/filter.h @@ -168,6 +168,16 @@ .off = OFF, \ .imm = 0 }) +/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */ + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h index 41446668ccce..d5677d39c1e4 100644 --- a/tools/include/linux/log2.h +++ b/tools/include/linux/log2.h @@ -13,12 +13,6 @@ #define _TOOLS_LINUX_LOG2_H /* - * deal with unrepresentable constant logarithms - */ -extern __attribute__((const, noreturn)) -int ____ilog2_NaN(void); - -/* * non-constant log of base 2 calculators * - the arch may override these in asm/bitops.h if they can be implemented * more efficiently than using fls() and fls64() @@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) #define ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ - (n) < 1 ? ____ilog2_NaN() : \ + (n) < 2 ? 0 : \ (n) & (1ULL << 63) ? 63 : \ (n) & (1ULL << 62) ? 62 : \ (n) & (1ULL << 61) ? 61 : \ @@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - (n) & (1ULL << 1) ? 1 : \ - (n) & (1ULL << 0) ? 0 : \ - ____ilog2_NaN() \ - ) : \ + 1 ) : \ (sizeof(n) <= 4) ? \ __ilog2_u32(n) : \ __ilog2_u64(n) \ diff --git a/tools/include/linux/spinlock.h b/tools/include/linux/spinlock.h new file mode 100644 index 000000000000..58397dcb19d6 --- /dev/null +++ b/tools/include/linux/spinlock.h @@ -0,0 +1,5 @@ +#define spinlock_t pthread_mutex_t +#define DEFINE_SPINLOCK(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; + +#define spin_lock_irqsave(x, f) (void)f, pthread_mutex_lock(x) +#define spin_unlock_irqrestore(x, f) (void)f, pthread_mutex_unlock(x) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index d2b0ac799d03..0539a0ceef38 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -63,6 +63,12 @@ struct bpf_insn { __s32 imm; /* signed immediate constant */ }; +/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ +struct bpf_lpm_trie_key { + __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ + __u8 data[0]; /* Arbitrary size */ +}; + /* BPF syscall commands, see bpf(2) man-page for details. */ enum bpf_cmd { BPF_MAP_CREATE, @@ -89,6 +95,7 @@ enum bpf_map_type { BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, + BPF_MAP_TYPE_LPM_TRIE, }; enum bpf_prog_type { @@ -437,6 +444,18 @@ union bpf_attr { * @xdp_md: pointer to xdp_md * @delta: An positive/negative integer to be added to xdp_md.data * Return: 0 on success or negative on error + * + * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) + * Copy a NUL terminated string from unsafe address. In case the string + * length is smaller than size, the target is not padded with further NUL + * bytes. In case the string length is larger than size, just count-1 + * bytes are copied and the last byte is set to NUL. + * @dst: destination address + * @size: maximum number of bytes to copy, including the trailing NUL + * @unsafe_ptr: unsafe address + * Return: + * > 0 length of the string including the trailing NUL on success + * < 0 error */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -483,7 +502,8 @@ union bpf_attr { FN(set_hash_invalid), \ FN(get_numa_node_id), \ FN(skb_change_head), \ - FN(xdp_adjust_head), + FN(xdp_adjust_head), \ + FN(probe_read_str), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -509,6 +529,7 @@ enum bpf_func_id { /* BPF_FUNC_l4_csum_replace flags. */ #define BPF_F_PSEUDO_HDR (1ULL << 4) #define BPF_F_MARK_MANGLED_0 (1ULL << 5) +#define BPF_F_MARK_ENFORCE (1ULL << 6) /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ #define BPF_F_INGRESS (1ULL << 0) diff --git a/tools/include/uapi/linux/bpf_perf_event.h b/tools/include/uapi/linux/bpf_perf_event.h new file mode 100644 index 000000000000..067427259820 --- /dev/null +++ b/tools/include/uapi/linux/bpf_perf_event.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef _UAPI__LINUX_BPF_PERF_EVENT_H__ +#define _UAPI__LINUX_BPF_PERF_EVENT_H__ + +#include <linux/types.h> +#include <linux/ptrace.h> + +struct bpf_perf_event_data { + struct pt_regs regs; + __u64 sample_period; +}; + +#endif /* _UAPI__LINUX_BPF_PERF_EVENT_H__ */ diff --git a/tools/leds/Makefile b/tools/leds/Makefile index c03a79ebf9c8..078b666fd78b 100644 --- a/tools/leds/Makefile +++ b/tools/leds/Makefile @@ -3,11 +3,11 @@ CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall -Wextra -g -I../../include/uapi -all: uledmon +all: uledmon led_hw_brightness_mon %: %.c $(CC) $(CFLAGS) -o $@ $^ clean: - $(RM) uledmon + $(RM) uledmon led_hw_brightness_mon .PHONY: all clean diff --git a/tools/leds/led_hw_brightness_mon.c b/tools/leds/led_hw_brightness_mon.c new file mode 100644 index 000000000000..64642ccfe442 --- /dev/null +++ b/tools/leds/led_hw_brightness_mon.c @@ -0,0 +1,84 @@ +/* + * led_hw_brightness_mon.c + * + * This program monitors LED brightness level changes having its origin + * in hardware/firmware, i.e. outside of kernel control. + * A timestamp and brightness value is printed each time the brightness changes. + * + * Usage: led_hw_brightness_mon <device-name> + * + * <device-name> is the name of the LED class device to be monitored. Pressing + * CTRL+C will exit. + */ + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <linux/uleds.h> + +int main(int argc, char const *argv[]) +{ + int fd, ret; + char brightness_file_path[LED_MAX_NAME_SIZE + 11]; + struct pollfd pollfd; + struct timespec ts; + char buf[11]; + + if (argc != 2) { + fprintf(stderr, "Requires <device-name> argument\n"); + return 1; + } + + snprintf(brightness_file_path, LED_MAX_NAME_SIZE, + "/sys/class/leds/%s/brightness_hw_changed", argv[1]); + + fd = open(brightness_file_path, O_RDONLY); + if (fd == -1) { + printf("Failed to open %s file\n", brightness_file_path); + return 1; + } + + /* + * read may fail if no hw brightness change has occurred so far, + * but it is required to avoid spurious poll notifications in + * the opposite case. + */ + read(fd, buf, sizeof(buf)); + + pollfd.fd = fd; + pollfd.events = POLLPRI; + + while (1) { + ret = poll(&pollfd, 1, -1); + if (ret == -1) { + printf("Failed to poll %s file (%d)\n", + brightness_file_path, ret); + ret = 1; + break; + } + + clock_gettime(CLOCK_MONOTONIC, &ts); + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) + break; + + ret = lseek(pollfd.fd, 0, SEEK_SET); + if (ret < 0) { + printf("lseek failed (%d)\n", ret); + break; + } + + printf("[%ld.%09ld] %d\n", ts.tv_sec, ts.tv_nsec, atoi(buf)); + } + + close(fd); + + return ret; +} diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 11c8d9bc762e..5d19fdf80292 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1387,7 +1387,7 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val) /* Allow writing to any other BAR, or expansion ROM */ iowrite(portoff, val, mask, &d->config_words[reg]); return true; - /* We let them overide latency timer and cacheline size */ + /* We let them override latency timer and cacheline size */ } else if (&d->config_words[reg] == (void *)&d->config.cacheline_size) { /* Only let them change the first two fields. */ if (mask == 0xFFFFFFFF) diff --git a/tools/lib/api/Makefile b/tools/lib/api/Makefile index adba83b325d5..eb6e0b36bfc1 100644 --- a/tools/lib/api/Makefile +++ b/tools/lib/api/Makefile @@ -17,7 +17,13 @@ MAKEFLAGS += --no-print-directory LIBFILE = $(OUTPUT)libapi.a CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC + +ifeq ($(CC), clang) + CFLAGS += -O3 +else + CFLAGS += -O6 +endif # Treat warnings as errors unless directed not to ifneq ($(WERROR),0) diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index f99f49e4a31e..4b6bfc43cccf 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -38,6 +38,10 @@ #define HUGETLBFS_MAGIC 0x958458f6 #endif +#ifndef BPF_FS_MAGIC +#define BPF_FS_MAGIC 0xcafe4a11 +#endif + static const char * const sysfs__fs_known_mountpoints[] = { "/sys", 0, @@ -75,6 +79,11 @@ static const char * const hugetlbfs__known_mountpoints[] = { 0, }; +static const char * const bpf_fs__known_mountpoints[] = { + "/sys/fs/bpf", + 0, +}; + struct fs { const char *name; const char * const *mounts; @@ -89,6 +98,7 @@ enum { FS__DEBUGFS = 2, FS__TRACEFS = 3, FS__HUGETLBFS = 4, + FS__BPF_FS = 5, }; #ifndef TRACEFS_MAGIC @@ -121,6 +131,11 @@ static struct fs fs__entries[] = { .mounts = hugetlbfs__known_mountpoints, .magic = HUGETLBFS_MAGIC, }, + [FS__BPF_FS] = { + .name = "bpf", + .mounts = bpf_fs__known_mountpoints, + .magic = BPF_FS_MAGIC, + }, }; static bool fs__read_mounts(struct fs *fs) @@ -280,6 +295,7 @@ FS(procfs, FS__PROCFS); FS(debugfs, FS__DEBUGFS); FS(tracefs, FS__TRACEFS); FS(hugetlbfs, FS__HUGETLBFS); +FS(bpf_fs, FS__BPF_FS); int filename__read_int(const char *filename, int *value) { diff --git a/tools/lib/api/fs/fs.h b/tools/lib/api/fs/fs.h index a63269f5d20c..6b332dc74498 100644 --- a/tools/lib/api/fs/fs.h +++ b/tools/lib/api/fs/fs.h @@ -22,6 +22,7 @@ FS(procfs) FS(debugfs) FS(tracefs) FS(hugetlbfs) +FS(bpf_fs) #undef FS diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c index 251b7c342a87..3e606b9c443e 100644 --- a/tools/lib/api/fs/tracing_path.c +++ b/tools/lib/api/fs/tracing_path.c @@ -86,9 +86,13 @@ void put_tracing_file(char *file) free(file); } -static int strerror_open(int err, char *buf, size_t size, const char *filename) +int tracing_path__strerror_open_tp(int err, char *buf, size_t size, + const char *sys, const char *name) { char sbuf[128]; + char filename[PATH_MAX]; + + snprintf(filename, PATH_MAX, "%s/%s", sys, name ?: "*"); switch (err) { case ENOENT: @@ -99,10 +103,19 @@ static int strerror_open(int err, char *buf, size_t size, const char *filename) * - jirka */ if (debugfs__configured() || tracefs__configured()) { - snprintf(buf, size, - "Error:\tFile %s/%s not found.\n" - "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n", - tracing_events_path, filename); + /* sdt markers */ + if (!strncmp(filename, "sdt_", 4)) { + snprintf(buf, size, + "Error:\tFile %s/%s not found.\n" + "Hint:\tSDT event cannot be directly recorded on.\n" + "\tPlease first use 'perf probe %s:%s' before recording it.\n", + tracing_events_path, filename, sys, name); + } else { + snprintf(buf, size, + "Error:\tFile %s/%s not found.\n" + "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n", + tracing_events_path, filename); + } break; } snprintf(buf, size, "%s", @@ -125,12 +138,3 @@ static int strerror_open(int err, char *buf, size_t size, const char *filename) return 0; } - -int tracing_path__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name) -{ - char path[PATH_MAX]; - - snprintf(path, PATH_MAX, "%s/%s", sys, name ?: "*"); - - return strerror_open(err, buf, size, path); -} diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index e2efddf10231..1f5300e56b44 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -132,7 +132,7 @@ else Q = @ endif -# Disable command line variables (CFLAGS) overide from top +# Disable command line variables (CFLAGS) override from top # level Makefile (perf), otherwise build Makefile will get # the same command line setup. MAKEOVERRIDES= diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index ae752fa4eaa7..207c2eeddab0 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -27,7 +27,7 @@ #include "bpf.h" /* - * When building perf, unistd.h is overrided. __NR_bpf is + * When building perf, unistd.h is overridden. __NR_bpf is * required to be defined explicitly. */ #ifndef __NR_bpf @@ -42,13 +42,13 @@ # endif #endif -static __u64 ptr_to_u64(void *ptr) +static inline __u64 ptr_to_u64(const void *ptr) { return (__u64) (unsigned long) ptr; } -static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, - unsigned int size) +static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, + unsigned int size) { return syscall(__NR_bpf, cmd, attr, size); } @@ -69,8 +69,8 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } -int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, - size_t insns_cnt, char *license, +int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, + size_t insns_cnt, const char *license, __u32 kern_version, char *log_buf, size_t log_buf_sz) { int fd; @@ -98,7 +98,7 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } -int bpf_map_update_elem(int fd, void *key, void *value, +int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags) { union bpf_attr attr; @@ -112,7 +112,7 @@ int bpf_map_update_elem(int fd, void *key, void *value, return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } -int bpf_map_lookup_elem(int fd, void *key, void *value) +int bpf_map_lookup_elem(int fd, const void *key, void *value) { union bpf_attr attr; @@ -124,7 +124,7 @@ int bpf_map_lookup_elem(int fd, void *key, void *value) return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } -int bpf_map_delete_elem(int fd, void *key) +int bpf_map_delete_elem(int fd, const void *key) { union bpf_attr attr; @@ -135,7 +135,7 @@ int bpf_map_delete_elem(int fd, void *key) return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); } -int bpf_map_get_next_key(int fd, void *key, void *next_key) +int bpf_map_get_next_key(int fd, const void *key, void *next_key) { union bpf_attr attr; diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 4ac6c4b84100..09c3dcac0496 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -22,23 +22,24 @@ #define __BPF_BPF_H #include <linux/bpf.h> +#include <stddef.h> int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, __u32 map_flags); /* Recommend log buffer size */ #define BPF_LOG_BUF_SIZE 65536 -int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, - size_t insns_cnt, char *license, +int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, + size_t insns_cnt, const char *license, __u32 kern_version, char *log_buf, size_t log_buf_sz); -int bpf_map_update_elem(int fd, void *key, void *value, +int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags); -int bpf_map_lookup_elem(int fd, void *key, void *value); -int bpf_map_delete_elem(int fd, void *key); -int bpf_map_get_next_key(int fd, void *key, void *next_key); +int bpf_map_lookup_elem(int fd, const void *key, void *value); +int bpf_map_delete_elem(int fd, const void *key); +int bpf_map_get_next_key(int fd, const void *key, void *next_key); int bpf_obj_pin(int fd, const char *pathname); int bpf_obj_get(const char *pathname); int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type, diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 84e6b35da4bd..ac6eb863b2a4 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -4,6 +4,7 @@ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org> * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com> * Copyright (C) 2015 Huawei Inc. + * Copyright (C) 2017 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,15 +23,21 @@ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> +#include <libgen.h> #include <inttypes.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <asm/unistd.h> +#include <linux/err.h> #include <linux/kernel.h> #include <linux/bpf.h> #include <linux/list.h> +#include <linux/limits.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> #include <libelf.h> #include <gelf.h> @@ -41,6 +48,10 @@ #define EM_BPF 247 #endif +#ifndef BPF_FS_MAGIC +#define BPF_FS_MAGIC 0xcafe4a11 +#endif + #define __printf(a, b) __attribute__((format(printf, a, b))) __printf(1, 2) @@ -779,7 +790,7 @@ static int bpf_program__collect_reloc(struct bpf_program *prog, size_t nr_maps, GElf_Shdr *shdr, Elf_Data *data, Elf_Data *symbols, - int maps_shndx) + int maps_shndx, struct bpf_map *maps) { int i, nrels; @@ -829,7 +840,15 @@ bpf_program__collect_reloc(struct bpf_program *prog, return -LIBBPF_ERRNO__RELOC; } - map_idx = sym.st_value / sizeof(struct bpf_map_def); + /* TODO: 'maps' is sorted. We can use bsearch to make it faster. */ + for (map_idx = 0; map_idx < nr_maps; map_idx++) { + if (maps[map_idx].offset == sym.st_value) { + pr_debug("relocation: find map %zd (%s) for insn %u\n", + map_idx, maps[map_idx].name, insn_idx); + break; + } + } + if (map_idx >= nr_maps) { pr_warning("bpf relocation: map_idx %d large than %d\n", (int)map_idx, (int)nr_maps - 1); @@ -953,7 +972,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) err = bpf_program__collect_reloc(prog, nr_maps, shdr, data, obj->efile.symbols, - obj->efile.maps_shndx); + obj->efile.maps_shndx, + obj->maps); if (err) return err; } @@ -1227,6 +1247,191 @@ out: return err; } +static int check_path(const char *path) +{ + struct statfs st_fs; + char *dname, *dir; + int err = 0; + + if (path == NULL) + return -EINVAL; + + dname = strdup(path); + if (dname == NULL) + return -ENOMEM; + + dir = dirname(dname); + if (statfs(dir, &st_fs)) { + pr_warning("failed to statfs %s: %s\n", dir, strerror(errno)); + err = -errno; + } + free(dname); + + if (!err && st_fs.f_type != BPF_FS_MAGIC) { + pr_warning("specified path %s is not on BPF FS\n", path); + err = -EINVAL; + } + + return err; +} + +int bpf_program__pin_instance(struct bpf_program *prog, const char *path, + int instance) +{ + int err; + + err = check_path(path); + if (err) + return err; + + if (prog == NULL) { + pr_warning("invalid program pointer\n"); + return -EINVAL; + } + + if (instance < 0 || instance >= prog->instances.nr) { + pr_warning("invalid prog instance %d of prog %s (max %d)\n", + instance, prog->section_name, prog->instances.nr); + return -EINVAL; + } + + if (bpf_obj_pin(prog->instances.fds[instance], path)) { + pr_warning("failed to pin program: %s\n", strerror(errno)); + return -errno; + } + pr_debug("pinned program '%s'\n", path); + + return 0; +} + +static int make_dir(const char *path) +{ + int err = 0; + + if (mkdir(path, 0700) && errno != EEXIST) + err = -errno; + + if (err) + pr_warning("failed to mkdir %s: %s\n", path, strerror(-err)); + return err; +} + +int bpf_program__pin(struct bpf_program *prog, const char *path) +{ + int i, err; + + err = check_path(path); + if (err) + return err; + + if (prog == NULL) { + pr_warning("invalid program pointer\n"); + return -EINVAL; + } + + if (prog->instances.nr <= 0) { + pr_warning("no instances of prog %s to pin\n", + prog->section_name); + return -EINVAL; + } + + err = make_dir(path); + if (err) + return err; + + for (i = 0; i < prog->instances.nr; i++) { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%d", path, i); + if (len < 0) + return -EINVAL; + else if (len >= PATH_MAX) + return -ENAMETOOLONG; + + err = bpf_program__pin_instance(prog, buf, i); + if (err) + return err; + } + + return 0; +} + +int bpf_map__pin(struct bpf_map *map, const char *path) +{ + int err; + + err = check_path(path); + if (err) + return err; + + if (map == NULL) { + pr_warning("invalid map pointer\n"); + return -EINVAL; + } + + if (bpf_obj_pin(map->fd, path)) { + pr_warning("failed to pin map: %s\n", strerror(errno)); + return -errno; + } + + pr_debug("pinned map '%s'\n", path); + return 0; +} + +int bpf_object__pin(struct bpf_object *obj, const char *path) +{ + struct bpf_program *prog; + struct bpf_map *map; + int err; + + if (!obj) + return -ENOENT; + + if (!obj->loaded) { + pr_warning("object not yet loaded; load it first\n"); + return -ENOENT; + } + + err = make_dir(path); + if (err) + return err; + + bpf_map__for_each(map, obj) { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, + bpf_map__name(map)); + if (len < 0) + return -EINVAL; + else if (len >= PATH_MAX) + return -ENAMETOOLONG; + + err = bpf_map__pin(map, buf); + if (err) + return err; + } + + bpf_object__for_each_program(prog, obj) { + char buf[PATH_MAX]; + int len; + + len = snprintf(buf, PATH_MAX, "%s/%s", path, + prog->section_name); + if (len < 0) + return -EINVAL; + else if (len >= PATH_MAX) + return -ENAMETOOLONG; + + err = bpf_program__pin(prog, buf); + if (err) + return err; + } + + return 0; +} + void bpf_object__close(struct bpf_object *obj) { size_t i; @@ -1419,37 +1624,33 @@ static void bpf_program__set_type(struct bpf_program *prog, prog->type = type; } -int bpf_program__set_tracepoint(struct bpf_program *prog) -{ - if (!prog) - return -EINVAL; - bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT); - return 0; -} - -int bpf_program__set_kprobe(struct bpf_program *prog) -{ - if (!prog) - return -EINVAL; - bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE); - return 0; -} - static bool bpf_program__is_type(struct bpf_program *prog, enum bpf_prog_type type) { return prog ? (prog->type == type) : false; } -bool bpf_program__is_tracepoint(struct bpf_program *prog) -{ - return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT); -} - -bool bpf_program__is_kprobe(struct bpf_program *prog) -{ - return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE); -} +#define BPF_PROG_TYPE_FNS(NAME, TYPE) \ +int bpf_program__set_##NAME(struct bpf_program *prog) \ +{ \ + if (!prog) \ + return -EINVAL; \ + bpf_program__set_type(prog, TYPE); \ + return 0; \ +} \ + \ +bool bpf_program__is_##NAME(struct bpf_program *prog) \ +{ \ + return bpf_program__is_type(prog, TYPE); \ +} \ + +BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER); +BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE); +BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS); +BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT); +BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT); +BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP); +BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT); int bpf_map__fd(struct bpf_map *map) { @@ -1537,3 +1738,10 @@ bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset) } return ERR_PTR(-ENOENT); } + +long libbpf_get_error(const void *ptr) +{ + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + return 0; +} diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index a5a8b86a06fe..b30394f9947a 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -22,8 +22,8 @@ #define __BPF_LIBBPF_H #include <stdio.h> +#include <stdint.h> #include <stdbool.h> -#include <linux/err.h> #include <sys/types.h> // for size_t enum libbpf_errno { @@ -65,6 +65,7 @@ struct bpf_object *bpf_object__open(const char *path); struct bpf_object *bpf_object__open_buffer(void *obj_buf, size_t obj_buf_sz, const char *name); +int bpf_object__pin(struct bpf_object *object, const char *path); void bpf_object__close(struct bpf_object *object); /* Load/unload object into/from kernel */ @@ -106,6 +107,9 @@ void *bpf_program__priv(struct bpf_program *prog); const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); int bpf_program__fd(struct bpf_program *prog); +int bpf_program__pin_instance(struct bpf_program *prog, const char *path, + int instance); +int bpf_program__pin(struct bpf_program *prog, const char *path); struct bpf_insn; @@ -174,11 +178,21 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n); /* * Adjust type of bpf program. Default is kprobe. */ +int bpf_program__set_socket_filter(struct bpf_program *prog); int bpf_program__set_tracepoint(struct bpf_program *prog); int bpf_program__set_kprobe(struct bpf_program *prog); +int bpf_program__set_sched_cls(struct bpf_program *prog); +int bpf_program__set_sched_act(struct bpf_program *prog); +int bpf_program__set_xdp(struct bpf_program *prog); +int bpf_program__set_perf_event(struct bpf_program *prog); +bool bpf_program__is_socket_filter(struct bpf_program *prog); bool bpf_program__is_tracepoint(struct bpf_program *prog); bool bpf_program__is_kprobe(struct bpf_program *prog); +bool bpf_program__is_sched_cls(struct bpf_program *prog); +bool bpf_program__is_sched_act(struct bpf_program *prog); +bool bpf_program__is_xdp(struct bpf_program *prog); +bool bpf_program__is_perf_event(struct bpf_program *prog); /* * We don't need __attribute__((packed)) now since it is @@ -223,5 +237,8 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); int bpf_map__set_priv(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv); void *bpf_map__priv(struct bpf_map *map); +int bpf_map__pin(struct bpf_map *map, const char *path); + +long libbpf_get_error(const void *ptr); #endif diff --git a/tools/lib/find_bit.c b/tools/lib/find_bit.c index 6d8b8f22cf55..42c15f906aac 100644 --- a/tools/lib/find_bit.c +++ b/tools/lib/find_bit.c @@ -34,7 +34,7 @@ static unsigned long _find_next_bit(const unsigned long *addr, { unsigned long tmp; - if (!nbits || start >= nbits) + if (unlikely(start >= nbits)) return nbits; tmp = addr[start / BITS_PER_LONG] ^ invert; diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile index 3f8cc44a0dbd..3d1c3b5b5150 100644 --- a/tools/lib/subcmd/Makefile +++ b/tools/lib/subcmd/Makefile @@ -19,7 +19,13 @@ MAKEFLAGS += --no-print-directory LIBFILE = $(OUTPUT)libsubcmd.a CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC + +ifeq ($(CC), clang) + CFLAGS += -O3 +else + CFLAGS += -O6 +endif # Treat warnings as errors unless directed not to ifneq ($(WERROR),0) diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c index 8aad81151d50..6bc24025d054 100644 --- a/tools/lib/subcmd/parse-options.c +++ b/tools/lib/subcmd/parse-options.c @@ -270,6 +270,8 @@ static int get_value(struct parse_opt_ctx_t *p, } if (get_arg(p, opt, flags, &arg)) return -1; + if (arg[0] == '-') + return opterror(opt, "expects an unsigned numerical value", flags); *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); @@ -302,6 +304,8 @@ static int get_value(struct parse_opt_ctx_t *p, } if (get_arg(p, opt, flags, &arg)) return -1; + if (arg[0] == '-') + return opterror(opt, "expects an unsigned numerical value", flags); *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); if (*s) return opterror(opt, "expects a numerical value", flags); diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h index 11c3be3bcce7..f054ca1b899d 100644 --- a/tools/lib/subcmd/parse-options.h +++ b/tools/lib/subcmd/parse-options.h @@ -1,6 +1,7 @@ #ifndef __SUBCMD_PARSE_OPTIONS_H #define __SUBCMD_PARSE_OPTIONS_H +#include <linux/kernel.h> #include <stdbool.h> #include <stdint.h> @@ -132,32 +133,32 @@ struct option { #define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } +#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), .argh = (a), .help = (h) } #define OPT_STRING_OPTARG(s, l, v, a, h, d) \ { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, const char **), (a), .help = (h), \ + .value = check_vtype(v, const char **), .argh =(a), .help = (h), \ .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } #define OPT_STRING_OPTARG_SET(s, l, v, os, a, h, d) \ { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, const char **), (a), .help = (h), \ + .value = check_vtype(v, const char **), .argh = (a), .help = (h), \ .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d), \ .set = check_vtype(os, bool *)} -#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} +#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), .argh = (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} #define OPT_DATE(s, l, v, h) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f) } #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } #define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ - .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ + .value = (v), .arg = (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} #define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ - .value = (v), (a), .help = (h), .callback = (f), \ + .value = (v), .argh = (a), .help = (h), .callback = (f), \ .flags = PARSE_OPT_OPTARG, .data = (d) } /* parse_options() will filter out the processed options and leave the diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 2616c66e10c1..9b8555ea3459 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -135,7 +135,7 @@ else Q = @ endif -# Disable command line variables (CFLAGS) overide from top +# Disable command line variables (CFLAGS) override from top # level Makefile (perf), otherwise build Makefile will get # the same command line setup. MAKEOVERRIDES= @@ -257,10 +257,16 @@ define do_install_plugins endef define do_generate_dynamic_list_file - (echo '{'; \ - $(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u; \ - echo '};'; \ - ) > $2 + symbol_type=`$(NM) -u -D $1 | awk 'NF>1 {print $$1}' | \ + xargs echo "U W w" | tr ' ' '\n' | sort -u | xargs echo`;\ + if [ "$$symbol_type" = "U W w" ];then \ + (echo '{'; \ + $(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u;\ + echo '};'; \ + ) > $2; \ + else \ + (echo Either missing one of [$1] or bad version of $(NM)) 1>&2;\ + fi endef install_lib: all_cmd install_plugins diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 14a4f623c1a5..7ce724fc0544 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -831,6 +831,7 @@ static void free_arg(struct print_arg *arg) free_flag_sym(arg->symbol.symbols); break; case PRINT_HEX: + case PRINT_HEX_STR: free_arg(arg->hex.field); free_arg(arg->hex.size); break; @@ -2629,10 +2630,11 @@ out_free: } static enum event_type -process_hex(struct event_format *event, struct print_arg *arg, char **tok) +process_hex_common(struct event_format *event, struct print_arg *arg, + char **tok, enum print_arg_type type) { memset(arg, 0, sizeof(*arg)); - arg->type = PRINT_HEX; + arg->type = type; if (alloc_and_process_delim(event, ",", &arg->hex.field)) goto out; @@ -2651,6 +2653,19 @@ out: } static enum event_type +process_hex(struct event_format *event, struct print_arg *arg, char **tok) +{ + return process_hex_common(event, arg, tok, PRINT_HEX); +} + +static enum event_type +process_hex_str(struct event_format *event, struct print_arg *arg, + char **tok) +{ + return process_hex_common(event, arg, tok, PRINT_HEX_STR); +} + +static enum event_type process_int_array(struct event_format *event, struct print_arg *arg, char **tok) { memset(arg, 0, sizeof(*arg)); @@ -3009,6 +3024,10 @@ process_function(struct event_format *event, struct print_arg *arg, free_token(token); return process_hex(event, arg, tok); } + if (strcmp(token, "__print_hex_str") == 0) { + free_token(token); + return process_hex_str(event, arg, tok); + } if (strcmp(token, "__print_array") == 0) { free_token(token); return process_int_array(event, arg, tok); @@ -3547,6 +3566,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg case PRINT_SYMBOL: case PRINT_INT_ARRAY: case PRINT_HEX: + case PRINT_HEX_STR: break; case PRINT_TYPE: val = eval_num_arg(data, size, event, arg->typecast.item); @@ -3962,6 +3982,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } break; case PRINT_HEX: + case PRINT_HEX_STR: if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) { unsigned long offset; offset = pevent_read_number(pevent, @@ -3981,7 +4002,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } len = eval_num_arg(data, size, event, arg->hex.size); for (i = 0; i < len; i++) { - if (i) + if (i && arg->type == PRINT_HEX) trace_seq_putc(s, ' '); trace_seq_printf(s, "%02x", hex[i]); } @@ -5204,13 +5225,13 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec) } /** - * pevent_data_prempt_count - parse the preempt count from the record + * pevent_data_preempt_count - parse the preempt count from the record * @pevent: a handle to the pevent * @rec: the record to parse * * This returns the preempt count from a record. */ -int pevent_data_prempt_count(struct pevent *pevent, struct pevent_record *rec) +int pevent_data_preempt_count(struct pevent *pevent, struct pevent_record *rec) { return parse_common_pc(pevent, rec->data); } @@ -5727,6 +5748,13 @@ static void print_args(struct print_arg *args) print_args(args->hex.size); printf(")"); break; + case PRINT_HEX_STR: + printf("__print_hex_str("); + print_args(args->hex.field); + printf(", "); + print_args(args->hex.size); + printf(")"); + break; case PRINT_INT_ARRAY: printf("__print_array("); print_args(args->int_array.field); diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 7aae746ec2fe..0c03538df74c 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -140,7 +140,7 @@ struct pevent_plugin_option { * struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = { * { * .name = "option-name", - * .plugin_alias = "overide-file-name", (optional) + * .plugin_alias = "override-file-name", (optional) * .description = "description of option to show users", * }, * { @@ -292,6 +292,7 @@ enum print_arg_type { PRINT_FUNC, PRINT_BITMASK, PRINT_DYNAMIC_ARRAY_LEN, + PRINT_HEX_STR, }; struct print_arg { @@ -709,7 +710,7 @@ void pevent_data_lat_fmt(struct pevent *pevent, int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); -int pevent_data_prempt_count(struct pevent *pevent, struct pevent_record *rec); +int pevent_data_preempt_count(struct pevent *pevent, struct pevent_record *rec); int pevent_data_flags(struct pevent *pevent, struct pevent_record *rec); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); struct cmdline; diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c index 65984f1c2974..c94e3641b046 100644 --- a/tools/lib/traceevent/kbuffer-parse.c +++ b/tools/lib/traceevent/kbuffer-parse.c @@ -315,6 +315,7 @@ static unsigned int old_update_pointers(struct kbuffer *kbuf) extend += delta; delta = extend; ptr += 4; + length = 0; break; case OLD_RINGBUF_TYPE_TIME_STAMP: diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c index a00ec190821a..42dbf73758f3 100644 --- a/tools/lib/traceevent/plugin_function.c +++ b/tools/lib/traceevent/plugin_function.c @@ -130,7 +130,7 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, unsigned long long pfunction; const char *func; const char *parent; - int index; + int index = 0; if (pevent_get_field_val(s, event, "ip", record, &function, 1)) return trace_seq_putc(s, '!'); diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h index f7350fcedc70..a59e061c0b4a 100644 --- a/tools/objtool/arch.h +++ b/tools/objtool/arch.h @@ -31,9 +31,8 @@ #define INSN_CALL_DYNAMIC 8 #define INSN_RETURN 9 #define INSN_CONTEXT_SWITCH 10 -#define INSN_BUG 11 -#define INSN_NOP 12 -#define INSN_OTHER 13 +#define INSN_NOP 11 +#define INSN_OTHER 12 #define INSN_LAST INSN_OTHER int arch_decode_instruction(struct elf *elf, struct section *sec, diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 039636ffb6c8..6ac99e3266eb 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -118,9 +118,6 @@ int arch_decode_instruction(struct elf *elf, struct section *sec, op2 == 0x35) /* sysenter, sysret */ *type = INSN_CONTEXT_SWITCH; - else if (op2 == 0x0b || op2 == 0xb9) - /* ud2 */ - *type = INSN_BUG; else if (op2 == 0x0d || op2 == 0x1f) /* nopl/nopw */ *type = INSN_NOP; diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index e8a1f699058a..066086dd59a8 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -51,7 +51,7 @@ struct instruction { unsigned int len, state; unsigned char type; unsigned long immediate; - bool alt_group, visited; + bool alt_group, visited, dead_end; struct symbol *call_dest; struct instruction *jump_dest; struct list_head alts; @@ -330,6 +330,54 @@ static int decode_instructions(struct objtool_file *file) } /* + * Find all uses of the unreachable() macro, which are code path dead ends. + */ +static int add_dead_ends(struct objtool_file *file) +{ + struct section *sec; + struct rela *rela; + struct instruction *insn; + bool found; + + sec = find_section_by_name(file->elf, ".rela.discard.unreachable"); + if (!sec) + return 0; + + list_for_each_entry(rela, &sec->rela_list, list) { + if (rela->sym->type != STT_SECTION) { + WARN("unexpected relocation symbol type in %s", sec->name); + return -1; + } + insn = find_insn(file, rela->sym->sec, rela->addend); + if (insn) + insn = list_prev_entry(insn, list); + else if (rela->addend == rela->sym->sec->len) { + found = false; + list_for_each_entry_reverse(insn, &file->insn_list, list) { + if (insn->sec == rela->sym->sec) { + found = true; + break; + } + } + + if (!found) { + WARN("can't find unreachable insn at %s+0x%x", + rela->sym->sec->name, rela->addend); + return -1; + } + } else { + WARN("can't find unreachable insn at %s+0x%x", + rela->sym->sec->name, rela->addend); + return -1; + } + + insn->dead_end = true; + } + + return 0; +} + +/* * Warnings shouldn't be reported for ignored functions. */ static void add_ignores(struct objtool_file *file) @@ -757,11 +805,20 @@ static struct rela *find_switch_table(struct objtool_file *file, insn->jump_dest->offset > orig_insn->offset)) break; + /* look for a relocation which references .rodata */ text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); - if (text_rela && text_rela->sym == file->rodata->sym) - return find_rela_by_dest(file->rodata, - text_rela->addend); + if (!text_rela || text_rela->sym != file->rodata->sym) + continue; + + /* + * Make sure the .rodata address isn't associated with a + * symbol. gcc jump tables are anonymous data. + */ + if (find_symbol_containing(file->rodata, text_rela->addend)) + continue; + + return find_rela_by_dest(file->rodata, text_rela->addend); } return NULL; @@ -843,6 +900,10 @@ static int decode_sections(struct objtool_file *file) if (ret) return ret; + ret = add_dead_ends(file); + if (ret) + return ret; + add_ignores(file); ret = add_jump_destinations(file); @@ -1037,13 +1098,13 @@ static int validate_branch(struct objtool_file *file, return 0; - case INSN_BUG: - return 0; - default: break; } + if (insn->dead_end) + return 0; + insn = next_insn_same_sec(file, insn); if (!insn) { WARN("%s: unexpected end of section", sec->name); @@ -1220,7 +1281,7 @@ int cmd_check(int argc, const char **argv) INIT_LIST_HEAD(&file.insn_list); hash_init(file.insn_hash); - file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard"); + file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard"); file.rodata = find_section_by_name(file.elf, ".rodata"); file.ignore_unreachables = false; file.c_file = find_section_by_name(file.elf, ".comment"); diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 0d7983ac63ef..d897702ce742 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -85,6 +85,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) return NULL; } +struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) +{ + struct symbol *sym; + + list_for_each_entry(sym, &sec->symbol_list, list) + if (sym->type != STT_SECTION && + offset >= sym->offset && offset < sym->offset + sym->len) + return sym; + + return NULL; +} + struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, unsigned int len) { diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index aa1ff6596684..731973e1a3f5 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h @@ -79,6 +79,7 @@ struct elf { struct elf *elf_open(const char *name); struct section *find_section_by_name(struct elf *elf, const char *name); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); +struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, unsigned int len); diff --git a/tools/perf/Build b/tools/perf/Build index b12d5d1666e3..9b79f8d7db50 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -3,10 +3,12 @@ perf-y += builtin-annotate.o perf-y += builtin-config.o perf-y += builtin-diff.o perf-y += builtin-evlist.o +perf-y += builtin-ftrace.o perf-y += builtin-help.o perf-y += builtin-sched.o perf-y += builtin-buildid-list.o perf-y += builtin-buildid-cache.o +perf-y += builtin-kallsyms.o perf-y += builtin-list.o perf-y += builtin-record.o perf-y += builtin-report.o @@ -39,8 +41,7 @@ CFLAGS_builtin-help.o += $(paths) CFLAGS_builtin-timechart.o += $(paths) CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \ -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" \ - -DPREFIX="BUILD_STR($(prefix_SQ))" \ - -include $(OUTPUT)PERF-VERSION-FILE + -DPREFIX="BUILD_STR($(prefix_SQ))" CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))" CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))" CFLAGS_builtin-report.o += -DDOCDIR="BUILD_STR($(srcdir_SQ)/Documentation)" diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 8ffbd272952d..a89273d8e744 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -39,6 +39,10 @@ OPTIONS --verbose:: Be more verbose. (Show symbol address, etc) +-q:: +--quiet:: + Do not show any message. (Suppress -v) + -D:: --dump-raw-trace:: Dump raw trace in ASCII. diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt index 3f06730c7f47..2da07e51e119 100644 --- a/tools/perf/Documentation/perf-c2c.txt +++ b/tools/perf/Documentation/perf-c2c.txt @@ -248,7 +248,7 @@ output fields set for caheline offsets output: Code address, Code symbol, Shared Object, Source line dso - coalesced by shared object -By default the coalescing is setup with 'pid,tid,iaddr'. +By default the coalescing is setup with 'pid,iaddr'. STDIO OUTPUT ------------ diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 9365b75fd04f..5b4fff3adc4b 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -498,6 +498,18 @@ record.*:: But if this option is 'no-cache', it will not update the build-id cache. 'skip' skips post-processing and does not update the cache. +diff.*:: + diff.order:: + This option sets the number of columns to sort the result. + The default is 0, which means sorting by baseline. + Setting it to 1 will sort the result by delta (or other + compute method selected). + + diff.compute:: + This options sets the method for computing the diff result. + Possible values are 'delta', 'delta-abs', 'ratio' and + 'wdiff'. Default is 'delta'. + SEE ALSO -------- linkperf:perf[1] diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 3e9490b9c533..a79c84ae61aa 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -73,6 +73,10 @@ OPTIONS Be verbose, for instance, show the raw counts in addition to the diff. +-q:: +--quiet:: + Do not show any message. (Suppress -v) + -f:: --force:: Don't do ownership validation. @@ -86,8 +90,9 @@ OPTIONS -c:: --compute:: - Differential computation selection - delta,ratio,wdiff (default is delta). - See COMPARISON METHODS section for more info. + Differential computation selection - delta, ratio, wdiff, delta-abs + (default is delta-abs). Default can be changed using diff.compute + config option. See COMPARISON METHODS section for more info. -p:: --period:: @@ -99,7 +104,11 @@ OPTIONS -o:: --order:: - Specify compute sorting column number. + Specify compute sorting column number. 0 means sorting by baseline + overhead and 1 (default) means sorting by computed value of column 1 + (data from the first file other base baseline). Values more than 1 + can be used only if enough data files are provided. + The default value can be set using the diff.order config option. --percentage:: Determine how to display the overhead percentage of filtered entries. @@ -181,6 +190,10 @@ with: relative to how entries are filtered. Use --percentage=absolute to prevent such fluctuation. +delta-abs +~~~~~~~~~ +Same as 'delta` method, but sort the result with the absolute values. + ratio ~~~~~ If specified the 'Ratio' column is displayed with value 'r' computed as: diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt new file mode 100644 index 000000000000..2d96de6132a9 --- /dev/null +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -0,0 +1,36 @@ +perf-ftrace(1) +============= + +NAME +---- +perf-ftrace - simple wrapper for kernel's ftrace functionality + + +SYNOPSIS +-------- +[verse] +'perf ftrace' <command> + +DESCRIPTION +----------- +The 'perf ftrace' command is a simple wrapper of kernel's ftrace +functionality. It only supports single thread tracing currently and +just reads trace_pipe in text and then write it to stdout. + +The following options apply to perf ftrace. + +OPTIONS +------- + +-t:: +--tracer=:: + Tracer to use: function_graph or function. + +-v:: +--verbose=:: + Verbosity level. + + +SEE ALSO +-------- +linkperf:perf-record[1], linkperf:perf-trace[1] diff --git a/tools/perf/Documentation/perf-kallsyms.txt b/tools/perf/Documentation/perf-kallsyms.txt new file mode 100644 index 000000000000..954ea9e21236 --- /dev/null +++ b/tools/perf/Documentation/perf-kallsyms.txt @@ -0,0 +1,24 @@ +perf-kallsyms(1) +============== + +NAME +---- +perf-kallsyms - Searches running kernel for symbols + +SYNOPSIS +-------- +[verse] +'perf kallsyms <options> symbol_name[,symbol_name...]' + +DESCRIPTION +----------- +This command searches the running kernel kallsyms file for the given symbol(s) +and prints information about it, including the DSO, the kallsyms begin/end +addresses and the addresses in the ELF kallsyms symbol table (for symbols in +modules). + +OPTIONS +------- +-v:: +--verbose=:: + Increase verbosity level, showing details about symbol table loading, etc. diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 5054d9147f0f..b16003ec14a7 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -157,7 +157,7 @@ OPTIONS -a:: --all-cpus:: - System-wide collection from all CPUs. + System-wide collection from all CPUs (default if no target is specified). -p:: --pid=:: @@ -421,9 +421,19 @@ Configure all used events to run in user space. --timestamp-filename Append timestamp to output file name. ---switch-output:: +--switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one -when receiving a SIGUSR2. +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. 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 diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index f2914f03ae7b..c04cc0647c16 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -25,6 +25,10 @@ OPTIONS --verbose:: Be more verbose. (show symbol address, etc) +-q:: +--quiet:: + Do not show any message. (Suppress -v) + -n:: --show-nr-samples:: Show the number of samples for each symbol diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 76173969ab80..d33deddb0146 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -143,6 +143,8 @@ OPTIONS for 'perf sched timehist' stop time is not given (i.e, time string is 'x.y,') then analysis goes to end of file. +--state:: + Show task state when it switched out. SEE ALSO -------- diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 5dc5c6a09ac4..4ed5f239ba7d 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -36,7 +36,7 @@ There are several variants of perf script: 'perf script report <script> [args]' to run and display the results of <script>. <script> is the name displayed in the output of 'perf - trace --list' i.e. the actual script name minus any language + script --list' i.e. the actual script name minus any language extension. The perf.data output from a previous run of 'perf script record <script>' is used and should be present for this command to succeed. [args] refers to the (mainly optional) args expected by @@ -76,7 +76,7 @@ OPTIONS Any command you can specify in a shell. -D:: ---dump-raw-script=:: +--dump-raw-trace=:: Display verbose dump of the trace data. -L:: diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index d96ccd4844df..aecf2a87e7d6 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -63,7 +63,7 @@ report:: -a:: --all-cpus:: - system-wide collection from all CPUs + system-wide collection from all CPUs (default if no target is specified) -c:: --scale:: diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 781b019751a4..afd728672b6f 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -35,7 +35,10 @@ OPTIONS -e:: --expr:: - List of syscalls to show, currently only syscall names. +--event:: + List of syscalls and other perf events (tracepoints, HW cache events, + etc) to show. + See 'perf list' for a complete list of events. Prefixing with ! shows all syscalls but the ones specified. You may need to escape it. @@ -135,9 +138,6 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. --kernel-syscall-graph:: Show the kernel callchains on the syscall exit path. ---event:: - Trace other events, see 'perf list' for a complete list. - --max-stack:: Set the stack depth limit when parsing the callchain, anything beyond the specified depth will be ignored. Note that at this point diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt index 8a6479c0eac9..170b0289a7bc 100644 --- a/tools/perf/Documentation/tips.txt +++ b/tools/perf/Documentation/tips.txt @@ -22,7 +22,7 @@ If you have debuginfo enabled, try: perf report -s sym,srcline For memory address profiling, try: perf mem record / perf mem report For tracepoint events, try: perf report -s trace_fields To record callchains for each sample: perf record -g -To record every process run by an user: perf record -u <user> +To record every process run by a user: perf record -u <user> Skip collecing build-id when recording: perf record -B To change sampling frequency to 100 Hz: perf record -F 100 See assembly instructions with percentage: perf annotate <symbol> diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index a511e5f31e36..8672f835ae4e 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -61,6 +61,7 @@ tools/include/asm-generic/bitops.h tools/include/linux/atomic.h tools/include/linux/bitops.h tools/include/linux/compiler.h +tools/include/linux/compiler-gcc.h tools/include/linux/coresight-pmu.h tools/include/linux/filter.h tools/include/linux/hash.h diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 76c84f0eec52..27c9fbca7bd9 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -144,8 +144,12 @@ ifndef DEBUG endif ifeq ($(DEBUG),0) +ifeq ($(CC), clang) + CFLAGS += -O3 +else CFLAGS += -O6 endif +endif ifdef PARSER_DEBUG PARSER_DEBUG_BISON := -t @@ -171,6 +175,10 @@ PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) +ifeq ($(CC), clang) + PYTHON_EMBED_CCOPTS := $(filter-out -specs=%,$(PYTHON_EMBED_CCOPTS)) +endif + FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS) FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS) @@ -291,8 +299,10 @@ else endif endif ifneq ($(feature-dwarf), 1) - msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); - NO_DWARF := 1 + ifndef NO_DWARF + msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); + NO_DWARF := 1 + endif else ifneq ($(feature-dwarf_getlocations), 1) msg := $(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.157); @@ -595,6 +605,9 @@ else PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) -lutil PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) + ifeq ($(CC), clang) + PYTHON_EMBED_CCOPTS := $(filter-out -specs=%,$(PYTHON_EMBED_CCOPTS)) + endif FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) ifneq ($(feature-libpython), 1) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 8bb16aa9d661..79fe31f20a17 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -661,6 +661,7 @@ ifndef NO_PERF_READ_VDSOX32 endif ifndef NO_JVMTI $(call QUIET_INSTALL, $(LIBJVMTI)) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \ $(INSTALL) $(OUTPUT)$(LIBJVMTI) '$(DESTDIR_SQ)$(libdir_SQ)'; endif $(call QUIET_INSTALL, libexec) \ @@ -725,13 +726,13 @@ config-clean: $(call QUIET_CLEAN, config) $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null -clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean +clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean fixdep-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT).config-detected $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents $(OUTPUT)$(LIBJVMTI).so $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ - $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ + $(OUTPUT)util/intel-pt-decoder/inat-tables.c \ $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ $(OUTPUT)pmu-events/pmu-events.c $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile index 18b13518d8d8..eebe1ec9d2ee 100644 --- a/tools/perf/arch/arm64/Makefile +++ b/tools/perf/arch/arm64/Makefile @@ -2,3 +2,4 @@ ifndef NO_DWARF PERF_HAVE_DWARF_REGS := 1 endif PERF_HAVE_JITDUMP := 1 +PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 diff --git a/tools/perf/arch/arm64/include/dwarf-regs-table.h b/tools/perf/arch/arm64/include/dwarf-regs-table.h index 26759363f921..36e375f5a211 100644 --- a/tools/perf/arch/arm64/include/dwarf-regs-table.h +++ b/tools/perf/arch/arm64/include/dwarf-regs-table.h @@ -2,12 +2,12 @@ /* This is included in perf/util/dwarf-regs.c */ static const char * const aarch64_regstr_tbl[] = { - "%r0", "%r1", "%r2", "%r3", "%r4", - "%r5", "%r6", "%r7", "%r8", "%r9", - "%r10", "%r11", "%r12", "%r13", "%r14", - "%r15", "%r16", "%r17", "%r18", "%r19", - "%r20", "%r21", "%r22", "%r23", "%r24", - "%r25", "%r26", "%r27", "%r28", "%r29", + "%x0", "%x1", "%x2", "%x3", "%x4", + "%x5", "%x6", "%x7", "%x8", "%x9", + "%x10", "%x11", "%x12", "%x13", "%x14", + "%x15", "%x16", "%x17", "%x18", "%x19", + "%x20", "%x21", "%x22", "%x23", "%x24", + "%x25", "%x26", "%x27", "%x28", "%x29", "%lr", "%sp", }; #endif diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c index d49efeb8172e..068b6189157b 100644 --- a/tools/perf/arch/arm64/util/dwarf-regs.c +++ b/tools/perf/arch/arm64/util/dwarf-regs.c @@ -10,17 +10,20 @@ #include <stddef.h> #include <dwarf-regs.h> +#include <linux/ptrace.h> /* for struct user_pt_regs */ +#include "util.h" struct pt_regs_dwarfnum { const char *name; unsigned int dwarfnum; }; -#define STR(s) #s #define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} #define GPR_DWARFNUM_NAME(num) \ {.name = STR(%x##num), .dwarfnum = num} #define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} +#define DWARFNUM2OFFSET(index) \ + (index * sizeof((struct user_pt_regs *)0)->regs[0]) /* * Reference: @@ -78,3 +81,13 @@ const char *get_arch_regstr(unsigned int n) return roff->name; return NULL; } + +int regs_query_register_offset(const char *name) +{ + const struct pt_regs_dwarfnum *roff; + + for (roff = regdwarfnum_table; roff->name != NULL; roff++) + if (!strcmp(roff->name, name)) + return DWARFNUM2OFFSET(roff->dwarfnum); + return -EINVAL; +} diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index bfbb6b5f609c..da04b8c5568a 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -130,8 +130,6 @@ int bench_futex_hash(int argc, const char **argv, } ncpus = sysconf(_SC_NPROCESSORS_ONLN); - nsecs = futexbench_sanitize_numeric(nsecs); - nfutexes = futexbench_sanitize_numeric(nfutexes); sigfillset(&act.sa_mask); act.sa_sigaction = toggle_done; @@ -139,8 +137,6 @@ int bench_futex_hash(int argc, const char **argv, if (!nthreads) /* default to the number of CPUs */ nthreads = ncpus; - else - nthreads = futexbench_sanitize_numeric(nthreads); worker = calloc(nthreads, sizeof(*worker)); if (!worker) diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c index 6d9d6c40a916..91877777ec6e 100644 --- a/tools/perf/bench/futex-lock-pi.c +++ b/tools/perf/bench/futex-lock-pi.c @@ -152,7 +152,6 @@ int bench_futex_lock_pi(int argc, const char **argv, goto err; ncpus = sysconf(_SC_NPROCESSORS_ONLN); - nsecs = futexbench_sanitize_numeric(nsecs); sigfillset(&act.sa_mask); act.sa_sigaction = toggle_done; @@ -160,8 +159,6 @@ int bench_futex_lock_pi(int argc, const char **argv, if (!nthreads) nthreads = ncpus; - else - nthreads = futexbench_sanitize_numeric(nthreads); worker = calloc(nthreads, sizeof(*worker)); if (!worker) diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index fd4ee95b689a..2b9705a8734c 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -128,8 +128,6 @@ int bench_futex_requeue(int argc, const char **argv, if (!nthreads) nthreads = ncpus; - else - nthreads = futexbench_sanitize_numeric(nthreads); worker = calloc(nthreads, sizeof(*worker)); if (!worker) diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c index beaa6c142477..2c8fa67ad537 100644 --- a/tools/perf/bench/futex-wake-parallel.c +++ b/tools/perf/bench/futex-wake-parallel.c @@ -217,12 +217,8 @@ int bench_futex_wake_parallel(int argc, const char **argv, sigaction(SIGINT, &act, NULL); ncpus = sysconf(_SC_NPROCESSORS_ONLN); - nwaking_threads = futexbench_sanitize_numeric(nwaking_threads); - if (!nblocked_threads) nblocked_threads = ncpus; - else - nblocked_threads = futexbench_sanitize_numeric(nblocked_threads); /* some sanity checks */ if (nwaking_threads > nblocked_threads || !nwaking_threads) diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index 46efcb98b5a4..e246b1b8388a 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -129,7 +129,6 @@ int bench_futex_wake(int argc, const char **argv, } ncpus = sysconf(_SC_NPROCESSORS_ONLN); - nwakes = futexbench_sanitize_numeric(nwakes); sigfillset(&act.sa_mask); act.sa_sigaction = toggle_done; @@ -137,8 +136,6 @@ int bench_futex_wake(int argc, const char **argv, if (!nthreads) nthreads = ncpus; - else - nthreads = futexbench_sanitize_numeric(nthreads); worker = calloc(nthreads, sizeof(*worker)); if (!worker) diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h index ba7c735c0c62..b2e06d1190d0 100644 --- a/tools/perf/bench/futex.h +++ b/tools/perf/bench/futex.h @@ -7,7 +7,6 @@ #ifndef _FUTEX_H #define _FUTEX_H -#include <stdlib.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> @@ -100,7 +99,4 @@ static inline int pthread_attr_setaffinity_np(pthread_attr_t *attr, } #endif -/* User input sanitation */ -#define futexbench_sanitize_numeric(__n) abs((__n)) - #endif /* _FUTEX_H */ diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 8efe904e486b..3083fc36282b 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -43,6 +43,7 @@ /* * Debug printf: */ +#undef dprintf #define dprintf(x...) do { if (g && g->p.show_details >= 1) printf(x); } while (0) struct thread_data { @@ -1573,13 +1574,13 @@ static int __bench_numa(const char *name) "GB/sec,", "total-speed", "GB/sec total speed"); if (g->p.show_details >= 2) { - char tname[32]; + char tname[14 + 2 * 10 + 1]; struct thread_data *td; for (p = 0; p < g->p.nr_proc; p++) { for (t = 0; t < g->p.nr_threads; t++) { - memset(tname, 0, 32); + memset(tname, 0, sizeof(tname)); td = g->threads + p*g->p.nr_threads + t; - snprintf(tname, 32, "process%d:thread%d", p, t); + snprintf(tname, sizeof(tname), "process%d:thread%d", p, t); print_res(tname, td->speed_gbs, "GB/sec", "thread-speed", "GB/sec/thread speed"); print_res(tname, td->system_time_ns / NSEC_PER_SEC, diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index ebb628332a6e..4f52d85f5ebc 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -410,6 +410,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('q', "quiet", &quiet, "do now show any message"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"), @@ -463,6 +464,9 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) annotate.sym_hist_filter = argv[0]; } + if (quiet) + perf_quiet_option(); + file.path = input_name; annotate.session = perf_session__new(&file, false, &annotate.tool); diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index f8ca7a4ebabc..e2b21723bbf8 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -58,7 +58,7 @@ struct c2c_hist_entry { struct hist_entry he; }; -static char const *coalesce_default = "pid,tid,iaddr"; +static char const *coalesce_default = "pid,iaddr"; struct perf_c2c { struct perf_tool tool; @@ -2476,6 +2476,7 @@ static int build_cl_output(char *cl_sort, bool no_source) "mean_rmt," "mean_lcl," "mean_load," + "tot_recs," "cpucnt,", add_sym ? "symbol," : "", add_dso ? "dso," : "", diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 933aeec46f4a..1b96a3122228 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -17,6 +17,7 @@ #include "util/symbol.h" #include "util/util.h" #include "util/data.h" +#include "util/config.h" #include <stdlib.h> #include <math.h> @@ -30,6 +31,7 @@ enum { PERF_HPP_DIFF__RATIO, PERF_HPP_DIFF__WEIGHTED_DIFF, PERF_HPP_DIFF__FORMULA, + PERF_HPP_DIFF__DELTA_ABS, PERF_HPP_DIFF__MAX_INDEX }; @@ -64,7 +66,7 @@ static bool force; static bool show_period; static bool show_formula; static bool show_baseline_only; -static unsigned int sort_compute; +static unsigned int sort_compute = 1; static s64 compute_wdiff_w1; static s64 compute_wdiff_w2; @@ -73,19 +75,22 @@ enum { COMPUTE_DELTA, COMPUTE_RATIO, COMPUTE_WEIGHTED_DIFF, + COMPUTE_DELTA_ABS, COMPUTE_MAX, }; const char *compute_names[COMPUTE_MAX] = { [COMPUTE_DELTA] = "delta", + [COMPUTE_DELTA_ABS] = "delta-abs", [COMPUTE_RATIO] = "ratio", [COMPUTE_WEIGHTED_DIFF] = "wdiff", }; -static int compute; +static int compute = COMPUTE_DELTA_ABS; static int compute_2_hpp[COMPUTE_MAX] = { [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA, + [COMPUTE_DELTA_ABS] = PERF_HPP_DIFF__DELTA_ABS, [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO, [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF, }; @@ -111,6 +116,10 @@ static struct header_column { .name = "Delta", .width = 7, }, + [PERF_HPP_DIFF__DELTA_ABS] = { + .name = "Delta Abs", + .width = 7, + }, [PERF_HPP_DIFF__RATIO] = { .name = "Ratio", .width = 14, @@ -298,6 +307,7 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, { switch (compute) { case COMPUTE_DELTA: + case COMPUTE_DELTA_ABS: return formula_delta(he, pair, buf, size); case COMPUTE_RATIO: return formula_ratio(he, pair, buf, size); @@ -461,6 +471,7 @@ static void hists__precompute(struct hists *hists) switch (compute) { case COMPUTE_DELTA: + case COMPUTE_DELTA_ABS: compute_delta(he, pair); break; case COMPUTE_RATIO: @@ -498,6 +509,13 @@ __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, return cmp_doubles(l, r); } + case COMPUTE_DELTA_ABS: + { + double l = fabs(left->diff.period_ratio_delta); + double r = fabs(right->diff.period_ratio_delta); + + return cmp_doubles(l, r); + } case COMPUTE_RATIO: { double l = left->diff.period_ratio; @@ -564,7 +582,7 @@ hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right, if (!p_left || !p_right) return p_left ? -1 : 1; - if (c != COMPUTE_DELTA) { + if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) { /* * The delta can be computed without the baseline, but * others are not. Put those entries which have no @@ -607,6 +625,15 @@ hist_entry__cmp_delta(struct perf_hpp_fmt *fmt, } static int64_t +hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt, + struct hist_entry *left, struct hist_entry *right) +{ + struct data__file *d = fmt_to_data_file(fmt); + + return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx); +} + +static int64_t hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt, struct hist_entry *left, struct hist_entry *right) { @@ -633,6 +660,14 @@ hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused, } static int64_t +hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *left, struct hist_entry *right) +{ + return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS, + sort_compute); +} + +static int64_t hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { @@ -656,7 +691,7 @@ static void hists__process(struct hists *hists) hists__precompute(hists); hists__output_resort(hists, NULL); - hists__fprintf(hists, true, 0, 0, 0, stdout, + hists__fprintf(hists, !quiet, 0, 0, 0, stdout, symbol_conf.use_callchain); } @@ -704,12 +739,14 @@ static void data_process(void) hists__link(hists_base, hists); } - fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", - perf_evsel__name(evsel_base)); + if (!quiet) { + fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", + perf_evsel__name(evsel_base)); + } first = false; - if (verbose || data__files_cnt > 2) + if (verbose > 0 || ((data__files_cnt > 2) && !quiet)) data__fprintf(); /* Don't sort callchain for perf diff */ @@ -772,10 +809,11 @@ static const char * const diff_usage[] = { static const struct option options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"), OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, "Show only items with match in baseline"), OPT_CALLBACK('c', "compute", &compute, - "delta,ratio,wdiff:w1,w2 (default delta)", + "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)", "Entries differential computation selection", setup_compute), OPT_BOOLEAN('p', "period", &show_period, @@ -945,6 +983,7 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, switch (idx) { case PERF_HPP_DIFF__DELTA: + case PERF_HPP_DIFF__DELTA_ABS: if (pair->diff.computed) diff = pair->diff.period_ratio_delta; else @@ -1118,6 +1157,10 @@ static void data__hpp_register(struct data__file *d, int idx) fmt->color = hpp__color_wdiff; fmt->sort = hist_entry__cmp_wdiff; break; + case PERF_HPP_DIFF__DELTA_ABS: + fmt->color = hpp__color_delta; + fmt->sort = hist_entry__cmp_delta_abs; + break; default: fmt->sort = hist_entry__cmp_nop; break; @@ -1195,6 +1238,9 @@ static int ui_init(void) case COMPUTE_WEIGHTED_DIFF: fmt->sort = hist_entry__cmp_wdiff_idx; break; + case COMPUTE_DELTA_ABS: + fmt->sort = hist_entry__cmp_delta_abs_idx; + break; default: BUG_ON(1); } @@ -1249,6 +1295,31 @@ static int data_init(int argc, const char **argv) return 0; } +static int diff__config(const char *var, const char *value, + void *cb __maybe_unused) +{ + if (!strcmp(var, "diff.order")) { + sort_compute = perf_config_int(var, value); + return 0; + } + if (!strcmp(var, "diff.compute")) { + if (!strcmp(value, "delta")) { + compute = COMPUTE_DELTA; + } else if (!strcmp(value, "delta-abs")) { + compute = COMPUTE_DELTA_ABS; + } else if (!strcmp(value, "ratio")) { + compute = COMPUTE_RATIO; + } else if (!strcmp(value, "wdiff")) { + compute = COMPUTE_WEIGHTED_DIFF; + } else { + pr_err("Invalid compute method: %s\n", value); + return -1; + } + } + + return 0; +} + int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) { int ret = hists__init(); @@ -1256,8 +1327,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) if (ret < 0) return ret; + perf_config(diff__config, NULL); + argc = parse_options(argc, argv, options, diff_usage, 0); + if (quiet) + perf_quiet_option(); + if (symbol__init(NULL) < 0) return -1; diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c new file mode 100644 index 000000000000..c3e643666c72 --- /dev/null +++ b/tools/perf/builtin-ftrace.c @@ -0,0 +1,265 @@ +/* + * builtin-ftrace.c + * + * Copyright (c) 2013 LG Electronics, Namhyung Kim <namhyung@kernel.org> + * + * Released under the GPL v2. + */ + +#include "builtin.h" +#include "perf.h" + +#include <unistd.h> +#include <signal.h> + +#include "debug.h" +#include <subcmd/parse-options.h> +#include "evlist.h" +#include "target.h" +#include "thread_map.h" +#include "util/config.h" + + +#define DEFAULT_TRACER "function_graph" + +struct perf_ftrace { + struct perf_evlist *evlist; + struct target target; + const char *tracer; +}; + +static bool done; + +static void sig_handler(int sig __maybe_unused) +{ + done = true; +} + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 if the fork fails, since + * we asked by setting its exec_error to the function below, + * ftrace__workload_exec_failed_signal. + * + * XXX We need to handle this more appropriately, emitting an error, etc. + */ +static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, + siginfo_t *info __maybe_unused, + void *ucontext __maybe_unused) +{ + /* workload_exec_errno = info->si_value.sival_int; */ + done = true; +} + +static int write_tracing_file(const char *name, const char *val) +{ + char *file; + int fd, ret = -1; + ssize_t size = strlen(val); + + file = get_tracing_file(name); + if (!file) { + pr_debug("cannot get tracing file: %s\n", name); + return -1; + } + + fd = open(file, O_WRONLY); + if (fd < 0) { + pr_debug("cannot open tracing file: %s\n", name); + goto out; + } + + if (write(fd, val, size) == size) + ret = 0; + else + pr_debug("write '%s' to tracing/%s failed\n", val, name); + + close(fd); +out: + put_tracing_file(file); + return ret; +} + +static int reset_tracing_files(struct perf_ftrace *ftrace __maybe_unused) +{ + if (write_tracing_file("tracing_on", "0") < 0) + return -1; + + if (write_tracing_file("current_tracer", "nop") < 0) + return -1; + + if (write_tracing_file("set_ftrace_pid", " ") < 0) + return -1; + + return 0; +} + +static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) +{ + char *trace_file; + int trace_fd; + char *trace_pid; + char buf[4096]; + struct pollfd pollfd = { + .events = POLLIN, + }; + + if (geteuid() != 0) { + pr_err("ftrace only works for root!\n"); + return -1; + } + + if (argc < 1) + return -1; + + signal(SIGINT, sig_handler); + signal(SIGUSR1, sig_handler); + signal(SIGCHLD, sig_handler); + + reset_tracing_files(ftrace); + + /* reset ftrace buffer */ + if (write_tracing_file("trace", "0") < 0) + goto out; + + if (perf_evlist__prepare_workload(ftrace->evlist, &ftrace->target, + argv, false, ftrace__workload_exec_failed_signal) < 0) + goto out; + + if (write_tracing_file("current_tracer", ftrace->tracer) < 0) { + pr_err("failed to set current_tracer to %s\n", ftrace->tracer); + goto out; + } + + if (asprintf(&trace_pid, "%d", thread_map__pid(ftrace->evlist->threads, 0)) < 0) { + pr_err("failed to allocate pid string\n"); + goto out; + } + + if (write_tracing_file("set_ftrace_pid", trace_pid) < 0) { + pr_err("failed to set pid: %s\n", trace_pid); + goto out_free_pid; + } + + trace_file = get_tracing_file("trace_pipe"); + if (!trace_file) { + pr_err("failed to open trace_pipe\n"); + goto out_free_pid; + } + + trace_fd = open(trace_file, O_RDONLY); + + put_tracing_file(trace_file); + + if (trace_fd < 0) { + pr_err("failed to open trace_pipe\n"); + goto out_free_pid; + } + + fcntl(trace_fd, F_SETFL, O_NONBLOCK); + pollfd.fd = trace_fd; + + if (write_tracing_file("tracing_on", "1") < 0) { + pr_err("can't enable tracing\n"); + goto out_close_fd; + } + + perf_evlist__start_workload(ftrace->evlist); + + while (!done) { + if (poll(&pollfd, 1, -1) < 0) + break; + + if (pollfd.revents & POLLIN) { + int n = read(trace_fd, buf, sizeof(buf)); + if (n < 0) + break; + if (fwrite(buf, n, 1, stdout) != 1) + break; + } + } + + write_tracing_file("tracing_on", "0"); + + /* read remaining buffer contents */ + while (true) { + int n = read(trace_fd, buf, sizeof(buf)); + if (n <= 0) + break; + if (fwrite(buf, n, 1, stdout) != 1) + break; + } + +out_close_fd: + close(trace_fd); +out_free_pid: + free(trace_pid); +out: + reset_tracing_files(ftrace); + + return done ? 0 : -1; +} + +static int perf_ftrace_config(const char *var, const char *value, void *cb) +{ + struct perf_ftrace *ftrace = cb; + + if (prefixcmp(var, "ftrace.")) + return 0; + + if (strcmp(var, "ftrace.tracer")) + return -1; + + if (!strcmp(value, "function_graph") || + !strcmp(value, "function")) { + ftrace->tracer = value; + return 0; + } + + pr_err("Please select \"function_graph\" (default) or \"function\"\n"); + return -1; +} + +int cmd_ftrace(int argc, const char **argv, const char *prefix __maybe_unused) +{ + int ret; + struct perf_ftrace ftrace = { + .tracer = DEFAULT_TRACER, + .target = { .uid = UINT_MAX, }, + }; + const char * const ftrace_usage[] = { + "perf ftrace [<options>] <command>", + "perf ftrace [<options>] -- <command> [<options>]", + NULL + }; + const struct option ftrace_options[] = { + OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", + "tracer to use: function_graph(default) or function"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose"), + OPT_END() + }; + + ret = perf_config(perf_ftrace_config, &ftrace); + if (ret < 0) + return -1; + + argc = parse_options(argc, argv, ftrace_options, ftrace_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (!argc) + usage_with_options(ftrace_usage, ftrace_options); + + ftrace.evlist = perf_evlist__new(); + if (ftrace.evlist == NULL) + return -ENOMEM; + + ret = perf_evlist__create_maps(ftrace.evlist, &ftrace.target); + if (ret < 0) + goto out_delete_evlist; + + ret = __cmd_ftrace(&ftrace, argc, argv); + +out_delete_evlist: + perf_evlist__delete(ftrace.evlist); + + return ret; +} diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 3bdb2c78a21b..aed0d844e8c2 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -434,7 +434,7 @@ int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused) const char * const builtin_help_subcommands[] = { "buildid-cache", "buildid-list", "diff", "evlist", "help", "list", "record", "report", "bench", "stat", "timechart", "top", "annotate", - "script", "sched", "kmem", "lock", "kvm", "test", "inject", "mem", "data", + "script", "sched", "kallsyms", "kmem", "lock", "kvm", "test", "inject", "mem", "data", #ifdef HAVE_LIBELF_SUPPORT "probe", #endif @@ -447,11 +447,13 @@ int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused) NULL }; const char *alias; - int rc = 0; + int rc; load_command_list("perf-", &main_cmds, &other_cmds); - perf_config(perf_help_config, &help_format); + rc = perf_config(perf_help_config, &help_format); + if (rc) + return rc; argc = parse_options_subcommand(argc, argv, builtin_help_options, builtin_help_subcommands, builtin_help_usage, 0); diff --git a/tools/perf/builtin-kallsyms.c b/tools/perf/builtin-kallsyms.c new file mode 100644 index 000000000000..224bfc454b4a --- /dev/null +++ b/tools/perf/builtin-kallsyms.c @@ -0,0 +1,67 @@ +/* + * builtin-kallsyms.c + * + * Builtin command: Look for a symbol in the running kernel and its modules + * + * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> + * + * Released under the GPL v2. (and only v2, not any later version) + */ +#include "builtin.h" +#include <linux/compiler.h> +#include <subcmd/parse-options.h> +#include "debug.h" +#include "machine.h" +#include "symbol.h" + +static int __cmd_kallsyms(int argc, const char **argv) +{ + int i; + struct machine *machine = machine__new_kallsyms(); + + if (machine == NULL) { + pr_err("Couldn't read /proc/kallsyms\n"); + return -1; + } + + for (i = 0; i < argc; ++i) { + struct map *map; + struct symbol *symbol = machine__find_kernel_function_by_name(machine, argv[i], &map); + + if (symbol == NULL) { + printf("%s: not found\n", argv[i]); + continue; + } + + printf("%s: %s %s %#" PRIx64 "-%#" PRIx64 " (%#" PRIx64 "-%#" PRIx64")\n", + symbol->name, map->dso->short_name, map->dso->long_name, + map->unmap_ip(map, symbol->start), map->unmap_ip(map, symbol->end), + symbol->start, symbol->end); + } + + machine__delete(machine); + return 0; +} + +int cmd_kallsyms(int argc, const char **argv, const char *prefix __maybe_unused) +{ + const struct option options[] = { + OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"), + OPT_END() + }; + const char * const kallsyms_usage[] = { + "perf kallsyms [<options>] symbol_name", + NULL + }; + + argc = parse_options(argc, argv, options, kallsyms_usage, 0); + if (argc < 1) + usage_with_options(kallsyms_usage, options); + + symbol_conf.sort_by_name = true; + symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); + if (symbol__init(NULL) < 0) + return -1; + + return __cmd_kallsyms(argc, argv); +} diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 915869e00d86..6da8d083e4e5 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -1065,7 +1065,7 @@ static void __print_page_alloc_result(struct perf_session *session, int n_lines) data = rb_entry(next, struct page_stat, node); sym = machine__find_kernel_function(machine, data->callsite, &map); - if (sym && sym->name) + if (sym) caller = sym->name; else scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite); @@ -1107,7 +1107,7 @@ static void __print_page_caller_result(struct perf_session *session, int n_lines data = rb_entry(next, struct page_stat, node); sym = machine__find_kernel_function(machine, data->callsite, &map); - if (sym && sym->name) + if (sym) caller = sym->name; else scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite); @@ -1920,10 +1920,12 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) NULL }; struct perf_session *session; - int ret = -1; const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n"; + int ret = perf_config(kmem_config, NULL); + + if (ret) + return ret; - perf_config(kmem_config, NULL); argc = parse_options_subcommand(argc, argv, kmem_options, kmem_subcommands, kmem_usage, 0); @@ -1948,6 +1950,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) if (session == NULL) return -1; + ret = -1; + if (kmem_slab) { if (!perf_evlist__find_tracepoint_by_name(session->evlist, "kmem:kmalloc")) { diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index ba9322ff858b..3b9d98b5feef 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -14,6 +14,7 @@ #include "util/parse-events.h" #include "util/cache.h" #include "util/pmu.h" +#include "util/debug.h" #include <subcmd/parse-options.h> static bool desc_flag = true; @@ -29,6 +30,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) "Print extra event descriptions. --no-desc to not print."), OPT_BOOLEAN('v', "long-desc", &long_desc_flag, "Print longer event descriptions."), + OPT_INCR(0, "debug", &verbose, + "Enable debugging output"), OPT_END() }; const char * const list_usage[] = { diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index cd7bc4d104e2..6114e07ca613 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -42,8 +42,8 @@ static int parse_record_events(const struct option *opt, fprintf(stderr, "%-13s%-*s%s\n", e->tag, - verbose ? 25 : 0, - verbose ? perf_mem_events__name(j) : "", + verbose > 0 ? 25 : 0, + verbose > 0 ? perf_mem_events__name(j) : "", e->supported ? ": available" : ""); } exit(0); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index f87996b0cb29..1fcebc31a508 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -552,6 +552,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, "Enable kernel symbol demangling"), OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"), + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", + "Look for files with symbols relative to this directory"), OPT_END() }; int ret; diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ec10e9427d9..bc84a375295d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -46,6 +46,15 @@ #include <asm/bug.h> #include <linux/time64.h> +struct switch_output { + bool enabled; + bool signal; + unsigned long size; + unsigned long time; + const char *str; + bool set; +}; + struct record { struct perf_tool tool; struct record_opts opts; @@ -62,10 +71,33 @@ struct record { bool no_buildid_cache_set; bool buildid_all; bool timestamp_filename; - bool switch_output; + struct switch_output switch_output; unsigned long long samples; }; +static volatile int auxtrace_record__snapshot_started; +static DEFINE_TRIGGER(auxtrace_snapshot_trigger); +static DEFINE_TRIGGER(switch_output_trigger); + +static bool switch_output_signal(struct record *rec) +{ + return rec->switch_output.signal && + trigger_is_ready(&switch_output_trigger); +} + +static bool switch_output_size(struct record *rec) +{ + return rec->switch_output.size && + trigger_is_ready(&switch_output_trigger) && + (rec->bytes_written >= rec->switch_output.size); +} + +static bool switch_output_time(struct record *rec) +{ + return rec->switch_output.time && + trigger_is_ready(&switch_output_trigger); +} + static int record__write(struct record *rec, void *bf, size_t size) { if (perf_data_file__write(rec->session->file, bf, size) < 0) { @@ -74,6 +106,10 @@ static int record__write(struct record *rec, void *bf, size_t size) } rec->bytes_written += size; + + if (switch_output_size(rec)) + trigger_hit(&switch_output_trigger); + return 0; } @@ -193,10 +229,6 @@ static volatile int done; static volatile int signr = -1; static volatile int child_finished; -static volatile int auxtrace_record__snapshot_started; -static DEFINE_TRIGGER(auxtrace_snapshot_trigger); -static DEFINE_TRIGGER(switch_output_trigger); - static void sig_handler(int sig) { if (sig == SIGCHLD) @@ -386,7 +418,7 @@ static int record__mmap(struct record *rec) static int record__open(struct record *rec) { - char msg[512]; + char msg[BUFSIZ]; struct perf_evsel *pos; struct perf_evlist *evlist = rec->evlist; struct perf_session *session = rec->session; @@ -400,7 +432,7 @@ static int record__open(struct record *rec) try_again: if (perf_evsel__open(pos, pos->cpus, pos->threads) < 0) { if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) { - if (verbose) + if (verbose > 0) ui__warning("%s\n", msg); goto try_again; } @@ -623,22 +655,23 @@ record__finish_output(struct record *rec) static int record__synthesize_workload(struct record *rec, bool tail) { - struct { - struct thread_map map; - struct thread_map_data map_data; - } thread_map; + int err; + struct thread_map *thread_map; if (rec->opts.tail_synthesize != tail) return 0; - thread_map.map.nr = 1; - thread_map.map.map[0].pid = rec->evlist->workload.pid; - thread_map.map.map[0].comm = NULL; - return perf_event__synthesize_thread_map(&rec->tool, &thread_map.map, + thread_map = thread_map__new_by_tid(rec->evlist->workload.pid); + if (thread_map == NULL) + return -1; + + err = perf_event__synthesize_thread_map(&rec->tool, thread_map, process_synthesized_event, &rec->session->machines.host, rec->opts.sample_address, rec->opts.proc_map_timeout); + thread_map__put(thread_map); + return err; } static int record__synthesize(struct record *rec, bool tail); @@ -712,6 +745,7 @@ static void workload_exec_failed_signal(int signo __maybe_unused, } static void snapshot_sig_handler(int sig); +static void alarm_sig_handler(int sig); int __weak perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused, @@ -842,11 +876,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) signal(SIGTERM, sig_handler); signal(SIGSEGV, sigsegv_handler); - if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) { + if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) { signal(SIGUSR2, snapshot_sig_handler); if (rec->opts.auxtrace_snapshot_mode) trigger_on(&auxtrace_snapshot_trigger); - if (rec->switch_output) + if (rec->switch_output.enabled) trigger_on(&switch_output_trigger); } else { signal(SIGUSR2, SIG_IGN); @@ -1043,6 +1077,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) err = fd; goto out_child; } + + /* re-arm the alarm */ + if (rec->switch_output.time) + alarm(rec->switch_output.time); } if (hits == rec->samples) { @@ -1352,6 +1390,78 @@ out_free: return ret; } +static void switch_output_size_warn(struct record *rec) +{ + u64 wakeup_size = perf_evlist__mmap_size(rec->opts.mmap_pages); + struct switch_output *s = &rec->switch_output; + + wakeup_size /= 2; + + if (s->size < wakeup_size) { + char buf[100]; + + unit_number__scnprintf(buf, sizeof(buf), wakeup_size); + pr_warning("WARNING: switch-output data size lower than " + "wakeup kernel buffer size (%s) " + "expect bigger perf.data sizes\n", buf); + } +} + +static int switch_output_setup(struct record *rec) +{ + struct switch_output *s = &rec->switch_output; + static struct parse_tag tags_size[] = { + { .tag = 'B', .mult = 1 }, + { .tag = 'K', .mult = 1 << 10 }, + { .tag = 'M', .mult = 1 << 20 }, + { .tag = 'G', .mult = 1 << 30 }, + { .tag = 0 }, + }; + static struct parse_tag tags_time[] = { + { .tag = 's', .mult = 1 }, + { .tag = 'm', .mult = 60 }, + { .tag = 'h', .mult = 60*60 }, + { .tag = 'd', .mult = 60*60*24 }, + { .tag = 0 }, + }; + unsigned long val; + + if (!s->set) + return 0; + + if (!strcmp(s->str, "signal")) { + s->signal = true; + pr_debug("switch-output with SIGUSR2 signal\n"); + goto enabled; + } + + val = parse_tag_value(s->str, tags_size); + if (val != (unsigned long) -1) { + s->size = val; + pr_debug("switch-output with %s size threshold\n", s->str); + goto enabled; + } + + val = parse_tag_value(s->str, tags_time); + if (val != (unsigned long) -1) { + s->time = val; + pr_debug("switch-output with %s time threshold (%lu seconds)\n", + s->str, s->time); + goto enabled; + } + + return -1; + +enabled: + rec->timestamp_filename = true; + s->enabled = true; + + if (s->size && !rec->opts.no_buffering) + switch_output_size_warn(rec); + + return 0; +} + static const char * const __record_usage[] = { "perf record [<options>] [<command>]", "perf record [<options>] -- <command> [<options>]", @@ -1519,8 +1629,10 @@ static struct option __record_options[] = { "Record build-id of all DSOs regardless of hits"), OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename, "append timestamp to output filename"), - OPT_BOOLEAN(0, "switch-output", &record.switch_output, - "Switch output when receive SIGUSR2"), + OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str, + &record.switch_output.set, "signal,size,time", + "Switch output when receive SIGUSR2 or cross size,time threshold", + "signal"), OPT_BOOLEAN(0, "dry-run", &dry_run, "Parse options then exit"), OPT_END() @@ -1559,12 +1671,18 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (rec->evlist == NULL) return -ENOMEM; - perf_config(perf_record_config, rec); + err = perf_config(perf_record_config, rec); + if (err) + return err; argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (quiet) + perf_quiet_option(); + + /* Make system wide (-a) the default target. */ if (!argc && target__none(&rec->opts.target)) - usage_with_options(record_usage, record_options); + rec->opts.target.system_wide = true; if (nr_cgroups && !rec->opts.target.system_wide) { usage_with_options_msg(record_usage, record_options, @@ -1578,8 +1696,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) return -EINVAL; } - if (rec->switch_output) - rec->timestamp_filename = true; + if (switch_output_setup(rec)) { + parse_options_usage(record_usage, record_options, "switch-output", 0); + return -EINVAL; + } + + if (rec->switch_output.time) { + signal(SIGALRM, alarm_sig_handler); + alarm(rec->switch_output.time); + } if (!rec->itr) { rec->itr = auxtrace_record__init(rec->evlist, &err); @@ -1629,7 +1754,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (rec->no_buildid_cache || rec->no_buildid) { disable_buildid_cache(); - } else if (rec->switch_output) { + } else if (rec->switch_output.enabled) { /* * In 'perf record --switch-output', disable buildid * generation by default to reduce data file switching @@ -1721,6 +1846,8 @@ out: static void snapshot_sig_handler(int sig __maybe_unused) { + struct record *rec = &record; + if (trigger_is_ready(&auxtrace_snapshot_trigger)) { trigger_hit(&auxtrace_snapshot_trigger); auxtrace_record__snapshot_started = 1; @@ -1728,6 +1855,14 @@ static void snapshot_sig_handler(int sig __maybe_unused) trigger_error(&auxtrace_snapshot_trigger); } - if (trigger_is_ready(&switch_output_trigger)) + if (switch_output_signal(rec)) + trigger_hit(&switch_output_trigger); +} + +static void alarm_sig_handler(int sig __maybe_unused) +{ + struct record *rec = &record; + + if (switch_output_time(rec)) trigger_hit(&switch_output_trigger); } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 06cc759a4597..0a88670e56f3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -320,6 +320,9 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report size_t size = sizeof(buf); int socked_id = hists->socket_filter; + if (quiet) + return 0; + if (symbol_conf.filter_relative) { nr_samples = hists->stats.nr_non_filtered_samples; nr_events = hists->stats.total_non_filtered_period; @@ -372,7 +375,11 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, { struct perf_evsel *pos; - fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", evlist->stats.total_lost_samples); + if (!quiet) { + fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", + evlist->stats.total_lost_samples); + } + evlist__for_each_entry(evlist, pos) { struct hists *hists = evsel__hists(pos); const char *evname = perf_evsel__name(pos); @@ -382,7 +389,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, continue; hists__fprintf_nr_sample_events(hists, rep, evname, stdout); - hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout, + hists__fprintf(hists, !quiet, 0, 0, rep->min_percent, stdout, symbol_conf.use_callchain); fprintf(stdout, "\n\n"); } @@ -716,6 +723,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "input file name"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"), OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, @@ -847,7 +855,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) if (ret < 0) return ret; - perf_config(report__config, &report); + ret = perf_config(report__config, &report); + if (ret) + return ret; argc = parse_options(argc, argv, options, report_usage, 0); if (argc) { @@ -861,6 +871,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) report.symbol_filter_str = argv[0]; } + if (quiet) + perf_quiet_option(); + if (symbol_conf.vmlinux_name && access(symbol_conf.vmlinux_name, R_OK)) { pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name); @@ -981,14 +994,14 @@ repeat: goto error; } - if (report.header || report.header_only) { + if ((report.header || report.header_only) && !quiet) { perf_session__fprintf_info(session, stdout, report.show_full_info); if (report.header_only) { ret = 0; goto error; } - } else if (use_browser == 0) { + } else if (use_browser == 0 && !quiet) { fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", stdout); } @@ -1007,7 +1020,7 @@ repeat: * providing it only in verbose mode not to bloat too * much struct symbol. */ - if (verbose) { + if (verbose > 0) { /* * XXX: Need to provide a less kludgy way to ask for * more space per symbol, the u32 is for the index on diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 5b134b0d1ff3..b94cf0de715a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -77,6 +77,22 @@ struct sched_atom { #define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP" +/* task state bitmask, copied from include/linux/sched.h */ +#define TASK_RUNNING 0 +#define TASK_INTERRUPTIBLE 1 +#define TASK_UNINTERRUPTIBLE 2 +#define __TASK_STOPPED 4 +#define __TASK_TRACED 8 +/* in tsk->exit_state */ +#define EXIT_DEAD 16 +#define EXIT_ZOMBIE 32 +#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) +/* in tsk->state again */ +#define TASK_DEAD 64 +#define TASK_WAKEKILL 128 +#define TASK_WAKING 256 +#define TASK_PARKED 512 + enum thread_state { THREAD_SLEEPING = 0, THREAD_WAIT_CPU, @@ -206,6 +222,7 @@ struct perf_sched { bool show_cpu_visual; bool show_wakeups; bool show_migrations; + bool show_state; u64 skipped_samples; const char *time_str; struct perf_time_interval ptime; @@ -216,13 +233,20 @@ struct perf_sched { struct thread_runtime { u64 last_time; /* time of previous sched in/out event */ u64 dt_run; /* run time */ - u64 dt_wait; /* time between CPU access (off cpu) */ + u64 dt_sleep; /* time between CPU access by sleep (off cpu) */ + u64 dt_iowait; /* time between CPU access by iowait (off cpu) */ + u64 dt_preempt; /* time between CPU access by preempt (off cpu) */ u64 dt_delay; /* time between wakeup and sched-in */ u64 ready_to_run; /* time of wakeup */ struct stats run_stats; u64 total_run_time; + u64 total_sleep_time; + u64 total_iowait_time; + u64 total_preempt_time; + u64 total_delay_time; + int last_state; u64 migrations; }; @@ -436,7 +460,7 @@ static struct task_desc *register_pid(struct perf_sched *sched, BUG_ON(!sched->tasks); sched->tasks[task->nr] = task; - if (verbose) + if (verbose > 0) printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm); return task; @@ -770,7 +794,7 @@ replay_wakeup_event(struct perf_sched *sched, const u32 pid = perf_evsel__intval(evsel, sample, "pid"); struct task_desc *waker, *wakee; - if (verbose) { + if (verbose > 0) { printf("sched_wakeup event %p\n", evsel); printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid); @@ -798,7 +822,7 @@ static int replay_switch_event(struct perf_sched *sched, int cpu = sample->cpu; s64 delta; - if (verbose) + if (verbose > 0) printf("sched_switch event %p\n", evsel); if (cpu >= MAX_CPUS || cpu < 0) @@ -846,7 +870,7 @@ static int replay_fork_event(struct perf_sched *sched, goto out_put; } - if (verbose) { + if (verbose > 0) { printf("fork event\n"); printf("... parent: %s/%d\n", thread__comm_str(parent), parent->tid); printf("... child: %s/%d\n", thread__comm_str(child), child->tid); @@ -1549,7 +1573,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp)); color_fprintf(stdout, color, " %12s secs ", stimestamp); - if (new_shortname || (verbose && sched_in->tid)) { + if (new_shortname || (verbose > 0 && sched_in->tid)) { const char *pid_color = color; if (thread__has_color(sched_in)) @@ -1821,6 +1845,9 @@ static void timehist_header(struct perf_sched *sched) printf(" %-*s %9s %9s %9s", comm_width, "task name", "wait time", "sch delay", "run time"); + if (sched->show_state) + printf(" %s", "state"); + printf("\n"); /* @@ -1831,9 +1858,14 @@ static void timehist_header(struct perf_sched *sched) if (sched->show_cpu_visual) printf(" %*s ", ncpus, ""); - printf(" %-*s %9s %9s %9s\n", comm_width, + printf(" %-*s %9s %9s %9s", comm_width, "[tid/pid]", "(msec)", "(msec)", "(msec)"); + if (sched->show_state) + printf(" %5s", ""); + + printf("\n"); + /* * separator */ @@ -1846,18 +1878,34 @@ static void timehist_header(struct perf_sched *sched) graph_dotted_line, graph_dotted_line, graph_dotted_line, graph_dotted_line); + if (sched->show_state) + printf(" %.5s", graph_dotted_line); + printf("\n"); } +static char task_state_char(struct thread *thread, int state) +{ + static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; + unsigned bit = state ? ffs(state) : 0; + + /* 'I' for idle */ + if (thread->tid == 0) + return 'I'; + + return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; +} + static void timehist_print_sample(struct perf_sched *sched, struct perf_sample *sample, struct addr_location *al, struct thread *thread, - u64 t) + u64 t, int state) { struct thread_runtime *tr = thread__priv(thread); u32 max_cpus = sched->max_cpu + 1; char tstr[64]; + u64 wait_time; timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); printf("%15s [%04d] ", tstr, sample->cpu); @@ -1880,10 +1928,15 @@ static void timehist_print_sample(struct perf_sched *sched, printf(" %-*s ", comm_width, timehist_get_commstr(thread)); - print_sched_time(tr->dt_wait, 6); + wait_time = tr->dt_sleep + tr->dt_iowait + tr->dt_preempt; + print_sched_time(wait_time, 6); + print_sched_time(tr->dt_delay, 6); print_sched_time(tr->dt_run, 6); + if (sched->show_state) + printf(" %5c ", task_state_char(thread, state)); + if (sched->show_wakeups) printf(" %-*s", comm_width, ""); @@ -1930,8 +1983,11 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, u64 t, u64 tprev) { r->dt_delay = 0; - r->dt_wait = 0; + r->dt_sleep = 0; + r->dt_iowait = 0; + r->dt_preempt = 0; r->dt_run = 0; + if (tprev) { r->dt_run = t - tprev; if (r->ready_to_run) { @@ -1943,12 +1999,25 @@ static void timehist_update_runtime_stats(struct thread_runtime *r, if (r->last_time > tprev) pr_debug("time travel: last sched out time for task > previous sched_switch event\n"); - else if (r->last_time) - r->dt_wait = tprev - r->last_time; + else if (r->last_time) { + u64 dt_wait = tprev - r->last_time; + + if (r->last_state == TASK_RUNNING) + r->dt_preempt = dt_wait; + else if (r->last_state == TASK_UNINTERRUPTIBLE) + r->dt_iowait = dt_wait; + else + r->dt_sleep = dt_wait; + } } update_stats(&r->run_stats, r->dt_run); - r->total_run_time += r->dt_run; + + r->total_run_time += r->dt_run; + r->total_delay_time += r->dt_delay; + r->total_sleep_time += r->dt_sleep; + r->total_iowait_time += r->dt_iowait; + r->total_preempt_time += r->dt_preempt; } static bool is_idle_sample(struct perf_sample *sample, @@ -1981,7 +2050,7 @@ static void save_task_callchain(struct perf_sched *sched, if (thread__resolve_callchain(thread, cursor, evsel, sample, NULL, NULL, sched->max_stack + 2) != 0) { - if (verbose) + if (verbose > 0) error("Failed to resolve callchain. Skipping\n"); return; @@ -1998,7 +2067,7 @@ static void save_task_callchain(struct perf_sched *sched, break; sym = node->sym; - if (sym && sym->name) { + if (sym) { if (!strcmp(sym->name, "schedule") || !strcmp(sym->name, "__schedule") || !strcmp(sym->name, "preempt_schedule")) @@ -2373,6 +2442,8 @@ static int timehist_sched_change_event(struct perf_tool *tool, struct thread_runtime *tr = NULL; u64 tprev, t = sample->time; int rc = 0; + int state = perf_evsel__intval(evsel, sample, "prev_state"); + if (machine__resolve(machine, &al, sample) < 0) { pr_err("problem processing %d event. skipping it\n", @@ -2447,8 +2518,10 @@ static int timehist_sched_change_event(struct perf_tool *tool, * time. we only care total run time and run stat. */ last_tr->dt_run = 0; - last_tr->dt_wait = 0; last_tr->dt_delay = 0; + last_tr->dt_sleep = 0; + last_tr->dt_iowait = 0; + last_tr->dt_preempt = 0; if (itr->cursor.nr) callchain_append(&itr->callchain, &itr->cursor, t - tprev); @@ -2458,7 +2531,7 @@ static int timehist_sched_change_event(struct perf_tool *tool, } if (!sched->summary_only) - timehist_print_sample(sched, sample, &al, thread, t); + timehist_print_sample(sched, sample, &al, thread, t, state); out: if (sched->hist_time.start == 0 && t >= ptime->start) @@ -2470,6 +2543,9 @@ out: /* time of this sched_switch event becomes last time task seen */ tr->last_time = sample->time; + /* last state is used to determine where to account wait time */ + tr->last_state = state; + /* sched out event for task so reset ready to run time */ tr->ready_to_run = 0; } @@ -2526,7 +2602,26 @@ static void print_thread_runtime(struct thread *t, printf("\n"); } +static void print_thread_waittime(struct thread *t, + struct thread_runtime *r) +{ + printf("%*s %5d %9" PRIu64 " ", + comm_width, timehist_get_commstr(t), t->ppid, + (u64) r->run_stats.n); + + print_sched_time(r->total_run_time, 8); + print_sched_time(r->total_sleep_time, 6); + printf(" "); + print_sched_time(r->total_iowait_time, 6); + printf(" "); + print_sched_time(r->total_preempt_time, 6); + printf(" "); + print_sched_time(r->total_delay_time, 6); + printf("\n"); +} + struct total_run_stats { + struct perf_sched *sched; u64 sched_count; u64 task_count; u64 total_run_time; @@ -2545,7 +2640,11 @@ static int __show_thread_runtime(struct thread *t, void *priv) stats->task_count++; stats->sched_count += r->run_stats.n; stats->total_run_time += r->total_run_time; - print_thread_runtime(t, r); + + if (stats->sched->show_state) + print_thread_waittime(t, r); + else + print_thread_runtime(t, r); } return 0; @@ -2633,18 +2732,24 @@ static void timehist_print_summary(struct perf_sched *sched, u64 hist_time = sched->hist_time.end - sched->hist_time.start; memset(&totals, 0, sizeof(totals)); + totals.sched = sched; if (sched->idle_hist) { printf("\nIdle-time summary\n"); printf("%*s parent sched-out ", comm_width, "comm"); printf(" idle-time min-idle avg-idle max-idle stddev migrations\n"); + } else if (sched->show_state) { + printf("\nWait-time summary\n"); + printf("%*s parent sched-in ", comm_width, "comm"); + printf(" run-time sleep iowait preempt delay\n"); } else { printf("\nRuntime summary\n"); printf("%*s parent sched-in ", comm_width, "comm"); printf(" run-time min-run avg-run max-run stddev migrations\n"); } printf("%*s (count) ", comm_width, ""); - printf(" (msec) (msec) (msec) (msec) %%\n"); + printf(" (msec) (msec) (msec) (msec) %s\n", + sched->show_state ? "(msec)" : "%"); printf("%.117s\n", graph_dotted_line); machine__for_each_thread(m, show_thread_runtime, &totals); @@ -3240,6 +3345,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN('I', "idle-hist", &sched.idle_hist, "Show idle events only"), OPT_STRING(0, "time", &sched.time_str, "str", "Time span for analysis (start,stop)"), + OPT_BOOLEAN(0, "state", &sched.show_state, "Show task state when sched-out"), OPT_PARENT(sched_options) }; diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 2f3ff69fc4e7..c0783b4f7b6c 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2180,7 +2180,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) "Show the mmap events"), OPT_BOOLEAN('\0', "show-switch-events", &script.show_switch_events, "Show context switch events (if recorded)"), - OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), + OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), OPT_BOOLEAN(0, "ns", &nanosecs, "Use 9 decimal places when displaying time"), OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", @@ -2212,6 +2212,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) PARSE_OPT_STOP_AT_NON_OPTION); file.path = input_name; + file.force = symbol_conf.force; if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a02f2e965628..13b54999ad79 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -533,7 +533,7 @@ static int store_counter_ids(struct perf_evsel *counter) static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; - char msg[512]; + char msg[BUFSIZ]; unsigned long long t0, t1; struct perf_evsel *counter; struct timespec ts; @@ -573,7 +573,7 @@ try_again: if (errno == EINVAL || errno == ENOSYS || errno == ENOENT || errno == EOPNOTSUPP || errno == ENXIO) { - if (verbose) + if (verbose > 0) ui__warning("%s event is not supported by the kernel.\n", perf_evsel__name(counter)); counter->supported = false; @@ -582,7 +582,7 @@ try_again: !(counter->leader->nr_members > 1)) continue; } else if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { - if (verbose) + if (verbose > 0) ui__warning("%s\n", msg); goto try_again; } @@ -1765,7 +1765,7 @@ static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, i cpu = map->map[idx]; - if (cpu >= env->nr_cpus_online) + if (cpu >= env->nr_cpus_avail) return -1; return cpu; @@ -2445,8 +2445,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) } else if (big_num_opt == 0) /* User passed --no-big-num */ big_num = false; + /* Make system wide (-a) the default target. */ if (!argc && target__none(&target)) - usage_with_options(stat_usage, stat_options); + target.system_wide = true; if (run_count < 0) { pr_err("Run count must be a positive number\n"); @@ -2538,7 +2539,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) status = 0; for (run_idx = 0; forever || run_idx < run_count; run_idx++) { - if (run_count != 1 && verbose) + if (run_count != 1 && verbose > 0) fprintf(output, "[ perf stat: executing run #%d ... ]\n", run_idx + 1); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 3df4178ba378..ab9077915763 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -643,7 +643,7 @@ repeat: case -1: if (errno == EINTR) continue; - /* Fall trhu */ + __fallthrough; default: c = getc(stdin); tcsetattr(0, TCSAFLUSH, &save); @@ -859,7 +859,7 @@ static void perf_top__mmap_read(struct perf_top *top) static int perf_top__start_counters(struct perf_top *top) { - char msg[512]; + char msg[BUFSIZ]; struct perf_evsel *counter; struct perf_evlist *evlist = top->evlist; struct record_opts *opts = &top->record_opts; @@ -871,7 +871,7 @@ try_again: if (perf_evsel__open(counter, top->evlist->cpus, top->evlist->threads) < 0) { if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { - if (verbose) + if (verbose > 0) ui__warning("%s\n", msg); goto try_again; } @@ -1216,7 +1216,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (top.evlist == NULL) return -ENOMEM; - perf_config(perf_top_config, &top); + status = perf_config(perf_top_config, &top); + if (status) + return status; argc = parse_options(argc, argv, options, top_usage, 0); if (argc) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 206bf72b77fc..256f1fac6f7e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -40,6 +40,7 @@ #include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */ #include <stdlib.h> +#include <string.h> #include <linux/err.h> #include <linux/filter.h> #include <linux/audit.h> @@ -1398,7 +1399,7 @@ static struct syscall *trace__syscall_info(struct trace *trace, return &trace->syscalls.table[id]; out_cant_read: - if (verbose) { + if (verbose > 0) { fprintf(trace->output, "Problems reading syscall %d", id); if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); @@ -1800,10 +1801,10 @@ static void print_location(FILE *f, struct perf_sample *sample, bool print_dso, bool print_sym) { - if ((verbose || print_dso) && al->map) + if ((verbose > 0 || print_dso) && al->map) fprintf(f, "%s@", al->map->dso->long_name); - if ((verbose || print_sym) && al->sym) + if ((verbose > 0 || print_sym) && al->sym) fprintf(f, "%s+0x%" PRIx64, al->sym->name, al->addr - al->sym->start); else if (al->map) @@ -2699,6 +2700,91 @@ static void evlist__set_evsel_handler(struct perf_evlist *evlist, void *handler) evsel->handler = handler; } +/* + * XXX: Hackish, just splitting the combined -e+--event (syscalls + * (raw_syscalls:{sys_{enter,exit}} + events (tracepoints, HW, SW, etc) to use + * existing facilities unchanged (trace->ev_qualifier + parse_options()). + * + * It'd be better to introduce a parse_options() variant that would return a + * list with the terms it didn't match to an event... + */ +static int trace__parse_events_option(const struct option *opt, const char *str, + int unset __maybe_unused) +{ + struct trace *trace = (struct trace *)opt->value; + const char *s = str; + char *sep = NULL, *lists[2] = { NULL, NULL, }; + int len = strlen(str), err = -1, list; + char *strace_groups_dir = system_path(STRACE_GROUPS_DIR); + char group_name[PATH_MAX]; + + if (strace_groups_dir == NULL) + return -1; + + if (*s == '!') { + ++s; + trace->not_ev_qualifier = true; + } + + while (1) { + if ((sep = strchr(s, ',')) != NULL) + *sep = '\0'; + + list = 0; + if (syscalltbl__id(trace->sctbl, s) >= 0) { + list = 1; + } else { + path__join(group_name, sizeof(group_name), strace_groups_dir, s); + if (access(group_name, R_OK) == 0) + list = 1; + } + + if (lists[list]) { + sprintf(lists[list] + strlen(lists[list]), ",%s", s); + } else { + lists[list] = malloc(len); + if (lists[list] == NULL) + goto out; + strcpy(lists[list], s); + } + + if (!sep) + break; + + *sep = ','; + s = sep + 1; + } + + if (lists[1] != NULL) { + struct strlist_config slist_config = { + .dirname = strace_groups_dir, + }; + + trace->ev_qualifier = strlist__new(lists[1], &slist_config); + if (trace->ev_qualifier == NULL) { + fputs("Not enough memory to parse event qualifier", trace->output); + goto out; + } + + if (trace__validate_ev_qualifier(trace)) + goto out; + } + + err = 0; + + if (lists[0]) { + struct option o = OPT_CALLBACK('e', "event", &trace->evlist, "event", + "event selector. use 'perf list' to list available events", + parse_events_option); + err = parse_events_option(&o, lists[0], 0); + } +out: + if (sep) + *sep = ','; + + return err; +} + int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) { const char *trace_usage[] = { @@ -2730,15 +2816,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) .max_stack = UINT_MAX, }; const char *output_name = NULL; - const char *ev_qualifier_str = NULL; const struct option trace_options[] = { - OPT_CALLBACK(0, "event", &trace.evlist, "event", - "event selector. use 'perf list' to list available events", - parse_events_option), + OPT_CALLBACK('e', "event", &trace, "event", + "event/syscall selector. use 'perf list' to list available events", + trace__parse_events_option), OPT_BOOLEAN(0, "comm", &trace.show_comm, "show the thread COMM next to its id"), OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), - OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"), + OPT_CALLBACK(0, "expr", &trace, "expr", "list of syscalls/events to trace", + trace__parse_events_option), OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", @@ -2863,7 +2949,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) return -1; } - if (!trace.trace_syscalls && ev_qualifier_str) { + if (!trace.trace_syscalls && trace.ev_qualifier) { pr_err("The -e option can't be used with --no-syscalls.\n"); goto out; } @@ -2878,28 +2964,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) trace.open_id = syscalltbl__id(trace.sctbl, "open"); - if (ev_qualifier_str != NULL) { - const char *s = ev_qualifier_str; - struct strlist_config slist_config = { - .dirname = system_path(STRACE_GROUPS_DIR), - }; - - trace.not_ev_qualifier = *s == '!'; - if (trace.not_ev_qualifier) - ++s; - trace.ev_qualifier = strlist__new(s, &slist_config); - if (trace.ev_qualifier == NULL) { - fputs("Not enough memory to parse event qualifier", - trace.output); - err = -ENOMEM; - goto out_close; - } - - err = trace__validate_ev_qualifier(&trace); - if (err) - goto out_close; - } - err = target__validate(&trace.opts.target); if (err) { target__strerror(&trace.opts.target, err, bf, sizeof(bf)); diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 0bcf68e98ccc..036e1e35b1a8 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -23,6 +23,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix); int cmd_evlist(int argc, const char **argv, const char *prefix); int cmd_help(int argc, const char **argv, const char *prefix); int cmd_sched(int argc, const char **argv, const char *prefix); +int cmd_kallsyms(int argc, const char **argv, const char *prefix); int cmd_list(int argc, const char **argv, const char *prefix); int cmd_record(int argc, const char **argv, const char *prefix); int cmd_report(int argc, const char **argv, const char *prefix); @@ -40,6 +41,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix); int cmd_inject(int argc, const char **argv, const char *prefix); int cmd_mem(int argc, const char **argv, const char *prefix); int cmd_data(int argc, const char **argv, const char *prefix); +int cmd_ftrace(int argc, const char **argv, const char *prefix); int find_scripts(char **scripts_array, char **scripts_path_array); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index ab5cbaa170d0..ac3efd396a72 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -11,7 +11,9 @@ perf-data mainporcelain common perf-diff mainporcelain common perf-config mainporcelain common perf-evlist mainporcelain common +perf-ftrace mainporcelain common perf-inject mainporcelain common +perf-kallsyms mainporcelain common perf-kmem mainporcelain common perf-kvm mainporcelain common perf-list mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index aa23b3347d6b..6d5479e03e0d 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -29,7 +29,6 @@ const char perf_usage_string[] = const char perf_more_info_string[] = "See 'perf help COMMAND' for more information on a specific command."; -int use_browser = -1; static int use_pager = -1; const char *input_name; @@ -47,6 +46,7 @@ static struct cmd_struct commands[] = { { "diff", cmd_diff, 0 }, { "evlist", cmd_evlist, 0 }, { "help", cmd_help, 0 }, + { "kallsyms", cmd_kallsyms, 0 }, { "list", cmd_list, 0 }, { "record", cmd_record, 0 }, { "report", cmd_report, 0 }, @@ -71,6 +71,7 @@ static struct cmd_struct commands[] = { { "inject", cmd_inject, 0 }, { "mem", cmd_mem, 0 }, { "data", cmd_data, 0 }, + { "ftrace", cmd_ftrace, 0 }, }; struct pager_config { @@ -89,11 +90,12 @@ static int pager_command_config(const char *var, const char *value, void *data) /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ int check_pager_config(const char *cmd) { + int err; struct pager_config c; c.cmd = cmd; c.val = -1; - perf_config(pager_command_config, &c); - return c.val; + err = perf_config(pager_command_config, &c); + return err ?: c.val; } static int browser_command_config(const char *var, const char *value, void *data) @@ -112,11 +114,12 @@ static int browser_command_config(const char *var, const char *value, void *data */ static int check_browser_config(const char *cmd) { + int err; struct pager_config c; c.cmd = cmd; c.val = -1; - perf_config(browser_command_config, &c); - return c.val; + err = perf_config(browser_command_config, &c); + return err ?: c.val; } static void commit_pager_choice(void) @@ -329,8 +332,6 @@ static int handle_alias(int *argcp, const char ***argv) return ret; } -const char perf_version_string[] = PERF_VERSION; - #define RUN_SETUP (1<<0) #define USE_PAGER (1<<1) @@ -510,6 +511,7 @@ static void cache_line_size(int *cacheline_sizep) int main(int argc, const char **argv) { + int err; const char *cmd; char sbuf[STRERR_BUFSIZE]; int value; @@ -535,7 +537,9 @@ int main(int argc, const char **argv) srandom(time(NULL)); perf_config__init(); - perf_config(perf_default_config, NULL); + err = perf_config(perf_default_config, NULL); + if (err) + return err; set_buildid_dir(NULL); /* get debugfs/tracefs mount point from /proc/mounts */ diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json new file mode 100644 index 000000000000..076459c51d4e --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-cache.json @@ -0,0 +1,317 @@ +[ + { + "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_C_CLOCKTICKS", + "PerPkg": "1", + "Unit": "CBO" + }, + { + "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_C_LLC_LOOKUP.ANY", + "Filter": "filter_state=0x1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x11", + "Unit": "CBO" + }, + { + "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_C_LLC_VICTIMS.M_STATE", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.UNCACHEABLE", + "Filter": "filter_opc=0x187", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_READ", + "Filter": "filter_opc=0x187,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_WRITE", + "Filter": "filter_opc=0x18f,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.RFO_LLC_PREFETCH", + "Filter": "filter_opc=0x190", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x191", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_LLC_PREFETCH", + "Filter": "filter_opc=0x192", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE", + "Filter": "filter_opc=0x180,filter_tid=0x3e", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x181", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_FULL", + "Filter": "filter_opc=0x18c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", + "Filter": "filter_opc=0x18d", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_LOCAL", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_REMOTE", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES", + "PerPkg": "1", + "UMask": "0xC", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_LOCAL", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_REMOTE", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "HA" + }, + { + "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x20", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPIFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPS", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPSFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x8", + "Unit": "HA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json new file mode 100644 index 000000000000..d17dc235f734 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-memory.json @@ -0,0 +1,83 @@ +[ + { + "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0xC", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_POWER_CHANNEL_PPD", + "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES", + "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_POWER_SELF_REFRESH", + "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.PAGE_MISS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.RD", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.WR", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "iMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json new file mode 100644 index 000000000000..b44d43088bbb --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellde/uncore-power.json @@ -0,0 +1,84 @@ +[ + { + "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0", + "Filter": "occ_sel=1", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3", + "Filter": "occ_sel=2", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6", + "Filter": "occ_sel=3", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xA", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x6", + "EventName": "UNC_P_FREQ_MAX_OS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x5", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x74", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json new file mode 100644 index 000000000000..076459c51d4e --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-cache.json @@ -0,0 +1,317 @@ +[ + { + "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_C_CLOCKTICKS", + "PerPkg": "1", + "Unit": "CBO" + }, + { + "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_C_LLC_LOOKUP.ANY", + "Filter": "filter_state=0x1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x11", + "Unit": "CBO" + }, + { + "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_C_LLC_VICTIMS.M_STATE", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.UNCACHEABLE", + "Filter": "filter_opc=0x187", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_READ", + "Filter": "filter_opc=0x187,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_WRITE", + "Filter": "filter_opc=0x18f,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.RFO_LLC_PREFETCH", + "Filter": "filter_opc=0x190", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x191", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_LLC_PREFETCH", + "Filter": "filter_opc=0x192", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE", + "Filter": "filter_opc=0x180,filter_tid=0x3e", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x181", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_FULL", + "Filter": "filter_opc=0x18c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", + "Filter": "filter_opc=0x18d", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_LOCAL", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_REMOTE", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES", + "PerPkg": "1", + "UMask": "0xC", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_LOCAL", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_REMOTE", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "HA" + }, + { + "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x20", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPIFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPS", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPSFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x8", + "Unit": "HA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json new file mode 100644 index 000000000000..39387f7909b2 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-interconnect.json @@ -0,0 +1,28 @@ +[ + { + "BriefDescription": "QPI clock ticks. Derived from unc_q_clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x14", + "EventName": "UNC_Q_CLOCKTICKS", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x2", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x4", + "Unit": "QPI LL" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json new file mode 100644 index 000000000000..d17dc235f734 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-memory.json @@ -0,0 +1,83 @@ +[ + { + "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0xC", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_POWER_CHANNEL_PPD", + "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES", + "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_POWER_SELF_REFRESH", + "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.PAGE_MISS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.RD", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.WR", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "iMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json new file mode 100644 index 000000000000..b44d43088bbb --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/broadwellx/uncore-power.json @@ -0,0 +1,84 @@ +[ + { + "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0", + "Filter": "occ_sel=1", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3", + "Filter": "occ_sel=2", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6", + "Filter": "occ_sel=3", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xA", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x6", + "EventName": "UNC_P_FREQ_MAX_OS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x5", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x74", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json new file mode 100644 index 000000000000..076459c51d4e --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-cache.json @@ -0,0 +1,317 @@ +[ + { + "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_C_CLOCKTICKS", + "PerPkg": "1", + "Unit": "CBO" + }, + { + "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any", + "Counter": "0,1,2,3", + "EventCode": "0x34", + "EventName": "UNC_C_LLC_LOOKUP.ANY", + "Filter": "filter_state=0x1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x11", + "Unit": "CBO" + }, + { + "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state", + "Counter": "0,1,2,3", + "EventCode": "0x37", + "EventName": "UNC_C_LLC_VICTIMS.M_STATE", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.UNCACHEABLE", + "Filter": "filter_opc=0x187", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_READ", + "Filter": "filter_opc=0x187,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "MMIO writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.MMIO_WRITE", + "Filter": "filter_opc=0x18f,filter_nc=1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.RFO_LLC_PREFETCH", + "Filter": "filter_opc=0x190", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x191", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_LLC_PREFETCH", + "Filter": "filter_opc=0x192", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "ItoM write misses (as part of fast string memcpy stores) + PCIe full line writes. Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write misses (full cache line). Derived from unc_c_tor_inserts.miss_opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe writes (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE", + "Filter": "filter_opc=0x180,filter_tid=0x3e", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "L2 demand and L2 prefetch code references to LLC. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x181", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_FULL", + "Filter": "filter_opc=0x18c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", + "Filter": "filter_opc=0x18d", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe write references (full cache line). Derived from unc_c_tor_inserts.opcode", + "Counter": "0,1,2,3", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_WRITE", + "Filter": "filter_opc=0x1c8,filter_tid=0x3e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to local home agent. Derived from unc_h_requests.reads_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_LOCAL", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "HA" + }, + { + "BriefDescription": "read requests to remote home agent. Derived from unc_h_requests.reads_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS_REMOTE", + "PerPkg": "1", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES", + "PerPkg": "1", + "UMask": "0xC", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to local home agent. Derived from unc_h_requests.writes_local", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_LOCAL", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to remote home agent. Derived from unc_h_requests.writes_remote", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES_REMOTE", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "HA" + }, + { + "BriefDescription": "Conflict requests (requests for same address from multiple agents simultaneously). Derived from unc_h_snoop_resp.rspcnflct", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPCNFLCT", + "PerPkg": "1", + "UMask": "0x40", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x20", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPIFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPS", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPSFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x8", + "Unit": "HA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json new file mode 100644 index 000000000000..39387f7909b2 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-interconnect.json @@ -0,0 +1,28 @@ +[ + { + "BriefDescription": "QPI clock ticks. Derived from unc_q_clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x14", + "EventName": "UNC_Q_CLOCKTICKS", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x2", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x4", + "Unit": "QPI LL" + } +] diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json new file mode 100644 index 000000000000..d17dc235f734 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-memory.json @@ -0,0 +1,83 @@ +[ + { + "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0xC", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory controller clock ticks. Derived from unc_m_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_POWER_CHANNEL_PPD", + "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES", + "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_POWER_SELF_REFRESH", + "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charges due to page misses. Derived from unc_m_pre_count.page_miss", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.PAGE_MISS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for reads. Derived from unc_m_pre_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.RD", + "PerPkg": "1", + "UMask": "0x4", + "Unit": "iMC" + }, + { + "BriefDescription": "Pre-charge for writes. Derived from unc_m_pre_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.WR", + "PerPkg": "1", + "UMask": "0x8", + "Unit": "iMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json b/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json new file mode 100644 index 000000000000..b44d43088bbb --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/haswellx/uncore-power.json @@ -0,0 +1,84 @@ +[ + { + "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C0 and C1. Derived from unc_p_power_state_occupancy.cores_c0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0", + "Filter": "occ_sel=1", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C3. Derived from unc_p_power_state_occupancy.cores_c3", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3", + "Filter": "occ_sel=2", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "C6 and C7. Derived from unc_p_power_state_occupancy.cores_c6", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6", + "Filter": "occ_sel=3", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "External Prochot. Derived from unc_p_prochot_external_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xA", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Thermal Strongest Upper Limit Cycles. Derived from unc_p_freq_max_limit_thermal_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "OS Strongest Upper Limit Cycles. Derived from unc_p_freq_max_os_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x6", + "EventName": "UNC_P_FREQ_MAX_OS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Power Strongest Upper Limit Cycles. Derived from unc_p_freq_max_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x5", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x74", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json new file mode 100644 index 000000000000..2efdc6772e0b --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-cache.json @@ -0,0 +1,322 @@ +[ + { + "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_C_CLOCKTICKS", + "PerPkg": "1", + "Unit": "CBO" + }, + { + "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any", + "Counter": "0,1", + "EventCode": "0x34", + "EventName": "UNC_C_LLC_LOOKUP.ANY", + "Filter": "filter_state=0x1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x11", + "Unit": "CBO" + }, + { + "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state", + "Counter": "0,1", + "EventCode": "0x37", + "EventName": "UNC_C_LLC_VICTIMS.M_STATE", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode.demand", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - Uncacheable reads. Derived from unc_c_tor_inserts.miss_opcode.uncacheable", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.UNCACHEABLE", + "Filter": "filter_opc=0x187", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for RFO. Derived from unc_c_tor_inserts.miss_opcode.rfo_prefetch", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.RFO_LLC_PREFETCH", + "Filter": "filter_opc=0x190", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for code reads. Derived from unc_c_tor_inserts.miss_opcode.code", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.CODE_LLC_PREFETCH", + "Filter": "filter_opc=0x191", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC prefetch misses for data reads. Derived from unc_c_tor_inserts.miss_opcode.data_read", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_LLC_PREFETCH", + "Filter": "filter_opc=0x192", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe allocating writes that miss LLC - DDIO misses. Derived from unc_c_tor_inserts.miss_opcode.ddio_miss", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_WRITE", + "Filter": "filter_opc=0x19c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe read current. Derived from unc_c_tor_inserts.miss_opcode.pcie_read", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for ItoM writes (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.miss_opcode.itom_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.ITOM_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe non-snoop reads. Derived from unc_c_tor_inserts.miss_opcode.pcie_read", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_NON_SNOOP_READ", + "Filter": "filter_opc=0x1e4", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.miss_opcode.pcie_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_NON_SNOOP_WRITE", + "Filter": "filter_opc=0x1e6", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode.streaming_full", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_FULL", + "Filter": "filter_opc=0x18c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode.streaming_partial", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", + "Filter": "filter_opc=0x18d", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Partial PCIe reads. Derived from unc_c_tor_inserts.opcode.pcie_partial", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_PARTIAL_READ", + "Filter": "filter_opc=0x195", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe allocating writes that hit in LLC (DDIO hits). Derived from unc_c_tor_inserts.opcode.ddio_hit", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_WRITE", + "Filter": "filter_opc=0x19c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode.pcie_read_current", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "ItoM write hits (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.opcode.itom_write_hit", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.ITOM_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop reads. Derived from unc_c_tor_inserts.opcode.pcie_read", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_READ", + "Filter": "filter_opc=0x1e4", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop writes (partial). Derived from unc_c_tor_inserts.opcode.pcie_partial_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE", + "Filter": "filter_opc=0x1e5", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.opcode.pcie_full_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_WRITE", + "Filter": "filter_opc=0x1e6", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy for all LLC misses that are addressed to local memory. Derived from unc_c_tor_occupancy.miss_local", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.MISS_LOCAL", + "PerPkg": "1", + "UMask": "0x2A", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode.llc_data_read", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy for all LLC misses that are addressed to remote memory. Derived from unc_c_tor_occupancy.miss_remote", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.MISS_REMOTE", + "PerPkg": "1", + "UMask": "0x8A", + "Unit": "CBO" + }, + { + "BriefDescription": "Read requests to home agent. Derived from unc_h_requests.reads", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "HA" + }, + { + "BriefDescription": "Write requests to home agent. Derived from unc_h_requests.writes", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES", + "PerPkg": "1", + "UMask": "0xC", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache along with writeback to memory. Derived from unc_h_snoop_resp.rsp_fwd_wb", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSP_FWD_WB", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x20", + "Unit": "HA" + }, + { + "BriefDescription": "M line forwarded from remote cache with no writeback to memory. Derived from unc_h_snoop_resp.rspifwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPIFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x4", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line response from remote cache. Derived from unc_h_snoop_resp.rsps", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPS", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x2", + "Unit": "HA" + }, + { + "BriefDescription": "Shared line forwarded from remote cache. Derived from unc_h_snoop_resp.rspsfwd", + "Counter": "0,1,2,3", + "EventCode": "0x21", + "EventName": "UNC_H_SNOOP_RESP.RSPSFWD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x8", + "Unit": "HA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json new file mode 100644 index 000000000000..d7e2fda1d695 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-interconnect.json @@ -0,0 +1,46 @@ +[ + { + "BriefDescription": "QPI clock ticks. Use to get percentages for QPI cycles events. Derived from unc_q_clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x14", + "EventName": "UNC_Q_CLOCKTICKS", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Cycles where receiving QPI link is in half-width mode. Derived from unc_q_rxl0p_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_Q_RxL0P_POWER_CYCLES", + "MetricExpr": "(UNC_Q_RxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Cycles where transmitting QPI link is in half-width mode. Derived from unc_q_txl0p_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_Q_TxL0P_POWER_CYCLES", + "MetricExpr": "(UNC_Q_TxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x2", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x4", + "Unit": "QPI LL" + } +] diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json new file mode 100644 index 000000000000..ac4ad4d6357b --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-memory.json @@ -0,0 +1,75 @@ +[ + { + "BriefDescription": "Memory page activates for reads and writes. Derived from unc_m_act_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_M_ACT_COUNT.RD", + "PerPkg": "1", + "UMask": "0x1", + "Umask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "Read requests to memory controller. Derived from unc_m_cas_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "Write requests to memory controller. Derived from unc_m_cas_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0xC", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory controller clock ticks. Use to generate percentages for memory controller CYCLES events. Derived from unc_m_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_POWER_CHANNEL_PPD", + "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES", + "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_POWER_SELF_REFRESH", + "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory page conflicts. Derived from unc_m_pre_count.page_miss", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.PAGE_MISS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "iMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json new file mode 100644 index 000000000000..dc2586db0dfc --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/ivytown/uncore-power.json @@ -0,0 +1,249 @@ +[ + { + "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_BAND0_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_BAND1_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_BAND2_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_BAND3_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_BAND0_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_BAND1_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_BAND2_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_BAND3_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0", + "Filter": "occ_sel=1", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c3", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3", + "Filter": "occ_sel=2", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in the chosen C-State. It can be used by itself to get the average number of cores in that C-state with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c6", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6", + "Filter": "occ_sel=3", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that we are in external PROCHOT mode. This mode is triggered when a sensor off the die determines that something off-die (like DRAM) is too hot and must throttle to avoid damaging the chip. Derived from unc_p_prochot_external_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xa", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when thermal conditions are the upper limit on frequency. This is related to the THERMAL_THROTTLE CYCLES_ABOVE_TEMP event, which always counts cycles when we are above the thermal temperature. This event (STRONGEST_UPPER_LIMIT) is sampled at the output of the algorithm that determines the actual frequency, while THERMAL_THROTTLE looks at the input. Derived from unc_p_freq_max_limit_thermal_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when the OS is the upper limit on frequency. Derived from unc_p_freq_max_os_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x6", + "EventName": "UNC_P_FREQ_MAX_OS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when power is the upper limit on frequency. Derived from unc_p_freq_max_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x5", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when current is the upper limit on frequency. Derived from unc_p_freq_max_current_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x7", + "EventName": "UNC_P_FREQ_MAX_CURRENT_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_CURRENT_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when the system is changing frequency. This can not be filtered by thread ID. One can also use it with the occupancy counter that monitors number of threads in C0 to estimate the performance impact that frequency transitions had on the system. Derived from unc_p_freq_trans_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x60", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", + "Filter": "filter_band0=1200", + "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", + "Filter": "filter_band1=2000", + "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", + "Filter": "filter_band2=3000", + "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", + "Filter": "filter_band3=4000", + "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band0=1200", + "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band1=2000", + "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band2=4000", + "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band3=4000", + "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json new file mode 100644 index 000000000000..2f23cf0129e7 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-cache.json @@ -0,0 +1,209 @@ +[ + { + "BriefDescription": "Uncore cache clock ticks. Derived from unc_c_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_C_CLOCKTICKS", + "PerPkg": "1", + "Unit": "CBO" + }, + { + "BriefDescription": "All LLC Misses (code+ data rd + data wr - including demand and prefetch). Derived from unc_c_llc_lookup.any", + "Counter": "0,1", + "EventCode": "0x34", + "EventName": "UNC_C_LLC_LOOKUP.ANY", + "Filter": "filter_state=0x1", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x11", + "Unit": "CBO" + }, + { + "BriefDescription": "M line evictions from LLC (writebacks to memory). Derived from unc_c_llc_victims.m_state", + "Counter": "0,1", + "EventCode": "0x37", + "EventName": "UNC_C_LLC_VICTIMS.M_STATE", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - demand and prefetch data reads - excludes LLC prefetches. Derived from unc_c_tor_inserts.miss_opcode.demand", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.DATA_READ", + "Filter": "filter_opc=0x182", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses - Uncacheable reads. Derived from unc_c_tor_inserts.miss_opcode.uncacheable", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.UNCACHEABLE", + "Filter": "filter_opc=0x187", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe allocating writes that miss LLC - DDIO misses. Derived from unc_c_tor_inserts.miss_opcode.ddio_miss", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.PCIE_WRITE", + "Filter": "filter_opc=0x19c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "LLC misses for ItoM writes (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.miss_opcode.itom_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_MISSES.ITOM_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (full cache line). Derived from unc_c_tor_inserts.opcode.streaming_full", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_FULL", + "Filter": "filter_opc=0x18c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Streaming stores (partial cache line). Derived from unc_c_tor_inserts.opcode.streaming_partial", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", + "Filter": "filter_opc=0x18d", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Partial PCIe reads. Derived from unc_c_tor_inserts.opcode.pcie_partial", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_PARTIAL_READ", + "Filter": "filter_opc=0x195", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe allocating writes that hit in LLC (DDIO hits). Derived from unc_c_tor_inserts.opcode.ddio_hit", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_WRITE", + "Filter": "filter_opc=0x19c", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe read current. Derived from unc_c_tor_inserts.opcode.pcie_read_current", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_READ", + "Filter": "filter_opc=0x19e", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "ItoM write hits (as part of fast string memcpy stores). Derived from unc_c_tor_inserts.opcode.itom_write_hit", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.ITOM_WRITE", + "Filter": "filter_opc=0x1c8", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop reads. Derived from unc_c_tor_inserts.opcode.pcie_read", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_READ", + "Filter": "filter_opc=0x1e4", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop writes (partial). Derived from unc_c_tor_inserts.opcode.pcie_partial_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_PARTIAL_WRITE", + "Filter": "filter_opc=0x1e5", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "PCIe non-snoop writes (full line). Derived from unc_c_tor_inserts.opcode.pcie_full_write", + "Counter": "0,1", + "EventCode": "0x35", + "EventName": "LLC_REFERENCES.PCIE_NS_WRITE", + "Filter": "filter_opc=0x1e6", + "PerPkg": "1", + "ScaleUnit": "64Bytes", + "UMask": "0x1", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for all LLC misses; we divide this by UNC_C_CLOCKTICKS to get average Q depth. Derived from unc_c_tor_occupancy.miss_all", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.MISS_ALL", + "Filter": "filter_opc=0x182", + "MetricExpr": "(UNC_C_TOR_OCCUPANCY.MISS_ALL / UNC_C_CLOCKTICKS) * 100.", + "PerPkg": "1", + "UMask": "0xa", + "Unit": "CBO" + }, + { + "BriefDescription": "Occupancy counter for LLC data reads (demand and L2 prefetch). Derived from unc_c_tor_occupancy.miss_opcode.llc_data_read", + "EventCode": "0x36", + "EventName": "UNC_C_TOR_OCCUPANCY.LLC_DATA_READ", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "CBO" + }, + { + "BriefDescription": "read requests to home agent. Derived from unc_h_requests.reads", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.READS", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "HA" + }, + { + "BriefDescription": "write requests to home agent. Derived from unc_h_requests.writes", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_H_REQUESTS.WRITES", + "PerPkg": "1", + "UMask": "0xc", + "Unit": "HA" + } +] diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json new file mode 100644 index 000000000000..63351876eb57 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-interconnect.json @@ -0,0 +1,46 @@ +[ + { + "BriefDescription": "QPI clock ticks. Used to get percentages of QPI cycles events. Derived from unc_q_clockticks", + "Counter": "0,1,2,3", + "EventCode": "0x14", + "EventName": "UNC_Q_CLOCKTICKS", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Cycles where receiving QPI link is in half-width mode. Derived from unc_q_rxl0p_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x10", + "EventName": "UNC_Q_RxL0P_POWER_CYCLES", + "MetricExpr": "(UNC_Q_RxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Cycles where transmitting QPI link is in half-width mode. Derived from unc_q_txl0p_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_Q_TxL0P_POWER_CYCLES", + "MetricExpr": "(UNC_Q_TxL0P_POWER_CYCLES / UNC_Q_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of data flits transmitted . Derived from unc_q_txl_flits_g0.data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x2", + "Unit": "QPI LL" + }, + { + "BriefDescription": "Number of non data (control) flits transmitted . Derived from unc_q_txl_flits_g0.non_data", + "Counter": "0,1,2,3", + "EventName": "UNC_Q_TxL_FLITS_G0.NON_DATA", + "PerPkg": "1", + "ScaleUnit": "8Bytes", + "UMask": "0x4", + "Unit": "QPI LL" + } +] diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json new file mode 100644 index 000000000000..e2cf6daa7b37 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-memory.json @@ -0,0 +1,79 @@ +[ + { + "BriefDescription": "Memory page activates. Derived from unc_m_act_count", + "Counter": "0,1,2,3", + "EventCode": "0x1", + "EventName": "UNC_M_ACT_COUNT", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "read requests to memory controller. Derived from unc_m_cas_count.rd", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "UMask": "0x3", + "Unit": "iMC" + }, + { + "BriefDescription": "write requests to memory controller. Derived from unc_m_cas_count.wr", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "UMask": "0xc", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory controller clock ticks. Used to get percentages of memory controller cycles events. Derived from unc_m_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_M_CLOCKTICKS", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles where DRAM ranks are in power down (CKE) mode. Derived from unc_m_power_channel_ppd", + "Counter": "0,1,2,3", + "EventCode": "0x85", + "EventName": "UNC_M_POWER_CHANNEL_PPD", + "MetricExpr": "(UNC_M_POWER_CHANNEL_PPD / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles all ranks are in critical thermal throttle. Derived from unc_m_power_critical_throttle_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x86", + "EventName": "UNC_M_POWER_CRITICAL_THROTTLE_CYCLES", + "MetricExpr": "(UNC_M_POWER_CRITICAL_THROTTLE_CYCLES / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Cycles Memory is in self refresh power mode. Derived from unc_m_power_self_refresh", + "Counter": "0,1,2,3", + "EventCode": "0x43", + "EventName": "UNC_M_POWER_SELF_REFRESH", + "MetricExpr": "(UNC_M_POWER_SELF_REFRESH / UNC_M_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "iMC" + }, + { + "BriefDescription": "Memory page conflicts. Derived from unc_m_pre_count.page_miss", + "Counter": "0,1,2,3", + "EventCode": "0x2", + "EventName": "UNC_M_PRE_COUNT.PAGE_MISS", + "PerPkg": "1", + "UMask": "0x1", + "Unit": "iMC" + }, + { + "BriefDescription": "Occupancy counter for memory read queue. Derived from unc_m_rpq_occupancy", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_M_RPQ_OCCUPANCY", + "PerPkg": "1", + "Unit": "iMC" + } +] diff --git a/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json new file mode 100644 index 000000000000..bbe36d547386 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/jaketown/uncore-power.json @@ -0,0 +1,248 @@ +[ + { + "BriefDescription": "PCU clock ticks. Use to get percentages of PCU cycles events. Derived from unc_p_clockticks", + "Counter": "0,1,2,3", + "EventName": "UNC_P_CLOCKTICKS", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_BAND0_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_BAND1_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_BAND2_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_BAND3_CYCLES", + "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned a frequency greater than or equal to the frequency that is configured in the filter. (filter_band0=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_BAND0_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND0_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transistioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band1=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_BAND1_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND1_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band2=XXX with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_BAND2_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND2_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to the frequency that is configured in the filter. (filter_band3=XXX, with XXX in 100Mhz units). One can also use inversion (filter_inv=1) to track cycles when we were less than the configured frequency. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_BAND3_TRANSITIONS", + "Filter": "edge=1", + "MetricExpr": "(UNC_P_FREQ_BAND3_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C0. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c0", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C0", + "Filter": "occ_sel=1", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C0 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C3. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events and occupancy triggering to capture other details. Derived from unc_p_power_state_occupancy.cores_c3", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C3", + "Filter": "occ_sel=2", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C3 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "This is an occupancy event that tracks the number of cores that are in C6. It can be used by itself to get the average number of cores in C0, with threshholding to generate histograms, or with other PCU events . Derived from unc_p_power_state_occupancy.cores_c6", + "Counter": "0,1,2,3", + "EventCode": "0x80", + "EventName": "UNC_P_POWER_STATE_OCCUPANCY.CORES_C6", + "Filter": "occ_sel=3", + "MetricExpr": "(UNC_P_POWER_STATE_OCCUPANCY.CORES_C6 / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that we are in external PROCHOT mode. This mode is triggered when a sensor off the die determines that something off-die (like DRAM) is too hot and must throttle to avoid damaging the chip. Derived from unc_p_prochot_external_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xa", + "EventName": "UNC_P_PROCHOT_EXTERNAL_CYCLES", + "MetricExpr": "(UNC_P_PROCHOT_EXTERNAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when temperature is the upper limit on frequency. Derived from unc_p_freq_max_limit_thermal_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x4", + "EventName": "UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_LIMIT_THERMAL_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when the OS is the upper limit on frequency. Derived from unc_p_freq_max_os_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x6", + "EventName": "UNC_P_FREQ_MAX_OS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_OS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when power is the upper limit on frequency. Derived from unc_p_freq_max_power_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x5", + "EventName": "UNC_P_FREQ_MAX_POWER_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_POWER_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles when current is the upper limit on frequency. Derived from unc_p_freq_max_current_cycles", + "Counter": "0,1,2,3", + "EventCode": "0x7", + "EventName": "UNC_P_FREQ_MAX_CURRENT_CYCLES", + "MetricExpr": "(UNC_P_FREQ_MAX_CURRENT_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Cycles spent changing Frequency. Derived from unc_p_freq_trans_cycles", + "Counter": "0,1,2,3", + "EventName": "UNC_P_FREQ_TRANS_CYCLES", + "MetricExpr": "(UNC_P_FREQ_TRANS_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_GE_1200MHZ_CYCLES", + "Filter": "filter_band0=1200", + "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_GE_2000MHZ_CYCLES", + "Filter": "filter_band1=2000", + "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_GE_3000MHZ_CYCLES", + "Filter": "filter_band2=3000", + "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore was running at a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_GE_4000MHZ_CYCLES", + "Filter": "filter_band3=4000", + "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 1.2Ghz. Derived from unc_p_freq_band0_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xb", + "EventName": "UNC_P_FREQ_GE_1200MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band0=1200", + "MetricExpr": "(UNC_P_FREQ_GE_1200MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of times that the uncore transitioned to a frequency greater than or equal to 2Ghz. Derived from unc_p_freq_band1_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xc", + "EventName": "UNC_P_FREQ_GE_2000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band1=2000", + "MetricExpr": "(UNC_P_FREQ_GE_2000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 3Ghz. Derived from unc_p_freq_band2_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xd", + "EventName": "UNC_P_FREQ_GE_3000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band2=4000", + "MetricExpr": "(UNC_P_FREQ_GE_3000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + }, + { + "BriefDescription": "Counts the number of cycles that the uncore transitioned to a frequency greater than or equal to 4Ghz. Derived from unc_p_freq_band3_cycles", + "Counter": "0,1,2,3", + "EventCode": "0xe", + "EventName": "UNC_P_FREQ_GE_4000MHZ_TRANSITIONS", + "Filter": "edge=1,filter_band3=4000", + "MetricExpr": "(UNC_P_FREQ_GE_4000MHZ_CYCLES / UNC_P_CLOCKTICKS) * 100.", + "PerPkg": "1", + "Unit": "PCU" + } +] diff --git a/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json b/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json new file mode 100644 index 000000000000..e3bcd86c4f56 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/knightslanding/uncore-memory.json @@ -0,0 +1,42 @@ +[ + { + "BriefDescription": "ddr bandwidth read (CPU traffic only) (MB/sec). ", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_CAS_COUNT.RD", + "PerPkg": "1", + "ScaleUnit": "6.4e-05MiB", + "UMask": "0x01", + "Unit": "imc" + }, + { + "BriefDescription": "ddr bandwidth write (CPU traffic only) (MB/sec). ", + "Counter": "0,1,2,3", + "EventCode": "0x03", + "EventName": "UNC_M_CAS_COUNT.WR", + "PerPkg": "1", + "ScaleUnit": "6.4e-05MiB", + "UMask": "0x02", + "Unit": "imc" + }, + { + "BriefDescription": "mcdram bandwidth read (CPU traffic only) (MB/sec). ", + "Counter": "0,1,2,3", + "EventCode": "0x01", + "EventName": "UNC_E_RPQ_INSERTS", + "PerPkg": "1", + "ScaleUnit": "6.4e-05MiB", + "UMask": "0x01", + "Unit": "edc_eclk" + }, + { + "BriefDescription": "mcdram bandwidth write (CPU traffic only) (MB/sec). ", + "Counter": "0,1,2,3", + "EventCode": "0x02", + "EventName": "UNC_E_WPQ_INSERTS", + "PerPkg": "1", + "ScaleUnit": "6.4e-05MiB", + "UMask": "0x01", + "Unit": "edc_eclk" + } +] diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c index 41611d7f9873..eed09346a72a 100644 --- a/tools/perf/pmu-events/jevents.c +++ b/tools/perf/pmu-events/jevents.c @@ -135,7 +135,6 @@ static struct field { const char *field; const char *kernel; } fields[] = { - { "EventCode", "event=" }, { "UMask", "umask=" }, { "CounterMask", "cmask=" }, { "Invert", "inv=" }, @@ -189,6 +188,27 @@ static struct msrmap *lookup_msr(char *map, jsmntok_t *val) return NULL; } +static struct map { + const char *json; + const char *perf; +} unit_to_pmu[] = { + { "CBO", "uncore_cbox" }, + { "QPI LL", "uncore_qpi" }, + { "SBO", "uncore_sbox" }, + {} +}; + +static const char *field_to_perf(struct map *table, char *map, jsmntok_t *val) +{ + int i; + + for (i = 0; table[i].json; i++) { + if (json_streq(map, val, table[i].json)) + return table[i].perf; + } + return NULL; +} + #define EXPECT(e, t, m) do { if (!(e)) { \ jsmntok_t *loc = (t); \ if (!(t)->start && (t) > tokens) \ @@ -270,7 +290,8 @@ static void print_events_table_prefix(FILE *fp, const char *tblname) } static int print_events_table_entry(void *data, char *name, char *event, - char *desc, char *long_desc) + char *desc, char *long_desc, + char *pmu, char *unit, char *perpkg) { struct perf_entry_data *pd = data; FILE *outfp = pd->outfp; @@ -288,7 +309,12 @@ static int print_events_table_entry(void *data, char *name, char *event, fprintf(outfp, "\t.topic = \"%s\",\n", topic); if (long_desc && long_desc[0]) fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc); - + if (pmu) + fprintf(outfp, "\t.pmu = \"%s\",\n", pmu); + if (unit) + fprintf(outfp, "\t.unit = \"%s\",\n", unit); + if (perpkg) + fprintf(outfp, "\t.perpkg = \"%s\",\n", perpkg); fprintf(outfp, "},\n"); return 0; @@ -335,7 +361,8 @@ static char *real_event(const char *name, char *event) /* Call func with each event in the json file */ int json_events(const char *fn, int (*func)(void *data, char *name, char *event, char *desc, - char *long_desc), + char *long_desc, + char *pmu, char *unit, char *perpkg), void *data) { int err = -EIO; @@ -343,6 +370,7 @@ int json_events(const char *fn, jsmntok_t *tokens, *tok; int i, j, len; char *map; + char buf[128]; if (!fn) return -ENOENT; @@ -356,6 +384,11 @@ int json_events(const char *fn, char *event = NULL, *desc = NULL, *name = NULL; char *long_desc = NULL; char *extra_desc = NULL; + char *pmu = NULL; + char *filter = NULL; + char *perpkg = NULL; + char *unit = NULL; + unsigned long long eventcode = 0; struct msrmap *msr = NULL; jsmntok_t *msrval = NULL; jsmntok_t *precise = NULL; @@ -376,6 +409,16 @@ int json_events(const char *fn, nz = !json_streq(map, val, "0"); if (match_field(map, field, nz, &event, val)) { /* ok */ + } else if (json_streq(map, field, "EventCode")) { + char *code = NULL; + addfield(map, &code, "", "", val); + eventcode |= strtoul(code, NULL, 0); + free(code); + } else if (json_streq(map, field, "ExtSel")) { + char *code = NULL; + addfield(map, &code, "", "", val); + eventcode |= strtoul(code, NULL, 0) << 21; + free(code); } else if (json_streq(map, field, "EventName")) { addfield(map, &name, "", "", val); } else if (json_streq(map, field, "BriefDescription")) { @@ -399,6 +442,28 @@ int json_events(const char *fn, addfield(map, &extra_desc, ". ", " Supports address when precise", NULL); + } else if (json_streq(map, field, "Unit")) { + const char *ppmu; + char *s; + + ppmu = field_to_perf(unit_to_pmu, map, val); + if (ppmu) { + pmu = strdup(ppmu); + } else { + if (!pmu) + pmu = strdup("uncore_"); + addfield(map, &pmu, "", "", val); + for (s = pmu; *s; s++) + *s = tolower(*s); + } + addfield(map, &desc, ". ", "Unit: ", NULL); + addfield(map, &desc, "", pmu, NULL); + } else if (json_streq(map, field, "Filter")) { + addfield(map, &filter, "", "", val); + } else if (json_streq(map, field, "ScaleUnit")) { + addfield(map, &unit, "", "", val); + } else if (json_streq(map, field, "PerPkg")) { + addfield(map, &perpkg, "", "", val); } /* ignore unknown fields */ } @@ -410,20 +475,29 @@ int json_events(const char *fn, addfield(map, &extra_desc, " ", "(Precise event)", NULL); } + snprintf(buf, sizeof buf, "event=%#llx", eventcode); + addfield(map, &event, ",", buf, NULL); if (desc && extra_desc) addfield(map, &desc, " ", extra_desc, NULL); if (long_desc && extra_desc) addfield(map, &long_desc, " ", extra_desc, NULL); + if (filter) + addfield(map, &event, ",", filter, NULL); if (msr != NULL) addfield(map, &event, ",", msr->pname, msrval); fixname(name); - err = func(data, name, real_event(name, event), desc, long_desc); + err = func(data, name, real_event(name, event), desc, long_desc, + pmu, unit, perpkg); free(event); free(desc); free(name); free(long_desc); free(extra_desc); + free(pmu); + free(filter); + free(perpkg); + free(unit); if (err) break; tok += j; diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h index b0eb2744b498..71e13de31092 100644 --- a/tools/perf/pmu-events/jevents.h +++ b/tools/perf/pmu-events/jevents.h @@ -3,7 +3,9 @@ int json_events(const char *fn, int (*func)(void *data, char *name, char *event, char *desc, - char *long_desc), + char *long_desc, + char *pmu, + char *unit, char *perpkg), void *data); char *get_cpu_str(void); diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c index f67bbb0aa36e..0544398d6e2d 100644 --- a/tools/perf/pmu-events/json.c +++ b/tools/perf/pmu-events/json.c @@ -49,7 +49,7 @@ static char *mapfile(const char *fn, size_t *size) int err; int fd = open(fn, O_RDONLY); - if (fd < 0 && verbose && fn) { + if (fd < 0 && verbose > 0 && fn) { pr_err("Error opening events file '%s': %s\n", fn, strerror(errno)); } diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index 2eaef595d8a0..c669a3cdb9f0 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -10,6 +10,9 @@ struct pmu_event { const char *desc; const char *topic; const char *long_desc; + const char *pmu; + const char *unit; + const char *perpkg; }; /* diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 6676c2dd6dcb..1cb3d9b540e9 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -44,6 +44,7 @@ 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 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 28d1605b0338..88dc51f4c27b 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -144,7 +144,7 @@ static int run_dir(const char *d, const char *perf) int vcnt = min(verbose, (int) sizeof(v) - 1); char cmd[3*PATH_MAX]; - if (verbose) + if (verbose > 0) vcnt++; snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 92343f43e44a..1a04fe77487d 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -5,11 +5,13 @@ #include <util/evlist.h> #include <linux/bpf.h> #include <linux/filter.h> +#include <api/fs/fs.h> #include <bpf/bpf.h> #include "tests.h" #include "llvm.h" #include "debug.h" #define NR_ITERS 111 +#define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test" #ifdef HAVE_LIBBPF_SUPPORT @@ -54,6 +56,7 @@ static struct { const char *msg_load_fail; int (*target_func)(void); int expect_result; + bool pin; } bpf_testcase_table[] = { { LLVM_TESTCASE_BASE, @@ -63,6 +66,17 @@ static struct { "load bpf object failed", &epoll_wait_loop, (NR_ITERS + 1) / 2, + false, + }, + { + LLVM_TESTCASE_BASE, + "BPF pinning", + "[bpf_pinning]", + "fix kbuild first", + "check your vmlinux setting?", + &epoll_wait_loop, + (NR_ITERS + 1) / 2, + true, }, #ifdef HAVE_BPF_PROLOGUE { @@ -73,6 +87,7 @@ static struct { "check your vmlinux setting?", &llseek_loop, (NR_ITERS + 1) / 4, + false, }, #endif { @@ -83,6 +98,7 @@ static struct { "libbpf error when dealing with relocation", NULL, 0, + false, }, }; @@ -226,10 +242,34 @@ static int __test__bpf(int idx) goto out; } - if (obj) + 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: bpf__clear(); return ret; diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index a77dcc0d24e3..83c4669cbc5b 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -247,6 +247,10 @@ static struct test generic_tests[] = { } }, { + .desc = "unit_number__scnprintf", + .func = test__unit_number__scnprint, + }, + { .func = NULL, }, }; @@ -295,7 +299,7 @@ static int run_test(struct test *test, int subtest) if (!dont_fork) { pr_debug("test child forked, pid %d\n", getpid()); - if (!verbose) { + if (verbose <= 0) { int nullfd = open("/dev/null", O_WRONLY); if (nullfd >= 0) { diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index ff5bc6363a79..d1f693041324 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -599,7 +599,7 @@ static int do_test_code_reading(bool try_kcore) continue; } - if (verbose) { + if (verbose > 0) { char errbuf[512]; perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c index a2b5ff9bf83d..bc5982f42dc3 100644 --- a/tools/perf/tests/fdarray.c +++ b/tools/perf/tests/fdarray.c @@ -19,7 +19,7 @@ static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE { int printed = 0; - if (!verbose) + if (verbose <= 0) return 0; printed += fprintf(fp, "\n%s: ", prefix); diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 02a33ebcd992..482b5365e68d 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -13,7 +13,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) struct bpf_object *obj; obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); - if (IS_ERR(obj)) + if (libbpf_get_error(obj)) return TEST_FAIL; bpf_object__close(obj); return TEST_OK; @@ -76,7 +76,7 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf, * Skip this test if user's .perfconfig doesn't set [llvm] section * and clang is not found in $PATH, and this is not perf test -v */ - if (!force && (verbose == 0 && + if (!force && (verbose <= 0 && !llvm_param.user_set_param && llvm__search_clang())) { pr_debug("No clang and no verbosive, skip this test\n"); diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 20c2e641c422..1dc838014422 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1779,15 +1779,14 @@ static int test_pmu_events(void) } while (!ret && (ent = readdir(dir))) { -#define MAX_NAME 100 struct evlist_test e; - char name[MAX_NAME]; + char name[2 * NAME_MAX + 1 + 12 + 3]; /* Names containing . are special and cannot be used directly */ if (strchr(ent->d_name, '.')) continue; - snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name); + snprintf(name, sizeof(name), "cpu/event=%s/u", ent->d_name); e.name = name; e.check = test__checkevent_pmu_events; @@ -1795,11 +1794,10 @@ static int test_pmu_events(void) ret = test_event(&e); if (ret) break; - snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); + snprintf(name, sizeof(name), "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); e.name = name; e.check = test__checkevent_pmu_events_mix; ret = test_event(&e); -#undef MAX_NAME } closedir(dir); @@ -1810,7 +1808,7 @@ static void debug_warn(const char *warn, va_list params) { char msg[1024]; - if (!verbose) + if (verbose <= 0) return; vsnprintf(msg, sizeof(msg), warn, params); diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index 81c6eeaca0f5..65dcf48a92fb 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -50,7 +50,8 @@ static int process_events(union perf_event **events, size_t count) } struct test_attr_event { - struct attr_event attr; + struct perf_event_header header; + struct perf_event_attr attr; u64 id; }; @@ -71,20 +72,16 @@ int test__parse_no_sample_id_all(int subtest __maybe_unused) int err; struct test_attr_event event1 = { - .attr = { - .header = { - .type = PERF_RECORD_HEADER_ATTR, - .size = sizeof(struct test_attr_event), - }, + .header = { + .type = PERF_RECORD_HEADER_ATTR, + .size = sizeof(struct test_attr_event), }, .id = 1, }; struct test_attr_event event2 = { - .attr = { - .header = { - .type = PERF_RECORD_HEADER_ATTR, - .size = sizeof(struct test_attr_event), - }, + .header = { + .type = PERF_RECORD_HEADER_ATTR, + .size = sizeof(struct test_attr_event), }, .id = 2, }; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 8f2e1de6d0ea..87893f3ba5f1 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -66,7 +66,7 @@ int test__PERF_RECORD(int subtest __maybe_unused) if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */ evlist = perf_evlist__new_default(); - if (evlist == NULL || argv == NULL) { + if (evlist == NULL) { pr_debug("Not enough memory to create evlist\n"); goto out; } @@ -172,13 +172,13 @@ int test__PERF_RECORD(int subtest __maybe_unused) err = perf_evlist__parse_sample(evlist, event, &sample); if (err < 0) { - if (verbose) + if (verbose > 0) perf_event__fprintf(event, stderr); pr_debug("Couldn't parse sample\n"); goto out_delete_evlist; } - if (verbose) { + if (verbose > 0) { pr_info("%" PRIu64" %d ", sample.time, sample.cpu); perf_event__fprintf(event, stderr); } diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c index 7a52834ee0d0..fa79509da535 100644 --- a/tools/perf/tests/python-use.c +++ b/tools/perf/tests/python-use.c @@ -15,7 +15,7 @@ int test__python_use(int subtest __maybe_unused) int ret; if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s", - PYTHONPATH, PYTHON, verbose ? "" : "2> /dev/null") < 0) + PYTHONPATH, PYTHON, verbose > 0 ? "" : "2> /dev/null") < 0) return -1; ret = system(cmd) ? -1 : 0; diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a512f0c8ff5b..1fa9b9d83aa5 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -96,6 +96,7 @@ int test__perf_hooks(int subtest); int test__clang(int subtest); const char *test__clang_subtest_get_desc(int subtest); int test__clang_subtest_get_nr(void); +int test__unit_number__scnprint(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index a4a4b4625ac3..f2d2e542d0ee 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -109,7 +109,7 @@ int test__thread_map_remove(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to allocate thread_map", threads); - if (verbose) + if (verbose > 0) thread_map__fprintf(threads, stderr); TEST_ASSERT_VAL("failed to remove thread", @@ -117,7 +117,7 @@ int test__thread_map_remove(int subtest __maybe_unused) TEST_ASSERT_VAL("thread_map count != 1", threads->nr == 1); - if (verbose) + if (verbose > 0) thread_map__fprintf(threads, stderr); TEST_ASSERT_VAL("failed to remove thread", @@ -125,7 +125,7 @@ int test__thread_map_remove(int subtest __maybe_unused) TEST_ASSERT_VAL("thread_map count != 0", threads->nr == 0); - if (verbose) + if (verbose > 0) thread_map__fprintf(threads, stderr); TEST_ASSERT_VAL("failed to not remove thread", diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index 98fe69ac553c..803f893550d6 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -65,7 +65,9 @@ static int check_cpu_topology(char *path, struct cpu_map *map) session = perf_session__new(&file, false, NULL); TEST_ASSERT_VAL("can't get session", session); - for (i = 0; i < session->header.env.nr_cpus_online; i++) { + for (i = 0; i < session->header.env.nr_cpus_avail; i++) { + if (!cpu_map__has(map, i)) + continue; pr_debug("CPU %d, core %d, socket %d\n", i, session->header.env.cpu[i].core_id, session->header.env.cpu[i].socket_id); diff --git a/tools/perf/tests/unit_number__scnprintf.c b/tools/perf/tests/unit_number__scnprintf.c new file mode 100644 index 000000000000..623c2aa53c4a --- /dev/null +++ b/tools/perf/tests/unit_number__scnprintf.c @@ -0,0 +1,37 @@ +#include <linux/compiler.h> +#include <linux/types.h> +#include "tests.h" +#include "util.h" +#include "debug.h" + +int test__unit_number__scnprint(int subtest __maybe_unused) +{ + struct { + u64 n; + const char *str; + } test[] = { + { 1, "1B" }, + { 10*1024, "10K" }, + { 20*1024*1024, "20M" }, + { 30*1024*1024*1024ULL, "30G" }, + { 0, "0B" }, + { 0, NULL }, + }; + unsigned i = 0; + + while (test[i].str) { + char buf[100]; + + unit_number__scnprintf(buf, sizeof(buf), test[i].n); + + pr_debug("n %" PRIu64 ", str '%s', buf '%s'\n", + test[i].n, test[i].str, buf); + + if (strcmp(test[i].str, buf)) + return TEST_FAIL; + + i++; + } + + return TEST_OK; +} diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index a5082331f246..862b043e5924 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -168,7 +168,7 @@ next_pair: err = -1; } - if (!verbose) + if (verbose <= 0) goto out; header_printed = false; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 641b40234a9d..fc4fb669ceee 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -501,8 +501,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; @@ -520,12 +520,34 @@ 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(&browser->hists->entries); while (nd) { @@ -535,21 +557,6 @@ __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; } } @@ -564,6 +571,15 @@ static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) ui_browser__reset_index(&browser->b); } +static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold) +{ + if (!browser->he_selection) + return; + + hist_entry__set_folding(browser->he_selection, browser, unfold); + browser->b.nr_entries = hist_browser__nr_entries(browser); +} + static void ui_browser__warn_lost_events(struct ui_browser *browser) { ui_browser__warning(browser, 4, @@ -637,10 +653,18 @@ int hist_browser__run(struct hist_browser *browser, const char *help) /* Collapse the whole world. */ hist_browser__set_folding(browser, false); break; + case 'c': + /* Collapse the selected entry. */ + hist_browser__set_folding_selected(browser, false); + break; case 'E': /* Expand the whole world. */ hist_browser__set_folding(browser, true); break; + case 'e': + /* Expand the selected entry. */ + hist_browser__set_folding_selected(browser, true); + break; case 'H': browser->show_headers = !browser->show_headers; hist_browser__update_rows(browser); diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 98a34664bb7e..9ce142de536d 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -73,7 +73,7 @@ static int map_browser__run(struct map_browser *browser) if (ui_browser__show(&browser->b, browser->map->dso->long_name, "Press ESC to exit, %s / to search", - verbose ? "" : "restart with -v to use") < 0) + verbose > 0 ? "" : "restart with -v to use") < 0) return -1; while (1) { @@ -81,7 +81,7 @@ static int map_browser__run(struct map_browser *browser) switch (key) { case '/': - if (verbose) + if (verbose > 0) map_browser__search(browser); default: break; @@ -117,7 +117,7 @@ int map__browse(struct map *map) if (maxaddr < pos->end) maxaddr = pos->end; - if (verbose) { + if (verbose > 0) { u32 *idx = symbol__browser_index(pos); *idx = mb.b.nr_entries; } diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 18cfcdc90356..5d632dca672a 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -648,7 +648,7 @@ unsigned int hists__sort_list_width(struct hists *hists) ret += fmt->width(fmt, &dummy_hpp, hists); } - if (verbose && hists__has(hists, sym)) /* Addr + origin */ + if (verbose > 0 && hists__has(hists, sym)) /* Addr + origin */ ret += 3 + BITS_PER_LONG / 4; return ret; diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 1f6b0994f4f4..50d13e58210f 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -7,6 +7,7 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; void *perf_gtk_handle; +int use_browser = -1; #ifdef HAVE_GTK2_SUPPORT static int setup_gtk_browser(void) diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 3840e3a87057..5da376bc1afc 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -162,6 +162,7 @@ CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET 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_header.o += -include $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE $(call rule_mkdir) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 06cc04e5806a..273f21fa32b5 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1768,7 +1768,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, printf("%-*.*s----\n", graph_dotted_len, graph_dotted_len, graph_dotted_line); - if (verbose) + if (verbose > 0) symbol__annotate_hits(sym, evsel); list_for_each_entry(pos, ¬es->src->source, node) { diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 36c861103291..bc6bc7062eb4 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -670,13 +670,13 @@ int bpf__probe(struct bpf_object *obj) err = convert_perf_probe_events(pev, 1); if (err < 0) { - pr_debug("bpf_probe: failed to convert perf probe events"); + 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"); + pr_debug("bpf_probe: failed to apply perf probe events\n"); goto out; } diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 8b610dd9e2f6..aba953421a03 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -48,6 +48,8 @@ static int parse_callchain_mode(const char *value) callchain_param.mode = CHAIN_FOLDED; return 0; } + + pr_err("Invalid callchain mode: %s\n", value); return -1; } @@ -63,6 +65,8 @@ static int parse_callchain_order(const char *value) callchain_param.order_set = true; return 0; } + + pr_err("Invalid callchain order: %s\n", value); return -1; } @@ -80,6 +84,8 @@ static int parse_callchain_sort_key(const char *value) callchain_param.branch_callstack = 1; return 0; } + + pr_err("Invalid callchain sort key: %s\n", value); return -1; } @@ -97,6 +103,8 @@ static int parse_callchain_value(const char *value) callchain_param.value = CCVAL_COUNT; return 0; } + + pr_err("Invalid callchain config key: %s\n", value); return -1; } @@ -210,13 +218,17 @@ int perf_callchain_config(const char *var, const char *value) return parse_callchain_sort_key(value); if (!strcmp(var, "threshold")) { callchain_param.min_percent = strtod(value, &endptr); - if (value == endptr) + if (value == endptr) { + pr_err("Invalid callchain threshold: %s\n", value); return -1; + } } if (!strcmp(var, "print-limit")) { callchain_param.print_limit = strtod(value, &endptr); - if (value == endptr) + if (value == endptr) { + pr_err("Invalid callchain print limit: %s\n", value); return -1; + } } return 0; diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 8fdee24725a7..eafbf11442b2 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -12,8 +12,8 @@ cgroupfs_find_mountpoint(char *buf, size_t maxlen) { FILE *fp; char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1]; + char path_v1[PATH_MAX + 1], path_v2[PATH_MAX + 2], *path; char *token, *saved_ptr = NULL; - int found = 0; fp = fopen("/proc/mounts", "r"); if (!fp) @@ -24,31 +24,43 @@ cgroupfs_find_mountpoint(char *buf, size_t maxlen) * and inspect every cgroupfs mount point to find one that has * perf_event subsystem */ + path_v1[0] = '\0'; + path_v2[0] = '\0'; + while (fscanf(fp, "%*s %"STR(PATH_MAX)"s %"STR(PATH_MAX)"s %" STR(PATH_MAX)"s %*d %*d\n", mountpoint, type, tokens) == 3) { - if (!strcmp(type, "cgroup")) { + if (!path_v1[0] && !strcmp(type, "cgroup")) { token = strtok_r(tokens, ",", &saved_ptr); while (token != NULL) { if (!strcmp(token, "perf_event")) { - found = 1; + strcpy(path_v1, mountpoint); break; } token = strtok_r(NULL, ",", &saved_ptr); } } - if (found) + + if (!path_v2[0] && !strcmp(type, "cgroup2")) + strcpy(path_v2, mountpoint); + + if (path_v1[0] && path_v2[0]) break; } fclose(fp); - if (!found) + + if (path_v1[0]) + path = path_v1; + else if (path_v2[0]) + path = path_v2; + else return -1; - if (strlen(mountpoint) < maxlen) { - strcpy(buf, mountpoint); + if (strlen(path) < maxlen) { + strcpy(buf, path); return 0; } return -1; diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 3d906dbbef74..0c7d5a4975cd 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -386,8 +386,10 @@ static int perf_buildid_config(const char *var, const char *value) if (!strcmp(var, "buildid.dir")) { const char *dir = perf_config_dirname(var, value); - if (!dir) + if (!dir) { + pr_err("Invalid buildid directory!\n"); return -1; + } strncpy(buildid_dir, dir, MAXPATHLEN-1); buildid_dir[MAXPATHLEN-1] = '\0'; } @@ -405,10 +407,9 @@ static int perf_default_core_config(const char *var __maybe_unused, static int perf_ui_config(const char *var, const char *value) { /* Add other config variables here. */ - if (!strcmp(var, "ui.show-headers")) { + if (!strcmp(var, "ui.show-headers")) symbol_conf.show_hist_headers = perf_config_bool(var, value); - return 0; - } + return 0; } @@ -646,8 +647,13 @@ static int perf_config_set__init(struct perf_config_set *set) goto out; } - if (stat(user_config, &st) < 0) + if (stat(user_config, &st) < 0) { + if (errno == ENOENT) + ret = 0; goto out_free; + } + + ret = 0; if (st.st_uid && (st.st_uid != geteuid())) { warning("File %s not owned by current user or root, " @@ -655,11 +661,8 @@ static int perf_config_set__init(struct perf_config_set *set) goto out_free; } - if (!st.st_size) - goto out_free; - - ret = perf_config_from_file(collect_config, user_config, set); - + if (st.st_size) + ret = perf_config_from_file(collect_config, user_config, set); out_free: free(user_config); } diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 2c0b52264a46..8c7504939113 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -9,6 +9,7 @@ #include "asm/bug.h" static int max_cpu_num; +static int max_present_cpu_num; static int max_node_num; static int *cpunode_map; @@ -442,6 +443,7 @@ static void set_max_cpu_num(void) /* set up default */ max_cpu_num = 4096; + max_present_cpu_num = 4096; mnt = sysfs__mountpoint(); if (!mnt) @@ -455,6 +457,17 @@ static void set_max_cpu_num(void) } ret = get_max_num(path, &max_cpu_num); + if (ret) + goto out; + + /* get the highest present cpu number for a sparse allocation */ + ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/present", mnt); + if (ret == PATH_MAX) { + pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); + goto out; + } + + ret = get_max_num(path, &max_present_cpu_num); out: if (ret) @@ -505,6 +518,15 @@ int cpu__max_cpu(void) return max_cpu_num; } +int cpu__max_present_cpu(void) +{ + if (unlikely(!max_present_cpu_num)) + set_max_cpu_num(); + + return max_present_cpu_num; +} + + int cpu__get_node(int cpu) { if (unlikely(cpunode_map == NULL)) { diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 06bd689f5989..1a0549af8f5c 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -62,6 +62,7 @@ int cpu__setup_cpunode_map(void); int cpu__max_node(void); int cpu__max_cpu(void); +int cpu__max_present_cpu(void); int cpu__get_node(int cpu); int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 7123f4de32cc..4e6cbc99f08e 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1473,7 +1473,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, }, }; struct ctf_writer *cw = &c.writer; - int err = -1; + int err; if (opts->all) { c.tool.comm = process_comm_event; @@ -1481,12 +1481,15 @@ int bt_convert__perf2ctf(const char *input, const char *path, c.tool.fork = process_fork_event; } - perf_config(convert__config, &c); + err = perf_config(convert__config, &c); + if (err) + return err; /* CTF writer */ if (ctf_writer__init(cw, path)) return -1; + err = -1; /* perf.data session */ session = perf_session__new(&file, 0, &c.tool); if (!session) diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index c1838b643108..03eb81f30d0d 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -203,11 +203,28 @@ int perf_debug_option(const char *str) v = (v < 0) || (v > 10) ? 0 : v; } + if (quiet) + v = -1; + *var->ptr = v; free(s); return 0; } +int perf_quiet_option(void) +{ + struct debug_variable *var = &debug_variables[0]; + + /* disable all debug messages */ + while (var->name) { + *var->ptr = -1; + var++; + } + + quiet = true; + return 0; +} + #define DEBUG_WRAPPER(__n, __l) \ static int pr_ ## __n ## _wrapper(const char *fmt, ...) \ { \ diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index d242adc3d5a2..98832f5531d3 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -54,5 +54,6 @@ int veprintf(int level, int var, const char *fmt, va_list args); int perf_debug_option(const char *str); void perf_debug_setup(void); +int perf_quiet_option(void); #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d2c6cdd9d42b..d38b62a700ca 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -9,6 +9,13 @@ #include "debug.h" #include "vdso.h" +static const char * const debuglink_paths[] = { + "%.0s%s", + "%s/%s", + "%s/.debug/%s", + "/usr/lib/debug%s/%s" +}; + char dso__symtab_origin(const struct dso *dso) { static const char origin[] = { @@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso, size_t len; switch (type) { - case DSO_BINARY_TYPE__DEBUGLINK: { - char *debuglink; + case DSO_BINARY_TYPE__DEBUGLINK: + { + const char *last_slash; + char dso_dir[PATH_MAX]; + char symfile[PATH_MAX]; + unsigned int i; len = __symbol__join_symfs(filename, size, dso->long_name); - debuglink = filename + len; - while (debuglink != filename && *debuglink != '/') - debuglink--; - if (*debuglink == '/') - debuglink++; + last_slash = filename + len; + while (last_slash != filename && *last_slash != '/') + last_slash--; - ret = -1; - if (!is_regular_file(filename)) + strncpy(dso_dir, filename, last_slash - filename); + dso_dir[last_slash-filename] = '\0'; + + if (!is_regular_file(filename)) { + ret = -1; + break; + } + + ret = filename__read_debuglink(filename, symfile, PATH_MAX); + if (ret) break; - ret = filename__read_debuglink(filename, debuglink, - size - (debuglink - filename)); + /* Check predefined locations where debug file might reside */ + ret = -1; + for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) { + snprintf(filename, size, + debuglink_paths[i], dso_dir, symfile); + if (is_regular_file(filename)) { + ret = 0; + break; + } } + break; + } case DSO_BINARY_TYPE__BUILD_ID_CACHE: if (dso__build_id_filename(dso, filename, size) == NULL) ret = -1; @@ -925,7 +951,7 @@ static struct dso *__dso__findlink_by_longname(struct rb_root *root, if (rc == 0) { /* * In case the new DSO is a duplicate of an existing - * one, print an one-time warning & put the new entry + * one, print a one-time warning & put the new entry * at the end of the list of duplicates. */ if (!dso || (dso == this)) @@ -1032,7 +1058,7 @@ int dso__name_len(const struct dso *dso) { if (!dso) return strlen("[unknown]"); - if (verbose) + if (verbose > 0) return dso->long_name_len; return dso->short_name_len; diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index bb964e86b09d..075fc77286bf 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -66,7 +66,7 @@ int perf_env__read_cpu_topology_map(struct perf_env *env) return 0; if (env->nr_cpus_avail == 0) - env->nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); + env->nr_cpus_avail = cpu__max_present_cpu(); nr_cpus = env->nr_cpus_avail; if (nr_cpus == -1) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8ab0d7da956b..4ea7ce72ed9c 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,5 +1,5 @@ #include <linux/types.h> -#include <uapi/linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */ +#include <linux/mman.h> /* To get things like MAP_HUGETLB even on older libc headers */ #include <api/fs/fs.h> #include "event.h" #include "debug.h" diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d92e02006fb8..b601f2814a30 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1184,7 +1184,7 @@ unsigned long perf_event_mlock_kb_in_pages(void) return pages; } -static size_t perf_evlist__mmap_size(unsigned long pages) +size_t perf_evlist__mmap_size(unsigned long pages) { if (pages == UINT_MAX) pages = perf_event_mlock_kb_in_pages(); @@ -1224,12 +1224,16 @@ static long parse_pages_arg(const char *str, unsigned long min, if (pages == 0 && min == 0) { /* leave number of pages at 0 */ } else if (!is_power_of_2(pages)) { + char buf[100]; + /* round pages up to next power of 2 */ pages = roundup_pow_of_two(pages); if (!pages) return -EINVAL; - pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", - pages * page_size, pages); + + unit_number__scnprintf(buf, sizeof(buf), pages * page_size); + pr_info("rounding mmap pages size to %s (%lu pages)\n", + buf, pages); } if (pages > max) @@ -1797,7 +1801,7 @@ int perf_evlist__start_workload(struct perf_evlist *evlist) */ ret = write(evlist->workload.cork_fd, &bf, 1); if (ret < 0) - perror("enable to write to pipe"); + perror("unable to write to pipe"); close(evlist->workload.cork_fd); return ret; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 4fd034f22d2f..389b9ccdf8c7 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -218,6 +218,8 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, bool overwrite); void perf_evlist__munmap(struct perf_evlist *evlist); +size_t perf_evlist__mmap_size(unsigned long pages); + void perf_evlist__disable(struct perf_evlist *evlist); void perf_evlist__enable(struct perf_evlist *evlist); void perf_evlist__toggle_enable(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 04e536ae4d88..ac59710b79e0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1448,8 +1448,8 @@ static bool ignore_missing_thread(struct perf_evsel *evsel, return true; } -static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads) +int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, + struct thread_map *threads) { int cpu, thread, nthreads; unsigned long flags = PERF_FLAG_FD_CLOEXEC; @@ -1459,6 +1459,30 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, if (perf_missing_features.write_backward && evsel->attr.write_backward) return -EINVAL; + if (cpus == NULL) { + static struct cpu_map *empty_cpu_map; + + if (empty_cpu_map == NULL) { + empty_cpu_map = cpu_map__dummy_new(); + if (empty_cpu_map == NULL) + return -ENOMEM; + } + + cpus = empty_cpu_map; + } + + if (threads == NULL) { + static struct thread_map *empty_thread_map; + + if (empty_thread_map == NULL) { + empty_thread_map = thread_map__new_by_tid(-1); + if (empty_thread_map == NULL) + return -ENOMEM; + } + + threads = empty_thread_map; + } + if (evsel->system_wide) nthreads = 1; else @@ -1655,46 +1679,16 @@ void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads) perf_evsel__free_fd(evsel); } -static struct { - struct cpu_map map; - int cpus[1]; -} empty_cpu_map = { - .map.nr = 1, - .cpus = { -1, }, -}; - -static struct { - struct thread_map map; - int threads[1]; -} empty_thread_map = { - .map.nr = 1, - .threads = { -1, }, -}; - -int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, - struct thread_map *threads) -{ - if (cpus == NULL) { - /* Work around old compiler warnings about strict aliasing */ - cpus = &empty_cpu_map.map; - } - - if (threads == NULL) - threads = &empty_thread_map.map; - - return __perf_evsel__open(evsel, cpus, threads); -} - int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) { - return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); + return perf_evsel__open(evsel, cpus, NULL); } int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) { - return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); + return perf_evsel__open(evsel, NULL, threads); } static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, @@ -2469,7 +2463,9 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, " -1: Allow use of (almost) all events by all users\n" ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" - ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", + ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN\n\n" + "To make this setting permanent, edit /etc/sysctl.conf too, e.g.:\n\n" + " kernel.perf_event_paranoid = -1\n" , target->system_wide ? "system-wide " : "", perf_event_paranoid()); case ENOENT: diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 6b2925542c0a..4ef5184819a0 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -168,7 +168,6 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, if (symbol_conf.bt_stop_list && node->sym && - node->sym->name && strlist__has_entry(symbol_conf.bt_stop_list, node->sym->name)) { break; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index d89c9c7ef4e5..05714d548584 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -41,6 +41,8 @@ static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; #define PERF_MAGIC __perf_magic2 +const char perf_version_string[] = PERF_VERSION; + struct perf_file_attr { struct perf_event_attr attr; struct perf_file_section ids; @@ -293,11 +295,7 @@ static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, u32 nrc, nra; int ret; - nr = sysconf(_SC_NPROCESSORS_CONF); - if (nr < 0) - return -1; - - nrc = (u32)(nr & UINT_MAX); + nrc = cpu__max_present_cpu(); nr = sysconf(_SC_NPROCESSORS_ONLN); if (nr < 0) @@ -503,24 +501,29 @@ static void free_cpu_topo(struct cpu_topo *tp) static struct cpu_topo *build_cpu_topology(void) { - struct cpu_topo *tp; + struct cpu_topo *tp = NULL; void *addr; u32 nr, i; size_t sz; long ncpus; int ret = -1; + struct cpu_map *map; - ncpus = sysconf(_SC_NPROCESSORS_CONF); - if (ncpus < 0) + ncpus = cpu__max_present_cpu(); + + /* build online CPU map */ + map = cpu_map__new(NULL); + if (map == NULL) { + pr_debug("failed to get system cpumap\n"); return NULL; + } nr = (u32)(ncpus & UINT_MAX); sz = nr * sizeof(char *); - addr = calloc(1, sizeof(*tp) + 2 * sz); if (!addr) - return NULL; + goto out_free; tp = addr; tp->cpu_nr = nr; @@ -530,10 +533,16 @@ static struct cpu_topo *build_cpu_topology(void) tp->thread_siblings = addr; for (i = 0; i < nr; i++) { + if (!cpu_map__has(map, i)) + continue; + ret = build_cpu_topo(tp, i); if (ret < 0) break; } + +out_free: + cpu_map__put(map); if (ret) { free_cpu_topo(tp); tp = NULL; @@ -1124,7 +1133,7 @@ static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused, { int nr, i; char *str; - int cpu_nr = ph->env.nr_cpus_online; + int cpu_nr = ph->env.nr_cpus_avail; nr = ph->env.nr_sibling_cores; str = ph->env.sibling_cores; @@ -1779,7 +1788,7 @@ static int process_cpu_topology(struct perf_file_section *section, u32 nr, i; char *str; struct strbuf sb; - int cpu_nr = ph->env.nr_cpus_online; + int cpu_nr = ph->env.nr_cpus_avail; u64 size = 0; ph->env.cpu = calloc(cpu_nr, sizeof(*ph->env.cpu)); @@ -1860,7 +1869,7 @@ static int process_cpu_topology(struct perf_file_section *section, if (ph->needs_swap) nr = bswap_32(nr); - if (nr > (u32)cpu_nr) { + if (nr != (u32)-1 && nr > (u32)cpu_nr) { pr_debug("socket_id number is too big." "You may need to upgrade the perf tool.\n"); goto free_cpu; @@ -2801,8 +2810,10 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, } event = pevent_find_event(pevent, evsel->attr.config); - if (event == NULL) + if (event == NULL) { + pr_debug("cannot find event format for %d\n", (int)evsel->attr.config); return -1; + } if (!evsel->name) { snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name); @@ -3201,6 +3212,7 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, case PERF_EVENT_UPDATE__SCALE: ev_scale = (struct event_update_event_scale *) ev->data; evsel->scale = ev_scale->scale; + break; case PERF_EVENT_UPDATE__CPUS: ev_cpus = (struct event_update_event_cpus *) ev->data; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7d1b7d33e644..eaf72a938fb4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -69,7 +69,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) */ if (h->ms.sym) { symlen = h->ms.sym->namelen + 4; - if (verbose) + if (verbose > 0) symlen += BITS_PER_LONG / 4 + 2 + 3; hists__new_col_len(hists, HISTC_SYMBOL, symlen); } else { @@ -93,7 +93,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) if (h->branch_info) { if (h->branch_info->from.sym) { symlen = (int)h->branch_info->from.sym->namelen + 4; - if (verbose) + if (verbose > 0) symlen += BITS_PER_LONG / 4 + 2 + 3; hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); @@ -107,7 +107,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) if (h->branch_info->to.sym) { symlen = (int)h->branch_info->to.sym->namelen + 4; - if (verbose) + if (verbose > 0) symlen += BITS_PER_LONG / 4 + 2 + 3; hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); @@ -2446,8 +2446,10 @@ int parse_filter_percentage(const struct option *opt __maybe_unused, symbol_conf.filter_relative = true; else if (!strcmp(arg, "absolute")) symbol_conf.filter_relative = false; - else + else { + pr_debug("Invalud percentage: %s\n", arg); return -1; + } return 0; } diff --git a/tools/perf/util/intel-pt-decoder/Build b/tools/perf/util/intel-pt-decoder/Build index 9b742ea8bfe8..7aca5d6d7e1f 100644 --- a/tools/perf/util/intel-pt-decoder/Build +++ b/tools/perf/util/intel-pt-decoder/Build @@ -23,4 +23,8 @@ $(OUTPUT)util/intel-pt-decoder/intel-pt-insn-decoder.o: util/intel-pt-decoder/in $(call rule_mkdir) $(call if_changed_dep,cc_o_c) -CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder -Wno-override-init +CFLAGS_intel-pt-insn-decoder.o += -I$(OUTPUT)util/intel-pt-decoder + +ifneq ($(CC), clang) + CFLAGS_intel-pt-insn-decoder.o += -Wno-override-init +endif diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index e4e7dc781d21..7cf7f7aca4d2 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -22,6 +22,7 @@ #include <errno.h> #include <stdint.h> #include <inttypes.h> +#include <linux/compiler.h> #include "../cache.h" #include "../util.h" @@ -1746,6 +1747,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) switch (decoder->packet.type) { case INTEL_PT_TIP_PGD: decoder->continuous_period = false; + __fallthrough; case INTEL_PT_TIP_PGE: case INTEL_PT_TIP: intel_pt_log("ERROR: Unexpected packet\n"); @@ -1799,6 +1801,8 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder) decoder->pge = false; decoder->continuous_period = false; intel_pt_clear_tx_flags(decoder); + __fallthrough; + case INTEL_PT_TNT: decoder->have_tma = false; intel_pt_log("ERROR: Unexpected packet\n"); @@ -1839,6 +1843,7 @@ static int intel_pt_walk_to_ip(struct intel_pt_decoder *decoder) switch (decoder->packet.type) { case INTEL_PT_TIP_PGD: decoder->continuous_period = false; + __fallthrough; case INTEL_PT_TIP_PGE: case INTEL_PT_TIP: decoder->pge = decoder->packet.type != INTEL_PT_TIP_PGD; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 7913363bde5c..4f3c758d875d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -31,7 +31,7 @@ #error Instruction buffer size too small #endif -/* Based on branch_type() from perf_event_intel_lbr.c */ +/* Based on branch_type() from arch/x86/events/intel/lbr.c */ static void intel_pt_insn_decoder(struct insn *insn, struct intel_pt_insn *intel_pt_insn) { diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c index 4f7b32020487..7528ae4f7e28 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c @@ -17,6 +17,7 @@ #include <string.h> #include <endian.h> #include <byteswap.h> +#include <linux/compiler.h> #include "intel-pt-pkt-decoder.h" @@ -498,6 +499,7 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf, case INTEL_PT_FUP: if (!(packet->count)) return snprintf(buf, buf_len, "%s no ip", name); + __fallthrough; case INTEL_PT_CYC: case INTEL_PT_VMCS: case INTEL_PT_MTC: diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 85d5eeb66c75..da20cd5612e9 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -2159,7 +2159,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event, addr_filters__init(&pt->filts); - perf_config(intel_pt_perf_config, pt); + err = perf_config(intel_pt_perf_config, pt); + if (err) + goto err_free; err = auxtrace_queues__init(&pt->queues); if (err) diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index b23ff44cf214..824356488ce6 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -48,8 +48,10 @@ int perf_llvm_config(const char *var, const char *value) llvm_param.kbuild_opts = strdup(value); else if (!strcmp(var, "dump-obj")) llvm_param.dump_obj = !!perf_config_bool(var, value); - else + else { + pr_debug("Invalid LLVM config option: %s\n", value); return -1; + } llvm_param.user_set_param = true; return 0; } diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9b33bef54581..71c9720d4973 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -87,6 +87,25 @@ out_delete: return NULL; } +struct machine *machine__new_kallsyms(void) +{ + struct machine *machine = machine__new_host(); + /* + * FIXME: + * 1) MAP__FUNCTION will go away when we stop loading separate maps for + * functions and data objects. + * 2) We should switch to machine__load_kallsyms(), i.e. not explicitely + * ask for not using the kcore parsing code, once this one is fixed + * to create a map per module. + */ + if (machine && __machine__load_kallsyms(machine, "/proc/kallsyms", MAP__FUNCTION, true) <= 0) { + machine__delete(machine); + machine = NULL; + } + + return machine; +} + static void dsos__purge(struct dsos *dsos) { struct dso *pos, *n; @@ -763,7 +782,7 @@ static u64 machine__get_running_kernel_start(struct machine *machine, int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) { - enum map_type type; + int type; u64 start = machine__get_running_kernel_start(machine, NULL); /* In case of renewal the kernel map, destroy previous one */ @@ -794,7 +813,7 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) void machine__destroy_kernel_maps(struct machine *machine) { - enum map_type type; + int type; for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; @@ -1546,7 +1565,7 @@ int machine__process_event(struct machine *machine, union perf_event *event, static bool symbol__match_regex(struct symbol *sym, regex_t *regex) { - if (sym->name && !regexec(regex, sym->name, 0, NULL, 0)) + if (!regexec(regex, sym->name, 0, NULL, 0)) return 1; return 0; } diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 354de6e56109..a28305029711 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -129,6 +129,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); void machines__set_comm_exec(struct machines *machines, bool comm_exec); struct machine *machine__new_host(void); +struct machine *machine__new_kallsyms(void); int machine__init(struct machine *machine, const char *root_dir, pid_t pid); void machine__exit(struct machine *machine); void machine__delete_threads(struct machine *machine); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4f9a71c63026..0a943e7b1ea7 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -387,10 +387,10 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) { const char *dsoname = "[unknown]"; - if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (map && map->dso) { if (symbol_conf.show_kernel_path && map->dso->long_name) dsoname = map->dso->long_name; - else if (map->dso->name) + else dsoname = map->dso->name; } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 3c876b8ba4de..67a8aebc67ab 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -211,6 +211,8 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) closedir(evt_dir); closedir(sys_dir); path = zalloc(sizeof(*path)); + if (!path) + return NULL; path->system = malloc(MAX_EVENT_LENGTH); if (!path->system) { free(path); @@ -252,8 +254,7 @@ struct tracepoint_path *tracepoint_name_to_path(const char *name) if (path->system == NULL || path->name == NULL) { zfree(&path->system); zfree(&path->name); - free(path); - path = NULL; + zfree(&path); } return path; @@ -310,10 +311,11 @@ __add_event(struct list_head *list, int *idx, event_attr_init(attr); - evsel = perf_evsel__new_idx(attr, (*idx)++); + evsel = perf_evsel__new_idx(attr, *idx); if (!evsel) return NULL; + (*idx)++; evsel->cpus = cpu_map__get(cpus); evsel->own_cpus = cpu_map__get(cpus); @@ -1477,10 +1479,9 @@ static void perf_pmu__parse_cleanup(void) for (i = 0; i < perf_pmu_events_list_num; i++) { p = perf_pmu_events_list + i; - free(p->symbol); + zfree(&p->symbol); } - free(perf_pmu_events_list); - perf_pmu_events_list = NULL; + zfree(&perf_pmu_events_list); perf_pmu_events_list_num = 0; } } @@ -1504,35 +1505,41 @@ static void perf_pmu__parse_init(void) struct perf_pmu_alias *alias; int len = 0; - pmu = perf_pmu__find("cpu"); - if ((pmu == NULL) || list_empty(&pmu->aliases)) { + pmu = NULL; + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + list_for_each_entry(alias, &pmu->aliases, list) { + if (strchr(alias->name, '-')) + len++; + len++; + } + } + + if (len == 0) { perf_pmu_events_list_num = -1; return; } - list_for_each_entry(alias, &pmu->aliases, list) { - if (strchr(alias->name, '-')) - len++; - len++; - } perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); if (!perf_pmu_events_list) return; perf_pmu_events_list_num = len; len = 0; - list_for_each_entry(alias, &pmu->aliases, list) { - struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; - char *tmp = strchr(alias->name, '-'); - - if (tmp != NULL) { - SET_SYMBOL(strndup(alias->name, tmp - alias->name), - PMU_EVENT_SYMBOL_PREFIX); - p++; - SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); - len += 2; - } else { - SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); - len++; + pmu = NULL; + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + list_for_each_entry(alias, &pmu->aliases, list) { + struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; + char *tmp = strchr(alias->name, '-'); + + if (tmp != NULL) { + SET_SYMBOL(strndup(alias->name, tmp - alias->name), + PMU_EVENT_SYMBOL_PREFIX); + p++; + SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); + len += 2; + } else { + SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); + len++; + } } } qsort(perf_pmu_events_list, len, @@ -1563,7 +1570,7 @@ perf_pmu__parse_check(const char *name) r = bsearch(&p, perf_pmu_events_list, (size_t) perf_pmu_events_list_num, sizeof(struct perf_pmu_event_symbol), comp_pmu); - free(p.symbol); + zfree(&p.symbol); return r ? r->type : PMU_EVENT_SYMBOL_ERR; } @@ -1710,8 +1717,8 @@ static void parse_events_print_error(struct parse_events_error *err, fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str); if (err->help) fprintf(stderr, "\n%s\n", err->help); - free(err->str); - free(err->help); + zfree(&err->str); + zfree(&err->help); } fprintf(stderr, "Run 'perf list' for a list of valid events\n"); @@ -2013,17 +2020,14 @@ static bool is_event_supported(u8 type, unsigned config) .config = config, .disabled = 1, }; - struct { - struct thread_map map; - int threads[1]; - } tmap = { - .map.nr = 1, - .threads = { 0 }, - }; + struct thread_map *tmap = thread_map__new_by_tid(0); + + if (tmap == NULL) + return false; evsel = perf_evsel__new(&attr); if (evsel) { - open_return = perf_evsel__open(evsel, NULL, &tmap.map); + open_return = perf_evsel__open(evsel, NULL, tmap); ret = open_return >= 0; if (open_return == -EACCES) { @@ -2035,7 +2039,7 @@ static bool is_event_supported(u8 type, unsigned config) * */ evsel->attr.exclude_kernel = 1; - ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; + ret = perf_evsel__open(evsel, NULL, tmap) >= 0; } perf_evsel__delete(evsel); } @@ -2314,24 +2318,20 @@ int parse_events__is_hardcoded_term(struct parse_events_term *term) return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; } -static int new_term(struct parse_events_term **_term, int type_val, - int type_term, char *config, - char *str, u64 num, int err_term, int err_val) +static int new_term(struct parse_events_term **_term, + struct parse_events_term *temp, + char *str, u64 num) { struct parse_events_term *term; - term = zalloc(sizeof(*term)); + term = malloc(sizeof(*term)); if (!term) return -ENOMEM; + *term = *temp; INIT_LIST_HEAD(&term->list); - term->type_val = type_val; - term->type_term = type_term; - term->config = config; - term->err_term = err_term; - term->err_val = err_val; - switch (type_val) { + switch (term->type_val) { case PARSE_EVENTS__TERM_TYPE_NUM: term->val.num = num; break; @@ -2349,15 +2349,22 @@ static int new_term(struct parse_events_term **_term, int type_val, int parse_events_term__num(struct parse_events_term **term, int type_term, char *config, u64 num, + bool no_value, void *loc_term_, void *loc_val_) { YYLTYPE *loc_term = loc_term_; YYLTYPE *loc_val = loc_val_; - return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, - config, NULL, num, - loc_term ? loc_term->first_column : 0, - loc_val ? loc_val->first_column : 0); + struct parse_events_term temp = { + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = type_term, + .config = config, + .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); } int parse_events_term__str(struct parse_events_term **term, @@ -2367,37 +2374,45 @@ int parse_events_term__str(struct parse_events_term **term, YYLTYPE *loc_term = loc_term_; YYLTYPE *loc_val = loc_val_; - return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, - config, str, 0, - loc_term ? loc_term->first_column : 0, - loc_val ? loc_val->first_column : 0); + struct parse_events_term temp = { + .type_val = PARSE_EVENTS__TERM_TYPE_STR, + .type_term = type_term, + .config = config, + .err_term = loc_term ? loc_term->first_column : 0, + .err_val = loc_val ? loc_val->first_column : 0, + }; + + return new_term(term, &temp, str, 0); } int parse_events_term__sym_hw(struct parse_events_term **term, char *config, unsigned idx) { struct event_symbol *sym; + struct parse_events_term temp = { + .type_val = PARSE_EVENTS__TERM_TYPE_STR, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, + .config = config ?: (char *) "event", + }; BUG_ON(idx >= PERF_COUNT_HW_MAX); sym = &event_symbols_hw[idx]; - if (config) - return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, - PARSE_EVENTS__TERM_TYPE_USER, config, - (char *) sym->symbol, 0, 0, 0); - else - return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, - PARSE_EVENTS__TERM_TYPE_USER, - (char *) "event", (char *) sym->symbol, - 0, 0, 0); + return new_term(term, &temp, (char *) sym->symbol, 0); } int parse_events_term__clone(struct parse_events_term **new, struct parse_events_term *term) { - return new_term(new, term->type_val, term->type_term, term->config, - term->val.str, term->val.num, - term->err_term, term->err_val); + struct parse_events_term temp = { + .type_val = term->type_val, + .type_term = term->type_term, + .config = term->config, + .err_term = term->err_term, + .err_val = term->err_val, + }; + + return new_term(new, &temp, term->val.str, term->val.num); } void parse_events_terms__purge(struct list_head *terms) @@ -2406,7 +2421,7 @@ void parse_events_terms__purge(struct list_head *terms) list_for_each_entry_safe(term, h, terms, list) { if (term->array.nr_ranges) - free(term->array.ranges); + zfree(&term->array.ranges); list_del_init(&term->list); free(term); } @@ -2422,7 +2437,7 @@ void parse_events_terms__delete(struct list_head *terms) void parse_events__clear_array(struct parse_events_array *a) { - free(a->ranges); + zfree(&a->ranges); } void parse_events_evlist_error(struct parse_events_evlist *data, diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index da246a3ddb69..1af6a267c21b 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -94,6 +94,7 @@ struct parse_events_term { int type_term; struct list_head list; bool used; + bool no_value; /* error string indexes for within parsed string */ int err_term; @@ -122,6 +123,7 @@ 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, + 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, diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 879115f93edc..30f018ea1370 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -12,9 +12,13 @@ #include <linux/list.h> #include <linux/types.h> #include "util.h" +#include "pmu.h" +#include "debug.h" #include "parse-events.h" #include "parse-events-bison.h" +void parse_events_error(YYLTYPE *loc, void *data, void *scanner, char const *msg); + #define ABORT_ON(val) \ do { \ if (val) \ @@ -236,15 +240,34 @@ PE_KERNEL_PMU_EVENT sep_dc struct list_head *head; struct parse_events_term *term; struct list_head *list; + struct perf_pmu *pmu = NULL; + int ok = 0; - ALLOC_LIST(head); - ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, 1, &@1, NULL)); - list_add_tail(&term->list, head); - + /* Add it for all PMUs that support the alias */ ALLOC_LIST(list); - ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); - parse_events_terms__delete(head); + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + struct perf_pmu_alias *alias; + + list_for_each_entry(alias, &pmu->aliases, list) { + if (!strcasecmp(alias->name, $1)) { + ALLOC_LIST(head); + ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, 1, false, &@1, NULL)); + list_add_tail(&term->list, head); + + if (!parse_events_add_pmu(data, list, + pmu->name, head)) { + pr_debug("%s -> %s/%s/\n", $1, + pmu->name, alias->str); + ok++; + } + + parse_events_terms__delete(head); + } + } + } + if (!ok) + YYABORT; $$ = list; } | @@ -259,7 +282,7 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc ALLOC_LIST(head); ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - &pmu_name, 1, &@1, NULL)); + &pmu_name, 1, false, &@1, NULL)); list_add_tail(&term->list, head); ALLOC_LIST(list); @@ -525,7 +548,7 @@ PE_NAME '=' PE_VALUE struct parse_events_term *term; ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $3, &@1, &@3)); + $1, $3, false, &@1, &@3)); $$ = term; } | @@ -543,7 +566,7 @@ PE_NAME struct parse_events_term *term; ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, 1, &@1, NULL)); + $1, 1, true, &@1, NULL)); $$ = term; } | @@ -568,7 +591,7 @@ PE_TERM '=' PE_VALUE { struct parse_events_term *term; - ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, &@1, &@3)); + ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, false, &@1, &@3)); $$ = term; } | @@ -576,7 +599,7 @@ PE_TERM { struct parse_events_term *term; - ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL)); + ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, true, &@1, NULL)); $$ = term; } | @@ -597,7 +620,7 @@ PE_NAME array '=' PE_VALUE struct parse_events_term *term; ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, - $1, $4, &@1, &@4)); + $1, $4, false, &@1, &@4)); term->array = $2; $$ = term; } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index dc6ccaa4e927..12f84dd2ac5d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -94,32 +94,10 @@ static int pmu_format(const char *name, struct list_head *format) return 0; } -static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +static int convert_scale(const char *scale, char **end, double *sval) { - struct stat st; - ssize_t sret; - char scale[128]; - int fd, ret = -1; - char path[PATH_MAX]; char *lc; - - snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); - - fd = open(path, O_RDONLY); - if (fd == -1) - return -1; - - if (fstat(fd, &st) < 0) - goto error; - - sret = read(fd, scale, sizeof(scale)-1); - if (sret < 0) - goto error; - - if (scale[sret - 1] == '\n') - scale[sret - 1] = '\0'; - else - scale[sret] = '\0'; + int ret = 0; /* * save current locale @@ -134,7 +112,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * lc = strdup(lc); if (!lc) { ret = -ENOMEM; - goto error; + goto out; } /* @@ -144,14 +122,42 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * */ setlocale(LC_NUMERIC, "C"); - alias->scale = strtod(scale, NULL); + *sval = strtod(scale, end); +out: /* restore locale */ setlocale(LC_NUMERIC, lc); - free(lc); + return ret; +} + +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ + struct stat st; + ssize_t sret; + char scale[128]; + int fd, ret = -1; + char path[PATH_MAX]; - ret = 0; + snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + if (fstat(fd, &st) < 0) + goto error; + + sret = read(fd, scale, sizeof(scale)-1); + if (sret < 0) + goto error; + + if (scale[sret - 1] == '\n') + scale[sret - 1] = '\0'; + else + scale[sret] = '\0'; + + ret = convert_scale(scale, NULL, &alias->scale); error: close(fd); return ret; @@ -223,11 +229,13 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, } static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, - char *desc, char *val, char *long_desc, - char *topic) + char *desc, char *val, + char *long_desc, char *topic, + char *unit, char *perpkg) { struct perf_pmu_alias *alias; int ret; + int num; alias = malloc(sizeof(*alias)); if (!alias) @@ -261,6 +269,13 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->long_desc = long_desc ? strdup(long_desc) : desc ? strdup(desc) : NULL; alias->topic = topic ? strdup(topic) : NULL; + if (unit) { + if (convert_scale(unit, &unit, &alias->scale) < 0) + return -1; + snprintf(alias->unit, sizeof(alias->unit), "%s", unit); + } + alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; + alias->str = strdup(val); list_add_tail(&alias->list, list); @@ -278,7 +293,8 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI buf[ret] = 0; - return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL); + return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, + NULL); } static inline bool pmu_alias_info_file(char *name) @@ -498,7 +514,7 @@ char * __weak get_cpuid_str(void) * to the current running CPU. Then, add all PMU events from that table * as aliases. */ -static void pmu_add_cpu_aliases(struct list_head *head) +static void pmu_add_cpu_aliases(struct list_head *head, const char *name) { int i; struct pmu_events_map *map; @@ -534,14 +550,21 @@ static void pmu_add_cpu_aliases(struct list_head *head) */ i = 0; while (1) { + const char *pname; + pe = &map->table[i++]; if (!pe->name) break; + pname = pe->pmu ? pe->pmu : "cpu"; + if (strncmp(pname, name, strlen(pname))) + continue; + /* need type casts to override 'const' */ __perf_pmu__new_alias(head, NULL, (char *)pe->name, (char *)pe->desc, (char *)pe->event, - (char *)pe->long_desc, (char *)pe->topic); + (char *)pe->long_desc, (char *)pe->topic, + (char *)pe->unit, (char *)pe->perpkg); } out: @@ -569,15 +592,16 @@ static struct perf_pmu *pmu_lookup(const char *name) if (pmu_format(name, &format)) return NULL; - if (pmu_aliases(name, &aliases)) + /* + * Check the type first to avoid unnecessary work. + */ + if (pmu_type(name, &type)) return NULL; - if (!strcmp(name, "cpu")) - pmu_add_cpu_aliases(&aliases); - - if (pmu_type(name, &type)) + if (pmu_aliases(name, &aliases)) return NULL; + pmu_add_cpu_aliases(&aliases, name); pmu = zalloc(sizeof(*pmu)); if (!pmu) return NULL; @@ -721,7 +745,7 @@ static int pmu_resolve_param_term(struct parse_events_term *term, } } - if (verbose) + if (verbose > 0) printf("Required parameter '%s' not specified\n", term->config); return -1; @@ -779,7 +803,7 @@ static int pmu_config_term(struct list_head *formats, format = pmu_find_format(formats, term->config); if (!format) { - if (verbose) + if (verbose > 0) printf("Invalid event/parameter '%s'\n", term->config); if (err) { char *pmu_term = pmu_formats_string(formats); @@ -810,11 +834,20 @@ static int pmu_config_term(struct list_head *formats, * Either directly use a numeric term, or try to translate string terms * using event parameters. */ - if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) + if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { + if (term->no_value && + bitmap_weight(format->bits, PERF_PMU_FORMAT_BITS) > 1) { + if (err) { + err->idx = term->err_val; + err->str = strdup("no value assigned for term"); + } + return -EINVAL; + } + val = term->val.num; - else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { + } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { if (strcmp(term->val.str, "?")) { - if (verbose) { + if (verbose > 0) { pr_info("Invalid sysfs entry %s=%s\n", term->config, term->val.str); } @@ -921,12 +954,12 @@ static int check_info_data(struct perf_pmu_alias *alias, * define unit, scale and snapshot, fail * if there's more than one. */ - if ((info->unit && alias->unit) || + if ((info->unit && alias->unit[0]) || (info->scale && alias->scale) || (info->snapshot && alias->snapshot)) return -EINVAL; - if (alias->unit) + if (alias->unit[0]) info->unit = alias->unit; if (alias->scale) @@ -1065,6 +1098,8 @@ struct sevent { char *name; char *desc; char *topic; + char *str; + char *pmu; }; static int cmp_sevent(const void *a, const void *b) @@ -1161,6 +1196,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, aliases[j].desc = long_desc ? alias->long_desc : alias->desc; aliases[j].topic = alias->topic; + aliases[j].str = alias->str; + aliases[j].pmu = pmu->name; j++; } if (pmu->selectable && @@ -1175,6 +1212,9 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, len = j; qsort(aliases, len, sizeof(struct sevent), cmp_sevent); for (j = 0; j < len; j++) { + /* Skip duplicates */ + if (j > 0 && !strcmp(aliases[j].name, aliases[j - 1].name)) + continue; if (name_only) { printf("%s ", aliases[j].name); continue; @@ -1192,6 +1232,8 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, printf("%*s", 8, "["); wordwrap(aliases[j].desc, 8, columns, 0); printf("]\n"); + if (verbose > 0) + printf("%*s%s/%s/\n", 8, "", aliases[j].pmu, aliases[j].str); } else printf(" %-50s [Kernel PMU event]\n", aliases[j].name); printed++; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 25712034c815..00852ddc7741 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -43,6 +43,7 @@ struct perf_pmu_alias { char *desc; char *long_desc; char *topic; + char *str; struct list_head terms; /* HEAD struct parse_events_term -> list */ struct list_head list; /* ELEM */ char unit[UNIT_MAX_LEN+1]; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6a6f44dd594b..28fb62c32678 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -594,7 +594,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, pr_debug("try to find information at %" PRIx64 " in %s\n", addr, tp->module ? : "kernel"); - dinfo = debuginfo_cache__open(tp->module, verbose == 0); + dinfo = debuginfo_cache__open(tp->module, verbose <= 0); if (dinfo) ret = debuginfo__find_probe_point(dinfo, (unsigned long)addr, pp); @@ -2061,7 +2061,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, bool is_kprobe) { struct symbol *sym = NULL; - struct map *map; + struct map *map = NULL; u64 addr = tp->address; int ret = -ENOENT; @@ -3023,20 +3023,17 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev, tev->nargs = pev->nargs; tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); - if (!tev->args) { - err = -ENOMEM; + if (!tev->args) goto errout; - } + for (i = 0; i < tev->nargs; i++) copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]); return 1; errout: - if (*tevs) { - clear_probe_trace_events(*tevs, 1); - *tevs = NULL; - } + clear_probe_trace_events(*tevs, 1); + *tevs = NULL; return err; } diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 0d9d6e0803b8..57cd268d4275 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -464,7 +464,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, /* Verify it is a data structure */ tag = dwarf_tag(&type); if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { - pr_warning("%s is not a data structure nor an union.\n", + pr_warning("%s is not a data structure nor a union.\n", varname); return -EINVAL; } @@ -479,7 +479,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, } else { /* Verify it is a data structure */ if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { - pr_warning("%s is not a data structure nor an union.\n", + pr_warning("%s is not a data structure nor a union.\n", varname); return -EINVAL; } diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build index 6516e220c247..82d28c67e0f3 100644 --- a/tools/perf/util/scripting-engines/Build +++ b/tools/perf/util/scripting-engines/Build @@ -1,6 +1,6 @@ libperf-$(CONFIG_LIBPERL) += trace-event-perl.o libperf-$(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-undef -Wno-switch-default +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 CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index e55a132f69b7..dff043a29589 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -217,6 +217,7 @@ static void define_event_symbols(struct event_format *event, cur_field_name); break; case PRINT_HEX: + case PRINT_HEX_STR: define_event_symbols(event, ev_name, args->hex.field); define_event_symbols(event, ev_name, args->hex.size); break; @@ -309,10 +310,10 @@ static SV *perl_process_callchain(struct perf_sample *sample, if (node->map) { struct map *map = node->map; const char *dsoname = "[unknown]"; - if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (map && map->dso) { if (symbol_conf.show_kernel_path && map->dso->long_name) dsoname = map->dso->long_name; - else if (map->dso->name) + else dsoname = map->dso->name; } if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) { @@ -350,8 +351,10 @@ static void perl_process_tracepoint(struct perf_sample *sample, if (evsel->attr.type != PERF_TYPE_TRACEPOINT) return; - if (!event) - die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); + if (!event) { + pr_debug("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); + return; + } pid = raw_field_value(event, "common_pid", data); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 089438da1f7f..783326cfbaa6 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -236,6 +236,7 @@ static void define_event_symbols(struct event_format *event, cur_field_name); break; case PRINT_HEX: + case PRINT_HEX_STR: define_event_symbols(event, ev_name, args->hex.field); define_event_symbols(event, ev_name, args->hex.size); break; @@ -368,10 +369,10 @@ static PyObject *python_process_callchain(struct perf_sample *sample, if (node->map) { struct map *map = node->map; const char *dsoname = "[unknown]"; - if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (map && map->dso) { if (symbol_conf.show_kernel_path && map->dso->long_name) dsoname = map->dso->long_name; - else if (map->dso->name) + else dsoname = map->dso->name; } pydict_set_item_string_decref(pyelem, "dso", diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f268201048a0..1dd617d116b5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -932,7 +932,7 @@ static void branch_stack__printf(struct perf_sample *sample) printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x\n", i, e->from, e->to, - e->flags.cycles, + (unsigned short)e->flags.cycles, e->flags.mispred ? "M" : " ", e->flags.predicted ? "P" : " ", e->flags.abort ? "A" : " ", @@ -1191,7 +1191,7 @@ static int u64 sample_type = evsel->attr.sample_type; u64 read_format = evsel->attr.read_format; - /* Standard sample delievery. */ + /* Standard sample delivery. */ if (!(sample_type & PERF_SAMPLE_READ)) return tool->sample(tool, event, sample, evsel, machine); @@ -1901,7 +1901,7 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr) { char *bracket; - enum map_type i; + int i; struct ref_reloc_sym *ref; ref = zalloc(sizeof(struct ref_reloc_sym)); diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index c8680984d2d6..af415febbc46 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -1,8 +1,15 @@ #!/usr/bin/python2 -from distutils.core import setup, Extension from os import getenv +cc = getenv("CC") +if cc == "clang": + from _sysconfigdata import build_time_vars + from re import sub + build_time_vars["CFLAGS"] = sub("-specs=[^ ]+", "", build_time_vars["CFLAGS"]) + +from distutils.core import setup, Extension + from distutils.command.build_ext import build_ext as _build_ext from distutils.command.install_lib import install_lib as _install_lib diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index df622f4e301e..0ff622288d24 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -151,7 +151,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) if (!dso_l || !dso_r) return cmp_null(dso_r, dso_l); - if (verbose) { + if (verbose > 0) { dso_name_l = dso_l->long_name; dso_name_r = dso_r->long_name; } else { @@ -172,8 +172,8 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, size_t size, unsigned int width) { if (map && map->dso) { - const char *dso_name = !verbose ? map->dso->short_name : - map->dso->long_name; + const char *dso_name = verbose > 0 ? map->dso->long_name : + map->dso->short_name; return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); } @@ -261,7 +261,7 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, { size_t ret = 0; - if (verbose) { + if (verbose > 0) { char o = map ? dso__symtab_origin(map->dso) : '!'; ret += repsep_snprintf(bf, size, "%-#*llx %c ", BITS_PER_LONG / 4 + 2, ip, o); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 7aff317fc7c4..796c847e2f00 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -108,7 +108,7 @@ struct hist_entry { /* * Since perf diff only supports the stdio output, TUI * fields are only accessed from perf report (or perf - * top). So make it an union to reduce memory usage. + * top). So make it a union to reduce memory usage. */ struct hist_entry_diff diff; struct /* for TUI */ { diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 39345c2ddfc2..0d51334a9b46 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -344,7 +344,7 @@ int perf_stat_process_counter(struct perf_stat_config *config, for (i = 0; i < 3; i++) update_stats(&ps->res_stats[i], count[i]); - if (verbose) { + if (verbose > 0) { fprintf(config->output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", perf_evsel__name(counter), count[0], count[1], count[2]); } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index bcae659b6546..efb53772e0ec 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -269,6 +269,7 @@ static int strfilter_node__sprint(struct strfilter_node *node, char *buf) len = strfilter_node__sprint_pt(node->l, buf); if (len < 0) return len; + __fallthrough; case '!': if (buf) { *(buf + len++) = *node->p; diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index d8dfaf64b32e..bddca519dd58 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -21,6 +21,8 @@ s64 perf_atoll(const char *str) case 'b': case 'B': if (*p) goto out_err; + + __fallthrough; case '\0': return length; default: diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index adbc6c02c3aa..4e59ddeb4eda 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -213,7 +213,7 @@ static bool want_demangle(bool is_kernel_sym) static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name) { - int demangle_flags = verbose ? (DMGL_PARAMS | DMGL_ANSI) : DMGL_NO_OPTS; + int demangle_flags = verbose > 0 ? (DMGL_PARAMS | DMGL_ANSI) : DMGL_NO_OPTS; char *demangled = NULL; /* diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index dc93940de351..9b4d8ba22fed 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -202,7 +202,7 @@ void symbols__fixup_end(struct rb_root *symbols) /* Last entry */ if (curr->end == curr->start) - curr->end = roundup(curr->start, 4096); + curr->end = roundup(curr->start, 4096) + 4096; } void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) @@ -1460,9 +1460,11 @@ int dso__load(struct dso *dso, struct map *map) * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work */ if (!dso->has_build_id && - is_regular_file(dso->long_name) && - filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0) + is_regular_file(dso->long_name)) { + __symbol__join_symfs(name, PATH_MAX, dso->long_name); + if (filename__read_build_id(name, build_id, BUILD_ID_SIZE) > 0) dso__set_build_id(dso, build_id); + } /* * Iterate over candidate debug images. diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index 7c6b33e8e2d2..63694e174e5c 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c @@ -21,7 +21,7 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym, unsigned long offset; size_t length; - if (sym && sym->name) { + if (sym) { length = fprintf(fp, "%s", sym->name); if (al && print_offsets) { if (al->addr < sym->end) diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index f9eab200fd75..7c3fcc538a70 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -93,7 +93,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid) { DIR *proc; int max_threads = 32, items, i; - char path[256]; + char path[NAME_MAX + 1 + 6]; struct dirent *dirent, **namelist = NULL; struct thread_map *threads = thread_map__alloc(max_threads); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index d995743cb673..e7d60d05596d 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -42,7 +42,7 @@ #include "evsel.h" #include "debug.h" -#define VERSION "0.5" +#define VERSION "0.6" static int output_fd; @@ -170,6 +170,12 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) return false; } +#define for_each_event(dir, dent, tps) \ + while ((dent = readdir(dir))) \ + if (dent->d_type == DT_DIR && \ + (strcmp(dent->d_name, ".")) && \ + (strcmp(dent->d_name, ".."))) \ + static int copy_event_system(const char *sys, struct tracepoint_path *tps) { struct dirent *dent; @@ -186,12 +192,10 @@ static int copy_event_system(const char *sys, struct tracepoint_path *tps) return -errno; } - while ((dent = readdir(dir))) { - if (dent->d_type != DT_DIR || - strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0 || - !name_in_tp_list(dent->d_name, tps)) + for_each_event(dir, dent, tps) { + if (!name_in_tp_list(dent->d_name, tps)) continue; + if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) { err = -ENOMEM; goto out; @@ -210,12 +214,10 @@ static int copy_event_system(const char *sys, struct tracepoint_path *tps) } rewinddir(dir); - while ((dent = readdir(dir))) { - if (dent->d_type != DT_DIR || - strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0 || - !name_in_tp_list(dent->d_name, tps)) + for_each_event(dir, dent, tps) { + if (!name_in_tp_list(dent->d_name, tps)) continue; + if (asprintf(&format, "%s/%s/format", sys, dent->d_name) < 0) { err = -ENOMEM; goto out; @@ -290,13 +292,11 @@ static int record_event_files(struct tracepoint_path *tps) goto out; } - while ((dent = readdir(dir))) { - if (dent->d_type != DT_DIR || - strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0 || - strcmp(dent->d_name, "ftrace") == 0 || + for_each_event(dir, dent, tps) { + if (strcmp(dent->d_name, "ftrace") == 0 || !system_in_tp_list(dent->d_name, tps)) continue; + count++; } @@ -307,13 +307,11 @@ static int record_event_files(struct tracepoint_path *tps) } rewinddir(dir); - while ((dent = readdir(dir))) { - if (dent->d_type != DT_DIR || - strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0 || - strcmp(dent->d_name, "ftrace") == 0 || + for_each_event(dir, dent, tps) { + if (strcmp(dent->d_name, "ftrace") == 0 || !system_in_tp_list(dent->d_name, tps)) continue; + if (asprintf(&sys, "%s/%s", path, dent->d_name) < 0) { err = -ENOMEM; goto out; @@ -379,6 +377,34 @@ out: return err; } +static int record_saved_cmdline(void) +{ + unsigned int size; + char *path; + struct stat st; + int ret, err = 0; + + path = get_tracing_file("saved_cmdlines"); + if (!path) { + pr_debug("can't get tracing/saved_cmdline"); + return -ENOMEM; + } + + ret = stat(path, &st); + if (ret < 0) { + /* not found */ + size = 0; + if (write(output_fd, &size, 8) != 8) + err = -EIO; + goto out; + } + err = record_file(path, 8); + +out: + put_tracing_file(path); + return err; +} + static void put_tracepoints_path(struct tracepoint_path *tps) { @@ -539,6 +565,9 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, if (err) goto out; err = record_ftrace_printk(); + if (err) + goto out; + err = record_saved_cmdline(); out: /* diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 33b52eaa39db..de0078e21408 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -160,6 +160,23 @@ void parse_ftrace_printk(struct pevent *pevent, } } +void parse_saved_cmdline(struct pevent *pevent, + char *file, unsigned int size __maybe_unused) +{ + char *comm; + char *line; + char *next = NULL; + int pid; + + line = strtok_r(file, "\n", &next); + while (line) { + sscanf(line, "%d %ms", &pid, &comm); + pevent_register_comm(pevent, comm, pid); + free(comm); + line = strtok_r(NULL, "\n", &next); + } +} + int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size) { return pevent_parse_event(pevent, buf, size, "ftrace"); diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index b67a0ccf5ab9..27420159bf69 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -260,39 +260,53 @@ static int read_header_files(struct pevent *pevent) static int read_ftrace_file(struct pevent *pevent, unsigned long long size) { + int ret; char *buf; buf = malloc(size); - if (buf == NULL) + if (buf == NULL) { + pr_debug("memory allocation failure\n"); return -1; + } - if (do_read(buf, size) < 0) { - free(buf); - return -1; + ret = do_read(buf, size); + if (ret < 0) { + pr_debug("error reading ftrace file.\n"); + goto out; } - parse_ftrace_file(pevent, buf, size); + ret = parse_ftrace_file(pevent, buf, size); + if (ret < 0) + pr_debug("error parsing ftrace file.\n"); +out: free(buf); - return 0; + return ret; } static int read_event_file(struct pevent *pevent, char *sys, unsigned long long size) { + int ret; char *buf; buf = malloc(size); - if (buf == NULL) + if (buf == NULL) { + pr_debug("memory allocation failure\n"); return -1; + } - if (do_read(buf, size) < 0) { + ret = do_read(buf, size); + if (ret < 0) { free(buf); - return -1; + goto out; } - parse_event_file(pevent, buf, size, sys); + ret = parse_event_file(pevent, buf, size, sys); + if (ret < 0) + pr_debug("error parsing event file.\n"); +out: free(buf); - return 0; + return ret; } static int read_ftrace_files(struct pevent *pevent) @@ -341,6 +355,36 @@ static int read_event_files(struct pevent *pevent) return 0; } +static int read_saved_cmdline(struct pevent *pevent) +{ + unsigned long long size; + char *buf; + int ret; + + /* it can have 0 size */ + size = read8(pevent); + if (!size) + return 0; + + buf = malloc(size + 1); + if (buf == NULL) { + pr_debug("memory allocation failure\n"); + return -1; + } + + ret = do_read(buf, size); + if (ret < 0) { + pr_debug("error reading saved cmdlines\n"); + goto out; + } + + parse_saved_cmdline(pevent, buf, size); + ret = 0; +out: + free(buf); + return ret; +} + ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) { char buf[BUFSIZ]; @@ -379,10 +423,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) return -1; if (show_version) printf("version = %s\n", version); - free(version); - if (do_read(buf, 1) < 0) + if (do_read(buf, 1) < 0) { + free(version); return -1; + } file_bigendian = buf[0]; host_bigendian = bigendian(); @@ -423,6 +468,11 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) err = read_ftrace_printk(pevent); if (err) goto out; + if (atof(version) >= 0.6) { + err = read_saved_cmdline(pevent); + if (err) + goto out; + } size = trace_data_size; repipe = false; @@ -438,5 +488,6 @@ ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) out: if (pevent) trace_event__cleanup(tevent); + free(version); return size; } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b0af9c81bb0d..1fbc044f9eb0 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -42,6 +42,7 @@ raw_field_value(struct event_format *event, const char *name, void *data); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); +void parse_saved_cmdline(struct pevent *pevent, char *file, unsigned int size); ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 6fec84dff3f7..bfb9b7987692 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -35,6 +35,7 @@ #include "util.h" #include "debug.h" #include "asm/bug.h" +#include "dso.h" extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, @@ -297,15 +298,58 @@ static int read_unwind_spec_debug_frame(struct dso *dso, int fd; u64 ofs = dso->data.debug_frame_offset; + /* debug_frame can reside in: + * - dso + * - debug pointed by symsrc_filename + * - gnu_debuglink, which doesn't necessary + * has to be pointed by symsrc_filename + */ if (ofs == 0) { fd = dso__data_get_fd(dso, machine); - if (fd < 0) - return -EINVAL; + if (fd >= 0) { + ofs = elf_section_offset(fd, ".debug_frame"); + dso__data_put_fd(dso); + } + + if (ofs <= 0) { + fd = open(dso->symsrc_filename, O_RDONLY); + if (fd >= 0) { + ofs = elf_section_offset(fd, ".debug_frame"); + close(fd); + } + } + + if (ofs <= 0) { + char *debuglink = malloc(PATH_MAX); + int ret = 0; + + ret = dso__read_binary_type_filename( + dso, DSO_BINARY_TYPE__DEBUGLINK, + machine->root_dir, debuglink, PATH_MAX); + if (!ret) { + fd = open(debuglink, O_RDONLY); + if (fd >= 0) { + ofs = elf_section_offset(fd, + ".debug_frame"); + close(fd); + } + } + if (ofs > 0) { + if (dso->symsrc_filename != NULL) { + pr_warning( + "%s: overwrite symsrc(%s,%s)\n", + __func__, + dso->symsrc_filename, + debuglink); + free(dso->symsrc_filename); + } + dso->symsrc_filename = debuglink; + } else { + free(debuglink); + } + } - /* Check the .debug_frame section for unwinding info */ - ofs = elf_section_offset(fd, ".debug_frame"); dso->data.debug_frame_offset = ofs; - dso__data_put_fd(dso); } *offset = ofs; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 9ddd98827d12..d8b45cea54d0 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -85,7 +85,7 @@ int mkdir_p(char *path, mode_t mode) return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; } -int rm_rf(char *path) +int rm_rf(const char *path) { DIR *dir; int ret = 0; @@ -789,3 +789,16 @@ int is_printable_array(char *p, unsigned int len) } return 1; } + +int unit_number__scnprintf(char *buf, size_t size, u64 n) +{ + char unit[4] = "BKMG"; + int i = 0; + + while (((n / 1024) > 1) && (i < 3)) { + n /= 1024; + i++; + } + + return scnprintf(buf, size, "%" PRIu64 "%c", n, unit[i]); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 1d639e38aa82..c74708da8571 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -209,7 +209,7 @@ static inline int sane_case(int x, int high) } int mkdir_p(char *path, mode_t mode); -int rm_rf(char *path); +int rm_rf(const char *path); struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dirent *)); bool lsdir_no_dot_filter(const char *name, struct dirent *d); int copyfile(const char *from, const char *to); @@ -363,4 +363,5 @@ int is_printable_array(char *p, unsigned int len); int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); +int unit_number__scnprintf(char *buf, size_t size, u64 n); #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/power/acpi/common/cmfsize.c b/tools/power/acpi/common/cmfsize.c index bc82596d7354..5b38dc2fec4f 100644 --- a/tools/power/acpi/common/cmfsize.c +++ b/tools/power/acpi/common/cmfsize.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c index 3919970f5aea..6e78413bb2cb 100644 --- a/tools/power/acpi/common/getopt.c +++ b/tools/power/acpi/common/getopt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c b/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c index 546cf4a503b7..82a2ff896a95 100644 --- a/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c +++ b/tools/power/acpi/os_specific/service_layers/oslinuxtbl.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/os_specific/service_layers/osunixdir.c b/tools/power/acpi/os_specific/service_layers/osunixdir.c index 66c4badf03e5..ea14eaeb268f 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixdir.c +++ b/tools/power/acpi/os_specific/service_layers/osunixdir.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/os_specific/service_layers/osunixmap.c b/tools/power/acpi/os_specific/service_layers/osunixmap.c index cbfbce18783d..cf9b5a54df92 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixmap.c +++ b/tools/power/acpi/os_specific/service_layers/osunixmap.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index 10648aaf6164..c04e8fea2c60 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -318,6 +318,28 @@ acpi_os_physical_table_override(struct acpi_table_header *existing_table, /****************************************************************************** * + * FUNCTION: acpi_os_enter_sleep + * + * PARAMETERS: sleep_state - Which sleep state to enter + * rega_value - Register A value + * regb_value - Register B value + * + * RETURN: Status + * + * DESCRIPTION: A hook before writing sleep registers to enter the sleep + * state. Return AE_CTRL_TERMINATE to skip further sleep register + * writes. + * + *****************************************************************************/ + +acpi_status acpi_os_enter_sleep(u8 sleep_state, u32 rega_value, u32 regb_value) +{ + + return (AE_OK); +} + +/****************************************************************************** + * * FUNCTION: acpi_os_redirect_output * * PARAMETERS: destination - An open file handle/pointer diff --git a/tools/power/acpi/tools/acpidump/acpidump.h b/tools/power/acpi/tools/acpidump/acpidump.h index 00423fc45e7c..d6aa40fce2b1 100644 --- a/tools/power/acpi/tools/acpidump/acpidump.h +++ b/tools/power/acpi/tools/acpidump/acpidump.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index 9031be1afe63..60df1fbd4a77 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index dd5b861dc4a8..31b5a7f74015 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index 7ff46be908f0..dd82afa897bd 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2016, Intel Corp. + * Copyright (C) 2000 - 2017, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 590d12a25f6e..3e701f0e9c14 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -285,20 +285,24 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human) /* --hwlimits / -l */ -static int get_hardware_limits(unsigned int cpu) +static int get_hardware_limits(unsigned int cpu, unsigned int human) { unsigned long min, max; - printf(_(" hardware limits: ")); if (cpufreq_get_hardware_limits(cpu, &min, &max)) { printf(_("Not Available\n")); return -EINVAL; } - print_speed(min); - printf(" - "); - print_speed(max); - printf("\n"); + if (human) { + printf(_(" hardware limits: ")); + print_speed(min); + printf(" - "); + print_speed(max); + printf("\n"); + } else { + printf("%lu %lu\n", min, max); + } return 0; } @@ -456,7 +460,7 @@ static void debug_output_one(unsigned int cpu) get_related_cpus(cpu); get_affected_cpus(cpu); get_latency(cpu, 1); - get_hardware_limits(cpu); + get_hardware_limits(cpu, 1); freqs = cpufreq_get_available_frequencies(cpu); if (freqs) { @@ -622,7 +626,7 @@ int cmd_freq_info(int argc, char **argv) ret = get_driver(cpu); break; case 'l': - ret = get_hardware_limits(cpu); + ret = get_hardware_limits(cpu, human); break; case 'w': ret = get_freq_hardware(cpu, human); @@ -639,7 +643,6 @@ int cmd_freq_info(int argc, char **argv) } if (ret) return ret; - printf("\n"); } return ret; } diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py new file mode 100755 index 000000000000..fd706ac0f347 --- /dev/null +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -0,0 +1,569 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +""" This utility can be used to debug and tune the performance of the +intel_pstate driver. This utility can be used in two ways: +- If there is Linux trace file with pstate_sample events enabled, then +this utility can parse the trace file and generate performance plots. +- If user has not specified a trace file as input via command line parameters, +then this utility enables and collects trace data for a user specified interval +and generates performance plots. + +Prerequisites: + Python version 2.7.x + gnuplot 5.0 or higher + gnuplot-py 1.8 + (Most of the distributions have these required packages. They may be called + gnuplot-py, phython-gnuplot. ) + + HWP (Hardware P-States are disabled) + Kernel config for Linux trace is enabled + + see print_help(): for Usage and Output details + +""" +from __future__ import print_function +from datetime import datetime +import subprocess +import os +import time +import re +import sys +import getopt +import Gnuplot +from numpy import * +from decimal import * + +__author__ = "Srinivas Pandruvada" +__copyright__ = " Copyright (c) 2017, Intel Corporation. " +__license__ = "GPL version 2" + + +MAX_CPUS = 256 + +# Define the csv file columns +C_COMM = 18 +C_GHZ = 17 +C_ELAPSED = 16 +C_SAMPLE = 15 +C_DURATION = 14 +C_LOAD = 13 +C_BOOST = 12 +C_FREQ = 11 +C_TSC = 10 +C_APERF = 9 +C_MPERF = 8 +C_TO = 7 +C_FROM = 6 +C_SCALED = 5 +C_CORE = 4 +C_USEC = 3 +C_SEC = 2 +C_CPU = 1 + +global sample_num, last_sec_cpu, last_usec_cpu, start_time, testname + +# 11 digits covers uptime to 115 days +getcontext().prec = 11 + +sample_num =0 +last_sec_cpu = [0] * MAX_CPUS +last_usec_cpu = [0] * MAX_CPUS + +def print_help(): + print('intel_pstate_tracer.py:') + print(' Usage:') + print(' If the trace file is available, then to simply parse and plot, use (sudo not required):') + print(' ./intel_pstate_tracer.py [-c cpus] -t <trace_file> -n <test_name>') + print(' Or') + print(' ./intel_pstate_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>') + print(' To generate trace file, parse and plot, use (sudo required):') + print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name>') + print(' Or') + print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name>') + print(' Optional argument:') + print(' cpus: comma separated list of CPUs') + print(' Output:') + print(' If not already present, creates a "results/test_name" folder in the current working directory with:') + print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') + print(' cpu???.csv - comma seperated values file for CPU number ???.') + print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.') + print(' Notes:') + print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.') + print(' Maximum number of CPUs is {0:d}. If there are more the script will abort with an error.'.format(MAX_CPUS)) + print(' Off-line CPUs cause the script to list some warnings, and create some empty files. Use the CPU mask feature for a clean run.') + print(' Empty y range warnings for autoscaled plots can occur and can be ignored.') + +def plot_perf_busy_with_sample(cpu_index): + """ Plot method to per cpu information """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_perf_busy_vs_samples.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:40]') + g_plot('set y2range [0:200]') + g_plot('set y2tics 0, 10') + g_plot('set title "{} : cpu perf busy vs. sample : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) +# Override common + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_SAMPLE, C_CORE)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_SAMPLE, C_SCALED)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_SAMPLE, C_BOOST)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_SAMPLE, C_TO)) + +def plot_perf_busy(cpu_index): + """ Plot some per cpu information """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_perf_busy.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:40]') + g_plot('set y2range [0:200]') + g_plot('set y2tics 0, 10') + g_plot('set title "{} : perf busy : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "P-State"') + g_plot('set y2label "Scaled Busy/performance/io-busy(%)"') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y2 title "performance",\\'.format(C_ELAPSED, C_CORE)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 2 axis x1y2 title "scaled-busy",\\'.format(C_ELAPSED, C_SCALED)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 3 axis x1y2 title "io-boost",\\'.format(C_ELAPSED, C_BOOST)) + g_plot('"' + file_name + '" using {:d}:{:d} with linespoints linestyle 4 axis x1y1 title "P-State"'.format(C_ELAPSED, C_TO)) + +def plot_durations(cpu_index): + """ Plot per cpu durations """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_durations.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) +# Should autoscale be used here? Should seconds be used here? + g_plot('set yrange [0:5000]') + g_plot('set ytics 0, 500') + g_plot('set title "{} : durations : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "Timer Duration (MilliSeconds)"') +# override common + g_plot('set key off') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_DURATION)) + +def plot_loads(cpu_index): + """ Plot per cpu loads """ + + file_name = 'cpu{:0>3}.csv'.format(cpu_index) + if os.path.exists(file_name): + output_png = "cpu%03d_loads.png" % cpu_index + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ytics 0, 10') + g_plot('set title "{} : loads : CPU {:0>3} : {:%F %H:%M}"'.format(testname, cpu_index, datetime.now())) + g_plot('set ylabel "CPU load (percent)"') +# override common + g_plot('set key off') + set_4_plot_linestyles(g_plot) + g_plot('plot "' + file_name + '" using {:d}:{:d} with linespoints linestyle 1 axis x1y1'.format(C_ELAPSED, C_LOAD)) + +def plot_pstate_cpu_with_sample(): + """ Plot all cpu information """ + + if os.path.exists('cpu.csv'): + output_png = 'all_cpu_pstates_vs_samples.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:40]') +# override common + g_plot('set xlabel "Samples"') + g_plot('set ylabel "P-State"') + g_plot('set title "{} : cpu pstate vs. sample : {:%F %H:%M}"'.format(testname, datetime.now())) + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_SAMPLE, C_TO) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_pstate_cpu(): + """ Plot all cpu information from csv files """ + + output_png = 'all_cpu_pstates.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:40]') + g_plot('set ylabel "P-State"') + g_plot('set title "{} : cpu pstates : {:%F %H:%M}"'.format(testname, datetime.now())) + +# the following command is really cool, but doesn't work with the CPU masking option because it aborts on the first missing file. +# plot_str = 'plot for [i=0:*] file=sprintf("cpu%03d.csv",i) title_s=sprintf("cpu%03d",i) file using 16:7 pt 7 ps 1 title title_s' +# + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_TO) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_load_cpu(): + """ Plot all cpu loads """ + + output_png = 'all_cpu_loads.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ylabel "CPU load (percent)"') + g_plot('set title "{} : cpu loads : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_LOAD) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_frequency_cpu(): + """ Plot all cpu frequencies """ + + output_png = 'all_cpu_frequencies.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:4]') + g_plot('set ylabel "CPU Frequency (GHz)"') + g_plot('set title "{} : cpu frequencies : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_FREQ) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_duration_cpu(): + """ Plot all cpu durations """ + + output_png = 'all_cpu_durations.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:5000]') + g_plot('set ytics 0, 500') + g_plot('set ylabel "Timer Duration (MilliSeconds)"') + g_plot('set title "{} : cpu durations : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_DURATION) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_scaled_cpu(): + """ Plot all cpu scaled busy """ + + output_png = 'all_cpu_scaled.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "Scaled Busy (Unitless)"') + g_plot('set title "{} : cpu scaled busy : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_SCALED) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_boost_cpu(): + """ Plot all cpu IO Boosts """ + + output_png = 'all_cpu_boost.png' + g_plot = common_all_gnuplot_settings(output_png) + g_plot('set yrange [0:100]') + g_plot('set ylabel "CPU IO Boost (percent)"') + g_plot('set title "{} : cpu io boost : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_BOOST) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def plot_ghz_cpu(): + """ Plot all cpu tsc ghz """ + + output_png = 'all_cpu_ghz.png' + g_plot = common_all_gnuplot_settings(output_png) +# autoscale this one, no set y range + g_plot('set ylabel "TSC Frequency (GHz)"') + g_plot('set title "{} : cpu TSC Frequencies (Sanity check calculation) : {:%F %H:%M}"'.format(testname, datetime.now())) + + title_list = subprocess.check_output('ls cpu???.csv | sed -e \'s/.csv//\'',shell=True).replace('\n', ' ') + plot_str = "plot for [i in title_list] i.'.csv' using {:d}:{:d} pt 7 ps 1 title i".format(C_ELAPSED, C_GHZ) + g_plot('title_list = "{}"'.format(title_list)) + g_plot(plot_str) + +def common_all_gnuplot_settings(output_png): + """ common gnuplot settings for multiple CPUs one one graph. """ + + g_plot = common_gnuplot_settings() + g_plot('set output "' + output_png + '"') + return(g_plot) + +def common_gnuplot_settings(): + """ common gnuplot settings. """ + + g_plot = Gnuplot.Gnuplot(persist=1) +# The following line is for rigor only. It seems to be assumed for .csv files + g_plot('set datafile separator \",\"') + g_plot('set ytics nomirror') + g_plot('set xtics nomirror') + g_plot('set xtics font ", 10"') + g_plot('set ytics font ", 10"') + g_plot('set tics out scale 1.0') + g_plot('set grid') + g_plot('set key out horiz') + g_plot('set key bot center') + g_plot('set key samplen 2 spacing .8 font ", 9"') + g_plot('set term png size 1200, 600') + g_plot('set title font ", 11"') + g_plot('set ylabel font ", 10"') + g_plot('set xlabel font ", 10"') + g_plot('set xlabel offset 0, 0.5') + g_plot('set xlabel "Elapsed Time (Seconds)"') + return(g_plot) + +def set_4_plot_linestyles(g_plot): + """ set the linestyles used for 4 plots in 1 graphs. """ + + g_plot('set style line 1 linetype 1 linecolor rgb "green" pointtype -1') + g_plot('set style line 2 linetype 1 linecolor rgb "red" pointtype -1') + g_plot('set style line 3 linetype 1 linecolor rgb "purple" pointtype -1') + g_plot('set style line 4 linetype 1 linecolor rgb "blue" pointtype -1') + +def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz): + """ Store master csv file information """ + + global graph_data_present + + if cpu_mask[cpu_int] == 0: + return + + try: + f_handle = open('cpu.csv', 'a') + string_buffer = "CPU_%03u, %05u, %06u, %u, %u, %u, %u, %u, %u, %u, %.4f, %u, %.2f, %.3f, %u, %.3f, %.3f, %s\n" % (cpu_int, int(time_pre_dec), int(time_post_dec), int(core_busy), int(scaled), int(_from), int(_to), int(mperf), int(aperf), int(tsc), freq_ghz, int(io_boost), load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm) + f_handle.write(string_buffer); + f_handle.close() + except: + print('IO error cpu.csv') + return + + graph_data_present = True; + +def split_csv(): + """ seperate the all csv file into per CPU csv files. """ + + global current_max_cpu + + if os.path.exists('cpu.csv'): + for index in range(0, current_max_cpu + 1): + if cpu_mask[int(index)] != 0: + os.system('grep -m 1 common_cpu cpu.csv > cpu{:0>3}.csv'.format(index)) + os.system('grep CPU_{:0>3} cpu.csv >> cpu{:0>3}.csv'.format(index, index)) + +def cleanup_data_files(): + """ clean up existing data files """ + + if os.path.exists('cpu.csv'): + os.remove('cpu.csv') + f_handle = open('cpu.csv', 'a') + f_handle.write('common_cpu, common_secs, common_usecs, core_busy, scaled_busy, from, to, mperf, aperf, tsc, freq, boost, load, duration_ms, sample_num, elapsed_time, tsc_ghz, common_comm') + f_handle.write('\n') + f_handle.close() + +def clear_trace_file(): + """ Clear trace file """ + + try: + f_handle = open('/sys/kernel/debug/tracing/trace', 'w') + f_handle.close() + except: + print('IO error clearing trace file ') + quit() + +def enable_trace(): + """ Enable trace """ + + try: + open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable' + , 'w').write("1") + except: + print('IO error enabling trace ') + quit() + +def disable_trace(): + """ Disable trace """ + + try: + open('/sys/kernel/debug/tracing/events/power/pstate_sample/enable' + , 'w').write("0") + except: + print('IO error disabling trace ') + quit() + +def set_trace_buffer_size(): + """ Set trace buffer size """ + + try: + open('/sys/kernel/debug/tracing/buffer_size_kb' + , 'w').write("10240") + except: + print('IO error setting trace buffer size ') + quit() + +def read_trace_data(filename): + """ Read and parse trace data """ + + global current_max_cpu + global sample_num, last_sec_cpu, last_usec_cpu, start_time + + try: + data = open(filename, 'r').read() + except: + print('Error opening ', filename) + quit() + + for line in data.splitlines(): + search_obj = \ + re.search(r'(^(.*?)\[)((\d+)[^\]])(.*?)(\d+)([.])(\d+)(.*?core_busy=)(\d+)(.*?scaled=)(\d+)(.*?from=)(\d+)(.*?to=)(\d+)(.*?mperf=)(\d+)(.*?aperf=)(\d+)(.*?tsc=)(\d+)(.*?freq=)(\d+)' + , line) + + if search_obj: + cpu = search_obj.group(3) + cpu_int = int(cpu) + cpu = str(cpu_int) + + time_pre_dec = search_obj.group(6) + time_post_dec = search_obj.group(8) + core_busy = search_obj.group(10) + scaled = search_obj.group(12) + _from = search_obj.group(14) + _to = search_obj.group(16) + mperf = search_obj.group(18) + aperf = search_obj.group(20) + tsc = search_obj.group(22) + freq = search_obj.group(24) + common_comm = search_obj.group(2).replace(' ', '') + + # Not all kernel versions have io_boost field + io_boost = '0' + search_obj = re.search(r'.*?io_boost=(\d+)', line) + if search_obj: + io_boost = search_obj.group(1) + + if sample_num == 0 : + start_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) + sample_num += 1 + + if last_sec_cpu[cpu_int] == 0 : + last_sec_cpu[cpu_int] = time_pre_dec + last_usec_cpu[cpu_int] = time_post_dec + else : + duration_us = (int(time_pre_dec) - int(last_sec_cpu[cpu_int])) * 1000000 + (int(time_post_dec) - int(last_usec_cpu[cpu_int])) + duration_ms = Decimal(duration_us) / Decimal(1000) + last_sec_cpu[cpu_int] = time_pre_dec + last_usec_cpu[cpu_int] = time_post_dec + elapsed_time = Decimal(time_pre_dec) + Decimal(time_post_dec) / Decimal(1000000) - start_time + load = Decimal(int(mperf)*100)/ Decimal(tsc) + freq_ghz = Decimal(freq)/Decimal(1000000) +# Sanity check calculation, typically anomalies indicate missed samples +# However, check for 0 (should never occur) + tsc_ghz = Decimal(0) + if duration_ms != Decimal(0) : + tsc_ghz = Decimal(tsc)/duration_ms/Decimal(1000000) + store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _to, mperf, aperf, tsc, freq_ghz, io_boost, common_comm, load, duration_ms, sample_num, elapsed_time, tsc_ghz) + + if cpu_int > current_max_cpu: + current_max_cpu = cpu_int +# End of for each trace line loop +# Now seperate the main overall csv file into per CPU csv files. + split_csv() + +interval = "" +filename = "" +cpu_list = "" +testname = "" +graph_data_present = False; + +valid1 = False +valid2 = False + +cpu_mask = zeros((MAX_CPUS,), dtype=int) + +try: + opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:",["help","trace_file=","interval=","cpu=","name="]) +except getopt.GetoptError: + print_help() + sys.exit(2) +for opt, arg in opts: + if opt == '-h': + print() + sys.exit() + elif opt in ("-t", "--trace_file"): + valid1 = True + location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) + filename = os.path.join(location, arg) + elif opt in ("-i", "--interval"): + valid1 = True + interval = arg + elif opt in ("-c", "--cpu"): + cpu_list = arg + elif opt in ("-n", "--name"): + valid2 = True + testname = arg + +if not (valid1 and valid2): + print_help() + sys.exit() + +if cpu_list: + for p in re.split("[,]", cpu_list): + if int(p) < MAX_CPUS : + cpu_mask[int(p)] = 1 +else: + for i in range (0, MAX_CPUS): + cpu_mask[i] = 1 + +if not os.path.exists('results'): + os.mkdir('results') + +os.chdir('results') +if os.path.exists(testname): + print('The test name directory already exists. Please provide a unique test name. Test re-run not supported, yet.') + sys.exit() +os.mkdir(testname) +os.chdir(testname) + +# Temporary (or perhaps not) +cur_version = sys.version_info +print('python version (should be >= 2.7):') +print(cur_version) + +# Left as "cleanup" for potential future re-run ability. +cleanup_data_files() + +if interval: + filename = "/sys/kernel/debug/tracing/trace" + clear_trace_file() + set_trace_buffer_size() + enable_trace() + print('Sleeping for ', interval, 'seconds') + time.sleep(int(interval)) + disable_trace() + +current_max_cpu = 0 + +read_trace_data(filename) + +if graph_data_present == False: + print('No valid data to plot') + sys.exit(2) + +for cpu_no in range(0, current_max_cpu + 1): + plot_perf_busy_with_sample(cpu_no) + plot_perf_busy(cpu_no) + plot_durations(cpu_no) + plot_loads(cpu_no) + +plot_pstate_cpu_with_sample() +plot_pstate_cpu() +plot_load_cpu() +plot_frequency_cpu() +plot_duration_cpu() +plot_scaled_cpu() +plot_boost_cpu() +plot_ghz_cpu() + +os.chdir('../../') diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 8abbef164b4e..621578aa12d6 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include @@ -32,7 +32,6 @@ EXTRA_WARNINGS += -Wold-style-definition EXTRA_WARNINGS += -Wpacked EXTRA_WARNINGS += -Wredundant-decls EXTRA_WARNINGS += -Wshadow -EXTRA_WARNINGS += -Wstrict-aliasing=3 EXTRA_WARNINGS += -Wstrict-prototypes EXTRA_WARNINGS += -Wswitch-default EXTRA_WARNINGS += -Wswitch-enum @@ -40,12 +39,26 @@ EXTRA_WARNINGS += -Wundef EXTRA_WARNINGS += -Wwrite-strings EXTRA_WARNINGS += -Wformat +ifneq ($(CC), clang) +EXTRA_WARNINGS += -Wstrict-aliasing=3 +endif + ifneq ($(findstring $(MAKEFLAGS), w),w) PRINT_DIR = --no-print-directory else NO_SUBDIR = : endif +ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 +ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) + silent=1 +endif +else # make-3.8x +ifneq ($(filter s% -s%,$(MAKEFLAGS)),) + silent=1 +endif +endif + # # Define a callable command for descending to a new directory # @@ -58,7 +71,7 @@ descend = \ QUIET_SUBDIR0 = +$(MAKE) $(COMMAND_O) -C # space to separate -C and subdir QUIET_SUBDIR1 = -ifneq ($(findstring $(MAKEFLAGS),s),s) +ifneq ($(silent),1) ifneq ($(V),1) QUIET_CC = @echo ' CC '$@; QUIET_CC_FPIC = @echo ' CC FPIC '$@; diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index be93ab02b490..0c8b61f8398e 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -179,6 +179,7 @@ my $localversion; my $iteration = 0; my $successes = 0; my $stty_orig; +my $run_command_status = 0; my $bisect_good; my $bisect_bad; @@ -1325,26 +1326,44 @@ sub wait_for_monitor; sub reboot { my ($time) = @_; + my $powercycle = 0; - # Make sure everything has been written to disk - run_ssh("sync"); + # test if the machine can be connected to within 5 seconds + my $stat = run_ssh("echo check machine status", 5); + if (!$stat) { + doprint("power cycle\n"); + $powercycle = 1; + } + + if ($powercycle) { + run_command "$power_cycle"; - if (defined($time)) { start_monitor; # flush out current monitor # May contain the reboot success line wait_for_monitor 1; - } - # try to reboot normally - if (run_command $reboot) { - if (defined($powercycle_after_reboot)) { - sleep $powercycle_after_reboot; + } else { + # Make sure everything has been written to disk + run_ssh("sync"); + + if (defined($time)) { + start_monitor; + # flush out current monitor + # May contain the reboot success line + wait_for_monitor 1; + } + + # try to reboot normally + if (run_command $reboot) { + if (defined($powercycle_after_reboot)) { + sleep $powercycle_after_reboot; + run_command "$power_cycle"; + } + } else { + # nope? power cycle it. run_command "$power_cycle"; } - } else { - # nope? power cycle it. - run_command "$power_cycle"; } if (defined($time)) { @@ -1412,6 +1431,10 @@ sub dodie { system("stty $stty_orig"); } + if (defined($post_test)) { + run_command $post_test; + } + die @_, "\n"; } @@ -1624,10 +1647,6 @@ sub save_logs { sub fail { - if (defined($post_test)) { - run_command $post_test; - } - if ($die_on_failure) { dodie @_; } @@ -1660,23 +1679,26 @@ sub fail { save_logs "fail", $store_failures; } + if (defined($post_test)) { + run_command $post_test; + } + return 1; } sub run_command { - my ($command, $redirect) = @_; + my ($command, $redirect, $timeout) = @_; my $start_time; my $end_time; my $dolog = 0; my $dord = 0; my $pid; - $start_time = time; - $command =~ s/\$SSH_USER/$ssh_user/g; $command =~ s/\$MACHINE/$machine/g; doprint("$command ... "); + $start_time = time; $pid = open(CMD, "$command 2>&1 |") or (fail "unable to exec $command" and return 0); @@ -1693,13 +1715,30 @@ sub run_command { $dord = 1; } - while (<CMD>) { - print LOG if ($dolog); - print RD if ($dord); + my $hit_timeout = 0; + + while (1) { + my $fp = \*CMD; + if (defined($timeout)) { + doprint "timeout = $timeout\n"; + } + my $line = wait_for_input($fp, $timeout); + if (!defined($line)) { + my $now = time; + if (defined($timeout) && (($now - $start_time) >= $timeout)) { + doprint "Hit timeout of $timeout, killing process\n"; + $hit_timeout = 1; + kill 9, $pid; + } + last; + } + print LOG $line if ($dolog); + print RD $line if ($dord); } waitpid($pid, 0); - my $failed = $?; + # shift 8 for real exit status + $run_command_status = $? >> 8; close(CMD); close(LOG) if ($dolog); @@ -1714,21 +1753,25 @@ sub run_command { doprint "[$delta seconds] "; } - if ($failed) { + if ($hit_timeout) { + $run_command_status = 1; + } + + if ($run_command_status) { doprint "FAILED!\n"; } else { doprint "SUCCESS\n"; } - return !$failed; + return !$run_command_status; } sub run_ssh { - my ($cmd) = @_; + my ($cmd, $timeout) = @_; my $cp_exec = $ssh_exec; $cp_exec =~ s/\$SSH_COMMAND/$cmd/g; - return run_command "$cp_exec"; + return run_command "$cp_exec", undef , $timeout; } sub run_scp { @@ -1837,6 +1880,7 @@ sub get_grub_index { sub wait_for_input { my ($fp, $time) = @_; + my $start_time; my $rin; my $rout; my $nr; @@ -1852,17 +1896,22 @@ sub wait_for_input vec($rin, fileno($fp), 1) = 1; vec($rin, fileno(\*STDIN), 1) = 1; + $start_time = time; + while (1) { $nr = select($rout=$rin, undef, undef, $time); - if ($nr <= 0) { - return undef; - } + last if ($nr <= 0); # copy data from stdin to the console if (vec($rout, fileno(\*STDIN), 1) == 1) { - sysread(\*STDIN, $buf, 1000); - syswrite($fp, $buf, 1000); + $nr = sysread(\*STDIN, $buf, 1000); + syswrite($fp, $buf, $nr) if ($nr > 0); + } + + # The timeout is based on time waiting for the fp data + if (vec($rout, fileno($fp), 1) != 1) { + last if (defined($time) && (time - $start_time > $time)); next; } @@ -1874,12 +1923,11 @@ sub wait_for_input last if ($ch eq "\n"); } - if (!length($line)) { - return undef; - } + last if (!length($line)); return $line; } + return undef; } sub reboot_to { @@ -2489,10 +2537,6 @@ sub halt { sub success { my ($i) = @_; - if (defined($post_test)) { - run_command $post_test; - } - $successes++; my $name = ""; @@ -2517,6 +2561,10 @@ sub success { doprint "Reboot and wait $sleep_time seconds\n"; reboot_to_good $sleep_time; } + + if (defined($post_test)) { + run_command $post_test; + } } sub answer_bisect { @@ -2537,16 +2585,15 @@ sub answer_bisect { } sub child_run_test { - my $failed = 0; # child should have no power $reboot_on_error = 0; $poweroff_on_error = 0; $die_on_failure = 1; - run_command $run_test, $testlog or $failed = 1; + run_command $run_test, $testlog; - exit $failed; + exit $run_command_status; } my $child_done; @@ -2629,7 +2676,7 @@ sub do_run_test { } waitpid $child_pid, 0; - $child_exit = $?; + $child_exit = $? >> 8; my $end_time = time; $test_time = $end_time - $start_time; @@ -3330,7 +3377,6 @@ sub config_bisect { save_config \%good_configs, $good_config; save_config \%bad_configs, $bad_config; - if (defined($config_bisect_check) && $config_bisect_check ne "0") { if ($config_bisect_check ne "good") { doprint "Testing bad config\n"; diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 45be8b55a663..798f17655433 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -887,7 +887,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 0+1; memdev->region_index = 4+1; memdev->region_size = SPA0_SIZE/2; - memdev->region_offset = t->spa_set_dma[0]; + memdev->region_offset = 1; memdev->address = 0; memdev->interleave_index = 0; memdev->interleave_ways = 2; @@ -902,7 +902,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 0+1; memdev->region_index = 5+1; memdev->region_size = SPA0_SIZE/2; - memdev->region_offset = t->spa_set_dma[0] + SPA0_SIZE/2; + memdev->region_offset = (1 << 8); memdev->address = 0; memdev->interleave_index = 0; memdev->interleave_ways = 2; @@ -917,7 +917,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 1+1; memdev->region_index = 4+1; memdev->region_size = SPA1_SIZE/4; - memdev->region_offset = t->spa_set_dma[1]; + memdev->region_offset = (1 << 16); memdev->address = SPA0_SIZE/2; memdev->interleave_index = 0; memdev->interleave_ways = 4; @@ -932,7 +932,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 1+1; memdev->region_index = 5+1; memdev->region_size = SPA1_SIZE/4; - memdev->region_offset = t->spa_set_dma[1] + SPA1_SIZE/4; + memdev->region_offset = (1 << 24); memdev->address = SPA0_SIZE/2; memdev->interleave_index = 0; memdev->interleave_ways = 4; @@ -947,7 +947,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 1+1; memdev->region_index = 6+1; memdev->region_size = SPA1_SIZE/4; - memdev->region_offset = t->spa_set_dma[1] + 2*SPA1_SIZE/4; + memdev->region_offset = (1ULL << 32); memdev->address = SPA0_SIZE/2; memdev->interleave_index = 0; memdev->interleave_ways = 4; @@ -962,7 +962,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 1+1; memdev->region_index = 7+1; memdev->region_size = SPA1_SIZE/4; - memdev->region_offset = t->spa_set_dma[1] + 3*SPA1_SIZE/4; + memdev->region_offset = (1ULL << 40); memdev->address = SPA0_SIZE/2; memdev->interleave_index = 0; memdev->interleave_ways = 4; @@ -1380,7 +1380,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->range_index = 11+1; memdev->region_index = 9+1; memdev->region_size = SPA0_SIZE; - memdev->region_offset = t->spa_set_dma[2]; + memdev->region_offset = (1ULL << 48); memdev->address = 0; memdev->interleave_index = 0; memdev->interleave_ways = 1; diff --git a/tools/testing/radix-tree/.gitignore b/tools/testing/radix-tree/.gitignore index 11d888ca6a92..d4706c0ffceb 100644 --- a/tools/testing/radix-tree/.gitignore +++ b/tools/testing/radix-tree/.gitignore @@ -1,2 +1,6 @@ +generated/map-shift.h +idr.c +idr-test main +multiorder radix-tree.c diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index 3635e4d3eca7..6a9480c03cbd 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile @@ -1,29 +1,50 @@ -CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE -LDFLAGS += -lpthread -lurcu -TARGETS = main -OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ - regression1.o regression2.o regression3.o multiorder.o \ - iteration_check.o benchmark.o - -ifdef BENCHMARK - CFLAGS += -DBENCHMARK=1 +CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE -fsanitize=address +LDFLAGS += -fsanitize=address +LDLIBS+= -lpthread -lurcu +TARGETS = main idr-test multiorder +CORE_OFILES := radix-tree.o idr.o linux.o test.o find_bit.o +OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \ + tag_check.o multiorder.o idr-test.o iteration_check.o benchmark.o + +ifndef SHIFT + SHIFT=3 endif -targets: $(TARGETS) +ifeq ($(BUILD), 32) + CFLAGS += -m32 + LDFLAGS += -m32 +endif + +targets: mapshift $(TARGETS) main: $(OFILES) - $(CC) $(CFLAGS) $(LDFLAGS) $(OFILES) -o main + +idr-test: idr-test.o $(CORE_OFILES) + +multiorder: multiorder.o $(CORE_OFILES) clean: - $(RM) -f $(TARGETS) *.o radix-tree.c + $(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h -find_next_bit.o: ../../lib/find_bit.c - $(CC) $(CFLAGS) -c -o $@ $< +vpath %.c ../../lib -$(OFILES): *.h */*.h \ +$(OFILES): Makefile *.h */*.h generated/map-shift.h \ ../../include/linux/*.h \ - ../../../include/linux/radix-tree.h + ../../include/asm/*.h \ + ../../../include/linux/radix-tree.h \ + ../../../include/linux/idr.h radix-tree.c: ../../../lib/radix-tree.c sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@ + +idr.c: ../../../lib/idr.c + sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@ + +.PHONY: mapshift + +mapshift: + @if ! grep -qws $(SHIFT) generated/map-shift.h; then \ + echo "#define RADIX_TREE_MAP_SHIFT $(SHIFT)" > \ + generated/map-shift.h; \ + fi diff --git a/tools/testing/radix-tree/benchmark.c b/tools/testing/radix-tree/benchmark.c index 215ca86c7605..99c40f3ed133 100644 --- a/tools/testing/radix-tree/benchmark.c +++ b/tools/testing/radix-tree/benchmark.c @@ -17,6 +17,9 @@ #include <time.h> #include "test.h" +#define for_each_index(i, base, order) \ + for (i = base; i < base + (1 << order); i++) + #define NSEC_PER_SEC 1000000000L static long long benchmark_iter(struct radix_tree_root *root, bool tagged) @@ -57,27 +60,176 @@ again: return nsec; } +static void benchmark_insert(struct radix_tree_root *root, + unsigned long size, unsigned long step, int order) +{ + struct timespec start, finish; + unsigned long index; + long long nsec; + + clock_gettime(CLOCK_MONOTONIC, &start); + + for (index = 0 ; index < size ; index += step) + item_insert_order(root, index, order); + + clock_gettime(CLOCK_MONOTONIC, &finish); + + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + + printv(2, "Size: %8ld, step: %8ld, order: %d, insertion: %15lld ns\n", + size, step, order, nsec); +} + +static void benchmark_tagging(struct radix_tree_root *root, + unsigned long size, unsigned long step, int order) +{ + struct timespec start, finish; + unsigned long index; + long long nsec; + + clock_gettime(CLOCK_MONOTONIC, &start); + + for (index = 0 ; index < size ; index += step) + radix_tree_tag_set(root, index, 0); + + clock_gettime(CLOCK_MONOTONIC, &finish); + + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + + printv(2, "Size: %8ld, step: %8ld, order: %d, tagging: %17lld ns\n", + size, step, order, nsec); +} + +static void benchmark_delete(struct radix_tree_root *root, + unsigned long size, unsigned long step, int order) +{ + struct timespec start, finish; + unsigned long index, i; + long long nsec; + + clock_gettime(CLOCK_MONOTONIC, &start); + + for (index = 0 ; index < size ; index += step) + for_each_index(i, index, order) + item_delete(root, i); + + clock_gettime(CLOCK_MONOTONIC, &finish); + + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + + printv(2, "Size: %8ld, step: %8ld, order: %d, deletion: %16lld ns\n", + size, step, order, nsec); +} + static void benchmark_size(unsigned long size, unsigned long step, int order) { RADIX_TREE(tree, GFP_KERNEL); long long normal, tagged; - unsigned long index; - for (index = 0 ; index < size ; index += step) { - item_insert_order(&tree, index, order); - radix_tree_tag_set(&tree, index, 0); - } + benchmark_insert(&tree, size, step, order); + benchmark_tagging(&tree, size, step, order); tagged = benchmark_iter(&tree, true); normal = benchmark_iter(&tree, false); - printf("Size %ld, step %6ld, order %d tagged %10lld ns, normal %10lld ns\n", - size, step, order, tagged, normal); + printv(2, "Size: %8ld, step: %8ld, order: %d, tagged iteration: %8lld ns\n", + size, step, order, tagged); + printv(2, "Size: %8ld, step: %8ld, order: %d, normal iteration: %8lld ns\n", + size, step, order, normal); + + benchmark_delete(&tree, size, step, order); item_kill_tree(&tree); rcu_barrier(); } +static long long __benchmark_split(unsigned long index, + int old_order, int new_order) +{ + struct timespec start, finish; + long long nsec; + RADIX_TREE(tree, GFP_ATOMIC); + + item_insert_order(&tree, index, old_order); + + clock_gettime(CLOCK_MONOTONIC, &start); + radix_tree_split(&tree, index, new_order); + clock_gettime(CLOCK_MONOTONIC, &finish); + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + + item_kill_tree(&tree); + + return nsec; + +} + +static void benchmark_split(unsigned long size, unsigned long step) +{ + int i, j, idx; + long long nsec = 0; + + + for (idx = 0; idx < size; idx += step) { + for (i = 3; i < 11; i++) { + for (j = 0; j < i; j++) { + nsec += __benchmark_split(idx, i, j); + } + } + } + + printv(2, "Size %8ld, step %8ld, split time %10lld ns\n", + size, step, nsec); + +} + +static long long __benchmark_join(unsigned long index, + unsigned order1, unsigned order2) +{ + unsigned long loc; + struct timespec start, finish; + long long nsec; + void *item, *item2 = item_create(index + 1, order1); + RADIX_TREE(tree, GFP_KERNEL); + + item_insert_order(&tree, index, order2); + item = radix_tree_lookup(&tree, index); + + clock_gettime(CLOCK_MONOTONIC, &start); + radix_tree_join(&tree, index + 1, order1, item2); + clock_gettime(CLOCK_MONOTONIC, &finish); + nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC + + (finish.tv_nsec - start.tv_nsec); + + loc = find_item(&tree, item); + if (loc == -1) + free(item); + + item_kill_tree(&tree); + + return nsec; +} + +static void benchmark_join(unsigned long step) +{ + int i, j, idx; + long long nsec = 0; + + for (idx = 0; idx < 1 << 10; idx += step) { + for (i = 1; i < 15; i++) { + for (j = 0; j < i; j++) { + nsec += __benchmark_join(idx, i, j); + } + } + } + + printv(2, "Size %8d, step %8ld, join time %10lld ns\n", + 1 << 10, step, nsec); +} + void benchmark(void) { unsigned long size[] = {1 << 10, 1 << 20, 0}; @@ -85,8 +237,8 @@ void benchmark(void) 128, 256, 512, 12345, 0}; int c, s; - printf("starting benchmarks\n"); - printf("RADIX_TREE_MAP_SHIFT = %d\n", RADIX_TREE_MAP_SHIFT); + printv(1, "starting benchmarks\n"); + printv(1, "RADIX_TREE_MAP_SHIFT = %d\n", RADIX_TREE_MAP_SHIFT); for (c = 0; size[c]; c++) for (s = 0; step[s]; s++) @@ -95,4 +247,11 @@ void benchmark(void) for (c = 0; size[c]; c++) for (s = 0; step[s]; s++) benchmark_size(size[c], step[s] << 9, 9); + + for (c = 0; size[c]; c++) + for (s = 0; step[s]; s++) + benchmark_split(size[c], step[s]); + + for (s = 0; step[s]; s++) + benchmark_join(step[s]); } diff --git a/tools/testing/radix-tree/generated/autoconf.h b/tools/testing/radix-tree/generated/autoconf.h index ad18cf5a2a3a..cf88dc5b8832 100644 --- a/tools/testing/radix-tree/generated/autoconf.h +++ b/tools/testing/radix-tree/generated/autoconf.h @@ -1,3 +1 @@ #define CONFIG_RADIX_TREE_MULTIORDER 1 -#define CONFIG_SHMEM 1 -#define CONFIG_SWAP 1 diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c new file mode 100644 index 000000000000..30cd0b296f1a --- /dev/null +++ b/tools/testing/radix-tree/idr-test.c @@ -0,0 +1,516 @@ +/* + * idr-test.c: Test the IDR API + * Copyright (c) 2016 Matthew Wilcox <willy@infradead.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include <linux/bitmap.h> +#include <linux/idr.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/errno.h> + +#include "test.h" + +#define DUMMY_PTR ((void *)0x12) + +int item_idr_free(int id, void *p, void *data) +{ + struct item *item = p; + assert(item->index == id); + free(p); + + return 0; +} + +void item_idr_remove(struct idr *idr, int id) +{ + struct item *item = idr_find(idr, id); + assert(item->index == id); + idr_remove(idr, id); + free(item); +} + +void idr_alloc_test(void) +{ + unsigned long i; + DEFINE_IDR(idr); + + assert(idr_alloc_cyclic(&idr, DUMMY_PTR, 0, 0x4000, GFP_KERNEL) == 0); + assert(idr_alloc_cyclic(&idr, DUMMY_PTR, 0x3ffd, 0x4000, GFP_KERNEL) == 0x3ffd); + idr_remove(&idr, 0x3ffd); + idr_remove(&idr, 0); + + for (i = 0x3ffe; i < 0x4003; i++) { + int id; + struct item *item; + + if (i < 0x4000) + item = item_create(i, 0); + else + item = item_create(i - 0x3fff, 0); + + id = idr_alloc_cyclic(&idr, item, 1, 0x4000, GFP_KERNEL); + assert(id == item->index); + } + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); +} + +void idr_replace_test(void) +{ + DEFINE_IDR(idr); + + idr_alloc(&idr, (void *)-1, 10, 11, GFP_KERNEL); + idr_replace(&idr, &idr, 10); + + idr_destroy(&idr); +} + +/* + * Unlike the radix tree, you can put a NULL pointer -- with care -- into + * the IDR. Some interfaces, like idr_find() do not distinguish between + * "present, value is NULL" and "not present", but that's exactly what some + * users want. + */ +void idr_null_test(void) +{ + int i; + DEFINE_IDR(idr); + + assert(idr_is_empty(&idr)); + + assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0); + assert(!idr_is_empty(&idr)); + idr_remove(&idr, 0); + assert(idr_is_empty(&idr)); + + assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0); + assert(!idr_is_empty(&idr)); + idr_destroy(&idr); + assert(idr_is_empty(&idr)); + + for (i = 0; i < 10; i++) { + assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == i); + } + + assert(idr_replace(&idr, DUMMY_PTR, 3) == NULL); + assert(idr_replace(&idr, DUMMY_PTR, 4) == NULL); + assert(idr_replace(&idr, NULL, 4) == DUMMY_PTR); + assert(idr_replace(&idr, DUMMY_PTR, 11) == ERR_PTR(-ENOENT)); + idr_remove(&idr, 5); + assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 5); + idr_remove(&idr, 5); + + for (i = 0; i < 9; i++) { + idr_remove(&idr, i); + assert(!idr_is_empty(&idr)); + } + idr_remove(&idr, 8); + assert(!idr_is_empty(&idr)); + idr_remove(&idr, 9); + assert(idr_is_empty(&idr)); + + assert(idr_alloc(&idr, NULL, 0, 0, GFP_KERNEL) == 0); + assert(idr_replace(&idr, DUMMY_PTR, 3) == ERR_PTR(-ENOENT)); + assert(idr_replace(&idr, DUMMY_PTR, 0) == NULL); + assert(idr_replace(&idr, NULL, 0) == DUMMY_PTR); + + idr_destroy(&idr); + assert(idr_is_empty(&idr)); + + for (i = 1; i < 10; i++) { + assert(idr_alloc(&idr, NULL, 1, 0, GFP_KERNEL) == i); + } + + idr_destroy(&idr); + assert(idr_is_empty(&idr)); +} + +void idr_nowait_test(void) +{ + unsigned int i; + DEFINE_IDR(idr); + + idr_preload(GFP_KERNEL); + + for (i = 0; i < 3; i++) { + struct item *item = item_create(i, 0); + assert(idr_alloc(&idr, item, i, i + 1, GFP_NOWAIT) == i); + } + + idr_preload_end(); + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); +} + +void idr_get_next_test(void) +{ + unsigned long i; + int nextid; + DEFINE_IDR(idr); + + int indices[] = {4, 7, 9, 15, 65, 128, 1000, 99999, 0}; + + for(i = 0; indices[i]; i++) { + struct item *item = item_create(indices[i], 0); + assert(idr_alloc(&idr, item, indices[i], indices[i+1], + GFP_KERNEL) == indices[i]); + } + + for(i = 0, nextid = 0; indices[i]; i++) { + idr_get_next(&idr, &nextid); + assert(nextid == indices[i]); + nextid++; + } + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); +} + +void idr_checks(void) +{ + unsigned long i; + DEFINE_IDR(idr); + + for (i = 0; i < 10000; i++) { + struct item *item = item_create(i, 0); + assert(idr_alloc(&idr, item, 0, 20000, GFP_KERNEL) == i); + } + + assert(idr_alloc(&idr, DUMMY_PTR, 5, 30, GFP_KERNEL) < 0); + + for (i = 0; i < 5000; i++) + item_idr_remove(&idr, i); + + idr_remove(&idr, 3); + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); + + assert(idr_is_empty(&idr)); + + idr_remove(&idr, 3); + idr_remove(&idr, 0); + + for (i = INT_MAX - 3UL; i < INT_MAX + 1UL; i++) { + struct item *item = item_create(i, 0); + assert(idr_alloc(&idr, item, i, i + 10, GFP_KERNEL) == i); + } + assert(idr_alloc(&idr, DUMMY_PTR, i - 2, i, GFP_KERNEL) == -ENOSPC); + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); + idr_destroy(&idr); + + assert(idr_is_empty(&idr)); + + for (i = 1; i < 10000; i++) { + struct item *item = item_create(i, 0); + assert(idr_alloc(&idr, item, 1, 20000, GFP_KERNEL) == i); + } + + idr_for_each(&idr, item_idr_free, &idr); + idr_destroy(&idr); + + idr_replace_test(); + idr_alloc_test(); + idr_null_test(); + idr_nowait_test(); + idr_get_next_test(); +} + +/* + * Check that we get the correct error when we run out of memory doing + * allocations. To ensure we run out of memory, just "forget" to preload. + * The first test is for not having a bitmap available, and the second test + * is for not being able to allocate a level of the radix tree. + */ +void ida_check_nomem(void) +{ + DEFINE_IDA(ida); + int id, err; + + err = ida_get_new_above(&ida, 256, &id); + assert(err == -EAGAIN); + err = ida_get_new_above(&ida, 1UL << 30, &id); + assert(err == -EAGAIN); +} + +/* + * Check what happens when we fill a leaf and then delete it. This may + * discover mishandling of IDR_FREE. + */ +void ida_check_leaf(void) +{ + DEFINE_IDA(ida); + int id; + unsigned long i; + + for (i = 0; i < IDA_BITMAP_BITS; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + assert(id == i); + } + + ida_destroy(&ida); + assert(ida_is_empty(&ida)); + + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + assert(id == 0); + ida_destroy(&ida); + assert(ida_is_empty(&ida)); +} + +/* + * Check handling of conversions between exceptional entries and full bitmaps. + */ +void ida_check_conv(void) +{ + DEFINE_IDA(ida); + int id; + unsigned long i; + + for (i = 0; i < IDA_BITMAP_BITS * 2; i += IDA_BITMAP_BITS) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, i + 1, &id)); + assert(id == i + 1); + assert(!ida_get_new_above(&ida, i + BITS_PER_LONG, &id)); + assert(id == i + BITS_PER_LONG); + ida_remove(&ida, i + 1); + ida_remove(&ida, i + BITS_PER_LONG); + assert(ida_is_empty(&ida)); + } + + assert(ida_pre_get(&ida, GFP_KERNEL)); + + for (i = 0; i < IDA_BITMAP_BITS * 2; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + assert(id == i); + } + + for (i = IDA_BITMAP_BITS * 2; i > 0; i--) { + ida_remove(&ida, i - 1); + } + assert(ida_is_empty(&ida)); + + for (i = 0; i < IDA_BITMAP_BITS + BITS_PER_LONG - 4; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + assert(id == i); + } + + for (i = IDA_BITMAP_BITS + BITS_PER_LONG - 4; i > 0; i--) { + ida_remove(&ida, i - 1); + } + assert(ida_is_empty(&ida)); + + radix_tree_cpu_dead(1); + for (i = 0; i < 1000000; i++) { + int err = ida_get_new(&ida, &id); + if (err == -EAGAIN) { + assert((i % IDA_BITMAP_BITS) == (BITS_PER_LONG - 2)); + assert(ida_pre_get(&ida, GFP_KERNEL)); + err = ida_get_new(&ida, &id); + } else { + assert((i % IDA_BITMAP_BITS) != (BITS_PER_LONG - 2)); + } + assert(!err); + assert(id == i); + } + ida_destroy(&ida); +} + +/* + * Check allocations up to and slightly above the maximum allowed (2^31-1) ID. + * Allocating up to 2^31-1 should succeed, and then allocating the next one + * should fail. + */ +void ida_check_max(void) +{ + DEFINE_IDA(ida); + int id, err; + unsigned long i, j; + + for (j = 1; j < 65537; j *= 2) { + unsigned long base = (1UL << 31) - j; + for (i = 0; i < j; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, base, &id)); + assert(id == base + i); + } + assert(ida_pre_get(&ida, GFP_KERNEL)); + err = ida_get_new_above(&ida, base, &id); + assert(err == -ENOSPC); + ida_destroy(&ida); + assert(ida_is_empty(&ida)); + rcu_barrier(); + } +} + +void ida_check_random(void) +{ + DEFINE_IDA(ida); + DECLARE_BITMAP(bitmap, 2048); + int id, err; + unsigned int i; + time_t s = time(NULL); + + repeat: + memset(bitmap, 0, sizeof(bitmap)); + for (i = 0; i < 100000; i++) { + int i = rand(); + int bit = i & 2047; + if (test_bit(bit, bitmap)) { + __clear_bit(bit, bitmap); + ida_remove(&ida, bit); + } else { + __set_bit(bit, bitmap); + do { + ida_pre_get(&ida, GFP_KERNEL); + err = ida_get_new_above(&ida, bit, &id); + } while (err == -ENOMEM); + assert(!err); + assert(id == bit); + } + } + ida_destroy(&ida); + if (time(NULL) < s + 10) + goto repeat; +} + +void ida_simple_get_remove_test(void) +{ + DEFINE_IDA(ida); + unsigned long i; + + for (i = 0; i < 10000; i++) { + assert(ida_simple_get(&ida, 0, 20000, GFP_KERNEL) == i); + } + assert(ida_simple_get(&ida, 5, 30, GFP_KERNEL) < 0); + + for (i = 0; i < 10000; i++) { + ida_simple_remove(&ida, i); + } + assert(ida_is_empty(&ida)); + + ida_destroy(&ida); +} + +void ida_checks(void) +{ + DEFINE_IDA(ida); + int id; + unsigned long i; + + radix_tree_cpu_dead(1); + ida_check_nomem(); + + for (i = 0; i < 10000; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + assert(id == i); + } + + ida_remove(&ida, 20); + ida_remove(&ida, 21); + for (i = 0; i < 3; i++) { + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new(&ida, &id)); + if (i == 2) + assert(id == 10000); + } + + for (i = 0; i < 5000; i++) + ida_remove(&ida, i); + + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 5000, &id)); + assert(id == 10001); + + ida_destroy(&ida); + + assert(ida_is_empty(&ida)); + + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 1, &id)); + assert(id == 1); + + ida_remove(&ida, id); + assert(ida_is_empty(&ida)); + ida_destroy(&ida); + assert(ida_is_empty(&ida)); + + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 1, &id)); + ida_destroy(&ida); + assert(ida_is_empty(&ida)); + + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 1, &id)); + assert(id == 1); + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 1025, &id)); + assert(id == 1025); + assert(ida_pre_get(&ida, GFP_KERNEL)); + assert(!ida_get_new_above(&ida, 10000, &id)); + assert(id == 10000); + ida_remove(&ida, 1025); + ida_destroy(&ida); + assert(ida_is_empty(&ida)); + + ida_check_leaf(); + ida_check_max(); + ida_check_conv(); + ida_check_random(); + ida_simple_get_remove_test(); + + radix_tree_cpu_dead(1); +} + +static void *ida_random_fn(void *arg) +{ + rcu_register_thread(); + ida_check_random(); + rcu_unregister_thread(); + return NULL; +} + +void ida_thread_tests(void) +{ + pthread_t threads[10]; + int i; + + for (i = 0; i < ARRAY_SIZE(threads); i++) + if (pthread_create(&threads[i], NULL, ida_random_fn, NULL)) { + perror("creating ida thread"); + exit(1); + } + + while (i--) + pthread_join(threads[i], NULL); +} + +int __weak main(void) +{ + radix_tree_init(); + idr_checks(); + ida_checks(); + ida_thread_tests(); + radix_tree_cpu_dead(1); + rcu_barrier(); + if (nr_allocated) + printf("nr_allocated = %d\n", nr_allocated); + return 0; +} diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c index 7572b7ed930e..a92bab513701 100644 --- a/tools/testing/radix-tree/iteration_check.c +++ b/tools/testing/radix-tree/iteration_check.c @@ -177,7 +177,7 @@ void iteration_test(unsigned order, unsigned test_duration) { int i; - printf("Running %siteration tests for %d seconds\n", + printv(1, "Running %siteration tests for %d seconds\n", order > 0 ? "multiorder " : "", test_duration); max_order = order; diff --git a/tools/testing/radix-tree/linux.c b/tools/testing/radix-tree/linux.c index d31ea7c9abec..cf48c8473f48 100644 --- a/tools/testing/radix-tree/linux.c +++ b/tools/testing/radix-tree/linux.c @@ -5,7 +5,7 @@ #include <unistd.h> #include <assert.h> -#include <linux/mempool.h> +#include <linux/gfp.h> #include <linux/poison.h> #include <linux/slab.h> #include <linux/radix-tree.h> @@ -13,6 +13,8 @@ int nr_allocated; int preempt_count; +int kmalloc_verbose; +int test_verbose; struct kmem_cache { pthread_mutex_t lock; @@ -22,27 +24,6 @@ struct kmem_cache { void (*ctor)(void *); }; -void *mempool_alloc(mempool_t *pool, int gfp_mask) -{ - return pool->alloc(gfp_mask, pool->data); -} - -void mempool_free(void *element, mempool_t *pool) -{ - pool->free(element, pool->data); -} - -mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data) -{ - mempool_t *ret = malloc(sizeof(*ret)); - - ret->alloc = alloc_fn; - ret->free = free_fn; - ret->data = pool_data; - return ret; -} - void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) { struct radix_tree_node *node; @@ -54,9 +35,9 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) if (cachep->nr_objs) { cachep->nr_objs--; node = cachep->objs; - cachep->objs = node->private_data; + cachep->objs = node->parent; pthread_mutex_unlock(&cachep->lock); - node->private_data = NULL; + node->parent = NULL; } else { pthread_mutex_unlock(&cachep->lock); node = malloc(cachep->size); @@ -65,6 +46,8 @@ void *kmem_cache_alloc(struct kmem_cache *cachep, int flags) } uatomic_inc(&nr_allocated); + if (kmalloc_verbose) + printf("Allocating %p from slab\n", node); return node; } @@ -72,6 +55,8 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) { assert(objp); uatomic_dec(&nr_allocated); + if (kmalloc_verbose) + printf("Freeing %p to slab\n", objp); pthread_mutex_lock(&cachep->lock); if (cachep->nr_objs > 10) { memset(objp, POISON_FREE, cachep->size); @@ -79,7 +64,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) } else { struct radix_tree_node *node = objp; cachep->nr_objs++; - node->private_data = cachep->objs; + node->parent = cachep->objs; cachep->objs = node; } pthread_mutex_unlock(&cachep->lock); @@ -89,6 +74,8 @@ void *kmalloc(size_t size, gfp_t gfp) { void *ret = malloc(size); uatomic_inc(&nr_allocated); + if (kmalloc_verbose) + printf("Allocating %p from malloc\n", ret); return ret; } @@ -97,6 +84,8 @@ void kfree(void *p) if (!p) return; uatomic_dec(&nr_allocated); + if (kmalloc_verbose) + printf("Freeing %p to malloc\n", p); free(p); } diff --git a/tools/testing/radix-tree/linux/bitops.h b/tools/testing/radix-tree/linux/bitops.h deleted file mode 100644 index a13e9bc76eec..000000000000 --- a/tools/testing/radix-tree/linux/bitops.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ -#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ - -#include <linux/types.h> -#include <linux/bitops/find.h> -#include <linux/bitops/hweight.h> -#include <linux/kernel.h> - -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITS_PER_BYTE 8 -#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) - -/** - * __set_bit - Set a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike set_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p |= mask; -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p &= ~mask; -} - -/** - * __change_bit - Toggle a bit in memory - * @nr: the bit to change - * @addr: the address to start counting from - * - * Unlike change_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - - *p ^= mask; -} - -/** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - unsigned long old = *p; - - *p = old | mask; - return (old & mask) != 0; -} - -/** - * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to clear - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - unsigned long old = *p; - - *p = old & ~mask; - return (old & mask) != 0; -} - -/* WARNING: non atomic and it can be reordered! */ -static inline int __test_and_change_bit(int nr, - volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - unsigned long old = *p; - - *p = old ^ mask; - return (old & mask) != 0; -} - -/** - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - */ -static inline int test_bit(int nr, const volatile unsigned long *addr) -{ - return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); -} - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static inline unsigned long __ffs(unsigned long word) -{ - int num = 0; - - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; -} - -unsigned long find_next_bit(const unsigned long *addr, - unsigned long size, - unsigned long offset); - -static inline unsigned long hweight_long(unsigned long w) -{ - return sizeof(w) == 4 ? hweight32(w) : hweight64(w); -} - -#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/__ffs.h b/tools/testing/radix-tree/linux/bitops/__ffs.h deleted file mode 100644 index 9a3274aecf83..000000000000 --- a/tools/testing/radix-tree/linux/bitops/__ffs.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS___FFS_H_ -#define _ASM_GENERIC_BITOPS___FFS_H_ - -#include <asm/types.h> - -/** - * __ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static inline unsigned long __ffs(unsigned long word) -{ - int num = 0; - -#if BITS_PER_LONG == 64 - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } -#endif - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) - num += 1; - return num; -} - -#endif /* _ASM_GENERIC_BITOPS___FFS_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/ffs.h b/tools/testing/radix-tree/linux/bitops/ffs.h deleted file mode 100644 index fbbb43af7dc0..000000000000 --- a/tools/testing/radix-tree/linux/bitops/ffs.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_FFS_H_ -#define _ASM_GENERIC_BITOPS_FFS_H_ - -/** - * ffs - find first bit set - * @x: the word to search - * - * This is defined the same way as - * the libc and compiler builtin ffs routines, therefore - * differs in spirit from the above ffz (man ffs). - */ -static inline int ffs(int x) -{ - int r = 1; - - if (!x) - return 0; - if (!(x & 0xffff)) { - x >>= 16; - r += 16; - } - if (!(x & 0xff)) { - x >>= 8; - r += 8; - } - if (!(x & 0xf)) { - x >>= 4; - r += 4; - } - if (!(x & 3)) { - x >>= 2; - r += 2; - } - if (!(x & 1)) { - x >>= 1; - r += 1; - } - return r; -} - -#endif /* _ASM_GENERIC_BITOPS_FFS_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/ffz.h b/tools/testing/radix-tree/linux/bitops/ffz.h deleted file mode 100644 index 6744bd4cdf46..000000000000 --- a/tools/testing/radix-tree/linux/bitops/ffz.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_FFZ_H_ -#define _ASM_GENERIC_BITOPS_FFZ_H_ - -/* - * ffz - find first zero in word. - * @word: The word to search - * - * Undefined if no zero exists, so code should check against ~0UL first. - */ -#define ffz(x) __ffs(~(x)) - -#endif /* _ASM_GENERIC_BITOPS_FFZ_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/find.h b/tools/testing/radix-tree/linux/bitops/find.h deleted file mode 100644 index 72a51e5a12ef..000000000000 --- a/tools/testing/radix-tree/linux/bitops/find.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_FIND_H_ -#define _ASM_GENERIC_BITOPS_FIND_H_ - -extern unsigned long find_next_bit(const unsigned long *addr, unsigned long - size, unsigned long offset); - -extern unsigned long find_next_zero_bit(const unsigned long *addr, unsigned - long size, unsigned long offset); - -#define find_first_bit(addr, size) find_next_bit((addr), (size), 0) -#define find_first_zero_bit(addr, size) find_next_zero_bit((addr), (size), 0) - -#endif /*_ASM_GENERIC_BITOPS_FIND_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/fls.h b/tools/testing/radix-tree/linux/bitops/fls.h deleted file mode 100644 index 850859bc5069..000000000000 --- a/tools/testing/radix-tree/linux/bitops/fls.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_FLS_H_ -#define _ASM_GENERIC_BITOPS_FLS_H_ - -/** - * fls - find last (most-significant) bit set - * @x: the word to search - * - * This is defined the same way as ffs. - * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. - */ - -static inline int fls(int x) -{ - int r = 32; - - if (!x) - return 0; - if (!(x & 0xffff0000u)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xff000000u)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xf0000000u)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xc0000000u)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000u)) { - x <<= 1; - r -= 1; - } - return r; -} - -#endif /* _ASM_GENERIC_BITOPS_FLS_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/fls64.h b/tools/testing/radix-tree/linux/bitops/fls64.h deleted file mode 100644 index 1b6b17ce2428..000000000000 --- a/tools/testing/radix-tree/linux/bitops/fls64.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_FLS64_H_ -#define _ASM_GENERIC_BITOPS_FLS64_H_ - -#include <asm/types.h> - -static inline int fls64(__u64 x) -{ - __u32 h = x >> 32; - if (h) - return fls(h) + 32; - return fls(x); -} - -#endif /* _ASM_GENERIC_BITOPS_FLS64_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/hweight.h b/tools/testing/radix-tree/linux/bitops/hweight.h deleted file mode 100644 index fbbc383771da..000000000000 --- a/tools/testing/radix-tree/linux/bitops/hweight.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_HWEIGHT_H_ -#define _ASM_GENERIC_BITOPS_HWEIGHT_H_ - -#include <asm/types.h> - -extern unsigned int hweight32(unsigned int w); -extern unsigned int hweight16(unsigned int w); -extern unsigned int hweight8(unsigned int w); -extern unsigned long hweight64(__u64 w); - -#endif /* _ASM_GENERIC_BITOPS_HWEIGHT_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/le.h b/tools/testing/radix-tree/linux/bitops/le.h deleted file mode 100644 index b9c7e5d2d2ad..000000000000 --- a/tools/testing/radix-tree/linux/bitops/le.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_LE_H_ -#define _ASM_GENERIC_BITOPS_LE_H_ - -#include <asm/types.h> -#include <asm/byteorder.h> - -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) - -#if defined(__LITTLE_ENDIAN) - -#define generic_test_le_bit(nr, addr) test_bit(nr, addr) -#define generic___set_le_bit(nr, addr) __set_bit(nr, addr) -#define generic___clear_le_bit(nr, addr) __clear_bit(nr, addr) - -#define generic_test_and_set_le_bit(nr, addr) test_and_set_bit(nr, addr) -#define generic_test_and_clear_le_bit(nr, addr) test_and_clear_bit(nr, addr) - -#define generic___test_and_set_le_bit(nr, addr) __test_and_set_bit(nr, addr) -#define generic___test_and_clear_le_bit(nr, addr) __test_and_clear_bit(nr, addr) - -#define generic_find_next_zero_le_bit(addr, size, offset) find_next_zero_bit(addr, size, offset) - -#elif defined(__BIG_ENDIAN) - -#define generic_test_le_bit(nr, addr) \ - test_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) -#define generic___set_le_bit(nr, addr) \ - __set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) -#define generic___clear_le_bit(nr, addr) \ - __clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) - -#define generic_test_and_set_le_bit(nr, addr) \ - test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) -#define generic_test_and_clear_le_bit(nr, addr) \ - test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) - -#define generic___test_and_set_le_bit(nr, addr) \ - __test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) -#define generic___test_and_clear_le_bit(nr, addr) \ - __test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) - -extern unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, - unsigned long size, unsigned long offset); - -#else -#error "Please fix <asm/byteorder.h>" -#endif - -#define generic_find_first_zero_le_bit(addr, size) \ - generic_find_next_zero_le_bit((addr), (size), 0) - -#endif /* _ASM_GENERIC_BITOPS_LE_H_ */ diff --git a/tools/testing/radix-tree/linux/bitops/non-atomic.h b/tools/testing/radix-tree/linux/bitops/non-atomic.h deleted file mode 100644 index 6a1bcb9d2c4a..000000000000 --- a/tools/testing/radix-tree/linux/bitops/non-atomic.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ -#define _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ - -#include <asm/types.h> - -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) - -/** - * __set_bit - Set a bit in memory - * @nr: the bit to set - * @addr: the address to start counting from - * - * Unlike set_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p |= mask; -} - -static inline void __clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p &= ~mask; -} - -/** - * __change_bit - Toggle a bit in memory - * @nr: the bit to change - * @addr: the address to start counting from - * - * Unlike change_bit(), this function is non-atomic and may be reordered. - * If it's called on the same region of memory simultaneously, the effect - * may be that only one operation succeeds. - */ -static inline void __change_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - - *p ^= mask; -} - -/** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_set_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old | mask; - return (old & mask) != 0; -} - -/** - * __test_and_clear_bit - Clear a bit and return its old value - * @nr: Bit to clear - * @addr: Address to count from - * - * This operation is non-atomic and can be reordered. - * If two examples of this operation race, one can appear to succeed - * but actually fail. You must protect multiple accesses with a lock. - */ -static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old & ~mask; - return (old & mask) != 0; -} - -/* WARNING: non atomic and it can be reordered! */ -static inline int __test_and_change_bit(int nr, - volatile unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BITOP_WORD(nr); - unsigned long old = *p; - - *p = old ^ mask; - return (old & mask) != 0; -} - -/** - * test_bit - Determine whether a bit is set - * @nr: bit number to test - * @addr: Address to start counting from - */ -static inline int test_bit(int nr, const volatile unsigned long *addr) -{ - return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); -} - -#endif /* _ASM_GENERIC_BITOPS_NON_ATOMIC_H_ */ diff --git a/tools/testing/radix-tree/linux/export.h b/tools/testing/radix-tree/linux/export.h deleted file mode 100644 index b6afd131998d..000000000000 --- a/tools/testing/radix-tree/linux/export.h +++ /dev/null @@ -1,2 +0,0 @@ - -#define EXPORT_SYMBOL(sym) diff --git a/tools/testing/radix-tree/linux/gfp.h b/tools/testing/radix-tree/linux/gfp.h index 5b09b2ce6c33..39a0dcb9475a 100644 --- a/tools/testing/radix-tree/linux/gfp.h +++ b/tools/testing/radix-tree/linux/gfp.h @@ -1,6 +1,8 @@ #ifndef _GFP_H #define _GFP_H +#include <linux/types.h> + #define __GFP_BITS_SHIFT 26 #define __GFP_BITS_MASK ((gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) @@ -13,10 +15,12 @@ #define __GFP_DIRECT_RECLAIM 0x400000u #define __GFP_KSWAPD_RECLAIM 0x2000000u -#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) +#define __GFP_RECLAIM (__GFP_DIRECT_RECLAIM|__GFP_KSWAPD_RECLAIM) + +#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) +#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) -#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) -#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags) { diff --git a/tools/testing/radix-tree/linux/idr.h b/tools/testing/radix-tree/linux/idr.h new file mode 100644 index 000000000000..4e342f2e37cf --- /dev/null +++ b/tools/testing/radix-tree/linux/idr.h @@ -0,0 +1 @@ +#include "../../../../include/linux/idr.h" diff --git a/tools/testing/radix-tree/linux/init.h b/tools/testing/radix-tree/linux/init.h index 360cabb3c4e7..1bb0afc21309 100644 --- a/tools/testing/radix-tree/linux/init.h +++ b/tools/testing/radix-tree/linux/init.h @@ -1 +1 @@ -/* An empty file stub that allows radix-tree.c to compile. */ +#define __init diff --git a/tools/testing/radix-tree/linux/kernel.h b/tools/testing/radix-tree/linux/kernel.h index 9b43b4975d83..b21a77fddcf7 100644 --- a/tools/testing/radix-tree/linux/kernel.h +++ b/tools/testing/radix-tree/linux/kernel.h @@ -1,64 +1,21 @@ #ifndef _KERNEL_H #define _KERNEL_H -#include <assert.h> +#include "../../include/linux/kernel.h" #include <string.h> #include <stdio.h> -#include <stddef.h> #include <limits.h> -#include "../../include/linux/compiler.h" -#include "../../include/linux/err.h" +#include <linux/compiler.h> +#include <linux/err.h> +#include <linux/bitops.h> +#include <linux/log2.h> #include "../../../include/linux/kconfig.h" -#ifdef BENCHMARK -#define RADIX_TREE_MAP_SHIFT 6 -#else -#define RADIX_TREE_MAP_SHIFT 3 -#endif - -#ifndef NULL -#define NULL 0 -#endif - -#define BUG_ON(expr) assert(!(expr)) -#define WARN_ON(expr) assert(!(expr)) -#define __init -#define __must_check -#define panic(expr) #define printk printf -#define __force -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define pr_debug printk - -#define smp_rmb() barrier() -#define smp_wmb() barrier() -#define cpu_relax() barrier() +#define pr_cont printk #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type, member) );}) -#define min(a, b) ((a) < (b) ? (a) : (b)) - -#define cond_resched() sched_yield() - -static inline int in_interrupt(void) -{ - return 0; -} - -/* - * This looks more complex than it should be. But we need to - * get the type for the ~ right in round_down (it needs to be - * as wide as the result!), and we want to evaluate the macro - * arguments just once each. - */ -#define __round_mask(x, y) ((__typeof__(x))((y)-1)) -#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) -#define round_down(x, y) ((x) & ~__round_mask(x, y)) - -#define xchg(ptr, x) uatomic_xchg(ptr, x) - #endif /* _KERNEL_H */ diff --git a/tools/testing/radix-tree/linux/mempool.h b/tools/testing/radix-tree/linux/mempool.h deleted file mode 100644 index 6a2dc55b41d6..000000000000 --- a/tools/testing/radix-tree/linux/mempool.h +++ /dev/null @@ -1,16 +0,0 @@ - -#include <linux/slab.h> - -typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); -typedef void (mempool_free_t)(void *element, void *pool_data); - -typedef struct { - mempool_alloc_t *alloc; - mempool_free_t *free; - void *data; -} mempool_t; - -void *mempool_alloc(mempool_t *pool, int gfp_mask); -void mempool_free(void *element, mempool_t *pool); -mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, - mempool_free_t *free_fn, void *pool_data); diff --git a/tools/testing/radix-tree/linux/percpu.h b/tools/testing/radix-tree/linux/percpu.h index 5837f1d56f17..3ea01a1a88c2 100644 --- a/tools/testing/radix-tree/linux/percpu.h +++ b/tools/testing/radix-tree/linux/percpu.h @@ -1,7 +1,10 @@ - +#define DECLARE_PER_CPU(type, val) extern type val #define DEFINE_PER_CPU(type, val) type val #define __get_cpu_var(var) var #define this_cpu_ptr(var) var +#define this_cpu_read(var) var +#define this_cpu_xchg(var, val) uatomic_xchg(&var, val) +#define this_cpu_cmpxchg(var, old, new) uatomic_cmpxchg(&var, old, new) #define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); (ptr); }) #define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu)) diff --git a/tools/testing/radix-tree/linux/preempt.h b/tools/testing/radix-tree/linux/preempt.h index 65c04c226965..35c5ac81529f 100644 --- a/tools/testing/radix-tree/linux/preempt.h +++ b/tools/testing/radix-tree/linux/preempt.h @@ -1,4 +1,14 @@ +#ifndef __LINUX_PREEMPT_H +#define __LINUX_PREEMPT_H + extern int preempt_count; #define preempt_disable() uatomic_inc(&preempt_count) #define preempt_enable() uatomic_dec(&preempt_count) + +static inline int in_interrupt(void) +{ + return 0; +} + +#endif /* __LINUX_PREEMPT_H */ diff --git a/tools/testing/radix-tree/linux/radix-tree.h b/tools/testing/radix-tree/linux/radix-tree.h index ce694ddd4aea..bf1bb231f9b5 100644 --- a/tools/testing/radix-tree/linux/radix-tree.h +++ b/tools/testing/radix-tree/linux/radix-tree.h @@ -1 +1,26 @@ +#ifndef _TEST_RADIX_TREE_H +#define _TEST_RADIX_TREE_H + +#include "generated/map-shift.h" #include "../../../../include/linux/radix-tree.h" + +extern int kmalloc_verbose; +extern int test_verbose; + +static inline void trace_call_rcu(struct rcu_head *head, + void (*func)(struct rcu_head *head)) +{ + if (kmalloc_verbose) + printf("Delaying free of %p to slab\n", (char *)head - + offsetof(struct radix_tree_node, rcu_head)); + call_rcu(head, func); +} + +#define printv(verbosity_level, fmt, ...) \ + if(test_verbose >= verbosity_level) \ + printf(fmt, ##__VA_ARGS__) + +#undef call_rcu +#define call_rcu(x, y) trace_call_rcu(x, y) + +#endif /* _TEST_RADIX_TREE_H */ diff --git a/tools/testing/radix-tree/linux/types.h b/tools/testing/radix-tree/linux/types.h deleted file mode 100644 index 8491d89873bb..000000000000 --- a/tools/testing/radix-tree/linux/types.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _TYPES_H -#define _TYPES_H - -#include "../../include/linux/types.h" - -#define __rcu -#define __read_mostly - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -typedef struct { - unsigned int x; -} spinlock_t; - -#define uninitialized_var(x) x = x - -#include <linux/gfp.h> - -#endif diff --git a/tools/testing/radix-tree/main.c b/tools/testing/radix-tree/main.c index f7e9801a6754..bc9a78449572 100644 --- a/tools/testing/radix-tree/main.c +++ b/tools/testing/radix-tree/main.c @@ -3,6 +3,7 @@ #include <unistd.h> #include <time.h> #include <assert.h> +#include <limits.h> #include <linux/slab.h> #include <linux/radix-tree.h> @@ -67,7 +68,7 @@ void big_gang_check(bool long_run) for (i = 0; i < (long_run ? 1000 : 3); i++) { __big_gang_check(); - printf("%d ", i); + printv(2, "%d ", i); fflush(stdout); } } @@ -128,14 +129,19 @@ void check_copied_tags(struct radix_tree_root *tree, unsigned long start, unsign putchar('.'); */ if (idx[i] < start || idx[i] > end) { if (item_tag_get(tree, idx[i], totag)) { - printf("%lu-%lu: %lu, tags %d-%d\n", start, end, idx[i], item_tag_get(tree, idx[i], fromtag), item_tag_get(tree, idx[i], totag)); + printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, + end, idx[i], item_tag_get(tree, idx[i], + fromtag), + item_tag_get(tree, idx[i], totag)); } assert(!item_tag_get(tree, idx[i], totag)); continue; } if (item_tag_get(tree, idx[i], fromtag) ^ item_tag_get(tree, idx[i], totag)) { - printf("%lu-%lu: %lu, tags %d-%d\n", start, end, idx[i], item_tag_get(tree, idx[i], fromtag), item_tag_get(tree, idx[i], totag)); + printv(2, "%lu-%lu: %lu, tags %d-%d\n", start, end, + idx[i], item_tag_get(tree, idx[i], fromtag), + item_tag_get(tree, idx[i], totag)); } assert(!(item_tag_get(tree, idx[i], fromtag) ^ item_tag_get(tree, idx[i], totag))); @@ -237,7 +243,7 @@ static void __locate_check(struct radix_tree_root *tree, unsigned long index, item = item_lookup(tree, index); index2 = find_item(tree, item); if (index != index2) { - printf("index %ld order %d inserted; found %ld\n", + printv(2, "index %ld order %d inserted; found %ld\n", index, order, index2); abort(); } @@ -288,43 +294,48 @@ static void single_thread_tests(bool long_run) { int i; - printf("starting single_thread_tests: %d allocated, preempt %d\n", + printv(1, "starting single_thread_tests: %d allocated, preempt %d\n", nr_allocated, preempt_count); multiorder_checks(); rcu_barrier(); - printf("after multiorder_check: %d allocated, preempt %d\n", + printv(2, "after multiorder_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); locate_check(); rcu_barrier(); - printf("after locate_check: %d allocated, preempt %d\n", + printv(2, "after locate_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); tag_check(); rcu_barrier(); - printf("after tag_check: %d allocated, preempt %d\n", + printv(2, "after tag_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); gang_check(); rcu_barrier(); - printf("after gang_check: %d allocated, preempt %d\n", + printv(2, "after gang_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); add_and_check(); rcu_barrier(); - printf("after add_and_check: %d allocated, preempt %d\n", + printv(2, "after add_and_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); dynamic_height_check(); rcu_barrier(); - printf("after dynamic_height_check: %d allocated, preempt %d\n", + printv(2, "after dynamic_height_check: %d allocated, preempt %d\n", + nr_allocated, preempt_count); + idr_checks(); + ida_checks(); + rcu_barrier(); + printv(2, "after idr_checks: %d allocated, preempt %d\n", nr_allocated, preempt_count); big_gang_check(long_run); rcu_barrier(); - printf("after big_gang_check: %d allocated, preempt %d\n", + printv(2, "after big_gang_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); for (i = 0; i < (long_run ? 2000 : 3); i++) { copy_tag_check(); - printf("%d ", i); + printv(2, "%d ", i); fflush(stdout); } rcu_barrier(); - printf("after copy_tag_check: %d allocated, preempt %d\n", + printv(2, "after copy_tag_check: %d allocated, preempt %d\n", nr_allocated, preempt_count); } @@ -334,25 +345,30 @@ int main(int argc, char **argv) int opt; unsigned int seed = time(NULL); - while ((opt = getopt(argc, argv, "ls:")) != -1) { + while ((opt = getopt(argc, argv, "ls:v")) != -1) { if (opt == 'l') long_run = true; else if (opt == 's') seed = strtoul(optarg, NULL, 0); + else if (opt == 'v') + test_verbose++; } printf("random seed %u\n", seed); srand(seed); + printf("running tests\n"); + rcu_register_thread(); radix_tree_init(); regression1_test(); regression2_test(); regression3_test(); - iteration_test(0, 10); - iteration_test(7, 20); + iteration_test(0, 10 + 90 * long_run); + iteration_test(7, 10 + 90 * long_run); single_thread_tests(long_run); + ida_thread_tests(); /* Free any remaining preallocated nodes */ radix_tree_cpu_dead(0); @@ -360,9 +376,11 @@ int main(int argc, char **argv) benchmark(); rcu_barrier(); - printf("after rcu_barrier: %d allocated, preempt %d\n", + printv(2, "after rcu_barrier: %d allocated, preempt %d\n", nr_allocated, preempt_count); rcu_unregister_thread(); + printf("tests completed\n"); + exit(0); } diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c index f79812a5e070..06c71178d07d 100644 --- a/tools/testing/radix-tree/multiorder.c +++ b/tools/testing/radix-tree/multiorder.c @@ -30,7 +30,7 @@ static void __multiorder_tag_test(int index, int order) /* our canonical entry */ base = index & ~((1 << order) - 1); - printf("Multiorder tag test with index %d, canonical entry %d\n", + printv(2, "Multiorder tag test with index %d, canonical entry %d\n", index, base); err = item_insert_order(&tree, index, order); @@ -150,7 +150,7 @@ static void multiorder_check(unsigned long index, int order) struct item *item2 = item_create(min, order); RADIX_TREE(tree, GFP_KERNEL); - printf("Multiorder index %ld, order %d\n", index, order); + printv(2, "Multiorder index %ld, order %d\n", index, order); assert(item_insert_order(&tree, index, order) == 0); @@ -188,7 +188,7 @@ static void multiorder_shrink(unsigned long index, int order) RADIX_TREE(tree, GFP_KERNEL); struct radix_tree_node *node; - printf("Multiorder shrink index %ld, order %d\n", index, order); + printv(2, "Multiorder shrink index %ld, order %d\n", index, order); assert(item_insert_order(&tree, 0, order) == 0); @@ -209,7 +209,8 @@ static void multiorder_shrink(unsigned long index, int order) item_check_absent(&tree, i); if (!item_delete(&tree, 0)) { - printf("failed to delete index %ld (order %d)\n", index, order); abort(); + printv(2, "failed to delete index %ld (order %d)\n", index, order); + abort(); } for (i = 0; i < 2*max; i++) @@ -234,7 +235,7 @@ void multiorder_iteration(void) void **slot; int i, j, err; - printf("Multiorder iteration test\n"); + printv(1, "Multiorder iteration test\n"); #define NUM_ENTRIES 11 int index[NUM_ENTRIES] = {0, 2, 4, 8, 16, 32, 34, 36, 64, 72, 128}; @@ -275,7 +276,7 @@ void multiorder_tagged_iteration(void) void **slot; int i, j; - printf("Multiorder tagged iteration test\n"); + printv(1, "Multiorder tagged iteration test\n"); #define MT_NUM_ENTRIES 9 int index[MT_NUM_ENTRIES] = {0, 2, 4, 16, 32, 40, 64, 72, 128}; @@ -355,6 +356,10 @@ void multiorder_tagged_iteration(void) item_kill_tree(&tree); } +/* + * Basic join checks: make sure we can't find an entry in the tree after + * a larger entry has replaced it + */ static void multiorder_join1(unsigned long index, unsigned order1, unsigned order2) { @@ -373,6 +378,10 @@ static void multiorder_join1(unsigned long index, item_kill_tree(&tree); } +/* + * Check that the accounting of exceptional entries is handled correctly + * by joining an exceptional entry to a normal pointer. + */ static void multiorder_join2(unsigned order1, unsigned order2) { RADIX_TREE(tree, GFP_KERNEL); @@ -386,6 +395,9 @@ static void multiorder_join2(unsigned order1, unsigned order2) assert(item2 == (void *)0x12UL); assert(node->exceptional == 1); + item2 = radix_tree_lookup(&tree, 0); + free(item2); + radix_tree_join(&tree, 0, order1, item1); item2 = __radix_tree_lookup(&tree, 1 << order2, &node, NULL); assert(item2 == item1); @@ -453,7 +465,7 @@ static void check_mem(unsigned old_order, unsigned new_order, unsigned alloc) { struct radix_tree_preload *rtp = &radix_tree_preloads; if (rtp->nr != 0) - printf("split(%u %u) remaining %u\n", old_order, new_order, + printv(2, "split(%u %u) remaining %u\n", old_order, new_order, rtp->nr); /* * Can't check for equality here as some nodes may have been @@ -461,7 +473,7 @@ static void check_mem(unsigned old_order, unsigned new_order, unsigned alloc) * nodes allocated since they should have all been preloaded. */ if (nr_allocated > alloc) - printf("split(%u %u) allocated %u %u\n", old_order, new_order, + printv(2, "split(%u %u) allocated %u %u\n", old_order, new_order, alloc, nr_allocated); } @@ -471,6 +483,7 @@ static void __multiorder_split(int old_order, int new_order) void **slot; struct radix_tree_iter iter; unsigned alloc; + struct item *item; radix_tree_preload(GFP_KERNEL); assert(item_insert_order(&tree, 0, old_order) == 0); @@ -479,7 +492,7 @@ static void __multiorder_split(int old_order, int new_order) /* Wipe out the preloaded cache or it'll confuse check_mem() */ radix_tree_cpu_dead(0); - radix_tree_tag_set(&tree, 0, 2); + item = radix_tree_tag_set(&tree, 0, 2); radix_tree_split_preload(old_order, new_order, GFP_KERNEL); alloc = nr_allocated; @@ -492,6 +505,7 @@ static void __multiorder_split(int old_order, int new_order) radix_tree_preload_end(); item_kill_tree(&tree); + free(item); } static void __multiorder_split2(int old_order, int new_order) @@ -633,3 +647,10 @@ void multiorder_checks(void) radix_tree_cpu_dead(0); } + +int __weak main(void) +{ + radix_tree_init(); + multiorder_checks(); + return 0; +} diff --git a/tools/testing/radix-tree/regression1.c b/tools/testing/radix-tree/regression1.c index 0d6813a61b37..bf97742fc18c 100644 --- a/tools/testing/radix-tree/regression1.c +++ b/tools/testing/radix-tree/regression1.c @@ -193,7 +193,7 @@ void regression1_test(void) long arg; /* Regression #1 */ - printf("running regression test 1, should finish in under a minute\n"); + printv(1, "running regression test 1, should finish in under a minute\n"); nr_threads = 2; pthread_barrier_init(&worker_barrier, NULL, nr_threads); @@ -216,5 +216,5 @@ void regression1_test(void) free(threads); - printf("regression test 1, done\n"); + printv(1, "regression test 1, done\n"); } diff --git a/tools/testing/radix-tree/regression2.c b/tools/testing/radix-tree/regression2.c index a41325d7a170..42dd2a33ed24 100644 --- a/tools/testing/radix-tree/regression2.c +++ b/tools/testing/radix-tree/regression2.c @@ -80,7 +80,7 @@ void regression2_test(void) unsigned long int start, end; struct page *pages[1]; - printf("running regression test 2 (should take milliseconds)\n"); + printv(1, "running regression test 2 (should take milliseconds)\n"); /* 0. */ for (i = 0; i <= max_slots - 1; i++) { p = page_alloc(); @@ -103,7 +103,7 @@ void regression2_test(void) /* 4. */ for (i = max_slots - 1; i >= 0; i--) - radix_tree_delete(&mt_tree, i); + free(radix_tree_delete(&mt_tree, i)); /* 5. */ // NOTE: start should not be 0 because radix_tree_gang_lookup_tag_slot @@ -114,7 +114,9 @@ void regression2_test(void) PAGECACHE_TAG_TOWRITE); /* We remove all the remained nodes */ - radix_tree_delete(&mt_tree, max_slots); + free(radix_tree_delete(&mt_tree, max_slots)); - printf("regression test 2, done\n"); + BUG_ON(!radix_tree_empty(&mt_tree)); + + printv(1, "regression test 2, done\n"); } diff --git a/tools/testing/radix-tree/regression3.c b/tools/testing/radix-tree/regression3.c index b594841fae85..670c3d2ae7b1 100644 --- a/tools/testing/radix-tree/regression3.c +++ b/tools/testing/radix-tree/regression3.c @@ -34,21 +34,21 @@ void regression3_test(void) void **slot; bool first; - printf("running regression test 3 (should take milliseconds)\n"); + printv(1, "running regression test 3 (should take milliseconds)\n"); radix_tree_insert(&root, 0, ptr0); radix_tree_tag_set(&root, 0, 0); first = true; radix_tree_for_each_tagged(slot, &root, &iter, 0, 0) { - printf("tagged %ld %p\n", iter.index, *slot); + printv(2, "tagged %ld %p\n", iter.index, *slot); if (first) { radix_tree_insert(&root, 1, ptr); radix_tree_tag_set(&root, 1, 0); first = false; } if (radix_tree_deref_retry(*slot)) { - printf("retry at %ld\n", iter.index); + printv(2, "retry at %ld\n", iter.index); slot = radix_tree_iter_retry(&iter); continue; } @@ -57,13 +57,13 @@ void regression3_test(void) first = true; radix_tree_for_each_slot(slot, &root, &iter, 0) { - printf("slot %ld %p\n", iter.index, *slot); + printv(2, "slot %ld %p\n", iter.index, *slot); if (first) { radix_tree_insert(&root, 1, ptr); first = false; } if (radix_tree_deref_retry(*slot)) { - printk("retry at %ld\n", iter.index); + printv(2, "retry at %ld\n", iter.index); slot = radix_tree_iter_retry(&iter); continue; } @@ -72,30 +72,30 @@ void regression3_test(void) first = true; radix_tree_for_each_contig(slot, &root, &iter, 0) { - printk("contig %ld %p\n", iter.index, *slot); + printv(2, "contig %ld %p\n", iter.index, *slot); if (first) { radix_tree_insert(&root, 1, ptr); first = false; } if (radix_tree_deref_retry(*slot)) { - printk("retry at %ld\n", iter.index); + printv(2, "retry at %ld\n", iter.index); slot = radix_tree_iter_retry(&iter); continue; } } radix_tree_for_each_slot(slot, &root, &iter, 0) { - printf("slot %ld %p\n", iter.index, *slot); + printv(2, "slot %ld %p\n", iter.index, *slot); if (!iter.index) { - printf("next at %ld\n", iter.index); + printv(2, "next at %ld\n", iter.index); slot = radix_tree_iter_resume(slot, &iter); } } radix_tree_for_each_contig(slot, &root, &iter, 0) { - printf("contig %ld %p\n", iter.index, *slot); + printv(2, "contig %ld %p\n", iter.index, *slot); if (!iter.index) { - printf("next at %ld\n", iter.index); + printv(2, "next at %ld\n", iter.index); slot = radix_tree_iter_resume(slot, &iter); } } @@ -103,9 +103,9 @@ void regression3_test(void) radix_tree_tag_set(&root, 0, 0); radix_tree_tag_set(&root, 1, 0); radix_tree_for_each_tagged(slot, &root, &iter, 0, 0) { - printf("tagged %ld %p\n", iter.index, *slot); + printv(2, "tagged %ld %p\n", iter.index, *slot); if (!iter.index) { - printf("next at %ld\n", iter.index); + printv(2, "next at %ld\n", iter.index); slot = radix_tree_iter_resume(slot, &iter); } } @@ -113,5 +113,5 @@ void regression3_test(void) radix_tree_delete(&root, 0); radix_tree_delete(&root, 1); - printf("regression test 3 passed\n"); + printv(1, "regression test 3 passed\n"); } diff --git a/tools/testing/radix-tree/tag_check.c b/tools/testing/radix-tree/tag_check.c index fd98c132207a..36dcf7d6945d 100644 --- a/tools/testing/radix-tree/tag_check.c +++ b/tools/testing/radix-tree/tag_check.c @@ -49,10 +49,10 @@ void simple_checks(void) } verify_tag_consistency(&tree, 0); verify_tag_consistency(&tree, 1); - printf("before item_kill_tree: %d allocated\n", nr_allocated); + printv(2, "before item_kill_tree: %d allocated\n", nr_allocated); item_kill_tree(&tree); rcu_barrier(); - printf("after item_kill_tree: %d allocated\n", nr_allocated); + printv(2, "after item_kill_tree: %d allocated\n", nr_allocated); } /* @@ -257,7 +257,7 @@ static void do_thrash(struct radix_tree_root *tree, char *thrash_state, int tag) gang_check(tree, thrash_state, tag); - printf("%d(%d) %d(%d) %d(%d) %d(%d) / " + printv(2, "%d(%d) %d(%d) %d(%d) %d(%d) / " "%d(%d) present, %d(%d) tagged\n", insert_chunk, nr_inserted, delete_chunk, nr_deleted, @@ -296,13 +296,13 @@ static void __leak_check(void) { RADIX_TREE(tree, GFP_KERNEL); - printf("%d: nr_allocated=%d\n", __LINE__, nr_allocated); + printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated); item_insert(&tree, 1000000); - printf("%d: nr_allocated=%d\n", __LINE__, nr_allocated); + printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated); item_delete(&tree, 1000000); - printf("%d: nr_allocated=%d\n", __LINE__, nr_allocated); + printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated); item_kill_tree(&tree); - printf("%d: nr_allocated=%d\n", __LINE__, nr_allocated); + printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated); } static void single_check(void) @@ -330,21 +330,50 @@ static void single_check(void) item_kill_tree(&tree); } +void radix_tree_clear_tags_test(void) +{ + unsigned long index; + struct radix_tree_node *node; + struct radix_tree_iter iter; + void **slot; + + RADIX_TREE(tree, GFP_KERNEL); + + item_insert(&tree, 0); + item_tag_set(&tree, 0, 0); + __radix_tree_lookup(&tree, 0, &node, &slot); + radix_tree_clear_tags(&tree, node, slot); + assert(item_tag_get(&tree, 0, 0) == 0); + + for (index = 0; index < 1000; index++) { + item_insert(&tree, index); + item_tag_set(&tree, index, 0); + } + + radix_tree_for_each_slot(slot, &tree, &iter, 0) { + radix_tree_clear_tags(&tree, iter.node, slot); + assert(item_tag_get(&tree, iter.index, 0) == 0); + } + + item_kill_tree(&tree); +} + void tag_check(void) { single_check(); extend_checks(); contract_checks(); rcu_barrier(); - printf("after extend_checks: %d allocated\n", nr_allocated); + printv(2, "after extend_checks: %d allocated\n", nr_allocated); __leak_check(); leak_check(); rcu_barrier(); - printf("after leak_check: %d allocated\n", nr_allocated); + printv(2, "after leak_check: %d allocated\n", nr_allocated); simple_checks(); rcu_barrier(); - printf("after simple_checks: %d allocated\n", nr_allocated); + printv(2, "after simple_checks: %d allocated\n", nr_allocated); thrash_tags(); rcu_barrier(); - printf("after thrash_tags: %d allocated\n", nr_allocated); + printv(2, "after thrash_tags: %d allocated\n", nr_allocated); + radix_tree_clear_tags_test(); } diff --git a/tools/testing/radix-tree/test.c b/tools/testing/radix-tree/test.c index e5726e373646..1a257d738a1e 100644 --- a/tools/testing/radix-tree/test.c +++ b/tools/testing/radix-tree/test.c @@ -29,15 +29,28 @@ int __item_insert(struct radix_tree_root *root, struct item *item) return __radix_tree_insert(root, item->index, item->order, item); } -int item_insert(struct radix_tree_root *root, unsigned long index) +struct item *item_create(unsigned long index, unsigned int order) { - return __item_insert(root, item_create(index, 0)); + struct item *ret = malloc(sizeof(*ret)); + + ret->index = index; + ret->order = order; + return ret; } int item_insert_order(struct radix_tree_root *root, unsigned long index, unsigned order) { - return __item_insert(root, item_create(index, order)); + struct item *item = item_create(index, order); + int err = __item_insert(root, item); + if (err) + free(item); + return err; +} + +int item_insert(struct radix_tree_root *root, unsigned long index) +{ + return item_insert_order(root, index, 0); } void item_sanity(struct item *item, unsigned long index) @@ -61,15 +74,6 @@ int item_delete(struct radix_tree_root *root, unsigned long index) return 0; } -struct item *item_create(unsigned long index, unsigned int order) -{ - struct item *ret = malloc(sizeof(*ret)); - - ret->index = index; - ret->order = order; - return ret; -} - void item_check_present(struct radix_tree_root *root, unsigned long index) { struct item *item; diff --git a/tools/testing/radix-tree/test.h b/tools/testing/radix-tree/test.h index 056a23b56467..0f8220cc6166 100644 --- a/tools/testing/radix-tree/test.h +++ b/tools/testing/radix-tree/test.h @@ -34,6 +34,9 @@ void tag_check(void); void multiorder_checks(void); void iteration_test(unsigned order, unsigned duration); void benchmark(void); +void idr_checks(void); +void ida_checks(void); +void ida_thread_tests(void); struct item * item_tag_set(struct radix_tree_root *root, unsigned long index, int tag); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 831022b12848..d8593f1251ec 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,6 +1,7 @@ TARGETS = bpf TARGETS += breakpoints TARGETS += capabilities +TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += efivarfs TARGETS += exec @@ -8,6 +9,7 @@ TARGETS += firmware TARGETS += ftrace TARGETS += futex TARGETS += gpio +TARGETS += intel_pstate TARGETS += ipc TARGETS += kcmp TARGETS += lib @@ -24,6 +26,7 @@ TARGETS += ptrace TARGETS += seccomp TARGETS += sigaltstack TARGETS += size +TARGETS += splice TARGETS += static_keys TARGETS += sync TARGETS += sysctl @@ -49,29 +52,44 @@ override LDFLAGS = override MAKEFLAGS = endif +BUILD := $(O) +ifndef BUILD + BUILD := $(KBUILD_OUTPUT) +endif +ifndef BUILD + BUILD := $(shell pwd) +endif + +export BUILD all: - for TARGET in $(TARGETS); do \ - make -C $$TARGET; \ + for TARGET in $(TARGETS); do \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET;\ done; run_tests: all for TARGET in $(TARGETS); do \ - make -C $$TARGET run_tests; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests;\ done; hotplug: for TARGET in $(TARGETS_HOTPLUG); do \ - make -C $$TARGET; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET;\ done; run_hotplug: hotplug for TARGET in $(TARGETS_HOTPLUG); do \ - make -C $$TARGET run_full_test; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET run_full_test;\ done; clean_hotplug: for TARGET in $(TARGETS_HOTPLUG); do \ - make -C $$TARGET clean; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET clean;\ done; run_pstore_crash: @@ -86,7 +104,8 @@ ifdef INSTALL_PATH @# Ask all targets to install their files mkdir -p $(INSTALL_PATH) for TARGET in $(TARGETS); do \ - make -C $$TARGET INSTALL_PATH=$(INSTALL_PATH)/$$TARGET install; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET INSTALL_PATH=$(INSTALL_PATH)/$$TARGET install; \ done; @# Ask all targets to emit their test scripts @@ -95,10 +114,11 @@ ifdef INSTALL_PATH echo "ROOT=\$$PWD" >> $(ALL_SCRIPT) for TARGET in $(TARGETS); do \ + BUILD_TARGET=$$BUILD/$$TARGET; \ echo "echo ; echo Running tests in $$TARGET" >> $(ALL_SCRIPT); \ echo "echo ========================================" >> $(ALL_SCRIPT); \ echo "cd $$TARGET" >> $(ALL_SCRIPT); \ - make -s --no-print-directory -C $$TARGET emit_tests >> $(ALL_SCRIPT); \ + make -s --no-print-directory OUTPUT=$$BUILD_TARGET -C $$TARGET emit_tests >> $(ALL_SCRIPT); \ echo "cd \$$ROOT" >> $(ALL_SCRIPT); \ done; @@ -109,7 +129,8 @@ endif clean: for TARGET in $(TARGETS); do \ - make -C $$TARGET clean; \ + BUILD_TARGET=$$BUILD/$$TARGET; \ + make OUTPUT=$$BUILD_TARGET -C $$TARGET clean;\ done; .PHONY: install diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 071431bedde8..541d9d7fad5a 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -1,3 +1,5 @@ test_verifier test_maps test_lru_map +test_lpm_map +test_tag diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 7a5f24543a5f..9af09e8099c0 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -1,13 +1,30 @@ -CFLAGS += -Wall -O2 -I../../../../usr/include +LIBDIR := ../../../lib +BPFDIR := $(LIBDIR)/bpf +APIDIR := ../../../include/uapi +GENDIR := ../../../../include/generated +GENHDR := $(GENDIR)/autoconf.h -test_objs = test_verifier test_maps test_lru_map +ifneq ($(wildcard $(GENHDR)),) + GENFLAGS := -DHAVE_GENHDR +endif -TEST_PROGS := test_verifier test_maps test_lru_map test_kmod.sh -TEST_FILES := $(test_objs) +CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) +LDLIBS += -lcap -all: $(test_objs) +TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map + +TEST_PROGS := test_kmod.sh include ../lib.mk -clean: - $(RM) $(test_objs) +BPFOBJ := $(OUTPUT)/bpf.o + +$(TEST_GEN_PROGS): $(BPFOBJ) + +.PHONY: force + +# force a rebuild of BPFOBJ when its dependencies are updated +force: + +$(BPFOBJ): force + $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ diff --git a/tools/testing/selftests/bpf/bpf_sys.h b/tools/testing/selftests/bpf/bpf_sys.h deleted file mode 100644 index 6b4565f2a3f2..000000000000 --- a/tools/testing/selftests/bpf/bpf_sys.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __BPF_SYS__ -#define __BPF_SYS__ - -#include <stdint.h> -#include <stdlib.h> - -#include <sys/syscall.h> - -#include <linux/bpf.h> - -static inline __u64 bpf_ptr_to_u64(const void *ptr) -{ - return (__u64)(unsigned long) ptr; -} - -static inline int bpf(int cmd, union bpf_attr *attr, unsigned int size) -{ -#ifdef __NR_bpf - return syscall(__NR_bpf, cmd, attr, size); -#else - fprintf(stderr, "No bpf syscall, kernel headers too old?\n"); - errno = ENOSYS; - return -1; -#endif -} - -static inline int bpf_map_lookup(int fd, const void *key, void *value) -{ - union bpf_attr attr = {}; - - attr.map_fd = fd; - attr.key = bpf_ptr_to_u64(key); - attr.value = bpf_ptr_to_u64(value); - - return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); -} - -static inline int bpf_map_update(int fd, const void *key, const void *value, - uint64_t flags) -{ - union bpf_attr attr = {}; - - attr.map_fd = fd; - attr.key = bpf_ptr_to_u64(key); - attr.value = bpf_ptr_to_u64(value); - attr.flags = flags; - - return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); -} - -static inline int bpf_map_delete(int fd, const void *key) -{ - union bpf_attr attr = {}; - - attr.map_fd = fd; - attr.key = bpf_ptr_to_u64(key); - - return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); -} - -static inline int bpf_map_next_key(int fd, const void *key, void *next_key) -{ - union bpf_attr attr = {}; - - attr.map_fd = fd; - attr.key = bpf_ptr_to_u64(key); - attr.next_key = bpf_ptr_to_u64(next_key); - - return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); -} - -static inline int bpf_map_create(enum bpf_map_type type, uint32_t size_key, - uint32_t size_value, uint32_t max_elem, - uint32_t flags) -{ - union bpf_attr attr = {}; - - attr.map_type = type; - attr.key_size = size_key; - attr.value_size = size_value; - attr.max_entries = max_elem; - attr.map_flags = flags; - - return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); -} - -static inline int bpf_prog_load(enum bpf_prog_type type, - const struct bpf_insn *insns, size_t size_insns, - const char *license, char *log, size_t size_log) -{ - union bpf_attr attr = {}; - - attr.prog_type = type; - attr.insns = bpf_ptr_to_u64(insns); - attr.insn_cnt = size_insns / sizeof(struct bpf_insn); - attr.license = bpf_ptr_to_u64(license); - - if (size_log > 0) { - attr.log_buf = bpf_ptr_to_u64(log); - attr.log_size = size_log; - attr.log_level = 1; - log[0] = 0; - } - - return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); -} - -#endif /* __BPF_SYS__ */ diff --git a/tools/testing/selftests/bpf/test_lpm_map.c b/tools/testing/selftests/bpf/test_lpm_map.c new file mode 100644 index 000000000000..e97565243d59 --- /dev/null +++ b/tools/testing/selftests/bpf/test_lpm_map.c @@ -0,0 +1,358 @@ +/* + * Randomized tests for eBPF longest-prefix-match maps + * + * This program runs randomized tests against the lpm-bpf-map. It implements a + * "Trivial Longest Prefix Match" (tlpm) based on simple, linear, singly linked + * lists. The implementation should be pretty straightforward. + * + * Based on tlpm, this inserts randomized data into bpf-lpm-maps and verifies + * the trie-based bpf-map implementation behaves the same way as tlpm. + */ + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <linux/bpf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <bpf/bpf.h> +#include "bpf_util.h" + +struct tlpm_node { + struct tlpm_node *next; + size_t n_bits; + uint8_t key[]; +}; + +static struct tlpm_node *tlpm_add(struct tlpm_node *list, + const uint8_t *key, + size_t n_bits) +{ + struct tlpm_node *node; + size_t n; + + /* add new entry with @key/@n_bits to @list and return new head */ + + n = (n_bits + 7) / 8; + node = malloc(sizeof(*node) + n); + assert(node); + + node->next = list; + node->n_bits = n_bits; + memcpy(node->key, key, n); + + return node; +} + +static void tlpm_clear(struct tlpm_node *list) +{ + struct tlpm_node *node; + + /* free all entries in @list */ + + while ((node = list)) { + list = list->next; + free(node); + } +} + +static struct tlpm_node *tlpm_match(struct tlpm_node *list, + const uint8_t *key, + size_t n_bits) +{ + struct tlpm_node *best = NULL; + size_t i; + + /* Perform longest prefix-match on @key/@n_bits. That is, iterate all + * entries and match each prefix against @key. Remember the "best" + * entry we find (i.e., the longest prefix that matches) and return it + * to the caller when done. + */ + + for ( ; list; list = list->next) { + for (i = 0; i < n_bits && i < list->n_bits; ++i) { + if ((key[i / 8] & (1 << (7 - i % 8))) != + (list->key[i / 8] & (1 << (7 - i % 8)))) + break; + } + + if (i >= list->n_bits) { + if (!best || i > best->n_bits) + best = list; + } + } + + return best; +} + +static void test_lpm_basic(void) +{ + struct tlpm_node *list = NULL, *t1, *t2; + + /* very basic, static tests to verify tlpm works as expected */ + + assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 8)); + + t1 = list = tlpm_add(list, (uint8_t[]){ 0xff }, 8); + assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8)); + assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16)); + assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0x00 }, 16)); + assert(!tlpm_match(list, (uint8_t[]){ 0x7f }, 8)); + assert(!tlpm_match(list, (uint8_t[]){ 0xfe }, 8)); + assert(!tlpm_match(list, (uint8_t[]){ 0xff }, 7)); + + t2 = list = tlpm_add(list, (uint8_t[]){ 0xff, 0xff }, 16); + assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff }, 8)); + assert(t2 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 16)); + assert(t1 == tlpm_match(list, (uint8_t[]){ 0xff, 0xff }, 15)); + assert(!tlpm_match(list, (uint8_t[]){ 0x7f, 0xff }, 16)); + + tlpm_clear(list); +} + +static void test_lpm_order(void) +{ + struct tlpm_node *t1, *t2, *l1 = NULL, *l2 = NULL; + size_t i, j; + + /* Verify the tlpm implementation works correctly regardless of the + * order of entries. Insert a random set of entries into @l1, and copy + * the same data in reverse order into @l2. Then verify a lookup of + * random keys will yield the same result in both sets. + */ + + for (i = 0; i < (1 << 12); ++i) + l1 = tlpm_add(l1, (uint8_t[]){ + rand() % 0xff, + rand() % 0xff, + }, rand() % 16 + 1); + + for (t1 = l1; t1; t1 = t1->next) + l2 = tlpm_add(l2, t1->key, t1->n_bits); + + for (i = 0; i < (1 << 8); ++i) { + uint8_t key[] = { rand() % 0xff, rand() % 0xff }; + + t1 = tlpm_match(l1, key, 16); + t2 = tlpm_match(l2, key, 16); + + assert(!t1 == !t2); + if (t1) { + assert(t1->n_bits == t2->n_bits); + for (j = 0; j < t1->n_bits; ++j) + assert((t1->key[j / 8] & (1 << (7 - j % 8))) == + (t2->key[j / 8] & (1 << (7 - j % 8)))); + } + } + + tlpm_clear(l1); + tlpm_clear(l2); +} + +static void test_lpm_map(int keysize) +{ + size_t i, j, n_matches, n_nodes, n_lookups; + struct tlpm_node *t, *list = NULL; + struct bpf_lpm_trie_key *key; + uint8_t *data, *value; + int r, map; + + /* Compare behavior of tlpm vs. bpf-lpm. Create a randomized set of + * prefixes and insert it into both tlpm and bpf-lpm. Then run some + * randomized lookups and verify both maps return the same result. + */ + + n_matches = 0; + n_nodes = 1 << 8; + n_lookups = 1 << 16; + + data = alloca(keysize); + memset(data, 0, keysize); + + value = alloca(keysize + 1); + memset(value, 0, keysize + 1); + + key = alloca(sizeof(*key) + keysize); + memset(key, 0, sizeof(*key) + keysize); + + map = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, + sizeof(*key) + keysize, + keysize + 1, + 4096, + BPF_F_NO_PREALLOC); + assert(map >= 0); + + for (i = 0; i < n_nodes; ++i) { + for (j = 0; j < keysize; ++j) + value[j] = rand() & 0xff; + value[keysize] = rand() % (8 * keysize + 1); + + list = tlpm_add(list, value, value[keysize]); + + key->prefixlen = value[keysize]; + memcpy(key->data, value, keysize); + r = bpf_map_update_elem(map, key, value, 0); + assert(!r); + } + + for (i = 0; i < n_lookups; ++i) { + for (j = 0; j < keysize; ++j) + data[j] = rand() & 0xff; + + t = tlpm_match(list, data, 8 * keysize); + + key->prefixlen = 8 * keysize; + memcpy(key->data, data, keysize); + r = bpf_map_lookup_elem(map, key, value); + assert(!r || errno == ENOENT); + assert(!t == !!r); + + if (t) { + ++n_matches; + assert(t->n_bits == value[keysize]); + for (j = 0; j < t->n_bits; ++j) + assert((t->key[j / 8] & (1 << (7 - j % 8))) == + (value[j / 8] & (1 << (7 - j % 8)))); + } + } + + close(map); + tlpm_clear(list); + + /* With 255 random nodes in the map, we are pretty likely to match + * something on every lookup. For statistics, use this: + * + * printf(" nodes: %zu\n" + * "lookups: %zu\n" + * "matches: %zu\n", n_nodes, n_lookups, n_matches); + */ +} + +/* Test the implementation with some 'real world' examples */ + +static void test_lpm_ipaddr(void) +{ + struct bpf_lpm_trie_key *key_ipv4; + struct bpf_lpm_trie_key *key_ipv6; + size_t key_size_ipv4; + size_t key_size_ipv6; + int map_fd_ipv4; + int map_fd_ipv6; + __u64 value; + + key_size_ipv4 = sizeof(*key_ipv4) + sizeof(__u32); + key_size_ipv6 = sizeof(*key_ipv6) + sizeof(__u32) * 4; + key_ipv4 = alloca(key_size_ipv4); + key_ipv6 = alloca(key_size_ipv6); + + map_fd_ipv4 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, + key_size_ipv4, sizeof(value), + 100, BPF_F_NO_PREALLOC); + assert(map_fd_ipv4 >= 0); + + map_fd_ipv6 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE, + key_size_ipv6, sizeof(value), + 100, BPF_F_NO_PREALLOC); + assert(map_fd_ipv6 >= 0); + + /* Fill data some IPv4 and IPv6 address ranges */ + value = 1; + key_ipv4->prefixlen = 16; + inet_pton(AF_INET, "192.168.0.0", key_ipv4->data); + assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0); + + value = 2; + key_ipv4->prefixlen = 24; + inet_pton(AF_INET, "192.168.0.0", key_ipv4->data); + assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0); + + value = 3; + key_ipv4->prefixlen = 24; + inet_pton(AF_INET, "192.168.128.0", key_ipv4->data); + assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0); + + value = 5; + key_ipv4->prefixlen = 24; + inet_pton(AF_INET, "192.168.1.0", key_ipv4->data); + assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0); + + value = 4; + key_ipv4->prefixlen = 23; + inet_pton(AF_INET, "192.168.0.0", key_ipv4->data); + assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0); + + value = 0xdeadbeef; + key_ipv6->prefixlen = 64; + inet_pton(AF_INET6, "2a00:1450:4001:814::200e", key_ipv6->data); + assert(bpf_map_update_elem(map_fd_ipv6, key_ipv6, &value, 0) == 0); + + /* Set tprefixlen to maximum for lookups */ + key_ipv4->prefixlen = 32; + key_ipv6->prefixlen = 128; + + /* Test some lookups that should come back with a value */ + inet_pton(AF_INET, "192.168.128.23", key_ipv4->data); + assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0); + assert(value == 3); + + inet_pton(AF_INET, "192.168.0.1", key_ipv4->data); + assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0); + assert(value == 2); + + inet_pton(AF_INET6, "2a00:1450:4001:814::", key_ipv6->data); + assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0); + assert(value == 0xdeadbeef); + + inet_pton(AF_INET6, "2a00:1450:4001:814::1", key_ipv6->data); + assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0); + assert(value == 0xdeadbeef); + + /* Test some lookups that should not match any entry */ + inet_pton(AF_INET, "10.0.0.1", key_ipv4->data); + assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 && + errno == ENOENT); + + inet_pton(AF_INET, "11.11.11.11", key_ipv4->data); + assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 && + errno == ENOENT); + + inet_pton(AF_INET6, "2a00:ffff::", key_ipv6->data); + assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == -1 && + errno == ENOENT); + + close(map_fd_ipv4); + close(map_fd_ipv6); +} + +int main(void) +{ + struct rlimit limit = { RLIM_INFINITY, RLIM_INFINITY }; + int i, ret; + + /* we want predictable, pseudo random tests */ + srand(0xf00ba1); + + /* allow unlimited locked memory */ + ret = setrlimit(RLIMIT_MEMLOCK, &limit); + if (ret < 0) + perror("Unable to lift memlock rlimit"); + + test_lpm_basic(); + test_lpm_order(); + + /* Test with 8, 16, 24, 32, ... 128 bit prefix length */ + for (i = 1; i <= 16; ++i) + test_lpm_map(i); + + test_lpm_ipaddr(); + + printf("test_lpm: OK\n"); + return 0; +} diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 9f7bd1915c21..00b0aff56e2e 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -18,7 +18,7 @@ #include <sys/wait.h> #include <sys/resource.h> -#include "bpf_sys.h" +#include <bpf/bpf.h> #include "bpf_util.h" #define LOCAL_FREE_TARGET (128) @@ -30,11 +30,11 @@ static int create_map(int map_type, int map_flags, unsigned int size) { int map_fd; - map_fd = bpf_map_create(map_type, sizeof(unsigned long long), + map_fd = bpf_create_map(map_type, sizeof(unsigned long long), sizeof(unsigned long long), size, map_flags); if (map_fd == -1) - perror("bpf_map_create"); + perror("bpf_create_map"); return map_fd; } @@ -45,9 +45,9 @@ static int map_subset(int map0, int map1) unsigned long long value0[nr_cpus], value1[nr_cpus]; int ret; - while (!bpf_map_next_key(map1, &next_key, &next_key)) { - assert(!bpf_map_lookup(map1, &next_key, value1)); - ret = bpf_map_lookup(map0, &next_key, value0); + while (!bpf_map_get_next_key(map1, &next_key, &next_key)) { + assert(!bpf_map_lookup_elem(map1, &next_key, value1)); + ret = bpf_map_lookup_elem(map0, &next_key, value0); if (ret) { printf("key:%llu not found from map. %s(%d)\n", next_key, strerror(errno), errno); @@ -119,52 +119,54 @@ static void test_lru_sanity0(int map_type, int map_flags) /* insert key=1 element */ key = 1; - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); /* BPF_NOEXIST means: add new element if it doesn't exist */ - assert(bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST) == -1 /* key=1 already exists */ - errno == EEXIST); + && errno == EEXIST); - assert(bpf_map_update(lru_map_fd, &key, value, -1) == -1 && + assert(bpf_map_update_elem(lru_map_fd, &key, value, -1) == -1 && errno == EINVAL); /* insert key=2 element */ /* check that key=2 is not found */ key = 2; - assert(bpf_map_lookup(lru_map_fd, &key, value) == -1 && + assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 && errno == ENOENT); /* BPF_EXIST means: update existing element */ - assert(bpf_map_update(lru_map_fd, &key, value, BPF_EXIST) == -1 && + assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_EXIST) == -1 && /* key=2 is not there */ errno == ENOENT); - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); /* insert key=3 element */ /* check that key=3 is not found */ key = 3; - assert(bpf_map_lookup(lru_map_fd, &key, value) == -1 && + assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1 && errno == ENOENT); /* check that key=1 can be found and mark the ref bit to * stop LRU from removing key=1 */ key = 1; - assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); assert(value[0] == 1234); key = 3; - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); /* key=2 has been removed from the LRU */ key = 2; - assert(bpf_map_lookup(lru_map_fd, &key, value) == -1); + assert(bpf_map_lookup_elem(lru_map_fd, &key, value) == -1); assert(map_equal(lru_map_fd, expected_map_fd)); @@ -217,14 +219,15 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) /* Insert 1 to tgt_free (+tgt_free keys) */ end_key = 1 + tgt_free; for (key = 1; key < end_key; key++) - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); /* Lookup 1 to tgt_free/2 */ end_key = 1 + batch_size; for (key = 1; key < end_key; key++) { - assert(!bpf_map_lookup(lru_map_fd, &key, value)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } /* Insert 1+tgt_free to 2*tgt_free @@ -234,9 +237,10 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) key = 1 + tgt_free; end_key = key + tgt_free; for (; key < end_key; key++) { - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -301,9 +305,10 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) /* Insert 1 to tgt_free (+tgt_free keys) */ end_key = 1 + tgt_free; for (key = 1; key < end_key; key++) - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); - /* Any bpf_map_update will require to acquire a new node + /* Any bpf_map_update_elem will require to acquire a new node * from LRU first. * * The local list is running out of free nodes. @@ -316,10 +321,12 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) */ key = 1; if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_delete(lru_map_fd, &key)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_delete_elem(lru_map_fd, &key)); } else { - assert(bpf_map_update(lru_map_fd, &key, value, BPF_EXIST)); + assert(bpf_map_update_elem(lru_map_fd, &key, value, + BPF_EXIST)); } /* Re-insert 1 to tgt_free/2 again and do a lookup @@ -328,12 +335,13 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) end_key = 1 + batch_size; value[0] = 4321; for (key = 1; key < end_key; key++) { - assert(bpf_map_lookup(lru_map_fd, &key, value)); - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_lookup(lru_map_fd, &key, value)); + assert(bpf_map_lookup_elem(lru_map_fd, &key, value)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); assert(value[0] == 4321); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } value[0] = 1234; @@ -344,14 +352,16 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) /* These newly added but not referenced keys will be * gone during the next LRU shrink. */ - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */ end_key = key + tgt_free; for (; key < end_key; key++) { - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -401,14 +411,15 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) /* Insert 1 to 2*tgt_free (+2*tgt_free keys) */ end_key = 1 + (2 * tgt_free); for (key = 1; key < end_key; key++) - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); /* Lookup key 1 to tgt_free*3/2 */ end_key = tgt_free + batch_size; for (key = 1; key < end_key; key++) { - assert(!bpf_map_lookup(lru_map_fd, &key, value)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } /* Add 1+2*tgt_free to tgt_free*5/2 @@ -417,9 +428,10 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) key = 2 * tgt_free + 1; end_key = key + batch_size; for (; key < end_key; key++) { - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -457,27 +469,29 @@ static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free) value[0] = 1234; for (key = 1; key <= 2 * tgt_free; key++) - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); key = 1; - assert(bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); + assert(bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); for (key = 1; key <= tgt_free; key++) { - assert(!bpf_map_lookup(lru_map_fd, &key, value)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } for (; key <= 2 * tgt_free; key++) { - assert(!bpf_map_delete(lru_map_fd, &key)); - assert(bpf_map_delete(lru_map_fd, &key)); + assert(!bpf_map_delete_elem(lru_map_fd, &key)); + assert(bpf_map_delete_elem(lru_map_fd, &key)); } end_key = key + 2 * tgt_free; for (; key < end_key; key++) { - assert(!bpf_map_update(lru_map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_update(expected_map_fd, &key, value, - BPF_NOEXIST)); + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -493,16 +507,16 @@ static void do_test_lru_sanity5(unsigned long long last_key, int map_fd) unsigned long long key, value[nr_cpus]; /* Ensure the last key inserted by previous CPU can be found */ - assert(!bpf_map_lookup(map_fd, &last_key, value)); + assert(!bpf_map_lookup_elem(map_fd, &last_key, value)); value[0] = 1234; key = last_key + 1; - assert(!bpf_map_update(map_fd, &key, value, BPF_NOEXIST)); - assert(!bpf_map_lookup(map_fd, &key, value)); + assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_lookup_elem(map_fd, &key, value)); /* Cannot find the last key because it was removed by LRU */ - assert(bpf_map_lookup(map_fd, &last_key, value)); + assert(bpf_map_lookup_elem(map_fd, &last_key, value)); } /* Test map with only one element */ @@ -523,7 +537,7 @@ static void test_lru_sanity5(int map_type, int map_flags) value[0] = 1234; key = 0; - assert(!bpf_map_update(map_fd, &key, value, BPF_NOEXIST)); + assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST)); while (sched_next_online(0, &next_cpu) != -1) { pid_t pid; diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index eedfef8d2946..a0aa2009b0e0 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -21,7 +21,7 @@ #include <linux/bpf.h> -#include "bpf_sys.h" +#include <bpf/bpf.h> #include "bpf_util.h" static int map_flags; @@ -31,7 +31,7 @@ static void test_hashmap(int task, void *data) long long key, next_key, value; int fd; - fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 2, map_flags); if (fd < 0) { printf("Failed to create hashmap '%s'!\n", strerror(errno)); @@ -41,74 +41,94 @@ static void test_hashmap(int task, void *data) key = 1; value = 1234; /* Insert key=1 element. */ - assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); value = 0; /* BPF_NOEXIST means add new element if it doesn't exist. */ - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && /* key=1 already exists. */ errno == EEXIST); /* -1 is an invalid flag. */ - assert(bpf_map_update(fd, &key, &value, -1) == -1 && errno == EINVAL); + assert(bpf_map_update_elem(fd, &key, &value, -1) == -1 && + errno == EINVAL); /* Check that key=1 can be found. */ - assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234); + assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); key = 2; /* Check that key=2 is not found. */ - assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); /* BPF_EXIST means update existing element. */ - assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 && /* key=2 is not there. */ errno == ENOENT); /* Insert key=2 element. */ - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); /* key=1 and key=2 were inserted, check that key=0 cannot be * inserted due to max_entries limit. */ key = 0; - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && errno == E2BIG); /* Update existing element, though the map is full. */ key = 1; - assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0); key = 2; - assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); - key = 1; - assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); + key = 3; + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && + errno == E2BIG); /* Check that key = 0 doesn't exist. */ key = 0; - assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); /* Iterate over two elements. */ - assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && (next_key == 1 || next_key == 2)); - assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && (next_key == 1 || next_key == 2)); - assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && errno == ENOENT); /* Delete both elements. */ key = 1; - assert(bpf_map_delete(fd, &key) == 0); + assert(bpf_map_delete_elem(fd, &key) == 0); key = 2; - assert(bpf_map_delete(fd, &key) == 0); - assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + assert(bpf_map_delete_elem(fd, &key) == 0); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); key = 0; /* Check that map is empty. */ - assert(bpf_map_next_key(fd, &key, &next_key) == -1 && + assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && errno == ENOENT); close(fd); } +static void test_hashmap_sizes(int task, void *data) +{ + int fd, i, j; + + for (i = 1; i <= 512; i <<= 1) + for (j = 1; j <= 1 << 18; j <<= 1) { + fd = bpf_create_map(BPF_MAP_TYPE_HASH, i, j, + 2, map_flags); + if (fd < 0) { + printf("Failed to create hashmap key=%d value=%d '%s'\n", + i, j, strerror(errno)); + exit(1); + } + close(fd); + usleep(10); /* give kernel time to destroy */ + } +} + static void test_hashmap_percpu(int task, void *data) { unsigned int nr_cpus = bpf_num_possible_cpus(); @@ -117,7 +137,7 @@ static void test_hashmap_percpu(int task, void *data) int expected_key_mask = 0; int fd, i; - fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), + fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), sizeof(value[0]), 2, map_flags); if (fd < 0) { printf("Failed to create hashmap '%s'!\n", strerror(errno)); @@ -130,53 +150,54 @@ static void test_hashmap_percpu(int task, void *data) key = 1; /* Insert key=1 element. */ assert(!(expected_key_mask & key)); - assert(bpf_map_update(fd, &key, value, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0); expected_key_mask |= key; /* BPF_NOEXIST means add new element if it doesn't exist. */ - assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 && /* key=1 already exists. */ errno == EEXIST); /* -1 is an invalid flag. */ - assert(bpf_map_update(fd, &key, value, -1) == -1 && errno == EINVAL); + assert(bpf_map_update_elem(fd, &key, value, -1) == -1 && + errno == EINVAL); /* Check that key=1 can be found. Value could be 0 if the lookup * was run from a different CPU. */ value[0] = 1; - assert(bpf_map_lookup(fd, &key, value) == 0 && value[0] == 100); + assert(bpf_map_lookup_elem(fd, &key, value) == 0 && value[0] == 100); key = 2; /* Check that key=2 is not found. */ - assert(bpf_map_lookup(fd, &key, value) == -1 && errno == ENOENT); + assert(bpf_map_lookup_elem(fd, &key, value) == -1 && errno == ENOENT); /* BPF_EXIST means update existing element. */ - assert(bpf_map_update(fd, &key, value, BPF_EXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == -1 && /* key=2 is not there. */ errno == ENOENT); /* Insert key=2 element. */ assert(!(expected_key_mask & key)); - assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == 0); + assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0); expected_key_mask |= key; /* key=1 and key=2 were inserted, check that key=0 cannot be * inserted due to max_entries limit. */ key = 0; - assert(bpf_map_update(fd, &key, value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == -1 && errno == E2BIG); /* Check that key = 0 doesn't exist. */ - assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); /* Iterate over two elements. */ - while (!bpf_map_next_key(fd, &key, &next_key)) { + while (!bpf_map_get_next_key(fd, &key, &next_key)) { assert((expected_key_mask & next_key) == next_key); expected_key_mask &= ~next_key; - assert(bpf_map_lookup(fd, &next_key, value) == 0); + assert(bpf_map_lookup_elem(fd, &next_key, value) == 0); for (i = 0; i < nr_cpus; i++) assert(value[i] == i + 100); @@ -187,18 +208,18 @@ static void test_hashmap_percpu(int task, void *data) /* Update with BPF_EXIST. */ key = 1; - assert(bpf_map_update(fd, &key, value, BPF_EXIST) == 0); + assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0); /* Delete both elements. */ key = 1; - assert(bpf_map_delete(fd, &key) == 0); + assert(bpf_map_delete_elem(fd, &key) == 0); key = 2; - assert(bpf_map_delete(fd, &key) == 0); - assert(bpf_map_delete(fd, &key) == -1 && errno == ENOENT); + assert(bpf_map_delete_elem(fd, &key) == 0); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); key = 0; /* Check that map is empty. */ - assert(bpf_map_next_key(fd, &key, &next_key) == -1 && + assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && errno == ENOENT); close(fd); @@ -209,7 +230,7 @@ static void test_arraymap(int task, void *data) int key, next_key, fd; long long value; - fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), + fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2, 0); if (fd < 0) { printf("Failed to create arraymap '%s'!\n", strerror(errno)); @@ -219,40 +240,40 @@ static void test_arraymap(int task, void *data) key = 1; value = 1234; /* Insert key=1 element. */ - assert(bpf_map_update(fd, &key, &value, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0); value = 0; - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && errno == EEXIST); /* Check that key=1 can be found. */ - assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 1234); + assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234); key = 0; /* Check that key=0 is also found and zero initialized. */ - assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0); + assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0); /* key=0 and key=1 were inserted, check that key=2 cannot be inserted * due to max_entries limit. */ key = 2; - assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == -1 && errno == E2BIG); /* Check that key = 2 doesn't exist. */ - assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); /* Iterate over two elements. */ - assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && next_key == 0); - assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && next_key == 1); - assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && errno == ENOENT); /* Delete shouldn't succeed. */ key = 1; - assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL); close(fd); } @@ -263,7 +284,7 @@ static void test_arraymap_percpu(int task, void *data) int key, next_key, fd, i; long values[nr_cpus]; - fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), + fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), sizeof(values[0]), 2, 0); if (fd < 0) { printf("Failed to create arraymap '%s'!\n", strerror(errno)); @@ -275,39 +296,39 @@ static void test_arraymap_percpu(int task, void *data) key = 1; /* Insert key=1 element. */ - assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); values[0] = 0; - assert(bpf_map_update(fd, &key, values, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) == -1 && errno == EEXIST); /* Check that key=1 can be found. */ - assert(bpf_map_lookup(fd, &key, values) == 0 && values[0] == 100); + assert(bpf_map_lookup_elem(fd, &key, values) == 0 && values[0] == 100); key = 0; /* Check that key=0 is also found and zero initialized. */ - assert(bpf_map_lookup(fd, &key, values) == 0 && + assert(bpf_map_lookup_elem(fd, &key, values) == 0 && values[0] == 0 && values[nr_cpus - 1] == 0); /* Check that key=2 cannot be inserted due to max_entries limit. */ key = 2; - assert(bpf_map_update(fd, &key, values, BPF_EXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) == -1 && errno == E2BIG); /* Check that key = 2 doesn't exist. */ - assert(bpf_map_lookup(fd, &key, values) == -1 && errno == ENOENT); + assert(bpf_map_lookup_elem(fd, &key, values) == -1 && errno == ENOENT); /* Iterate over two elements. */ - assert(bpf_map_next_key(fd, &key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && next_key == 0); - assert(bpf_map_next_key(fd, &next_key, &next_key) == 0 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && next_key == 1); - assert(bpf_map_next_key(fd, &next_key, &next_key) == -1 && + assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && errno == ENOENT); /* Delete shouldn't succeed. */ key = 1; - assert(bpf_map_delete(fd, &key) == -1 && errno == EINVAL); + assert(bpf_map_delete_elem(fd, &key) == -1 && errno == EINVAL); close(fd); } @@ -315,11 +336,14 @@ static void test_arraymap_percpu(int task, void *data) static void test_arraymap_percpu_many_keys(void) { unsigned int nr_cpus = bpf_num_possible_cpus(); - unsigned int nr_keys = 20000; + /* nr_keys is not too large otherwise the test stresses percpu + * allocator more than anything else + */ + unsigned int nr_keys = 2000; long values[nr_cpus]; int key, fd, i; - fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), + fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), sizeof(values[0]), nr_keys, 0); if (fd < 0) { printf("Failed to create per-cpu arraymap '%s'!\n", @@ -331,13 +355,13 @@ static void test_arraymap_percpu_many_keys(void) values[i] = i + 10; for (key = 0; key < nr_keys; key++) - assert(bpf_map_update(fd, &key, values, BPF_ANY) == 0); + assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); for (key = 0; key < nr_keys; key++) { for (i = 0; i < nr_cpus; i++) values[i] = 0; - assert(bpf_map_lookup(fd, &key, values) == 0); + assert(bpf_map_lookup_elem(fd, &key, values) == 0); for (i = 0; i < nr_cpus; i++) assert(values[i] == i + 10); @@ -357,7 +381,7 @@ static void test_map_large(void) } key; int fd, i, value; - fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), MAP_SIZE, map_flags); if (fd < 0) { printf("Failed to create large map '%s'!\n", strerror(errno)); @@ -368,22 +392,22 @@ static void test_map_large(void) key = (struct bigkey) { .c = i }; value = i; - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); } key.c = -1; - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && errno == E2BIG); /* Iterate through all elements. */ for (i = 0; i < MAP_SIZE; i++) - assert(bpf_map_next_key(fd, &key, &key) == 0); - assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); + assert(bpf_map_get_next_key(fd, &key, &key) == 0); + assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); key.c = 0; - assert(bpf_map_lookup(fd, &key, &value) == 0 && value == 0); + assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0); key.a = 1; - assert(bpf_map_lookup(fd, &key, &value) == -1 && errno == ENOENT); + assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); close(fd); } @@ -417,6 +441,7 @@ static void test_map_stress(void) { run_parallel(100, test_hashmap, NULL); run_parallel(100, test_hashmap_percpu, NULL); + run_parallel(100, test_hashmap_sizes, NULL); run_parallel(100, test_arraymap, NULL); run_parallel(100, test_arraymap_percpu, NULL); @@ -437,10 +462,12 @@ static void do_work(int fn, void *data) key = value = i; if (do_update) { - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == 0); - assert(bpf_map_update(fd, &key, &value, BPF_EXIST) == 0); + assert(bpf_map_update_elem(fd, &key, &value, + BPF_NOEXIST) == 0); + assert(bpf_map_update_elem(fd, &key, &value, + BPF_EXIST) == 0); } else { - assert(bpf_map_delete(fd, &key) == 0); + assert(bpf_map_delete_elem(fd, &key) == 0); } } } @@ -450,7 +477,7 @@ static void test_map_parallel(void) int i, fd, key = 0, value = 0; int data[2]; - fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), MAP_SIZE, map_flags); if (fd < 0) { printf("Failed to create map for parallel test '%s'!\n", @@ -468,20 +495,20 @@ static void test_map_parallel(void) run_parallel(TASKS, do_work, data); /* Check that key=0 is already there. */ - assert(bpf_map_update(fd, &key, &value, BPF_NOEXIST) == -1 && + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == -1 && errno == EEXIST); /* Check that all elements were inserted. */ key = -1; for (i = 0; i < MAP_SIZE; i++) - assert(bpf_map_next_key(fd, &key, &key) == 0); - assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); + assert(bpf_map_get_next_key(fd, &key, &key) == 0); + assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); /* Another check for all elements */ for (i = 0; i < MAP_SIZE; i++) { key = MAP_SIZE - i - 1; - assert(bpf_map_lookup(fd, &key, &value) == 0 && + assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == key); } @@ -491,7 +518,7 @@ static void test_map_parallel(void) /* Nothing should be left. */ key = -1; - assert(bpf_map_next_key(fd, &key, &key) == -1 && errno == ENOENT); + assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); } static void run_all_tests(void) diff --git a/tools/testing/selftests/bpf/test_tag.c b/tools/testing/selftests/bpf/test_tag.c new file mode 100644 index 000000000000..de409fc50c35 --- /dev/null +++ b/tools/testing/selftests/bpf/test_tag.c @@ -0,0 +1,203 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <sched.h> +#include <limits.h> +#include <assert.h> + +#include <sys/socket.h> +#include <sys/resource.h> + +#include <linux/filter.h> +#include <linux/bpf.h> +#include <linux/if_alg.h> + +#include <bpf/bpf.h> + +#include "../../../include/linux/filter.h" + +static struct bpf_insn prog[BPF_MAXINSNS]; + +static void bpf_gen_imm_prog(unsigned int insns, int fd_map) +{ + int i; + + srand(time(NULL)); + for (i = 0; i < insns; i++) + prog[i] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, rand()); + prog[i - 1] = BPF_EXIT_INSN(); +} + +static void bpf_gen_map_prog(unsigned int insns, int fd_map) +{ + int i, j = 0; + + for (i = 0; i + 1 < insns; i += 2) { + struct bpf_insn tmp[] = { + BPF_LD_MAP_FD(j++ % BPF_REG_10, fd_map) + }; + + memcpy(&prog[i], tmp, sizeof(tmp)); + } + if (insns % 2 == 0) + prog[insns - 2] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, 42); + prog[insns - 1] = BPF_EXIT_INSN(); +} + +static int bpf_try_load_prog(int insns, int fd_map, + void (*bpf_filler)(unsigned int insns, + int fd_map)) +{ + int fd_prog; + + bpf_filler(insns, fd_map); + fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0, + NULL, 0); + assert(fd_prog > 0); + if (fd_map > 0) + bpf_filler(insns, 0); + return fd_prog; +} + +static int __hex2bin(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + return -1; +} + +static int hex2bin(uint8_t *dst, const char *src, size_t count) +{ + while (count--) { + int hi = __hex2bin(*src++); + int lo = __hex2bin(*src++); + + if ((hi < 0) || (lo < 0)) + return -1; + *dst++ = (hi << 4) | lo; + } + return 0; +} + +static void tag_from_fdinfo(int fd_prog, uint8_t *tag, uint32_t len) +{ + const int prefix_len = sizeof("prog_tag:\t") - 1; + char buff[256]; + int ret = -1; + FILE *fp; + + snprintf(buff, sizeof(buff), "/proc/%d/fdinfo/%d", getpid(), + fd_prog); + fp = fopen(buff, "r"); + assert(fp); + + while (fgets(buff, sizeof(buff), fp)) { + if (strncmp(buff, "prog_tag:\t", prefix_len)) + continue; + ret = hex2bin(tag, buff + prefix_len, len); + break; + } + + fclose(fp); + assert(!ret); +} + +static void tag_from_alg(int insns, uint8_t *tag, uint32_t len) +{ + static const struct sockaddr_alg alg = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = "sha1", + }; + int fd_base, fd_alg, ret; + ssize_t size; + + fd_base = socket(AF_ALG, SOCK_SEQPACKET, 0); + assert(fd_base > 0); + + ret = bind(fd_base, (struct sockaddr *)&alg, sizeof(alg)); + assert(!ret); + + fd_alg = accept(fd_base, NULL, 0); + assert(fd_alg > 0); + + insns *= sizeof(struct bpf_insn); + size = write(fd_alg, prog, insns); + assert(size == insns); + + size = read(fd_alg, tag, len); + assert(size == len); + + close(fd_alg); + close(fd_base); +} + +static void tag_dump(const char *prefix, uint8_t *tag, uint32_t len) +{ + int i; + + printf("%s", prefix); + for (i = 0; i < len; i++) + printf("%02x", tag[i]); + printf("\n"); +} + +static void tag_exit_report(int insns, int fd_map, uint8_t *ftag, + uint8_t *atag, uint32_t len) +{ + printf("Program tag mismatch for %d insns%s!\n", insns, + fd_map < 0 ? "" : " with map"); + + tag_dump(" fdinfo result: ", ftag, len); + tag_dump(" af_alg result: ", atag, len); + exit(1); +} + +static void do_test(uint32_t *tests, int start_insns, int fd_map, + void (*bpf_filler)(unsigned int insns, int fd)) +{ + int i, fd_prog; + + for (i = start_insns; i <= BPF_MAXINSNS; i++) { + uint8_t ftag[8], atag[sizeof(ftag)]; + + fd_prog = bpf_try_load_prog(i, fd_map, bpf_filler); + tag_from_fdinfo(fd_prog, ftag, sizeof(ftag)); + tag_from_alg(i, atag, sizeof(atag)); + if (memcmp(ftag, atag, sizeof(ftag))) + tag_exit_report(i, fd_map, ftag, atag, sizeof(ftag)); + + close(fd_prog); + sched_yield(); + (*tests)++; + } +} + +int main(void) +{ + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + uint32_t tests = 0; + int i, fd_map; + + setrlimit(RLIMIT_MEMLOCK, &rinf); + fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), + sizeof(int), 1, BPF_F_NO_PREALLOC); + assert(fd_map > 0); + + for (i = 0; i < 5; i++) { + do_test(&tests, 2, -1, bpf_gen_imm_prog); + do_test(&tests, 3, fd_map, bpf_gen_map_prog); + } + + printf("test_tag: OK (%u tests)\n", tests); + close(fd_map); + return 0; +} diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 853d7e43434a..c848e90b6421 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -8,7 +8,11 @@ * License as published by the Free Software Foundation. */ +#include <asm/types.h> +#include <linux/types.h> +#include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> @@ -16,6 +20,7 @@ #include <stdbool.h> #include <sched.h> +#include <sys/capability.h> #include <sys/resource.h> #include <linux/unistd.h> @@ -23,9 +28,17 @@ #include <linux/bpf_perf_event.h> #include <linux/bpf.h> -#include "../../../include/linux/filter.h" +#include <bpf/bpf.h> -#include "bpf_sys.h" +#ifdef HAVE_GENHDR +# include "autoconf.h" +#else +# if defined(__i386) || defined(__x86_64) || defined(__s390x__) || defined(__aarch64__) +# define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 +# endif +#endif + +#include "../../../include/linux/filter.h" #ifndef ARRAY_SIZE # define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -34,6 +47,8 @@ #define MAX_INSNS 512 #define MAX_FIXUPS 8 +#define F_NEEDS_EFFICIENT_UNALIGNED_ACCESS (1 << 0) + struct bpf_test { const char *descr; struct bpf_insn insns[MAX_INSNS]; @@ -48,6 +63,7 @@ struct bpf_test { REJECT } result, result_unpriv; enum bpf_prog_type prog_type; + uint8_t flags; }; /* Note we want this to be 64 bit aligned so that the end of our array is @@ -859,15 +875,451 @@ static struct bpf_test tests[] = { .result = REJECT, }, { - "check non-u32 access to cb", + "check cb access: byte", .insns = { - BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1, + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 1), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 2), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 3), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1])), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1]) + 1), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1]) + 2), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1]) + 3), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2])), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2]) + 1), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2]) + 2), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2]) + 3), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3])), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3]) + 1), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3]) + 2), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3]) + 3), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4])), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 1), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 2), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) + 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) + 2), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) + 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1]) + 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1]) + 2), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1]) + 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2]) + 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2]) + 2), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2]) + 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3]) + 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3]) + 2), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3]) + 3), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4])), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 1), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 2), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 3), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check cb access: byte, oob 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 4), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: byte, oob 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) - 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: byte, oob 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 4), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: byte, oob 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) - 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: byte, wrong type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, + }, + { + "check cb access: half", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 2), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1])), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1]) + 2), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2])), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2]) + 2), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3])), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3]) + 2), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4])), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 2), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) + 2), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1])), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1]) + 2), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2])), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2]) + 2), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3])), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3]) + 2), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4])), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 2), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check cb access: half, unaligned", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 1), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: half, oob 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 4), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: half, oob 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) - 2), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: half, oob 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 4), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: half, oob 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) - 2), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: half, wrong type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, offsetof(struct __sk_buff, cb[0])), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", - .errstr_unpriv = "R1 leaks addr", .result = REJECT, + .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, + }, + { + "check cb access: word", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3])), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4])), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[1])), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2])), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[3])), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4])), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check cb access: word, unaligned 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) + 2), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: word, unaligned 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 1), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: word, unaligned 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 2), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: word, unaligned 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 3), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: double", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[2])), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0])), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[2])), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check cb access: double, unaligned 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[1])), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: double, unaligned 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[3])), + BPF_EXIT_INSN(), + }, + .errstr = "misaligned access", + .result = REJECT, + }, + { + "check cb access: double, oob 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4])), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, oob 2", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[4]) + 8), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, oob 3", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0]) - 8), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, oob 4", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4])), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, oob 5", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[4]) + 8), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, oob 6", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, cb[0]) - 8), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check cb access: double, wrong type", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, cb[0])), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, }, { "check out of range skb->cb access", @@ -1890,6 +2342,131 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { + "direct packet access: test11 (shift, good access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 22), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 8), + BPF_MOV64_IMM(BPF_REG_3, 144), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 23), + BPF_ALU64_IMM(BPF_RSH, BPF_REG_5, 3), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test12 (and, good access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 22), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 8), + BPF_MOV64_IMM(BPF_REG_3, 144), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 23), + BPF_ALU64_IMM(BPF_AND, BPF_REG_5, 15), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test13 (branches, good access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 22), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 13), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_MOV64_IMM(BPF_REG_4, 1), + BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_4, 2), + BPF_MOV64_IMM(BPF_REG_3, 14), + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_MOV64_IMM(BPF_REG_3, 24), + BPF_MOV64_REG(BPF_REG_5, BPF_REG_3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 23), + BPF_ALU64_IMM(BPF_AND, BPF_REG_5, 15), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test14 (pkt_ptr += 0, CONST_IMM, good access)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 22), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 7), + BPF_MOV64_IMM(BPF_REG_5, 12), + BPF_ALU64_IMM(BPF_RSH, BPF_REG_5, 4), + BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test15 (spill with xadd)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 8), + BPF_MOV64_IMM(BPF_REG_5, 4096), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0), + BPF_STX_XADD(BPF_DW, BPF_REG_4, BPF_REG_5, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_4, 0), + BPF_STX_MEM(BPF_W, BPF_REG_2, BPF_REG_5, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R2 invalid mem access 'inv'", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { "helper access to packet: test1, valid packet_ptr range", .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, @@ -2392,6 +2969,7 @@ static struct bpf_test tests[] = { .errstr_unpriv = "R0 pointer arithmetic prohibited", .result_unpriv = REJECT, .result = ACCEPT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "valid map access into an array with a variable", @@ -2415,6 +2993,7 @@ static struct bpf_test tests[] = { .errstr_unpriv = "R0 pointer arithmetic prohibited", .result_unpriv = REJECT, .result = ACCEPT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "valid map access into an array with a signed variable", @@ -2442,6 +3021,7 @@ static struct bpf_test tests[] = { .errstr_unpriv = "R0 pointer arithmetic prohibited", .result_unpriv = REJECT, .result = ACCEPT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "invalid map access into an array with a constant", @@ -2483,6 +3063,7 @@ static struct bpf_test tests[] = { .errstr = "R0 min value is outside of the array range", .result_unpriv = REJECT, .result = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "invalid map access into an array with a variable", @@ -2506,6 +3087,7 @@ static struct bpf_test tests[] = { .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", .result_unpriv = REJECT, .result = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "invalid map access into an array with no floor check", @@ -2532,6 +3114,7 @@ static struct bpf_test tests[] = { .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", .result_unpriv = REJECT, .result = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "invalid map access into an array with a invalid max check", @@ -2558,6 +3141,7 @@ static struct bpf_test tests[] = { .errstr = "invalid access to map value, value_size=48 off=44 size=8", .result_unpriv = REJECT, .result = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "invalid map access into an array with a invalid max check", @@ -2587,6 +3171,7 @@ static struct bpf_test tests[] = { .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", .result_unpriv = REJECT, .result = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "multiple registers share map_lookup_elem result", @@ -2710,6 +3295,7 @@ static struct bpf_test tests[] = { .result = REJECT, .errstr_unpriv = "R0 pointer arithmetic prohibited", .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, }, { "constant register |= constant should keep constant type", @@ -2876,6 +3462,26 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_LWT_XMIT, }, { + "overlapping checks for direct packet access", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 6), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 1), + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_2, 6), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_LWT_XMIT, + }, + { "invalid access of tc_classid for LWT_IN", .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, @@ -2905,6 +3511,1216 @@ static struct bpf_test tests[] = { .result = REJECT, .errstr = "invalid bpf_context access", }, + { + "helper access to map: full range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to map: partial range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to map: empty range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=0 size=0", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to map: out-of-bound range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val) + 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=0 size=56", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to map: negative range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, -8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=0 size=-8", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): full range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): partial range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): empty range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): out-of-bound range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo) + 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=4 size=52", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): negative range (> adjustment)", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, -8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=4 size=-8", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const imm): negative range (< adjustment)", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_2, -1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): full range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, + offsetof(struct test_val, foo)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): partial range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, + offsetof(struct test_val, foo)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): empty range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): out-of-bound range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, + offsetof(struct test_val, foo)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo) + 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=4 size=52", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): negative range (> adjustment)", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, + offsetof(struct test_val, foo)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, -8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=4 size=-8", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via const reg): negative range (< adjustment)", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_3, + offsetof(struct test_val, foo)), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, -1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via variable): full range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_3, + offsetof(struct test_val, foo), 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via variable): partial range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_3, + offsetof(struct test_val, foo), 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via variable): empty range", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_3, + offsetof(struct test_val, foo), 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via variable): no max check", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is negative, either use unsigned index or do a if (index >=0) check", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to adjusted map (via variable): wrong max check", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_3, + offsetof(struct test_val, foo), 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), + BPF_MOV64_IMM(BPF_REG_2, + sizeof(struct test_val) - + offsetof(struct test_val, foo) + 1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=4 size=45", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "map element value is preserved across register spilling", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -184), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 42), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 leaks addr", + .result = ACCEPT, + .result_unpriv = REJECT, + }, + { + "map element value or null is marked on register spilling", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -152), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 42), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 leaks addr", + .result = ACCEPT, + .result_unpriv = REJECT, + }, + { + "map element value store of cleared call register", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R1 !read_ok", + .errstr = "R1 !read_ok", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value with unaligned store", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 17), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 2, 43), + BPF_ST_MEM(BPF_DW, BPF_REG_0, -2, 44), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + BPF_ST_MEM(BPF_DW, BPF_REG_8, 0, 32), + BPF_ST_MEM(BPF_DW, BPF_REG_8, 2, 33), + BPF_ST_MEM(BPF_DW, BPF_REG_8, -2, 34), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_8, 5), + BPF_ST_MEM(BPF_DW, BPF_REG_8, 0, 22), + BPF_ST_MEM(BPF_DW, BPF_REG_8, 4, 23), + BPF_ST_MEM(BPF_DW, BPF_REG_8, -7, 24), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_8), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 3), + BPF_ST_MEM(BPF_DW, BPF_REG_7, 0, 22), + BPF_ST_MEM(BPF_DW, BPF_REG_7, 4, 23), + BPF_ST_MEM(BPF_DW, BPF_REG_7, -4, 24), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result = ACCEPT, + .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, + }, + { + "map element value with unaligned load", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 11), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGE, BPF_REG_1, MAX_ENTRIES, 9), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 3), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 2), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 5), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 4), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result = ACCEPT, + .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, + }, + { + "map element value illegal alu op, 1", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 8), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "invalid mem access 'inv'", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value illegal alu op, 2", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ALU32_IMM(BPF_ADD, BPF_REG_0, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "invalid mem access 'inv'", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value illegal alu op, 3", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ALU64_IMM(BPF_DIV, BPF_REG_0, 42), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "invalid mem access 'inv'", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value illegal alu op, 4", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ENDIAN(BPF_FROM_BE, BPF_REG_0, 64), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "invalid mem access 'inv'", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value illegal alu op, 5", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_IMM(BPF_REG_3, 4096), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), + BPF_STX_XADD(BPF_DW, BPF_REG_2, BPF_REG_3, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 invalid mem access 'inv'", + .errstr = "R0 invalid mem access 'inv'", + .result = REJECT, + .result_unpriv = REJECT, + }, + { + "map element value is preserved across register spilling", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, + offsetof(struct test_val, foo)), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 42), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -184), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, 0), + BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 42), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .result = ACCEPT, + .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, + }, + { + "helper access to variable memory: stack, bitwise AND + JMP, correct bounds", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -32), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, bitwise AND, zero included", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-64 access_size=0", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, bitwise AND + JMP, wrong max", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 65), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-64 access_size=65", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP, correct bounds", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -32), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP (signed), correct bounds", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -32), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, 64, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JSGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP, bounds + offset", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 5), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 3), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-64 access_size=65", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP, wrong max", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 65, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-64 access_size=65", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP, no max check", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R2 unbounded memory access", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP, no min check", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 64, 3), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-64 access_size=0", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: stack, JMP (signed), no min check", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 16), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, 64, 3), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R2 min value is negative", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: map, JMP, correct bounds", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, + sizeof(struct test_val), 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: map, JMP, wrong max", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 10), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, + sizeof(struct test_val) + 1, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "invalid access to map value, value_size=48 off=0 size=49", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: map adjusted, JMP, correct bounds", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 11), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 20), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, + sizeof(struct test_val) - 20, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: map adjusted, JMP, wrong max", + .insns = { + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 11), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 20), + BPF_MOV64_IMM(BPF_REG_2, sizeof(struct test_val)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, + sizeof(struct test_val) - 19, 4), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr = "R1 min value is outside of the array range", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size > 0 not allowed on NULL", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 64), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .errstr = "R1 type=imm expected=fp", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: size = 0 not allowed on != NULL", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .errstr = "invalid stack type R1 off=-8 access_size=0", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: 8 bytes leak", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -128), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -128), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 63), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), + BPF_EXIT_INSN(), + }, + .errstr = "invalid indirect read from stack off -64+32 size 64", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: 8 bytes no leak (init memory)", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -64), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -56), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -48), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -40), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -32), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -24), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -16), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -64), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 32), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 32), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "invalid and of negative number", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_IMM(BPF_REG_1, 6), + BPF_ALU64_IMM(BPF_AND, BPF_REG_1, -4), + BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", + .result = REJECT, + .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, + }, + { + "invalid range check", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 12), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_9, 1), + BPF_ALU32_IMM(BPF_MOD, BPF_REG_1, 2), + BPF_ALU32_IMM(BPF_ADD, BPF_REG_1, 1), + BPF_ALU32_REG(BPF_AND, BPF_REG_9, BPF_REG_1), + BPF_ALU32_IMM(BPF_ADD, BPF_REG_9, 1), + BPF_ALU32_IMM(BPF_RSH, BPF_REG_9, 1), + BPF_MOV32_IMM(BPF_REG_3, 1), + BPF_ALU32_REG(BPF_SUB, BPF_REG_3, BPF_REG_9), + BPF_ALU32_IMM(BPF_MUL, BPF_REG_3, 0x10000000), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_3), + BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0), + BPF_MOV64_REG(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map2 = { 3 }, + .errstr_unpriv = "R0 pointer arithmetic prohibited", + .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", + .result = REJECT, + .result_unpriv = REJECT, + .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, + } }; static int probe_filter_length(const struct bpf_insn *fp) @@ -2921,7 +4737,7 @@ static int create_map(uint32_t size_value, uint32_t max_elem) { int fd; - fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(long long), + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(long long), size_value, max_elem, BPF_F_NO_PREALLOC); if (fd < 0) printf("Failed to create hash map '%s'!\n", strerror(errno)); @@ -2933,7 +4749,7 @@ static int create_prog_array(void) { int fd; - fd = bpf_map_create(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), + fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), sizeof(int), 4, 0); if (fd < 0) printf("Failed to create prog array '%s'!\n", strerror(errno)); @@ -2982,25 +4798,36 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, static void do_test_single(struct bpf_test *test, bool unpriv, int *passes, int *errors) { + int fd_prog, expected_ret, reject_from_alignment; struct bpf_insn *prog = test->insns; int prog_len = probe_filter_length(prog); int prog_type = test->prog_type; int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; - int fd_prog, expected_ret; const char *expected_err; do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3); - fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, - prog, prog_len * sizeof(struct bpf_insn), - "GPL", bpf_vlog, sizeof(bpf_vlog)); + fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, + prog, prog_len, "GPL", 0, bpf_vlog, + sizeof(bpf_vlog)); expected_ret = unpriv && test->result_unpriv != UNDEF ? test->result_unpriv : test->result; expected_err = unpriv && test->errstr_unpriv ? test->errstr_unpriv : test->errstr; + + reject_from_alignment = fd_prog < 0 && + (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS) && + strstr(bpf_vlog, "Unknown alignment."); +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (reject_from_alignment) { + printf("FAIL\nFailed due to alignment despite having efficient unaligned access: '%s'!\n", + strerror(errno)); + goto fail_log; + } +#endif if (expected_ret == ACCEPT) { - if (fd_prog < 0) { + if (fd_prog < 0 && !reject_from_alignment) { printf("FAIL\nFailed to load prog '%s'!\n", strerror(errno)); goto fail_log; @@ -3010,14 +4837,15 @@ static void do_test_single(struct bpf_test *test, bool unpriv, printf("FAIL\nUnexpected success to load!\n"); goto fail_log; } - if (!strstr(bpf_vlog, expected_err)) { + if (!strstr(bpf_vlog, expected_err) && !reject_from_alignment) { printf("FAIL\nUnexpected error message!\n"); goto fail_log; } } (*passes)++; - printf("OK\n"); + printf("OK%s\n", reject_from_alignment ? + " (NOTE: reject due to unknown alignment)" : ""); close_fds: close(fd_prog); close(fd_f1); @@ -3031,6 +4859,57 @@ fail_log: goto close_fds; } +static bool is_admin(void) +{ + cap_t caps; + cap_flag_value_t sysadmin = CAP_CLEAR; + const cap_value_t cap_val = CAP_SYS_ADMIN; + +#ifdef CAP_IS_SUPPORTED + if (!CAP_IS_SUPPORTED(CAP_SETFCAP)) { + perror("cap_get_flag"); + return false; + } +#endif + caps = cap_get_proc(); + if (!caps) { + perror("cap_get_proc"); + return false; + } + if (cap_get_flag(caps, cap_val, CAP_EFFECTIVE, &sysadmin)) + perror("cap_get_flag"); + if (cap_free(caps)) + perror("cap_free"); + return (sysadmin == CAP_SET); +} + +static int set_admin(bool admin) +{ + cap_t caps; + const cap_value_t cap_val = CAP_SYS_ADMIN; + int ret = -1; + + caps = cap_get_proc(); + if (!caps) { + perror("cap_get_proc"); + return -1; + } + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_val, + admin ? CAP_SET : CAP_CLEAR)) { + perror("cap_set_flag"); + goto out; + } + if (cap_set_proc(caps)) { + perror("cap_set_proc"); + goto out; + } + ret = 0; +out: + if (cap_free(caps)) + perror("cap_free"); + return ret; +} + static int do_test(bool unpriv, unsigned int from, unsigned int to) { int i, passes = 0, errors = 0; @@ -3041,11 +4920,19 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to) /* Program types that are not supported by non-root we * skip right away. */ - if (unpriv && test->prog_type) - continue; + if (!test->prog_type) { + if (!unpriv) + set_admin(false); + printf("#%d/u %s ", i, test->descr); + do_test_single(test, true, &passes, &errors); + if (!unpriv) + set_admin(true); + } - printf("#%d %s ", i, test->descr); - do_test_single(test, unpriv, &passes, &errors); + if (!unpriv) { + printf("#%d/p %s ", i, test->descr); + do_test_single(test, false, &passes, &errors); + } } printf("Summary: %d PASSED, %d FAILED\n", passes, errors); @@ -3057,7 +4944,7 @@ int main(int argc, char **argv) struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; struct rlimit rlim = { 1 << 20, 1 << 20 }; unsigned int from = 0, to = ARRAY_SIZE(tests); - bool unpriv = geteuid() != 0; + bool unpriv = !is_admin(); if (argc == 3) { unsigned int l = atoi(argv[argc - 2]); diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile index 61b79e8df1f4..72aa103e4141 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -3,17 +3,13 @@ uname_M := $(shell uname -m 2>/dev/null || echo not) ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) ifeq ($(ARCH),x86) -TEST_PROGS := breakpoint_test +TEST_GEN_PROGS := breakpoint_test endif ifeq ($(ARCH),aarch64) -TEST_PROGS := breakpoint_test_arm64 +TEST_GEN_PROGS := breakpoint_test_arm64 endif -TEST_PROGS += step_after_suspend_test - -all: $(TEST_PROGS) +TEST_GEN_PROGS += step_after_suspend_test include ../lib.mk -clean: - rm -fr breakpoint_test breakpoint_test_arm64 step_after_suspend_test diff --git a/tools/testing/selftests/capabilities/Makefile b/tools/testing/selftests/capabilities/Makefile index 008602aed920..29b8adfdac71 100644 --- a/tools/testing/selftests/capabilities/Makefile +++ b/tools/testing/selftests/capabilities/Makefile @@ -1,15 +1,8 @@ -TEST_FILES := validate_cap -TEST_PROGS := test_execve - -BINARIES := $(TEST_FILES) $(TEST_PROGS) +TEST_GEN_FILES := validate_cap +TEST_GEN_PROGS := test_execve CFLAGS += -O2 -g -std=gnu99 -Wall LDLIBS += -lcap-ng -lrt -ldl -all: $(BINARIES) - -clean: - $(RM) $(BINARIES) - include ../lib.mk diff --git a/tools/testing/selftests/cpufreq/Makefile b/tools/testing/selftests/cpufreq/Makefile new file mode 100644 index 000000000000..3955cd96f3a2 --- /dev/null +++ b/tools/testing/selftests/cpufreq/Makefile @@ -0,0 +1,8 @@ +all: + +TEST_PROGS := main.sh +TEST_FILES := cpu.sh cpufreq.sh governor.sh module.sh special-tests.sh + +include ../lib.mk + +clean: diff --git a/tools/testing/selftests/cpufreq/cpu.sh b/tools/testing/selftests/cpufreq/cpu.sh new file mode 100755 index 000000000000..8e08a83d65f2 --- /dev/null +++ b/tools/testing/selftests/cpufreq/cpu.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# CPU helpers + +# protect against multiple inclusion +if [ $FILE_CPU ]; then + return 0 +else + FILE_CPU=DONE +fi + +source cpufreq.sh + +for_each_cpu() +{ + cpus=$(ls $CPUROOT | grep "cpu[0-9].*") + for cpu in $cpus; do + $@ $cpu + done +} + +for_each_non_boot_cpu() +{ + cpus=$(ls $CPUROOT | grep "cpu[1-9].*") + for cpu in $cpus; do + $@ $cpu + done +} + +#$1: cpu +offline_cpu() +{ + printf "Offline $1\n" + echo 0 > $CPUROOT/$1/online +} + +#$1: cpu +online_cpu() +{ + printf "Online $1\n" + echo 1 > $CPUROOT/$1/online +} + +#$1: cpu +reboot_cpu() +{ + offline_cpu $1 + online_cpu $1 +} + +# Reboot CPUs +# param: number of times we want to run the loop +reboot_cpus() +{ + printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n" + + for i in `seq 1 $1`; do + for_each_non_boot_cpu offline_cpu + for_each_non_boot_cpu online_cpu + printf "\n" + done + + printf "\n%s\n\n" "------------------------------------------------" +} + +# Prints warning for all CPUs with missing cpufreq directory +print_unmanaged_cpus() +{ + for_each_cpu cpu_should_have_cpufreq_directory +} + +# Counts CPUs with cpufreq directories +count_cpufreq_managed_cpus() +{ + count=0; + + for cpu in `ls $CPUROOT | grep "cpu[0-9].*"`; do + if [ -d $CPUROOT/$cpu/cpufreq ]; then + let count=count+1; + fi + done + + echo $count; +} diff --git a/tools/testing/selftests/cpufreq/cpufreq.sh b/tools/testing/selftests/cpufreq/cpufreq.sh new file mode 100755 index 000000000000..1ed3832030b4 --- /dev/null +++ b/tools/testing/selftests/cpufreq/cpufreq.sh @@ -0,0 +1,241 @@ +#!/bin/bash + +# protect against multiple inclusion +if [ $FILE_CPUFREQ ]; then + return 0 +else + FILE_CPUFREQ=DONE +fi + +source cpu.sh + + +# $1: cpu +cpu_should_have_cpufreq_directory() +{ + if [ ! -d $CPUROOT/$1/cpufreq ]; then + printf "Warning: No cpufreq directory present for $1\n" + fi +} + +cpu_should_not_have_cpufreq_directory() +{ + if [ -d $CPUROOT/$1/cpufreq ]; then + printf "Warning: cpufreq directory present for $1\n" + fi +} + +for_each_policy() +{ + policies=$(ls $CPUFREQROOT| grep "policy[0-9].*") + for policy in $policies; do + $@ $policy + done +} + +for_each_policy_concurrent() +{ + policies=$(ls $CPUFREQROOT| grep "policy[0-9].*") + for policy in $policies; do + $@ $policy & + done +} + +# $1: Path +read_cpufreq_files_in_dir() +{ + local files=`ls $1` + + printf "Printing directory: $1\n\n" + + for file in $files; do + if [ -f $1/$file ]; then + printf "$file:" + cat $1/$file + else + printf "\n" + read_cpufreq_files_in_dir "$1/$file" + fi + done + printf "\n" +} + + +read_all_cpufreq_files() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + read_cpufreq_files_in_dir $CPUFREQROOT + + printf "%s\n\n" "------------------------------------------------" +} + + +# UPDATE CPUFREQ FILES + +# $1: directory path +update_cpufreq_files_in_dir() +{ + local files=`ls $1` + + printf "Updating directory: $1\n\n" + + for file in $files; do + if [ -f $1/$file ]; then + # file is writable ? + local wfile=$(ls -l $1/$file | awk '$1 ~ /^.*w.*/ { print $NF; }') + + if [ ! -z $wfile ]; then + # scaling_setspeed is a special file and we + # should skip updating it + if [ $file != "scaling_setspeed" ]; then + local val=$(cat $1/$file) + printf "Writing $val to: $file\n" + echo $val > $1/$file + fi + fi + else + printf "\n" + update_cpufreq_files_in_dir "$1/$file" + fi + done + + printf "\n" +} + +# Update all writable files with their existing values +update_all_cpufreq_files() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + update_cpufreq_files_in_dir $CPUFREQROOT + + printf "%s\n\n" "------------------------------------------------" +} + + +# CHANGE CPU FREQUENCIES + +# $1: policy +find_current_freq() +{ + cat $CPUFREQROOT/$1/scaling_cur_freq +} + +# $1: policy +# $2: frequency +set_cpu_frequency() +{ + printf "Change frequency for $1 to $2\n" + echo $2 > $CPUFREQROOT/$1/scaling_setspeed +} + +# $1: policy +test_all_frequencies() +{ + local filepath="$CPUFREQROOT/$1" + + backup_governor $1 + + local found=$(switch_governor $1 "userspace") + if [ $found = 1 ]; then + printf "${FUNCNAME[0]}: userspace governor not available for: $1\n" + return; + fi + + printf "Switched governor for $1 to userspace\n\n" + + local freqs=$(cat $filepath/scaling_available_frequencies) + printf "Available frequencies for $1: $freqs\n\n" + + # Set all frequencies one-by-one + for freq in $freqs; do + set_cpu_frequency $1 $freq + done + + printf "\n" + + restore_governor $1 +} + +# $1: loop count +shuffle_frequency_for_all_cpus() +{ + printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n" + + for i in `seq 1 $1`; do + for_each_policy test_all_frequencies + done + printf "\n%s\n\n" "------------------------------------------------" +} + +# Basic cpufreq tests +cpufreq_basic_tests() +{ + printf "*** RUNNING CPUFREQ SANITY TESTS ***\n" + printf "====================================\n\n" + + count=$(count_cpufreq_managed_cpus) + if [ $count = 0 ]; then + printf "No cpu is managed by cpufreq core, exiting\n" + exit; + else + printf "CPUFreq manages: $count CPUs\n\n" + fi + + # Detect & print which CPUs are not managed by cpufreq + print_unmanaged_cpus + + # read/update all cpufreq files + read_all_cpufreq_files + update_all_cpufreq_files + + # hotplug cpus + reboot_cpus 5 + + # Test all frequencies + shuffle_frequency_for_all_cpus 2 + + # Test all governors + shuffle_governors_for_all_cpus 1 +} + +# Suspend/resume +# $1: "suspend" or "hibernate", $2: loop count +do_suspend() +{ + printf "** Test: Running ${FUNCNAME[0]}: Trying $1 for $2 loops **\n\n" + + # Is the directory available + if [ ! -d $SYSFS/power/ -o ! -f $SYSFS/power/state ]; then + printf "$SYSFS/power/state not available\n" + return 1 + fi + + if [ $1 = "suspend" ]; then + filename="mem" + elif [ $1 = "hibernate" ]; then + filename="disk" + else + printf "$1 is not a valid option\n" + return 1 + fi + + if [ -n $filename ]; then + present=$(cat $SYSFS/power/state | grep $filename) + + if [ -z "$present" ]; then + printf "Tried to $1 but $filename isn't present in $SYSFS/power/state\n" + return 1; + fi + + for i in `seq 1 $2`; do + printf "Starting $1\n" + echo $filename > $SYSFS/power/state + printf "Came out of $1\n" + + printf "Do basic tests after finishing $1 to verify cpufreq state\n\n" + cpufreq_basic_tests + done + fi +} diff --git a/tools/testing/selftests/cpufreq/governor.sh b/tools/testing/selftests/cpufreq/governor.sh new file mode 100755 index 000000000000..def645103555 --- /dev/null +++ b/tools/testing/selftests/cpufreq/governor.sh @@ -0,0 +1,153 @@ +#!/bin/bash +# +# Test governors + +# protect against multiple inclusion +if [ $FILE_GOVERNOR ]; then + return 0 +else + FILE_GOVERNOR=DONE +fi + +source cpu.sh +source cpufreq.sh + +CUR_GOV= +CUR_FREQ= + +# Find governor's directory path +# $1: policy, $2: governor +find_gov_directory() +{ + if [ -d $CPUFREQROOT/$2 ]; then + printf "$CPUFREQROOT/$2\n" + elif [ -d $CPUFREQROOT/$1/$2 ]; then + printf "$CPUFREQROOT/$1/$2\n" + else + printf "INVALID\n" + fi +} + +# $1: policy +find_current_governor() +{ + cat $CPUFREQROOT/$1/scaling_governor +} + +# $1: policy +backup_governor() +{ + CUR_GOV=$(find_current_governor $1) + + printf "Governor backup done for $1: $CUR_GOV\n" + + if [ $CUR_GOV == "userspace" ]; then + CUR_FREQ=$(find_current_freq $1) + printf "Governor frequency backup done for $1: $CUR_FREQ\n" + fi + + printf "\n" +} + +# $1: policy +restore_governor() +{ + __switch_governor $1 $CUR_GOV + + printf "Governor restored for $1 to $CUR_GOV\n" + + if [ $CUR_GOV == "userspace" ]; then + set_cpu_frequency $1 $CUR_FREQ + printf "Governor frequency restored for $1: $CUR_FREQ\n" + fi + + printf "\n" +} + +# param: +# $1: policy, $2: governor +__switch_governor() +{ + echo $2 > $CPUFREQROOT/$1/scaling_governor +} + +# param: +# $1: cpu, $2: governor +__switch_governor_for_cpu() +{ + echo $2 > $CPUROOT/$1/cpufreq/scaling_governor +} + +# SWITCH GOVERNORS + +# $1: cpu, $2: governor +switch_governor() +{ + local filepath=$CPUFREQROOT/$1/scaling_available_governors + + # check if governor is available + local found=$(cat $filepath | grep $2 | wc -l) + if [ $found = 0 ]; then + echo 1; + return + fi + + __switch_governor $1 $2 + echo 0; +} + +# $1: policy, $2: governor +switch_show_governor() +{ + cur_gov=find_current_governor + if [ $cur_gov == "userspace" ]; then + cur_freq=find_current_freq + fi + + # switch governor + __switch_governor $1 $2 + + printf "\nSwitched governor for $1 to $2\n\n" + + if [ $2 == "userspace" -o $2 == "powersave" -o $2 == "performance" ]; then + printf "No files to read for $2 governor\n\n" + return + fi + + # show governor files + local govpath=$(find_gov_directory $1 $2) + read_cpufreq_files_in_dir $govpath +} + +# $1: function to be called, $2: policy +call_for_each_governor() +{ + local filepath=$CPUFREQROOT/$2/scaling_available_governors + + # Exit if cpu isn't managed by cpufreq core + if [ ! -f $filepath ]; then + return; + fi + + backup_governor $2 + + local governors=$(cat $filepath) + printf "Available governors for $2: $governors\n" + + for governor in $governors; do + $1 $2 $governor + done + + restore_governor $2 +} + +# $1: loop count +shuffle_governors_for_all_cpus() +{ + printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n" + + for i in `seq 1 $1`; do + for_each_policy call_for_each_governor switch_show_governor + done + printf "%s\n\n" "------------------------------------------------" +} diff --git a/tools/testing/selftests/cpufreq/main.sh b/tools/testing/selftests/cpufreq/main.sh new file mode 100755 index 000000000000..01bac76ac0ec --- /dev/null +++ b/tools/testing/selftests/cpufreq/main.sh @@ -0,0 +1,194 @@ +#!/bin/bash + +source cpu.sh +source cpufreq.sh +source governor.sh +source module.sh +source special-tests.sh + +FUNC=basic # do basic tests by default +OUTFILE=cpufreq_selftest +SYSFS= +CPUROOT= +CPUFREQROOT= + +helpme() +{ + printf "Usage: $0 [-h] [-todg args] + [-h <help>] + [-o <output-file-for-dump>] + [-t <basic: Basic cpufreq testing + suspend: suspend/resume, + hibernate: hibernate/resume, + modtest: test driver or governor modules. Only to be used with -d or -g options, + sptest1: Simple governor switch to produce lockdep. + sptest2: Concurrent governor switch to produce lockdep. + sptest3: Governor races, shuffle between governors quickly. + sptest4: CPU hotplugs with updates to cpufreq files.>] + [-d <driver's module name: only with \"-t modtest>\"] + [-g <governor's module name: only with \"-t modtest>\"] + \n" + exit 2 +} + +prerequisite() +{ + msg="skip all tests:" + + if [ $UID != 0 ]; then + echo $msg must be run as root >&2 + exit 2 + fi + + taskset -p 01 $$ + + SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` + + if [ ! -d "$SYSFS" ]; then + echo $msg sysfs is not mounted >&2 + exit 2 + fi + + CPUROOT=$SYSFS/devices/system/cpu + CPUFREQROOT="$CPUROOT/cpufreq" + + if ! ls $CPUROOT/cpu* > /dev/null 2>&1; then + echo $msg cpus not available in sysfs >&2 + exit 2 + fi + + if ! ls $CPUROOT/cpufreq > /dev/null 2>&1; then + echo $msg cpufreq directory not available in sysfs >&2 + exit 2 + fi +} + +parse_arguments() +{ + while getopts ht:o:d:g: arg + do + case $arg in + h) # --help + helpme + ;; + + t) # --func_type (Function to perform: basic, suspend, hibernate, modtest, sptest1/2/3/4 (default: basic)) + FUNC=$OPTARG + ;; + + o) # --output-file (Output file to store dumps) + OUTFILE=$OPTARG + ;; + + d) # --driver-mod-name (Name of the driver module) + DRIVER_MOD=$OPTARG + ;; + + g) # --governor-mod-name (Name of the governor module) + GOVERNOR_MOD=$OPTARG + ;; + + \?) + helpme + ;; + esac + done +} + +do_test() +{ + # Check if CPUs are managed by cpufreq or not + count=$(count_cpufreq_managed_cpus) + + if [ $count = 0 -a $FUNC != "modtest" ]; then + echo "No cpu is managed by cpufreq core, exiting" + exit 2; + fi + + case "$FUNC" in + "basic") + cpufreq_basic_tests + ;; + + "suspend") + do_suspend "suspend" 1 + ;; + + "hibernate") + do_suspend "hibernate" 1 + ;; + + "modtest") + # Do we have modules in place? + if [ -z $DRIVER_MOD ] && [ -z $GOVERNOR_MOD ]; then + echo "No driver or governor module passed with -d or -g" + exit 2; + fi + + if [ $DRIVER_MOD ]; then + if [ $GOVERNOR_MOD ]; then + module_test $DRIVER_MOD $GOVERNOR_MOD + else + module_driver_test $DRIVER_MOD + fi + else + if [ $count = 0 ]; then + echo "No cpu is managed by cpufreq core, exiting" + exit 2; + fi + + module_governor_test $GOVERNOR_MOD + fi + ;; + + "sptest1") + simple_lockdep + ;; + + "sptest2") + concurrent_lockdep + ;; + + "sptest3") + governor_race + ;; + + "sptest4") + hotplug_with_updates + ;; + + *) + echo "Invalid [-f] function type" + helpme + ;; + esac +} + +# clear dumps +# $1: file name +clear_dumps() +{ + echo "" > $1.txt + echo "" > $1.dmesg_cpufreq.txt + echo "" > $1.dmesg_full.txt +} + +# $1: output file name +dmesg_dumps() +{ + dmesg | grep cpufreq >> $1.dmesg_cpufreq.txt + + # We may need the full logs as well + dmesg >> $1.dmesg_full.txt +} + +# Parse arguments +parse_arguments $@ + +# Make sure all requirements are met +prerequisite + +# Run requested functions +clear_dumps $OUTFILE +do_test >> $OUTFILE.txt +dmesg_dumps $OUTFILE diff --git a/tools/testing/selftests/cpufreq/module.sh b/tools/testing/selftests/cpufreq/module.sh new file mode 100755 index 000000000000..8ff2244a33a1 --- /dev/null +++ b/tools/testing/selftests/cpufreq/module.sh @@ -0,0 +1,243 @@ +#!/bin/bash +# +# Modules specific tests cases + +# protect against multiple inclusion +if [ $FILE_MODULE ]; then + return 0 +else + FILE_MODULE=DONE +fi + +source cpu.sh +source cpufreq.sh +source governor.sh + +# Check basic insmod/rmmod +# $1: module +test_basic_insmod_rmmod() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + printf "Inserting $1 module\n" + # insert module + insmod $1 + if [ $? != 0 ]; then + printf "Insmod $1 failed\n" + exit; + fi + + printf "Removing $1 module\n" + # remove module + rmmod $1 + if [ $? != 0 ]; then + printf "rmmod $1 failed\n" + exit; + fi + + printf "\n" +} + +# Insert cpufreq driver module and perform basic tests +# $1: cpufreq-driver module to insert +# $2: If we want to play with CPUs (1) or not (0) +module_driver_test_single() +{ + printf "** Test: Running ${FUNCNAME[0]} for driver $1 and cpus_hotplug=$2 **\n\n" + + if [ $2 -eq 1 ]; then + # offline all non-boot CPUs + for_each_non_boot_cpu offline_cpu + printf "\n" + fi + + # insert module + printf "Inserting $1 module\n\n" + insmod $1 + if [ $? != 0 ]; then + printf "Insmod $1 failed\n" + return; + fi + + if [ $2 -eq 1 ]; then + # online all non-boot CPUs + for_each_non_boot_cpu online_cpu + printf "\n" + fi + + # run basic tests + cpufreq_basic_tests + + # remove module + printf "Removing $1 module\n\n" + rmmod $1 + if [ $? != 0 ]; then + printf "rmmod $1 failed\n" + return; + fi + + # There shouldn't be any cpufreq directories now. + for_each_cpu cpu_should_not_have_cpufreq_directory + printf "\n" +} + +# $1: cpufreq-driver module to insert +module_driver_test() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + # check if module is present or not + ls $1 > /dev/null + if [ $? != 0 ]; then + printf "$1: not present in `pwd` folder\n" + return; + fi + + # test basic module tests + test_basic_insmod_rmmod $1 + + # Do simple module test + module_driver_test_single $1 0 + + # Remove CPUs before inserting module and then bring them back + module_driver_test_single $1 1 + printf "\n" +} + +# find governor name based on governor module name +# $1: governor module name +find_gov_name() +{ + if [ $1 = "cpufreq_ondemand.ko" ]; then + printf "ondemand" + elif [ $1 = "cpufreq_conservative.ko" ]; then + printf "conservative" + elif [ $1 = "cpufreq_userspace.ko" ]; then + printf "userspace" + elif [ $1 = "cpufreq_performance.ko" ]; then + printf "performance" + elif [ $1 = "cpufreq_powersave.ko" ]; then + printf "powersave" + elif [ $1 = "cpufreq_schedutil.ko" ]; then + printf "schedutil" + fi +} + +# $1: governor string, $2: governor module, $3: policy +# example: module_governor_test_single "ondemand" "cpufreq_ondemand.ko" 2 +module_governor_test_single() +{ + printf "** Test: Running ${FUNCNAME[0]} for $3 **\n\n" + + backup_governor $3 + + # switch to new governor + printf "Switch from $CUR_GOV to $1\n" + switch_show_governor $3 $1 + + # try removing module, it should fail as governor is used + printf "Removing $2 module\n\n" + rmmod $2 + if [ $? = 0 ]; then + printf "WARN: rmmod $2 succeeded even if governor is used\n" + insmod $2 + else + printf "Pass: unable to remove $2 while it is being used\n\n" + fi + + # switch back to old governor + printf "Switchback to $CUR_GOV from $1\n" + restore_governor $3 + printf "\n" +} + +# Insert cpufreq governor module and perform basic tests +# $1: cpufreq-governor module to insert +module_governor_test() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + # check if module is present or not + ls $1 > /dev/null + if [ $? != 0 ]; then + printf "$1: not present in `pwd` folder\n" + return; + fi + + # test basic module tests + test_basic_insmod_rmmod $1 + + # insert module + printf "Inserting $1 module\n\n" + insmod $1 + if [ $? != 0 ]; then + printf "Insmod $1 failed\n" + return; + fi + + # switch to new governor for each cpu + for_each_policy module_governor_test_single $(find_gov_name $1) $1 + + # remove module + printf "Removing $1 module\n\n" + rmmod $1 + if [ $? != 0 ]; then + printf "rmmod $1 failed\n" + return; + fi + printf "\n" +} + +# test modules: driver and governor +# $1: driver module, $2: governor module +module_test() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n\n" + + # check if modules are present or not + ls $1 $2 > /dev/null + if [ $? != 0 ]; then + printf "$1 or $2: is not present in `pwd` folder\n" + return; + fi + + # TEST1: Insert gov after driver + # insert driver module + printf "Inserting $1 module\n\n" + insmod $1 + if [ $? != 0 ]; then + printf "Insmod $1 failed\n" + return; + fi + + # run governor tests + module_governor_test $2 + + # remove driver module + printf "Removing $1 module\n\n" + rmmod $1 + if [ $? != 0 ]; then + printf "rmmod $1 failed\n" + return; + fi + + # TEST2: Insert driver after governor + # insert governor module + printf "Inserting $2 module\n\n" + insmod $2 + if [ $? != 0 ]; then + printf "Insmod $2 failed\n" + return; + fi + + # run governor tests + module_driver_test $1 + + # remove driver module + printf "Removing $2 module\n\n" + rmmod $2 + if [ $? != 0 ]; then + printf "rmmod $2 failed\n" + return; + fi +} diff --git a/tools/testing/selftests/cpufreq/special-tests.sh b/tools/testing/selftests/cpufreq/special-tests.sh new file mode 100755 index 000000000000..58b730f23ef7 --- /dev/null +++ b/tools/testing/selftests/cpufreq/special-tests.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# +# Special test cases reported by people + +# Testcase 1: Reported here: http://marc.info/?l=linux-pm&m=140618592709858&w=2 + +# protect against multiple inclusion +if [ $FILE_SPECIAL ]; then + return 0 +else + FILE_SPECIAL=DONE +fi + +source cpu.sh +source cpufreq.sh +source governor.sh + +# Test 1 +# $1: policy +__simple_lockdep() +{ + # switch to ondemand + __switch_governor $1 "ondemand" + + # cat ondemand files + local ondir=$(find_gov_directory $1 "ondemand") + if [ -z $ondir ]; then + printf "${FUNCNAME[0]}Ondemand directory not created, quit" + return + fi + + cat $ondir/* + + # switch to conservative + __switch_governor $1 "conservative" +} + +simple_lockdep() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n" + + for_each_policy __simple_lockdep +} + +# Test 2 +# $1: policy +__concurrent_lockdep() +{ + for i in `seq 0 100`; do + __simple_lockdep $1 + done +} + +concurrent_lockdep() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n" + + for_each_policy_concurrent __concurrent_lockdep +} + +# Test 3 +quick_shuffle() +{ + # this is called concurrently from governor_race + for I in `seq 1000` + do + echo ondemand | sudo tee $CPUFREQROOT/policy*/scaling_governor & + echo userspace | sudo tee $CPUFREQROOT/policy*/scaling_governor & + done +} + +governor_race() +{ + printf "** Test: Running ${FUNCNAME[0]} **\n" + + # run 8 concurrent instances + for I in `seq 8` + do + quick_shuffle & + done +} + +# Test 4 +# $1: cpu +hotplug_with_updates_cpu() +{ + local filepath="$CPUROOT/$1/cpufreq" + + # switch to ondemand + __switch_governor_for_cpu $1 "ondemand" + + for i in `seq 1 5000` + do + reboot_cpu $1 + done & + + local freqs=$(cat $filepath/scaling_available_frequencies) + local oldfreq=$(cat $filepath/scaling_min_freq) + + for j in `seq 1 5000` + do + # Set all frequencies one-by-one + for freq in $freqs; do + echo $freq > $filepath/scaling_min_freq + done + done + + # restore old freq + echo $oldfreq > $filepath/scaling_min_freq +} + +hotplug_with_updates() +{ + for_each_non_boot_cpu hotplug_with_updates_cpu +} diff --git a/tools/testing/selftests/drivers/gpu/drm_mm.sh b/tools/testing/selftests/drivers/gpu/drm_mm.sh new file mode 100755 index 000000000000..96dd55c92799 --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/drm_mm.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Runs API tests for struct drm_mm (DRM range manager) + +if ! /sbin/modprobe -n -q test-drm_mm; then + echo "drivers/gpu/drm_mm: [skip]" + exit 77 +fi + +if /sbin/modprobe -q test-drm_mm; then + /sbin/modprobe -q -r test-drm_mm + echo "drivers/gpu/drm_mm: ok" +else + echo "drivers/gpu/drm_mm: [FAIL]" + exit 1 +fi diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile index 736c3ddfc787..c49dcea69319 100644 --- a/tools/testing/selftests/efivarfs/Makefile +++ b/tools/testing/selftests/efivarfs/Makefile @@ -1,13 +1,7 @@ CFLAGS = -Wall -test_objs = open-unlink create-read - -all: $(test_objs) - +TEST_GEN_FILES := open-unlink create-read TEST_PROGS := efivarfs.sh -TEST_FILES := $(test_objs) include ../lib.mk -clean: - rm -f $(test_objs) diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile index d4300602bf37..2e13035dff7f 100644 --- a/tools/testing/selftests/exec/Makefile +++ b/tools/testing/selftests/exec/Makefile @@ -1,27 +1,23 @@ CFLAGS = -Wall -BINARIES = execveat -DEPS = execveat.symlink execveat.denatured script subdir -all: $(BINARIES) $(DEPS) -subdir: +TEST_GEN_PROGS := execveat +TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir +# Makefile is a run-time dependency, since it's accessed by the execveat test +TEST_FILES := Makefile + +EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* + +include ../lib.mk + +$(OUTPUT)/subdir: mkdir -p $@ -script: +$(OUTPUT)/script: echo '#!/bin/sh' > $@ echo 'exit $$*' >> $@ chmod +x $@ -execveat.symlink: execveat - ln -s -f $< $@ -execveat.denatured: execveat +$(OUTPUT)/execveat.symlink: $(OUTPUT)/execveat + cd $(OUTPUT) && ln -s -f $(shell basename $<) $(shell basename $@) +$(OUTPUT)/execveat.denatured: $(OUTPUT)/execveat cp $< $@ chmod -x $@ -%: %.c - $(CC) $(CFLAGS) -o $@ $^ - -TEST_PROGS := execveat -# Makefile is a run-time dependency, since it's accessed by the execveat test -TEST_FILES := $(DEPS) Makefile - -include ../lib.mk -clean: - rm -rf $(BINARIES) $(DEPS) subdir.moved execveat.moved xxxxx* diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile index 9bf82234855b..1894d625af2d 100644 --- a/tools/testing/selftests/firmware/Makefile +++ b/tools/testing/selftests/firmware/Makefile @@ -3,7 +3,7 @@ # No binaries, but make sure arg-less "make" doesn't trigger "run_tests" all: -TEST_PROGS := fw_filesystem.sh fw_userhelper.sh +TEST_PROGS := fw_filesystem.sh fw_fallback.sh include ../lib.mk diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh new file mode 100755 index 000000000000..2e4c22d5abf7 --- /dev/null +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -0,0 +1,224 @@ +#!/bin/sh +# This validates that the kernel will fall back to using the fallback mechanism +# to load firmware it can't find on disk itself. We must request a firmware +# that the kernel won't find, and any installed helper (e.g. udev) also +# won't find so that we can do the load ourself manually. +set -e + +modprobe test_firmware + +DIR=/sys/devices/virtual/misc/test_firmware + +# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ +# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that +# as an indicator for CONFIG_FW_LOADER_USER_HELPER. +HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) + +if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +else + echo "usermode helper disabled so ignoring test" + exit 0 +fi + +FWPATH=$(mktemp -d) +FW="$FWPATH/test-firmware.bin" + +test_finish() +{ + echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout + rm -f "$FW" + rmdir "$FWPATH" +} + +load_fw() +{ + local name="$1" + local file="$2" + + # This will block until our load (below) has finished. + echo -n "$name" >"$DIR"/trigger_request & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo 1 >"$DIR"/"$name"/loading + cat "$file" >"$DIR"/"$name"/data + echo 0 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + +load_fw_cancel() +{ + local name="$1" + local file="$2" + + # This will block until our load (below) has finished. + echo -n "$name" >"$DIR"/trigger_request 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo -1 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + +load_fw_custom() +{ + local name="$1" + local file="$2" + + echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo 1 >"$DIR"/"$name"/loading + cat "$file" >"$DIR"/"$name"/data + echo 0 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + + +load_fw_custom_cancel() +{ + local name="$1" + local file="$2" + + echo -n "$name" >"$DIR"/trigger_custom_fallback 2>/dev/null & + + # Give kernel a chance to react. + local timeout=10 + while [ ! -e "$DIR"/"$name"/loading ]; do + sleep 0.1 + timeout=$(( $timeout - 1 )) + if [ "$timeout" -eq 0 ]; then + echo "$0: firmware interface never appeared" >&2 + exit 1 + fi + done + + echo -1 >"$DIR"/"$name"/loading + + # Wait for request to finish. + wait +} + + +trap "test_finish" EXIT + +# This is an unlikely real-world firmware content. :) +echo "ABCD0123" >"$FW" +NAME=$(basename "$FW") + +DEVPATH="$DIR"/"nope-$NAME"/loading + +# Test failure when doing nothing (timeout works). +echo -n 2 >/sys/class/firmware/timeout +echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null & + +# Give the kernel some time to load the loading file, must be less +# than the timeout above. +sleep 1 +if [ ! -f $DEVPATH ]; then + echo "$0: fallback mechanism immediately cancelled" + echo "" + echo "The file never appeared: $DEVPATH" + echo "" + echo "This might be a distribution udev rule setup by your distribution" + echo "to immediately cancel all fallback requests, this must be" + echo "removed before running these tests. To confirm look for" + echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules" + echo "and see if you have something like this:" + echo "" + echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\"" + echo "" + echo "If you do remove this file or comment out this line before" + echo "proceeding with these tests." + exit 1 +fi + +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not expected to match" >&2 + exit 1 +else + echo "$0: timeout works" +fi + +# Put timeout high enough for us to do work but not so long that failures +# slow down this test too much. +echo 4 >/sys/class/firmware/timeout + +# Load this script instead of the desired firmware. +load_fw "$NAME" "$0" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not expected to match" >&2 + exit 1 +else + echo "$0: firmware comparison works" +fi + +# Do a proper load, which should work correctly. +load_fw "$NAME" "$FW" +if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 +else + echo "$0: fallback mechanism works" +fi + +load_fw_cancel "nope-$NAME" "$FW" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 +else + echo "$0: cancelling fallback mechanism works" +fi + +load_fw_custom "$NAME" "$FW" +if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 +else + echo "$0: custom fallback loading mechanism works" +fi + +load_fw_custom_cancel "nope-$NAME" "$FW" +if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 +else + echo "$0: cancelling custom fallback mechanism works" +fi + +exit 0 diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 5c495ad7958a..e35691239350 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -5,9 +5,24 @@ # know so we can be sure we're not accidentally testing the user helper. set -e -modprobe test_firmware - DIR=/sys/devices/virtual/misc/test_firmware +TEST_DIR=$(dirname $0) + +test_modprobe() +{ + if [ ! -d $DIR ]; then + echo "$0: $DIR not present" + echo "You must have the following enabled in your kernel:" + cat $TEST_DIR/config + exit 1 + fi +} + +trap "test_modprobe" EXIT + +if [ ! -d $DIR ]; then + modprobe test_firmware +fi # CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ # These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that @@ -48,18 +63,18 @@ echo "ABCD0123" >"$FW" NAME=$(basename "$FW") -if printf '\000' >"$DIR"/trigger_request; then +if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then echo "$0: empty filename should not succeed" >&2 exit 1 fi -if printf '\000' >"$DIR"/trigger_async_request; then +if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then echo "$0: empty filename should not succeed (async)" >&2 exit 1 fi # Request a firmware that doesn't exist, it should fail. -if echo -n "nope-$NAME" >"$DIR"/trigger_request; then +if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then echo "$0: firmware shouldn't have loaded" >&2 exit 1 fi diff --git a/tools/testing/selftests/firmware/fw_userhelper.sh b/tools/testing/selftests/firmware/fw_userhelper.sh deleted file mode 100755 index b9983f8e09f6..000000000000 --- a/tools/testing/selftests/firmware/fw_userhelper.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/sh -# This validates that the kernel will fall back to using the user helper -# to load firmware it can't find on disk itself. We must request a firmware -# that the kernel won't find, and any installed helper (e.g. udev) also -# won't find so that we can do the load ourself manually. -set -e - -modprobe test_firmware - -DIR=/sys/devices/virtual/misc/test_firmware - -# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ -# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that -# as an indicator for CONFIG_FW_LOADER_USER_HELPER. -HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) - -if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then - OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) -else - echo "usermode helper disabled so ignoring test" - exit 0 -fi - -FWPATH=$(mktemp -d) -FW="$FWPATH/test-firmware.bin" - -test_finish() -{ - echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout - rm -f "$FW" - rmdir "$FWPATH" -} - -load_fw() -{ - local name="$1" - local file="$2" - - # This will block until our load (below) has finished. - echo -n "$name" >"$DIR"/trigger_request & - - # Give kernel a chance to react. - local timeout=10 - while [ ! -e "$DIR"/"$name"/loading ]; do - sleep 0.1 - timeout=$(( $timeout - 1 )) - if [ "$timeout" -eq 0 ]; then - echo "$0: firmware interface never appeared" >&2 - exit 1 - fi - done - - echo 1 >"$DIR"/"$name"/loading - cat "$file" >"$DIR"/"$name"/data - echo 0 >"$DIR"/"$name"/loading - - # Wait for request to finish. - wait -} - -trap "test_finish" EXIT - -# This is an unlikely real-world firmware content. :) -echo "ABCD0123" >"$FW" -NAME=$(basename "$FW") - -# Test failure when doing nothing (timeout works). -echo 1 >/sys/class/firmware/timeout -echo -n "$NAME" >"$DIR"/trigger_request -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not expected to match" >&2 - exit 1 -else - echo "$0: timeout works" -fi - -# Put timeout high enough for us to do work but not so long that failures -# slow down this test too much. -echo 4 >/sys/class/firmware/timeout - -# Load this script instead of the desired firmware. -load_fw "$NAME" "$0" -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not expected to match" >&2 - exit 1 -else - echo "$0: firmware comparison works" -fi - -# Do a proper load, which should work correctly. -load_fw "$NAME" "$FW" -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded" >&2 - exit 1 -else - echo "$0: user helper firmware loading works" -fi - -exit 0 diff --git a/tools/testing/selftests/ftrace/Makefile b/tools/testing/selftests/ftrace/Makefile index 4e6ed13e7f66..a8a5e21850e7 100644 --- a/tools/testing/selftests/ftrace/Makefile +++ b/tools/testing/selftests/ftrace/Makefile @@ -1,9 +1,7 @@ all: TEST_PROGS := ftracetest -TEST_DIRS := test.d +TEST_FILES := test.d +EXTRA_CLEAN := $(OUTPUT)/logs/* include ../lib.mk - -clean: - rm -rf logs/* diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index 6a1752956283..653c5cd9e44d 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -3,13 +3,18 @@ SUBDIRS := functional TEST_PROGS := run.sh .PHONY: all clean -all: - for DIR in $(SUBDIRS); do $(MAKE) -C $$DIR $@ ; done include ../lib.mk +all: + for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$$OUTPUT/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + done + override define RUN_TESTS - ./run.sh + @if [ `dirname $(OUTPUT)` = $(PWD) ]; then ./run.sh; fi endef override define INSTALL_RULE @@ -17,7 +22,9 @@ override define INSTALL_RULE install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) @for SUBDIR in $(SUBDIRS); do \ - $(MAKE) -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \ + BUILD_TARGET=$$OUTPUT/$$SUBDIR; \ + mkdir $$BUILD_TARGET -p; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \ done; endef @@ -26,4 +33,8 @@ override define EMIT_TESTS endef clean: - for DIR in $(SUBDIRS); do $(MAKE) -C $$DIR $@ ; done + for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$$OUTPUT/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + done diff --git a/tools/testing/selftests/futex/functional/Makefile b/tools/testing/selftests/futex/functional/Makefile index 9d6b75ef7b5d..a648e7a6cbc3 100644 --- a/tools/testing/selftests/futex/functional/Makefile +++ b/tools/testing/selftests/futex/functional/Makefile @@ -2,8 +2,11 @@ INCLUDES := -I../include -I../../ CFLAGS := $(CFLAGS) -g -O2 -Wall -D_GNU_SOURCE -pthread $(INCLUDES) LDFLAGS := $(LDFLAGS) -pthread -lrt -HEADERS := ../include/futextest.h -TARGETS := \ +HEADERS := \ + ../include/futextest.h \ + ../include/atomic.h \ + ../include/logging.h +TEST_GEN_FILES := \ futex_wait_timeout \ futex_wait_wouldblock \ futex_requeue_pi \ @@ -12,14 +15,8 @@ TARGETS := \ futex_wait_uninitialized_heap \ futex_wait_private_mapped_file -TEST_PROGS := $(TARGETS) run.sh - -.PHONY: all clean -all: $(TARGETS) - -$(TARGETS): $(HEADERS) +TEST_PROGS := run.sh include ../../lib.mk -clean: - rm -f $(TARGETS) +$(TEST_GEN_FILES): $(HEADERS) diff --git a/tools/testing/selftests/futex/include/logging.h b/tools/testing/selftests/futex/include/logging.h index 014aa01197af..e14469103f07 100644 --- a/tools/testing/selftests/futex/include/logging.h +++ b/tools/testing/selftests/futex/include/logging.h @@ -21,6 +21,7 @@ #ifndef _LOGGING_H #define _LOGGING_H +#include <stdio.h> #include <string.h> #include <unistd.h> #include <linux/futex.h> diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore new file mode 100644 index 000000000000..7d14f743d1a4 --- /dev/null +++ b/tools/testing/selftests/gpio/.gitignore @@ -0,0 +1 @@ +gpio-mockup-chardev diff --git a/tools/testing/selftests/intel_pstate/Makefile b/tools/testing/selftests/intel_pstate/Makefile index f5f1a28715ff..19678e90efb2 100644 --- a/tools/testing/selftests/intel_pstate/Makefile +++ b/tools/testing/selftests/intel_pstate/Makefile @@ -1,15 +1,10 @@ -CC := $(CROSS_COMPILE)gcc CFLAGS := $(CFLAGS) -Wall -D_GNU_SOURCE LDFLAGS := $(LDFLAGS) -lm -TARGETS := msr aperf +TEST_GEN_FILES := msr aperf -TEST_PROGS := $(TARGETS) run.sh +TEST_PROGS := run.sh -.PHONY: all clean -all: $(TARGETS) +include ../lib.mk -$(TARGETS): $(HEADERS) - -clean: - rm -f $(TARGETS) +$(TEST_GEN_FILES): $(HEADERS) diff --git a/tools/testing/selftests/intel_pstate/aperf.c b/tools/testing/selftests/intel_pstate/aperf.c index 6046e183f4ad..cd72f3dc83e9 100644 --- a/tools/testing/selftests/intel_pstate/aperf.c +++ b/tools/testing/selftests/intel_pstate/aperf.c @@ -14,7 +14,7 @@ void usage(char *name) { } int main(int argc, char **argv) { - int i, cpu, fd; + unsigned int i, cpu, fd; char msr_file_name[64]; long long tsc, old_tsc, new_tsc; long long aperf, old_aperf, new_aperf; diff --git a/tools/testing/selftests/ipc/.gitignore b/tools/testing/selftests/ipc/.gitignore index 84b66a3c1f74..9af04c9353c0 100644 --- a/tools/testing/selftests/ipc/.gitignore +++ b/tools/testing/selftests/ipc/.gitignore @@ -1 +1,2 @@ msgque_test +msgque diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile index 25d2e702c68a..30ef4c7f53ea 100644 --- a/tools/testing/selftests/ipc/Makefile +++ b/tools/testing/selftests/ipc/Makefile @@ -11,12 +11,7 @@ endif CFLAGS += -I../../../../usr/include/ -all: - $(CC) $(CFLAGS) msgque.c -o msgque_test - -TEST_PROGS := msgque_test +TEST_GEN_PROGS := msgque include ../lib.mk -clean: - rm -fr ./msgque_test diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index 2ae7450a9a89..47aa9887f9d4 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile @@ -1,10 +1,8 @@ CFLAGS += -I../../../../usr/include/ -all: kcmp_test +TEST_GEN_PROGS := kcmp_test -TEST_PROGS := kcmp_test +EXTRA_CLEAN := $(OUTPUT)/kcmp-test-file include ../lib.mk -clean: - $(RM) kcmp_test kcmp-test-file diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index 50a93f5f13d6..775c589ac3c0 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -2,9 +2,19 @@ # Makefile can operate with or without the kbuild infrastructure. CC := $(CROSS_COMPILE)gcc +ifeq (0,$(MAKELEVEL)) +OUTPUT := $(shell pwd) +endif + +TEST_GEN_PROGS := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS)) +TEST_GEN_FILES := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_FILES)) + +all: $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) + define RUN_TESTS - @for TEST in $(TEST_PROGS); do \ - (./$$TEST && echo "selftests: $$TEST [PASS]") || echo "selftests: $$TEST [FAIL]"; \ + @for TEST in $(TEST_GEN_PROGS) $(TEST_PROGS); do \ + BASENAME_TEST=`basename $$TEST`; \ + cd `dirname $$TEST`; (./$$BASENAME_TEST && echo "selftests: $$BASENAME_TEST [PASS]") || echo "selftests: $$BASENAME_TEST [FAIL]"; cd -;\ done; endef @@ -14,8 +24,13 @@ run_tests: all define INSTALL_RULE @if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \ mkdir -p ${INSTALL_PATH}; \ - echo "rsync -a $(TEST_DIRS) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/"; \ - rsync -a $(TEST_DIRS) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/; \ + echo "rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/"; \ + rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/; \ + fi + @if [ "X$(TEST_GEN_PROGS)$(TEST_GEN_PROGS_EXTENDED)$(TEST_GEN_FILES)" != "X" ]; then \ + mkdir -p ${INSTALL_PATH}; \ + echo "rsync -a $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/"; \ + rsync -a $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/; \ fi endef @@ -27,12 +42,25 @@ else endif define EMIT_TESTS - @for TEST in $(TEST_PROGS); do \ - echo "(./$$TEST && echo \"selftests: $$TEST [PASS]\") || echo \"selftests: $$TEST [FAIL]\""; \ + @for TEST in $(TEST_GEN_PROGS) $(TEST_PROGS); do \ + BASENAME_TEST=`basename $$TEST`; \ + echo "(./$$BASENAME_TEST && echo \"selftests: $$BASENAME_TEST [PASS]\") || echo \"selftests: $$BASENAME_TEST [FAIL]\""; \ done; endef emit_tests: $(EMIT_TESTS) +clean: + $(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN) + +$(OUTPUT)/%:%.c + $(LINK.c) $^ $(LDLIBS) -o $@ + +$(OUTPUT)/%.o:%.S + $(COMPILE.S) $^ -o $@ + +$(OUTPUT)/%:%.S + $(LINK.S) $^ $(LDLIBS) -o $@ + .PHONY: run_tests all clean install emit_tests diff --git a/tools/testing/selftests/lib/prime_numbers.sh b/tools/testing/selftests/lib/prime_numbers.sh new file mode 100755 index 000000000000..da4cbcd766f5 --- /dev/null +++ b/tools/testing/selftests/lib/prime_numbers.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Checks fast/slow prime_number generation for inconsistencies + +if ! /sbin/modprobe -q -r prime_numbers; then + echo "prime_numbers: [SKIP]" + exit 77 +fi + +if /sbin/modprobe -q prime_numbers selftest=65536; then + /sbin/modprobe -q -r prime_numbers + echo "prime_numbers: ok" +else + echo "prime_numbers: [FAIL]" + exit 1 +fi diff --git a/tools/testing/selftests/locking/ww_mutex.sh b/tools/testing/selftests/locking/ww_mutex.sh new file mode 100644 index 000000000000..6905da965f3b --- /dev/null +++ b/tools/testing/selftests/locking/ww_mutex.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Runs API tests for struct ww_mutex (Wait/Wound mutexes) + +if /sbin/modprobe -q test-ww_mutex; then + /sbin/modprobe -q -r test-ww_mutex + echo "locking/ww_mutex: ok" +else + echo "locking/ww_mutex: [FAIL]" + exit 1 +fi diff --git a/tools/testing/selftests/membarrier/Makefile b/tools/testing/selftests/membarrier/Makefile index a1a97085847d..02845532b059 100644 --- a/tools/testing/selftests/membarrier/Makefile +++ b/tools/testing/selftests/membarrier/Makefile @@ -1,10 +1,6 @@ CFLAGS += -g -I../../../../usr/include/ -TEST_PROGS := membarrier_test - -all: $(TEST_PROGS) +TEST_GEN_PROGS := membarrier_test include ../lib.mk -clean: - $(RM) $(TEST_PROGS) diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index fd396ac811b6..79891d033de1 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile @@ -1,22 +1,13 @@ -CC = $(CROSS_COMPILE)gcc CFLAGS += -D_FILE_OFFSET_BITS=64 CFLAGS += -I../../../../include/uapi/ CFLAGS += -I../../../../include/ CFLAGS += -I../../../../usr/include/ -TEST_PROGS := memfd_test - -all: $(TEST_PROGS) - -include ../lib.mk - -build_fuse: fuse_mnt fuse_test +TEST_PROGS := run_fuse_test.sh +TEST_GEN_FILES := memfd_test fuse_mnt fuse_test fuse_mnt.o: CFLAGS += $(shell pkg-config fuse --cflags) fuse_mnt: LDFLAGS += $(shell pkg-config fuse --libs) -run_fuse: build_fuse - @./run_fuse_test.sh || echo "fuse_test: [FAIL]" +include ../lib.mk -clean: - $(RM) memfd_test fuse_test diff --git a/tools/testing/selftests/mount/Makefile b/tools/testing/selftests/mount/Makefile index 5e35c9c50b72..9093d7ffe87f 100644 --- a/tools/testing/selftests/mount/Makefile +++ b/tools/testing/selftests/mount/Makefile @@ -1,14 +1,11 @@ # Makefile for mount selftests. CFLAGS = -Wall \ -O2 -all: unprivileged-remount-test -unprivileged-remount-test: unprivileged-remount-test.c - $(CC) $(CFLAGS) unprivileged-remount-test.c -o unprivileged-remount-test +TEST_GEN_PROGS := unprivileged-remount-test include ../lib.mk -TEST_PROGS := unprivileged-remount-test override RUN_TESTS := if [ -f /proc/self/uid_map ] ; \ then \ ./unprivileged-remount-test ; \ @@ -17,5 +14,3 @@ override RUN_TESTS := if [ -f /proc/self/uid_map ] ; \ fi override EMIT_TESTS := echo "$(RUN_TESTS)" -clean: - rm -f unprivileged-remount-test diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index eebac29acbd9..79a664aeb8d7 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile @@ -1,8 +1,6 @@ CFLAGS += -O2 LDLIBS = -lrt -lpthread -lpopt -TEST_PROGS := mq_open_tests mq_perf_tests - -all: $(TEST_PROGS) +TEST_GEN_PROGS := mq_open_tests mq_perf_tests include ../lib.mk @@ -16,5 +14,3 @@ override define EMIT_TESTS echo "./mq_perf_tests || echo \"selftests: mq_perf_tests [FAIL]\"" endef -clean: - rm -f mq_open_tests mq_perf_tests diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index e24e4c82542e..fbfe5d0d5c2e 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -3,20 +3,13 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g CFLAGS += -I../../../../usr/include/ -NET_PROGS = socket -NET_PROGS += psock_fanout psock_tpacket -NET_PROGS += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -NET_PROGS += reuseport_dualstack - -all: $(NET_PROGS) reuseport_bpf_numa: LDFLAGS += -lnuma -%: %.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh -TEST_FILES := $(NET_PROGS) +TEST_GEN_FILES = socket +TEST_GEN_FILES += psock_fanout psock_tpacket +TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa +TEST_GEN_FILES += reuseport_dualstack include ../lib.mk -clean: - $(RM) $(NET_PROGS) diff --git a/tools/testing/selftests/net/psock_lib.h b/tools/testing/selftests/net/psock_lib.h index 24bc7ec1be7d..a77da88bf946 100644 --- a/tools/testing/selftests/net/psock_lib.h +++ b/tools/testing/selftests/net/psock_lib.h @@ -40,14 +40,39 @@ static __maybe_unused void sock_setfilter(int fd, int lvl, int optnum) { + /* the filter below checks for all of the following conditions that + * are based on the contents of create_payload() + * ether type 0x800 and + * ip proto udp and + * skb->len == DATA_LEN and + * udp[38] == 'a' or udp[38] == 'b' + * It can be generated from the following bpf_asm input: + * ldh [12] + * jne #0x800, drop ; ETH_P_IP + * ldb [23] + * jneq #17, drop ; IPPROTO_UDP + * ld len ; ld skb->len + * jlt #100, drop ; DATA_LEN + * ldb [80] + * jeq #97, pass ; DATA_CHAR + * jne #98, drop ; DATA_CHAR_1 + * pass: + * ret #-1 + * drop: + * ret #0 + */ struct sock_filter bpf_filter[] = { - { 0x80, 0, 0, 0x00000000 }, /* LD pktlen */ - { 0x35, 0, 4, DATA_LEN }, /* JGE DATA_LEN [f goto nomatch]*/ - { 0x30, 0, 0, 0x00000050 }, /* LD ip[80] */ - { 0x15, 1, 0, DATA_CHAR }, /* JEQ DATA_CHAR [t goto match]*/ - { 0x15, 0, 1, DATA_CHAR_1}, /* JEQ DATA_CHAR_1 [t goto match]*/ - { 0x06, 0, 0, 0x00000060 }, /* RET match */ - { 0x06, 0, 0, 0x00000000 }, /* RET no match */ + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 8, 0x00000800 }, + { 0x30, 0, 0, 0x00000017 }, + { 0x15, 0, 6, 0x00000011 }, + { 0x80, 0, 0, 0000000000 }, + { 0x35, 0, 4, 0x00000064 }, + { 0x30, 0, 0, 0x00000050 }, + { 0x15, 1, 0, 0x00000061 }, + { 0x15, 0, 1, 0x00000062 }, + { 0x06, 0, 0, 0xffffffff }, + { 0x06, 0, 0, 0000000000 }, }; struct sock_fprog bpf_prog; diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c index 24adf709bd9d..7f6cd9fdacf3 100644 --- a/tools/testing/selftests/net/psock_tpacket.c +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -110,7 +110,7 @@ static unsigned int total_packets, total_bytes; static int pfsocket(int ver) { - int ret, sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + int ret, sock = socket(PF_PACKET, SOCK_RAW, 0); if (sock == -1) { perror("socket"); exit(1); @@ -239,7 +239,6 @@ static void walk_v1_v2_rx(int sock, struct ring *ring) bug_on(ring->type != PACKET_RX_RING); pair_udp_open(udp_sock, PORT_BASE); - pair_udp_setfilter(sock); memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; @@ -311,20 +310,33 @@ static inline void __v2_tx_user_ready(struct tpacket2_hdr *hdr) __sync_synchronize(); } -static inline int __v1_v2_tx_kernel_ready(void *base, int version) +static inline int __v3_tx_kernel_ready(struct tpacket3_hdr *hdr) +{ + return !(hdr->tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING)); +} + +static inline void __v3_tx_user_ready(struct tpacket3_hdr *hdr) +{ + hdr->tp_status = TP_STATUS_SEND_REQUEST; + __sync_synchronize(); +} + +static inline int __tx_kernel_ready(void *base, int version) { switch (version) { case TPACKET_V1: return __v1_tx_kernel_ready(base); case TPACKET_V2: return __v2_tx_kernel_ready(base); + case TPACKET_V3: + return __v3_tx_kernel_ready(base); default: bug_on(1); return 0; } } -static inline void __v1_v2_tx_user_ready(void *base, int version) +static inline void __tx_user_ready(void *base, int version) { switch (version) { case TPACKET_V1: @@ -333,6 +345,9 @@ static inline void __v1_v2_tx_user_ready(void *base, int version) case TPACKET_V2: __v2_tx_user_ready(base); break; + case TPACKET_V3: + __v3_tx_user_ready(base); + break; } } @@ -348,7 +363,22 @@ static void __v1_v2_set_packet_loss_discard(int sock) } } -static void walk_v1_v2_tx(int sock, struct ring *ring) +static inline void *get_next_frame(struct ring *ring, int n) +{ + uint8_t *f0 = ring->rd[0].iov_base; + + switch (ring->version) { + case TPACKET_V1: + case TPACKET_V2: + return ring->rd[n].iov_base; + case TPACKET_V3: + return f0 + (n * ring->req3.tp_frame_size); + default: + bug_on(1); + } +} + +static void walk_tx(int sock, struct ring *ring) { struct pollfd pfd; int rcv_sock, ret; @@ -360,9 +390,19 @@ static void walk_v1_v2_tx(int sock, struct ring *ring) .sll_family = PF_PACKET, .sll_halen = ETH_ALEN, }; + int nframes; + + /* TPACKET_V{1,2} sets up the ring->rd* related variables based + * on frames (e.g., rd_num is tp_frame_nr) whereas V3 sets these + * up based on blocks (e.g, rd_num is tp_block_nr) + */ + if (ring->version <= TPACKET_V2) + nframes = ring->rd_num; + else + nframes = ring->req3.tp_frame_nr; bug_on(ring->type != PACKET_TX_RING); - bug_on(ring->rd_num < NUM_PACKETS); + bug_on(nframes < NUM_PACKETS); rcv_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (rcv_sock == -1) { @@ -388,10 +428,11 @@ static void walk_v1_v2_tx(int sock, struct ring *ring) create_payload(packet, &packet_len); while (total_packets > 0) { - while (__v1_v2_tx_kernel_ready(ring->rd[frame_num].iov_base, - ring->version) && + void *next = get_next_frame(ring, frame_num); + + while (__tx_kernel_ready(next, ring->version) && total_packets > 0) { - ppd.raw = ring->rd[frame_num].iov_base; + ppd.raw = next; switch (ring->version) { case TPACKET_V1: @@ -413,14 +454,27 @@ static void walk_v1_v2_tx(int sock, struct ring *ring) packet_len); total_bytes += ppd.v2->tp_h.tp_snaplen; break; + case TPACKET_V3: { + struct tpacket3_hdr *tx = next; + + tx->tp_snaplen = packet_len; + tx->tp_len = packet_len; + tx->tp_next_offset = 0; + + memcpy((uint8_t *)tx + TPACKET3_HDRLEN - + sizeof(struct sockaddr_ll), packet, + packet_len); + total_bytes += tx->tp_snaplen; + break; + } } status_bar_update(); total_packets--; - __v1_v2_tx_user_ready(ppd.raw, ring->version); + __tx_user_ready(next, ring->version); - frame_num = (frame_num + 1) % ring->rd_num; + frame_num = (frame_num + 1) % nframes; } poll(&pfd, 1, 1); @@ -460,7 +514,7 @@ static void walk_v1_v2(int sock, struct ring *ring) if (ring->type == PACKET_RX_RING) walk_v1_v2_rx(sock, ring); else - walk_v1_v2_tx(sock, ring); + walk_tx(sock, ring); } static uint64_t __v3_prev_block_seq_num = 0; @@ -546,7 +600,6 @@ static void walk_v3_rx(int sock, struct ring *ring) bug_on(ring->type != PACKET_RX_RING); pair_udp_open(udp_sock, PORT_BASE); - pair_udp_setfilter(sock); memset(&pfd, 0, sizeof(pfd)); pfd.fd = sock; @@ -583,7 +636,7 @@ static void walk_v3(int sock, struct ring *ring) if (ring->type == PACKET_RX_RING) walk_v3_rx(sock, ring); else - bug_on(1); + walk_tx(sock, ring); } static void __v1_v2_fill(struct ring *ring, unsigned int blocks) @@ -602,12 +655,13 @@ static void __v1_v2_fill(struct ring *ring, unsigned int blocks) ring->flen = ring->req.tp_frame_size; } -static void __v3_fill(struct ring *ring, unsigned int blocks) +static void __v3_fill(struct ring *ring, unsigned int blocks, int type) { - ring->req3.tp_retire_blk_tov = 64; - ring->req3.tp_sizeof_priv = 0; - ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; - + if (type == PACKET_RX_RING) { + ring->req3.tp_retire_blk_tov = 64; + ring->req3.tp_sizeof_priv = 0; + ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; + } ring->req3.tp_block_size = getpagesize() << 2; ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7; ring->req3.tp_block_nr = blocks; @@ -641,7 +695,7 @@ static void setup_ring(int sock, struct ring *ring, int version, int type) break; case TPACKET_V3: - __v3_fill(ring, blocks); + __v3_fill(ring, blocks, type); ret = setsockopt(sock, SOL_PACKET, type, &ring->req3, sizeof(ring->req3)); break; @@ -685,6 +739,8 @@ static void bind_ring(int sock, struct ring *ring) { int ret; + pair_udp_setfilter(sock); + ring->ll.sll_family = PF_PACKET; ring->ll.sll_protocol = htons(ETH_P_ALL); ring->ll.sll_ifindex = if_nametoindex("lo"); @@ -796,6 +852,7 @@ int main(void) ret |= test_tpacket(TPACKET_V2, PACKET_TX_RING); ret |= test_tpacket(TPACKET_V3, PACKET_RX_RING); + ret |= test_tpacket(TPACKET_V3, PACKET_TX_RING); if (ret) return 1; diff --git a/tools/testing/selftests/nsfs/Makefile b/tools/testing/selftests/nsfs/Makefile index 2306054a901a..9ff7c7f80625 100644 --- a/tools/testing/selftests/nsfs/Makefile +++ b/tools/testing/selftests/nsfs/Makefile @@ -1,12 +1,5 @@ -TEST_PROGS := owner pidns +TEST_GEN_PROGS := owner pidns CFLAGS := -Wall -Werror -all: owner pidns -owner: owner.c -pidns: pidns.c - -clean: - $(RM) owner pidns - include ../lib.mk diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index c2c4211ba58b..bf13fc2297aa 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile @@ -34,31 +34,35 @@ endif all: $(SUB_DIRS) $(SUB_DIRS): - $(MAKE) -k -C $@ all + BUILD_TARGET=$(OUTPUT)/$@; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $@ all include ../lib.mk override define RUN_TESTS @for TARGET in $(SUB_DIRS); do \ - $(MAKE) -C $$TARGET run_tests; \ + BUILD_TARGET=$(OUTPUT)/$$TARGET; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests;\ done; endef override define INSTALL_RULE @for TARGET in $(SUB_DIRS); do \ - $(MAKE) -C $$TARGET install; \ + BUILD_TARGET=$(OUTPUT)/$$TARGET; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install;\ done; endef override define EMIT_TESTS @for TARGET in $(SUB_DIRS); do \ - $(MAKE) -s -C $$TARGET emit_tests; \ + BUILD_TARGET=$(OUTPUT)/$$TARGET; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests;\ done; endef clean: @for TARGET in $(SUB_DIRS); do \ - $(MAKE) -C $$TARGET clean; \ + BUILD_TARGET=$(OUTPUT)/$$TARGET; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean; \ done; rm -f tags diff --git a/tools/testing/selftests/powerpc/alignment/Makefile b/tools/testing/selftests/powerpc/alignment/Makefile index ad6a4e49da91..16b22004e75f 100644 --- a/tools/testing/selftests/powerpc/alignment/Makefile +++ b/tools/testing/selftests/powerpc/alignment/Makefile @@ -1,10 +1,5 @@ -TEST_PROGS := copy_unaligned copy_first_unaligned paste_unaligned paste_last_unaligned - -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c ../utils.c copy_paste_unaligned_common.c +TEST_GEN_PROGS := copy_unaligned copy_first_unaligned paste_unaligned paste_last_unaligned include ../../lib.mk -clean: - rm -f $(TEST_PROGS) +$(TEST_GEN_PROGS): ../harness.c ../utils.c copy_paste_unaligned_common.c diff --git a/tools/testing/selftests/powerpc/benchmarks/Makefile b/tools/testing/selftests/powerpc/benchmarks/Makefile index 545077f98f72..fb96a89bd953 100644 --- a/tools/testing/selftests/powerpc/benchmarks/Makefile +++ b/tools/testing/selftests/powerpc/benchmarks/Makefile @@ -1,16 +1,11 @@ -TEST_PROGS := gettimeofday context_switch mmap_bench futex_bench null_syscall +TEST_GEN_PROGS := gettimeofday context_switch mmap_bench futex_bench null_syscall CFLAGS += -O2 -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c - -context_switch: ../utils.c -context_switch: CFLAGS += -maltivec -mvsx -mabi=altivec -context_switch: LDLIBS += -lpthread - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(TEST_GEN_PROGS): ../harness.c + +$(OUTPUT)/context_switch: ../utils.c +$(OUTPUT)/context_switch: CFLAGS += -maltivec -mvsx -mabi=altivec +$(OUTPUT)/context_switch: LDLIBS += -lpthread diff --git a/tools/testing/selftests/powerpc/context_switch/Makefile b/tools/testing/selftests/powerpc/context_switch/Makefile index e164d1466466..e9351bb4285d 100644 --- a/tools/testing/selftests/powerpc/context_switch/Makefile +++ b/tools/testing/selftests/powerpc/context_switch/Makefile @@ -1,10 +1,5 @@ -TEST_PROGS := cp_abort - -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c ../utils.c +TEST_GEN_PROGS := cp_abort include ../../lib.mk -clean: - rm -f $(TEST_PROGS) +$(TEST_GEN_PROGS): ../harness.c ../utils.c diff --git a/tools/testing/selftests/powerpc/copyloops/Makefile b/tools/testing/selftests/powerpc/copyloops/Makefile index 384843ea0d40..681ab19d0a84 100644 --- a/tools/testing/selftests/powerpc/copyloops/Makefile +++ b/tools/testing/selftests/powerpc/copyloops/Makefile @@ -7,19 +7,14 @@ CFLAGS += -maltivec # Use our CFLAGS for the implicit .S rule ASFLAGS = $(CFLAGS) -TEST_PROGS := copyuser_64 copyuser_power7 memcpy_64 memcpy_power7 +TEST_GEN_PROGS := copyuser_64 copyuser_power7 memcpy_64 memcpy_power7 EXTRA_SOURCES := validate.c ../harness.c -all: $(TEST_PROGS) - -copyuser_64: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_base -copyuser_power7: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_power7 -memcpy_64: CPPFLAGS += -D COPY_LOOP=test_memcpy -memcpy_power7: CPPFLAGS += -D COPY_LOOP=test_memcpy_power7 - -$(TEST_PROGS): $(EXTRA_SOURCES) - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(OUTPUT)/copyuser_64: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_base +$(OUTPUT)/copyuser_power7: CPPFLAGS += -D COPY_LOOP=test___copy_tofrom_user_power7 +$(OUTPUT)/memcpy_64: CPPFLAGS += -D COPY_LOOP=test_memcpy +$(OUTPUT)/memcpy_power7: CPPFLAGS += -D COPY_LOOP=test_memcpy_power7 + +$(TEST_GEN_PROGS): $(EXTRA_SOURCES) diff --git a/tools/testing/selftests/powerpc/dscr/Makefile b/tools/testing/selftests/powerpc/dscr/Makefile index 49327ee84e3a..c5639deb8887 100644 --- a/tools/testing/selftests/powerpc/dscr/Makefile +++ b/tools/testing/selftests/powerpc/dscr/Makefile @@ -1,14 +1,9 @@ -TEST_PROGS := dscr_default_test dscr_explicit_test dscr_user_test \ +TEST_GEN_PROGS := dscr_default_test dscr_explicit_test dscr_user_test \ dscr_inherit_test dscr_inherit_exec_test dscr_sysfs_test \ dscr_sysfs_thread_test -dscr_default_test: LDLIBS += -lpthread - -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(OUTPUT)/dscr_default_test: LDLIBS += -lpthread + +$(TEST_GEN_PROGS): ../harness.c diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index 248a820048df..66d31de60b9a 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c @@ -114,9 +114,11 @@ int test_harness(int (test_function)(void), char *name) rc = run_test(test_function, name); - if (rc == MAGIC_SKIP_RETURN_VALUE) + if (rc == MAGIC_SKIP_RETURN_VALUE) { test_skip(name); - else + /* so that skipped test is not marked as failed */ + rc = 0; + } else test_finish(name, rc); return rc; diff --git a/tools/testing/selftests/powerpc/include/vsx_asm.h b/tools/testing/selftests/powerpc/include/vsx_asm.h index d828bfb6ef2d..54064ced9e95 100644 --- a/tools/testing/selftests/powerpc/include/vsx_asm.h +++ b/tools/testing/selftests/powerpc/include/vsx_asm.h @@ -16,56 +16,56 @@ */ FUNC_START(load_vsx) li r5,0 - lxvx vs20,r5,r3 + lxvd2x vs20,r5,r3 addi r5,r5,16 - lxvx vs21,r5,r3 + lxvd2x vs21,r5,r3 addi r5,r5,16 - lxvx vs22,r5,r3 + lxvd2x vs22,r5,r3 addi r5,r5,16 - lxvx vs23,r5,r3 + lxvd2x vs23,r5,r3 addi r5,r5,16 - lxvx vs24,r5,r3 + lxvd2x vs24,r5,r3 addi r5,r5,16 - lxvx vs25,r5,r3 + lxvd2x vs25,r5,r3 addi r5,r5,16 - lxvx vs26,r5,r3 + lxvd2x vs26,r5,r3 addi r5,r5,16 - lxvx vs27,r5,r3 + lxvd2x vs27,r5,r3 addi r5,r5,16 - lxvx vs28,r5,r3 + lxvd2x vs28,r5,r3 addi r5,r5,16 - lxvx vs29,r5,r3 + lxvd2x vs29,r5,r3 addi r5,r5,16 - lxvx vs30,r5,r3 + lxvd2x vs30,r5,r3 addi r5,r5,16 - lxvx vs31,r5,r3 + lxvd2x vs31,r5,r3 blr FUNC_END(load_vsx) FUNC_START(store_vsx) li r5,0 - stxvx vs20,r5,r3 + stxvd2x vs20,r5,r3 addi r5,r5,16 - stxvx vs21,r5,r3 + stxvd2x vs21,r5,r3 addi r5,r5,16 - stxvx vs22,r5,r3 + stxvd2x vs22,r5,r3 addi r5,r5,16 - stxvx vs23,r5,r3 + stxvd2x vs23,r5,r3 addi r5,r5,16 - stxvx vs24,r5,r3 + stxvd2x vs24,r5,r3 addi r5,r5,16 - stxvx vs25,r5,r3 + stxvd2x vs25,r5,r3 addi r5,r5,16 - stxvx vs26,r5,r3 + stxvd2x vs26,r5,r3 addi r5,r5,16 - stxvx vs27,r5,r3 + stxvd2x vs27,r5,r3 addi r5,r5,16 - stxvx vs28,r5,r3 + stxvd2x vs28,r5,r3 addi r5,r5,16 - stxvx vs29,r5,r3 + stxvd2x vs29,r5,r3 addi r5,r5,16 - stxvx vs30,r5,r3 + stxvd2x vs30,r5,r3 addi r5,r5,16 - stxvx vs31,r5,r3 + stxvd2x vs31,r5,r3 blr FUNC_END(store_vsx) diff --git a/tools/testing/selftests/powerpc/math/Makefile b/tools/testing/selftests/powerpc/math/Makefile index a505b66d408a..fa8bae920c91 100644 --- a/tools/testing/selftests/powerpc/math/Makefile +++ b/tools/testing/selftests/powerpc/math/Makefile @@ -1,22 +1,17 @@ -TEST_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal vsx_preempt +TEST_GEN_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal vsx_preempt -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c -$(TEST_PROGS): CFLAGS += -O2 -g -pthread -m64 -maltivec - -fpu_syscall: fpu_asm.S -fpu_preempt: fpu_asm.S -fpu_signal: fpu_asm.S +include ../../lib.mk -vmx_syscall: vmx_asm.S -vmx_preempt: vmx_asm.S -vmx_signal: vmx_asm.S +$(TEST_GEN_PROGS): ../harness.c +$(TEST_GEN_PROGS): CFLAGS += -O2 -g -pthread -m64 -maltivec -vsx_preempt: CFLAGS += -mvsx -vsx_preempt: vsx_asm.S +$(OUTPUT)/fpu_syscall: fpu_asm.S +$(OUTPUT)/fpu_preempt: fpu_asm.S +$(OUTPUT)/fpu_signal: fpu_asm.S -include ../../lib.mk +$(OUTPUT)/vmx_syscall: vmx_asm.S +$(OUTPUT)/vmx_preempt: vmx_asm.S +$(OUTPUT)/vmx_signal: vmx_asm.S -clean: - rm -f $(TEST_PROGS) *.o +$(OUTPUT)/vsx_preempt: CFLAGS += -mvsx +$(OUTPUT)/vsx_preempt: vsx_asm.S diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 3bdb96eae558..1cffe54dccfb 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile @@ -1,19 +1,15 @@ noarg: $(MAKE) -C ../ -TEST_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao -TEST_FILES := tempfile +TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao +TEST_GEN_FILES := tempfile -all: $(TEST_PROGS) $(TEST_FILES) - -$(TEST_PROGS): ../harness.c +include ../../lib.mk -prot_sao: ../utils.c +$(TEST_GEN_PROGS): ../harness.c -include ../../lib.mk +$(OUTPUT)/prot_sao: ../utils.c -tempfile: - dd if=/dev/zero of=tempfile bs=64k count=1 +$(OUTPUT)/tempfile: + dd if=/dev/zero of=$@ bs=64k count=1 -clean: - rm -f $(TEST_PROGS) tempfile diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile index ac41a7177f2e..e4e55d1d3e0f 100644 --- a/tools/testing/selftests/powerpc/pmu/Makefile +++ b/tools/testing/selftests/powerpc/pmu/Makefile @@ -1,44 +1,44 @@ noarg: $(MAKE) -C ../ -TEST_PROGS := count_instructions l3_bank_test per_event_excludes +TEST_GEN_PROGS := count_instructions l3_bank_test per_event_excludes EXTRA_SOURCES := ../harness.c event.c lib.c ../utils.c -all: $(TEST_PROGS) ebb +include ../../lib.mk + +all: $(TEST_GEN_PROGS) ebb -$(TEST_PROGS): $(EXTRA_SOURCES) +$(TEST_GEN_PROGS): $(EXTRA_SOURCES) # loop.S can only be built 64-bit -count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) +$(OUTPUT)/count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) $(CC) $(CFLAGS) -m64 -o $@ $^ -per_event_excludes: ../utils.c - -include ../../lib.mk +$(OUTPUT)/per_event_excludes: ../utils.c DEFAULT_RUN_TESTS := $(RUN_TESTS) override define RUN_TESTS $(DEFAULT_RUN_TESTS) - $(MAKE) -C ebb run_tests + TARGET=ebb; 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) - $(MAKE) -s -C ebb emit_tests + TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests endef DEFAULT_INSTALL_RULE := $(INSTALL_RULE) override define INSTALL_RULE $(DEFAULT_INSTALL_RULE) - $(MAKE) -C ebb install + TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install endef clean: - rm -f $(TEST_PROGS) loop.o - $(MAKE) -C ebb clean + $(RM) $(TEST_GEN_PROGS) $(OUTPUT)/loop.o + TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean ebb: - $(MAKE) -k -C $@ all + TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all .PHONY: all run_tests clean ebb diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index 8d2279c4bb4b..6001fb0a377a 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -4,7 +4,7 @@ noarg: # The EBB handler is 64-bit code and everything links against it CFLAGS += -m64 -TEST_PROGS := reg_access_test event_attributes_test cycles_test \ +TEST_GEN_PROGS := reg_access_test event_attributes_test cycles_test \ cycles_with_freeze_test pmc56_overflow_test \ ebb_vs_cpu_event_test cpu_event_vs_ebb_test \ cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test \ @@ -16,16 +16,11 @@ TEST_PROGS := reg_access_test event_attributes_test cycles_test \ lost_exception_test no_handler_test \ cycles_with_mmcr2_test -all: $(TEST_PROGS) +include ../../../lib.mk -$(TEST_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c \ +$(TEST_GEN_PROGS): ../../harness.c ../../utils.c ../event.c ../lib.c \ ebb.c ebb_handler.S trace.c busy_loop.S -instruction_count_test: ../loop.S - -lost_exception_test: ../lib.c - -include ../../../lib.mk +$(OUTPUT)/instruction_count_test: ../loop.S -clean: - rm -f $(TEST_PROGS) +$(OUTPUT)/lost_exception_test: ../lib.c diff --git a/tools/testing/selftests/powerpc/primitives/Makefile b/tools/testing/selftests/powerpc/primitives/Makefile index b68c6221d3d1..175366db7be8 100644 --- a/tools/testing/selftests/powerpc/primitives/Makefile +++ b/tools/testing/selftests/powerpc/primitives/Makefile @@ -1,12 +1,7 @@ CFLAGS += -I$(CURDIR) -TEST_PROGS := load_unaligned_zeropad - -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c +TEST_GEN_PROGS := load_unaligned_zeropad include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(TEST_GEN_PROGS): ../harness.c diff --git a/tools/testing/selftests/powerpc/stringloops/Makefile b/tools/testing/selftests/powerpc/stringloops/Makefile index 2a728f4d2873..557b9379f3bb 100644 --- a/tools/testing/selftests/powerpc/stringloops/Makefile +++ b/tools/testing/selftests/powerpc/stringloops/Makefile @@ -2,14 +2,9 @@ CFLAGS += -m64 CFLAGS += -I$(CURDIR) -TEST_PROGS := memcmp +TEST_GEN_PROGS := memcmp EXTRA_SOURCES := memcmp_64.S ../harness.c -all: $(TEST_PROGS) - -$(TEST_PROGS): $(EXTRA_SOURCES) - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(TEST_GEN_PROGS): $(EXTRA_SOURCES) diff --git a/tools/testing/selftests/powerpc/switch_endian/Makefile b/tools/testing/selftests/powerpc/switch_endian/Makefile index e21d10674e54..b92c2a132c4f 100644 --- a/tools/testing/selftests/powerpc/switch_endian/Makefile +++ b/tools/testing/selftests/powerpc/switch_endian/Makefile @@ -1,18 +1,15 @@ -TEST_PROGS := switch_endian_test +TEST_GEN_PROGS := switch_endian_test ASFLAGS += -O2 -Wall -g -nostdlib -m64 -all: $(TEST_PROGS) +EXTRA_CLEAN = $(OUTPUT)/*.o $(OUTPUT)/check-reversed.S -switch_endian_test: check-reversed.S +include ../../lib.mk + +$(OUTPUT)/switch_endian_test: $(OUTPUT)/check-reversed.S -check-reversed.o: check.o +$(OUTPUT)/check-reversed.o: $(OUTPUT)/check.o $(CROSS_COMPILE)objcopy -j .text --reverse-bytes=4 -O binary $< $@ -check-reversed.S: check-reversed.o +$(OUTPUT)/check-reversed.S: $(OUTPUT)/check-reversed.o hexdump -v -e '/1 ".byte 0x%02X\n"' $< > $@ - -include ../../lib.mk - -clean: - rm -f $(TEST_PROGS) *.o check-reversed.S diff --git a/tools/testing/selftests/powerpc/syscalls/Makefile b/tools/testing/selftests/powerpc/syscalls/Makefile index b35c7945bec5..da22ca7c38c1 100644 --- a/tools/testing/selftests/powerpc/syscalls/Makefile +++ b/tools/testing/selftests/powerpc/syscalls/Makefile @@ -1,12 +1,7 @@ -TEST_PROGS := ipc_unmuxed +TEST_GEN_PROGS := ipc_unmuxed CFLAGS += -I../../../../../usr/include -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) *.o +$(TEST_GEN_PROGS): ../harness.c diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index c6c53c82fdd6..5576ee6a51f2 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -1,23 +1,19 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu \ tm-signal-context-chk-vmx tm-signal-context-chk-vsx -TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ +TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ tm-vmxcopy tm-fork tm-tar tm-tmspr $(SIGNAL_CONTEXT_CHK_TESTS) -all: $(TEST_PROGS) +include ../../lib.mk -$(TEST_PROGS): ../harness.c ../utils.c +$(TEST_GEN_PROGS): ../harness.c ../utils.c CFLAGS += -mhtm -tm-syscall: tm-syscall-asm.S -tm-syscall: CFLAGS += -I../../../../../usr/include -tm-tmspr: CFLAGS += -pthread +$(OUTPUT)/tm-syscall: tm-syscall-asm.S +$(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include +$(OUTPUT)/tm-tmspr: CFLAGS += -pthread +SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S $(SIGNAL_CONTEXT_CHK_TESTS): CFLAGS += -mhtm -m64 -mvsx - -include ../../lib.mk - -clean: - rm -f $(TEST_PROGS) *.o diff --git a/tools/testing/selftests/powerpc/vphn/Makefile b/tools/testing/selftests/powerpc/vphn/Makefile index a485f2e286ae..f8ced26748f8 100644 --- a/tools/testing/selftests/powerpc/vphn/Makefile +++ b/tools/testing/selftests/powerpc/vphn/Makefile @@ -1,12 +1,8 @@ -TEST_PROGS := test-vphn +TEST_GEN_PROGS := test-vphn CFLAGS += -m64 -all: $(TEST_PROGS) - -$(TEST_PROGS): ../harness.c - include ../../lib.mk -clean: - rm -f $(TEST_PROGS) +$(TEST_GEN_PROGS): ../harness.c + diff --git a/tools/testing/selftests/pstore/Makefile b/tools/testing/selftests/pstore/Makefile index bd7abe24ea08..c5f2440ba1f7 100644 --- a/tools/testing/selftests/pstore/Makefile +++ b/tools/testing/selftests/pstore/Makefile @@ -5,11 +5,9 @@ all: TEST_PROGS := pstore_tests pstore_post_reboot_tests TEST_FILES := common_tests pstore_crash_test +EXTRA_CLEAN := logs/* *uuid include ../lib.mk run_crash: @sh pstore_crash_test || { echo "pstore_crash_test: [FAIL]"; exit 1; } - -clean: - rm -rf logs/* *uuid diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile index 453927fea90c..8a2bc5562179 100644 --- a/tools/testing/selftests/ptrace/Makefile +++ b/tools/testing/selftests/ptrace/Makefile @@ -1,11 +1,5 @@ CFLAGS += -iquote../../../../include/uapi -Wall -peeksiginfo: peeksiginfo.c -all: peeksiginfo - -clean: - rm -f peeksiginfo - -TEST_PROGS := peeksiginfo +TEST_GEN_PROGS := peeksiginfo include ../lib.mk diff --git a/tools/testing/selftests/rcutorture/configs/lock/CFLIST b/tools/testing/selftests/rcutorture/configs/lock/CFLIST index b9611c523723..41bae5824339 100644 --- a/tools/testing/selftests/rcutorture/configs/lock/CFLIST +++ b/tools/testing/selftests/rcutorture/configs/lock/CFLIST @@ -4,3 +4,4 @@ LOCK03 LOCK04 LOCK05 LOCK06 +LOCK07 diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK07 b/tools/testing/selftests/rcutorture/configs/lock/LOCK07 new file mode 100644 index 000000000000..1d1da1477fc3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK07 @@ -0,0 +1,6 @@ +CONFIG_SMP=y +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_PREEMPT_NONE=n +CONFIG_PREEMPT_VOLUNTARY=n +CONFIG_PREEMPT=y diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot new file mode 100644 index 000000000000..97dadd1a9e45 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK07.boot @@ -0,0 +1 @@ +locktorture.torture_type=ww_mutex_lock diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon index f824b4c9d9d9..d2d2a86139db 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/CFcommon +++ b/tools/testing/selftests/rcutorture/configs/rcu/CFcommon @@ -1,5 +1,2 @@ CONFIG_RCU_TORTURE_TEST=y CONFIG_PRINTK_TIME=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY01 b/tools/testing/selftests/rcutorture/configs/rcu/TINY01 index 0a63e073a00c..6db705e55487 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TINY01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY01 @@ -7,6 +7,7 @@ CONFIG_HZ_PERIODIC=n CONFIG_NO_HZ_IDLE=y CONFIG_NO_HZ_FULL=n CONFIG_RCU_TRACE=n +#CHECK#CONFIG_RCU_STALL_COMMON=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_PREEMPT_COUNT=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 b/tools/testing/selftests/rcutorture/configs/rcu/TINY02 index f1892e0371c9..a59f7686e219 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY02 @@ -8,7 +8,8 @@ CONFIG_NO_HZ_IDLE=n CONFIG_NO_HZ_FULL=n CONFIG_RCU_TRACE=y CONFIG_PROVE_LOCKING=y +CONFIG_PROVE_RCU_REPEATEDLY=y #CHECK#CONFIG_PROVE_RCU=y CONFIG_DEBUG_LOCK_ALLOC=y -CONFIG_DEBUG_OBJECTS_RCU_HEAD=n +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_PREEMPT_COUNT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 index f572b873c620..359cb258f639 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 @@ -16,3 +16,6 @@ CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_RCU_BOOST=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 b/tools/testing/selftests/rcutorture/configs/rcu/TREE02 index ef6a22c44dea..c1ab5926568b 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02 @@ -20,3 +20,7 @@ CONFIG_PROVE_LOCKING=n CONFIG_RCU_BOOST=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 b/tools/testing/selftests/rcutorture/configs/rcu/TREE03 index 7a17c503b382..3b93ee544e70 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03 @@ -17,3 +17,6 @@ CONFIG_RCU_BOOST=y CONFIG_RCU_KTHREAD_PRIO=2 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 index 17cbe098b115..5af758e783c7 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 @@ -19,3 +19,7 @@ CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y +CONFIG_RCU_EQS_DEBUG=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 b/tools/testing/selftests/rcutorture/configs/rcu/TREE05 index 1257d3227b1e..d4cdc0d74e16 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05 @@ -19,3 +19,6 @@ CONFIG_PROVE_LOCKING=y #CHECK#CONFIG_PROVE_RCU=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 b/tools/testing/selftests/rcutorture/configs/rcu/TREE06 index d3e456b74cbe..4cb02bd28f08 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06 @@ -20,3 +20,6 @@ CONFIG_PROVE_LOCKING=y #CHECK#CONFIG_PROVE_RCU=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 index 3956b4131f72..b12a3ea1867e 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 @@ -19,3 +19,6 @@ CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y +CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 b/tools/testing/selftests/rcutorture/configs/rcu/TREE08 index bb9b0c1a23c2..099cc63c6a3b 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08 @@ -17,8 +17,8 @@ CONFIG_RCU_FANOUT_LEAF=2 CONFIG_RCU_NOCB_CPU=y CONFIG_RCU_NOCB_CPU_ALL=y CONFIG_DEBUG_LOCK_ALLOC=n -CONFIG_PROVE_LOCKING=y -#CHECK#CONFIG_PROVE_RCU=y +CONFIG_PROVE_LOCKING=n CONFIG_RCU_BOOST=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y +CONFIG_RCU_EQS_DEBUG=y diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt index 4e2b1893d40d..364801b1a230 100644 --- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt +++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt @@ -14,6 +14,7 @@ CONFIG_NO_HZ_FULL_SYSIDLE -- Do one. CONFIG_PREEMPT -- Do half. (First three and #8.) CONFIG_PROVE_LOCKING -- Do several, covering CONFIG_DEBUG_LOCK_ALLOC=y and not. CONFIG_PROVE_RCU -- Hardwired to CONFIG_PROVE_LOCKING. +CONFIG_PROVE_RCU_REPEATEDLY -- Do one. CONFIG_RCU_BOOST -- one of PREEMPT_RCU. CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing. CONFIG_RCU_FANOUT -- Cover hierarchy, but overlap with others. @@ -25,7 +26,12 @@ CONFIG_RCU_NOCB_CPU_NONE -- Do one. CONFIG_RCU_NOCB_CPU_ZERO -- Do one. CONFIG_RCU_TRACE -- Do half. CONFIG_SMP -- Need one !SMP for PREEMPT_RCU. -!RCU_EXPERT -- Do a few, but these have to be vanilla configurations. +CONFIG_RCU_EXPERT=n -- Do a few, but these have to be vanilla configurations. +CONFIG_RCU_EQS_DEBUG -- Do at least one for CONFIG_NO_HZ_FULL and not. +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP -- Do for all but a couple TREE scenarios. +CONFIG_RCU_TORTURE_TEST_SLOW_INIT -- Do for all but a couple TREE scenarios. +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT -- Do for all but a couple TREE scenarios. + RCU-bh: Do one with PREEMPT and one with !PREEMPT. RCU-sched: Do one with PREEMPT but not BOOST. @@ -72,7 +78,30 @@ CONFIG_RCU_TORTURE_TEST_RUNNABLE Always used in KVM testing. +CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT_DELAY +CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY +CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP_DELAY + + Inspection suffices, ignore. + CONFIG_PREEMPT_RCU CONFIG_TREE_RCU +CONFIG_TINY_RCU + + These are controlled by CONFIG_PREEMPT and/or CONFIG_SMP. + +CONFIG_SPARSE_RCU_POINTER + + Makes sense only for sparse runs, not for kernel builds. + +CONFIG_SRCU +CONFIG_TASKS_RCU + + Selected by CONFIG_RCU_TORTURE_TEST, so cannot disable. + +CONFIG_RCU_TRACE + + Implied by CONFIG_RCU_TRACE for Tree RCU. + - These are controlled by CONFIG_PREEMPT. +boot parameters ignored: TBD diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore new file mode 100644 index 000000000000..712a3d41a325 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/.gitignore @@ -0,0 +1 @@ +srcu.c diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile new file mode 100644 index 000000000000..16b01559fa55 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/Makefile @@ -0,0 +1,16 @@ +all: srcu.c store_buffering + +LINUX_SOURCE = ../../../../../.. + +modified_srcu_input = $(LINUX_SOURCE)/include/linux/srcu.h \ + $(LINUX_SOURCE)/kernel/rcu/srcu.c + +modified_srcu_output = include/linux/srcu.h srcu.c + +include/linux/srcu.h: srcu.c + +srcu.c: modify_srcu.awk Makefile $(modified_srcu_input) + awk -f modify_srcu.awk $(modified_srcu_input) $(modified_srcu_output) + +store_buffering: + @cd tests/store_buffering; make diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/delay.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/export.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/mutex.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/percpu.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/preempt.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/rcupdate.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/sched.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/smp.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/linux/workqueue.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/empty_includes/uapi/linux/types.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore new file mode 100644 index 000000000000..1d016e66980a --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/.gitignore @@ -0,0 +1 @@ +srcu.h diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h new file mode 100644 index 000000000000..f2860dd1b407 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/kconfig.h @@ -0,0 +1 @@ +#include <LINUX_SOURCE/linux/kconfig.h> diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h new file mode 100644 index 000000000000..4a3d538fef12 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/include/linux/types.h @@ -0,0 +1,155 @@ +/* + * This header has been modifies to remove definitions of types that + * are defined in standard userspace headers or are problematic for some + * other reason. + */ + +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#define __EXPORTED_HEADERS__ +#include <uapi/linux/types.h> + +#ifndef __ASSEMBLY__ + +#define DECLARE_BITMAP(name, bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +typedef __u32 __kernel_dev_t; + +/* bsd */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +/* sysv */ +typedef unsigned char unchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ + +typedef __u8 u_int8_t; +typedef __s8 int8_t; +typedef __u16 u_int16_t; +typedef __s16 int16_t; +typedef __u32 u_int32_t; +typedef __s32 int32_t; + +#endif /* !(__BIT_TYPES_DEFINED__) */ + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; + +/* this is a special 64bit data type that is 8-byte aligned */ +#define aligned_u64 __u64 __attribute__((aligned(8))) +#define aligned_be64 __be64 __attribute__((aligned(8))) +#define aligned_le64 __le64 __attribute__((aligned(8))) + +/** + * The type used for indexing onto a disc or disc partition. + * + * Linux always considers sectors to be 512 bytes long independently + * of the devices real block size. + * + * blkcnt_t is the type of the inode's block count. + */ +#ifdef CONFIG_LBDAF +typedef u64 sector_t; +#else +typedef unsigned long sector_t; +#endif + +/* + * The type of an index into the pagecache. + */ +#define pgoff_t unsigned long + +/* + * A dma_addr_t can hold any valid DMA address, i.e., any address returned + * by the DMA API. + * + * If the DMA API only uses 32-bit addresses, dma_addr_t need only be 32 + * bits wide. Bus addresses, e.g., PCI BARs, may be wider than 32 bits, + * but drivers do memory-mapped I/O to ioremapped kernel virtual addresses, + * so they don't care about the size of the actual bus addresses. + */ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +typedef u64 dma_addr_t; +#else +typedef u32 dma_addr_t; +#endif + +#ifdef CONFIG_PHYS_ADDR_T_64BIT +typedef u64 phys_addr_t; +#else +typedef u32 phys_addr_t; +#endif + +typedef phys_addr_t resource_size_t; + +/* + * This type is the placeholder for a hardware interrupt number. It has to be + * big enough to enclose whatever representation is used by a given platform. + */ +typedef unsigned long irq_hw_number_t; + +typedef struct { + int counter; +} atomic_t; + +#ifdef CONFIG_64BIT +typedef struct { + long counter; +} atomic64_t; +#endif + +struct list_head { + struct list_head *next, *prev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +/** + * struct callback_head - callback structure for use with RCU and task_work + * @next: next update requests in a list + * @func: actual update function to call after the grace period. + * + * The struct is aligned to size of pointer. On most architectures it happens + * naturally due ABI requirements, but some architectures (like CRIS) have + * weird ABI and we need to ask it explicitly. + * + * The alignment is required to guarantee that bits 0 and 1 of @next will be + * clear under normal conditions -- as long as we use call_rcu(), + * call_rcu_bh(), call_rcu_sched(), or call_srcu() to queue callback. + * + * This guarantee is important for few reasons: + * - future call_rcu_lazy() will make use of lower bits in the pointer; + * - the structure shares storage spacer in struct page with @compound_head, + * which encode PageTail() in bit 0. The guarantee is needed to avoid + * false-positive PageTail(). + */ +struct callback_head { + struct callback_head *next; + void (*func)(struct callback_head *head); +} __attribute__((aligned(sizeof(void *)))); +#define rcu_head callback_head + +typedef void (*rcu_callback_t)(struct rcu_head *head); +typedef void (*call_rcu_func_t)(struct rcu_head *head, rcu_callback_t func); + +/* clocksource cycle base type */ +typedef u64 cycle_t; + +#endif /* __ASSEMBLY__ */ +#endif /* _LINUX_TYPES_H */ diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk new file mode 100755 index 000000000000..8ff89043d0a9 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk @@ -0,0 +1,375 @@ +#!/bin/awk -f + +# Modify SRCU for formal verification. The first argument should be srcu.h and +# the second should be srcu.c. Outputs modified srcu.h and srcu.c into the +# current directory. + +BEGIN { + if (ARGC != 5) { + print "Usange: input.h input.c output.h output.c" > "/dev/stderr"; + exit 1; + } + h_output = ARGV[3]; + c_output = ARGV[4]; + ARGC = 3; + + # Tokenize using FS and not RS as FS supports regular expressions. Each + # record is one line of source, except that backslashed lines are + # combined. Comments are treated as field separators, as are quotes. + quote_regexp="\"([^\\\\\"]|\\\\.)*\""; + comment_regexp="\\/\\*([^*]|\\*+[^*/])*\\*\\/|\\/\\/.*(\n|$)"; + FS="([ \\\\\t\n\v\f;,.=(){}+*/<>&|^-]|\\[|\\]|" comment_regexp "|" quote_regexp ")+"; + + inside_srcu_struct = 0; + inside_srcu_init_def = 0; + srcu_init_param_name = ""; + in_macro = 0; + brace_nesting = 0; + paren_nesting = 0; + + # Allow the manipulation of the last field separator after has been + # seen. + last_fs = ""; + # Whether the last field separator was intended to be output. + last_fs_print = 0; + + # rcu_batches stores the initialization for each instance of struct + # rcu_batch + + in_comment = 0; + + outputfile = ""; +} + +{ + prev_outputfile = outputfile; + if (FILENAME ~ /\.h$/) { + outputfile = h_output; + if (FNR != NR) { + print "Incorrect file order" > "/dev/stderr"; + exit 1; + } + } + else + outputfile = c_output; + + if (prev_outputfile && outputfile != prev_outputfile) { + new_outputfile = outputfile; + outputfile = prev_outputfile; + update_fieldsep("", 0); + outputfile = new_outputfile; + } +} + +# Combine the next line into $0. +function combine_line() { + ret = getline next_line; + if (ret == 0) { + # Don't allow two consecutive getlines at the end of the file + if (eof_found) { + print "Error: expected more input." > "/dev/stderr"; + exit 1; + } else { + eof_found = 1; + } + } else if (ret == -1) { + print "Error reading next line of file" FILENAME > "/dev/stderr"; + exit 1; + } + $0 = $0 "\n" next_line; +} + +# Combine backslashed lines and multiline comments. +function combine_backslashes() { + while (/\\$|\/\*([^*]|\*+[^*\/])*\**$/) { + combine_line(); + } +} + +function read_line() { + combine_line(); + combine_backslashes(); +} + +# Print out field separators and update variables that depend on them. Only +# print if p is true. Call with sep="" and p=0 to print out the last field +# separator. +function update_fieldsep(sep, p) { + # Count braces + sep_tmp = sep; + gsub(quote_regexp "|" comment_regexp, "", sep_tmp); + while (1) + { + if (sub("[^{}()]*\\{", "", sep_tmp)) { + brace_nesting++; + continue; + } + if (sub("[^{}()]*\\}", "", sep_tmp)) { + brace_nesting--; + if (brace_nesting < 0) { + print "Unbalanced braces!" > "/dev/stderr"; + exit 1; + } + continue; + } + if (sub("[^{}()]*\\(", "", sep_tmp)) { + paren_nesting++; + continue; + } + if (sub("[^{}()]*\\)", "", sep_tmp)) { + paren_nesting--; + if (paren_nesting < 0) { + print "Unbalanced parenthesis!" > "/dev/stderr"; + exit 1; + } + continue; + } + + break; + } + + if (last_fs_print) + printf("%s", last_fs) > outputfile; + last_fs = sep; + last_fs_print = p; +} + +# Shifts the fields down by n positions. Calls next if there are no more. If p +# is true then print out field separators. +function shift_fields(n, p) { + do { + if (match($0, FS) > 0) { + update_fieldsep(substr($0, RSTART, RLENGTH), p); + if (RSTART + RLENGTH <= length()) + $0 = substr($0, RSTART + RLENGTH); + else + $0 = ""; + } else { + update_fieldsep("", 0); + print "" > outputfile; + next; + } + } while (--n > 0); +} + +# Shifts and prints the first n fields. +function print_fields(n) { + do { + update_fieldsep("", 0); + printf("%s", $1) > outputfile; + shift_fields(1, 1); + } while (--n > 0); +} + +{ + combine_backslashes(); +} + +# Print leading FS +{ + if (match($0, "^(" FS ")+") > 0) { + update_fieldsep(substr($0, RSTART, RLENGTH), 1); + if (RSTART + RLENGTH <= length()) + $0 = substr($0, RSTART + RLENGTH); + else + $0 = ""; + } +} + +# Parse the line. +{ + while (NF > 0) { + if ($1 == "struct" && NF < 3) { + read_line(); + continue; + } + + if (FILENAME ~ /\.h$/ && !inside_srcu_struct && + brace_nesting == 0 && paren_nesting == 0 && + $1 == "struct" && $2 == "srcu_struct" && + $0 ~ "^struct(" FS ")+srcu_struct(" FS ")+\\{") { + inside_srcu_struct = 1; + print_fields(2); + continue; + } + if (inside_srcu_struct && brace_nesting == 0 && + paren_nesting == 0) { + inside_srcu_struct = 0; + update_fieldsep("", 0); + for (name in rcu_batches) + print "extern struct rcu_batch " name ";" > outputfile; + } + + if (inside_srcu_struct && $1 == "struct" && $2 == "rcu_batch") { + # Move rcu_batches outside of the struct. + rcu_batches[$3] = ""; + shift_fields(3, 1); + sub(/;[[:space:]]*$/, "", last_fs); + continue; + } + + if (FILENAME ~ /\.h$/ && !inside_srcu_init_def && + $1 == "#define" && $2 == "__SRCU_STRUCT_INIT") { + inside_srcu_init_def = 1; + srcu_init_param_name = $3; + in_macro = 1; + print_fields(3); + continue; + } + if (inside_srcu_init_def && brace_nesting == 0 && + paren_nesting == 0) { + inside_srcu_init_def = 0; + in_macro = 0; + continue; + } + + if (inside_srcu_init_def && brace_nesting == 1 && + paren_nesting == 0 && last_fs ~ /\.[[:space:]]*$/ && + $1 ~ /^[[:alnum:]_]+$/) { + name = $1; + if (name in rcu_batches) { + # Remove the dot. + sub(/\.[[:space:]]*$/, "", last_fs); + + old_record = $0; + do + shift_fields(1, 0); + while (last_fs !~ /,/ || paren_nesting > 0); + end_loc = length(old_record) - length($0); + end_loc += index(last_fs, ",") - length(last_fs); + + last_fs = substr(last_fs, index(last_fs, ",") + 1); + last_fs_print = 1; + + match(old_record, "^"name"("FS")+="); + start_loc = RSTART + RLENGTH; + + len = end_loc - start_loc; + initializer = substr(old_record, start_loc, len); + gsub(srcu_init_param_name "\\.", "", initializer); + rcu_batches[name] = initializer; + continue; + } + } + + # Don't include a nonexistent file + if (!in_macro && $1 == "#include" && /^#include[[:space:]]+"rcu\.h"/) { + update_fieldsep("", 0); + next; + } + + # Ignore most preprocessor stuff. + if (!in_macro && $1 ~ /#/) { + break; + } + + if (brace_nesting > 0 && $1 ~ "^[[:alnum:]_]+$" && NF < 2) { + read_line(); + continue; + } + if (brace_nesting > 0 && + $0 ~ "^[[:alnum:]_]+[[:space:]]*(\\.|->)[[:space:]]*[[:alnum:]_]+" && + $2 in rcu_batches) { + # Make uses of rcu_batches global. Somewhat unreliable. + shift_fields(1, 0); + print_fields(1); + continue; + } + + if ($1 == "static" && NF < 3) { + read_line(); + continue; + } + if ($1 == "static" && ($2 == "bool" && $3 == "try_check_zero" || + $2 == "void" && $3 == "srcu_flip")) { + shift_fields(1, 1); + print_fields(2); + continue; + } + + # Distinguish between read-side and write-side memory barriers. + if ($1 == "smp_mb" && NF < 2) { + read_line(); + continue; + } + if (match($0, /^smp_mb[[:space:]();\/*]*[[:alnum:]]/)) { + barrier_letter = substr($0, RLENGTH, 1); + if (barrier_letter ~ /A|D/) + new_barrier_name = "sync_smp_mb"; + else if (barrier_letter ~ /B|C/) + new_barrier_name = "rs_smp_mb"; + else { + print "Unrecognized memory barrier." > "/dev/null"; + exit 1; + } + + shift_fields(1, 1); + printf("%s", new_barrier_name) > outputfile; + continue; + } + + # Skip definition of rcu_synchronize, since it is already + # defined in misc.h. Only present in old versions of srcu. + if (brace_nesting == 0 && paren_nesting == 0 && + $1 == "struct" && $2 == "rcu_synchronize" && + $0 ~ "^struct(" FS ")+rcu_synchronize(" FS ")+\\{") { + shift_fields(2, 0); + while (brace_nesting) { + if (NF < 2) + read_line(); + shift_fields(1, 0); + } + } + + # Skip definition of wakeme_after_rcu for the same reason + if (brace_nesting == 0 && $1 == "static" && $2 == "void" && + $3 == "wakeme_after_rcu") { + while (NF < 5) + read_line(); + shift_fields(3, 0); + do { + while (NF < 3) + read_line(); + shift_fields(1, 0); + } while (paren_nesting || brace_nesting); + } + + if ($1 ~ /^(unsigned|long)$/ && NF < 3) { + read_line(); + continue; + } + + # Give srcu_batches_completed the correct type for old SRCU. + if (brace_nesting == 0 && $1 == "long" && + $2 == "srcu_batches_completed") { + update_fieldsep("", 0); + printf("unsigned ") > outputfile; + print_fields(2); + continue; + } + if (brace_nesting == 0 && $1 == "unsigned" && $2 == "long" && + $3 == "srcu_batches_completed") { + print_fields(3); + continue; + } + + # Just print out the input code by default. + print_fields(1); + } + update_fieldsep("", 0); + print > outputfile; + next; +} + +END { + update_fieldsep("", 0); + + if (brace_nesting != 0) { + print "Unbalanced braces!" > "/dev/stderr"; + exit 1; + } + + # Define the rcu_batches + for (name in rcu_batches) + print "struct rcu_batch " name " = " rcu_batches[name] ";" > c_output; +} diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h new file mode 100644 index 000000000000..a64955447995 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/assume.h @@ -0,0 +1,16 @@ +#ifndef ASSUME_H +#define ASSUME_H + +/* Provide an assumption macro that can be disabled for gcc. */ +#ifdef RUN +#define assume(x) \ + do { \ + /* Evaluate x to suppress warnings. */ \ + (void) (x); \ + } while (0) + +#else +#define assume(x) __CPROVER_assume(x) +#endif + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h new file mode 100644 index 000000000000..6687acc08e6d --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h @@ -0,0 +1,41 @@ +#ifndef BARRIERS_H +#define BARRIERS_H + +#define barrier() __asm__ __volatile__("" : : : "memory") + +#ifdef RUN +#define smp_mb() __sync_synchronize() +#define smp_mb__after_unlock_lock() __sync_synchronize() +#else +/* + * Copied from CBMC's implementation of __sync_synchronize(), which + * seems to be disabled by default. + */ +#define smp_mb() __CPROVER_fence("WWfence", "RRfence", "RWfence", "WRfence", \ + "WWcumul", "RRcumul", "RWcumul", "WRcumul") +#define smp_mb__after_unlock_lock() __CPROVER_fence("WWfence", "RRfence", "RWfence", "WRfence", \ + "WWcumul", "RRcumul", "RWcumul", "WRcumul") +#endif + +/* + * Allow memory barriers to be disabled in either the read or write side + * of SRCU individually. + */ + +#ifndef NO_SYNC_SMP_MB +#define sync_smp_mb() smp_mb() +#else +#define sync_smp_mb() do {} while (0) +#endif + +#ifndef NO_READ_SIDE_SMP_MB +#define rs_smp_mb() smp_mb() +#else +#define rs_smp_mb() do {} while (0) +#endif + +#define ACCESS_ONCE(x) (*(volatile typeof(x) *) &(x)) +#define READ_ONCE(x) ACCESS_ONCE(x) +#define WRITE_ONCE(x, val) (ACCESS_ONCE(x) = (val)) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h new file mode 100644 index 000000000000..2a80e91f78e7 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/bug_on.h @@ -0,0 +1,13 @@ +#ifndef BUG_ON_H +#define BUG_ON_H + +#include <assert.h> + +#define BUG() assert(0) +#define BUG_ON(x) assert(!(x)) + +/* Does it make sense to treat warnings as errors? */ +#define WARN() BUG() +#define WARN_ON(x) (BUG_ON(x), false) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c new file mode 100644 index 000000000000..29eb5d2697ed --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/combined_source.c @@ -0,0 +1,13 @@ +#include <config.h> + +/* Include all source files. */ + +#include "include_srcu.c" + +#include "preempt.c" +#include "misc.c" + +/* Used by test.c files */ +#include <pthread.h> +#include <stdlib.h> +#include <linux/srcu.h> diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h new file mode 100644 index 000000000000..a60038aeea7a --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/config.h @@ -0,0 +1,27 @@ +/* "Cheater" definitions based on restricted Kconfig choices. */ + +#undef CONFIG_TINY_RCU +#undef __CHECKER__ +#undef CONFIG_DEBUG_LOCK_ALLOC +#undef CONFIG_DEBUG_OBJECTS_RCU_HEAD +#undef CONFIG_HOTPLUG_CPU +#undef CONFIG_MODULES +#undef CONFIG_NO_HZ_FULL_SYSIDLE +#undef CONFIG_PREEMPT_COUNT +#undef CONFIG_PREEMPT_RCU +#undef CONFIG_PROVE_RCU +#undef CONFIG_RCU_NOCB_CPU +#undef CONFIG_RCU_NOCB_CPU_ALL +#undef CONFIG_RCU_STALL_COMMON +#undef CONFIG_RCU_TRACE +#undef CONFIG_RCU_USER_QS +#undef CONFIG_TASKS_RCU +#define CONFIG_TREE_RCU + +#define CONFIG_GENERIC_ATOMIC64 + +#if NR_CPUS > 1 +#define CONFIG_SMP +#else +#undef CONFIG_SMP +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c new file mode 100644 index 000000000000..5ec582a53018 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/include_srcu.c @@ -0,0 +1,31 @@ +#include <config.h> + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <pthread.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> + +#include "int_typedefs.h" + +#include "barriers.h" +#include "bug_on.h" +#include "locks.h" +#include "misc.h" +#include "preempt.h" +#include "percpu.h" +#include "workqueues.h" + +#ifdef USE_SIMPLE_SYNC_SRCU +#define synchronize_srcu(sp) synchronize_srcu_original(sp) +#endif + +#include <srcu.c> + +#ifdef USE_SIMPLE_SYNC_SRCU +#undef synchronize_srcu + +#include "simple_sync_srcu.c" +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h new file mode 100644 index 000000000000..3aad63917858 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/int_typedefs.h @@ -0,0 +1,33 @@ +#ifndef INT_TYPEDEFS_H +#define INT_TYPEDEFS_H + +#include <inttypes.h> + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; + +typedef int8_t __s8; +typedef uint8_t __u8; +typedef int16_t __s16; +typedef uint16_t __u16; +typedef int32_t __s32; +typedef uint32_t __u32; +typedef int64_t __s64; +typedef uint64_t __u64; + +#define S8_C(x) INT8_C(x) +#define U8_C(x) UINT8_C(x) +#define S16_C(x) INT16_C(x) +#define U16_C(x) UINT16_C(x) +#define S32_C(x) INT32_C(x) +#define U32_C(x) UINT32_C(x) +#define S64_C(x) INT64_C(x) +#define U64_C(x) UINT64_C(x) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h new file mode 100644 index 000000000000..356004665576 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/locks.h @@ -0,0 +1,220 @@ +#ifndef LOCKS_H +#define LOCKS_H + +#include <limits.h> +#include <pthread.h> +#include <stdbool.h> + +#include "assume.h" +#include "bug_on.h" +#include "preempt.h" + +int nondet_int(void); + +#define __acquire(x) +#define __acquires(x) +#define __release(x) +#define __releases(x) + +/* Only use one lock mechanism. Select which one. */ +#ifdef PTHREAD_LOCK +struct lock_impl { + pthread_mutex_t mutex; +}; + +static inline void lock_impl_lock(struct lock_impl *lock) +{ + BUG_ON(pthread_mutex_lock(&lock->mutex)); +} + +static inline void lock_impl_unlock(struct lock_impl *lock) +{ + BUG_ON(pthread_mutex_unlock(&lock->mutex)); +} + +static inline bool lock_impl_trylock(struct lock_impl *lock) +{ + int err = pthread_mutex_trylock(&lock->mutex); + + if (!err) + return true; + else if (err == EBUSY) + return false; + BUG(); +} + +static inline void lock_impl_init(struct lock_impl *lock) +{ + pthread_mutex_init(&lock->mutex, NULL); +} + +#define LOCK_IMPL_INITIALIZER {.mutex = PTHREAD_MUTEX_INITIALIZER} + +#else /* !defined(PTHREAD_LOCK) */ +/* Spinlock that assumes that it always gets the lock immediately. */ + +struct lock_impl { + bool locked; +}; + +static inline bool lock_impl_trylock(struct lock_impl *lock) +{ +#ifdef RUN + /* TODO: Should this be a test and set? */ + return __sync_bool_compare_and_swap(&lock->locked, false, true); +#else + __CPROVER_atomic_begin(); + bool old_locked = lock->locked; + lock->locked = true; + __CPROVER_atomic_end(); + + /* Minimal barrier to prevent accesses leaking out of lock. */ + __CPROVER_fence("RRfence", "RWfence"); + + return !old_locked; +#endif +} + +static inline void lock_impl_lock(struct lock_impl *lock) +{ + /* + * CBMC doesn't support busy waiting, so just assume that the + * lock is available. + */ + assume(lock_impl_trylock(lock)); + + /* + * If the lock was already held by this thread then the assumption + * is unsatisfiable (deadlock). + */ +} + +static inline void lock_impl_unlock(struct lock_impl *lock) +{ +#ifdef RUN + BUG_ON(!__sync_bool_compare_and_swap(&lock->locked, true, false)); +#else + /* Minimal barrier to prevent accesses leaking out of lock. */ + __CPROVER_fence("RWfence", "WWfence"); + + __CPROVER_atomic_begin(); + bool old_locked = lock->locked; + lock->locked = false; + __CPROVER_atomic_end(); + + BUG_ON(!old_locked); +#endif +} + +static inline void lock_impl_init(struct lock_impl *lock) +{ + lock->locked = false; +} + +#define LOCK_IMPL_INITIALIZER {.locked = false} + +#endif /* !defined(PTHREAD_LOCK) */ + +/* + * Implement spinlocks using the lock mechanism. Wrap the lock to prevent mixing + * locks of different types. + */ +typedef struct { + struct lock_impl internal_lock; +} spinlock_t; + +#define SPIN_LOCK_UNLOCKED {.internal_lock = LOCK_IMPL_INITIALIZER} +#define __SPIN_LOCK_UNLOCKED(x) SPIN_LOCK_UNLOCKED +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED + +static inline void spin_lock_init(spinlock_t *lock) +{ + lock_impl_init(&lock->internal_lock); +} + +static inline void spin_lock(spinlock_t *lock) +{ + /* + * Spin locks also need to be removed in order to eliminate all + * memory barriers. They are only used by the write side anyway. + */ +#ifndef NO_SYNC_SMP_MB + preempt_disable(); + lock_impl_lock(&lock->internal_lock); +#endif +} + +static inline void spin_unlock(spinlock_t *lock) +{ +#ifndef NO_SYNC_SMP_MB + lock_impl_unlock(&lock->internal_lock); + preempt_enable(); +#endif +} + +/* Don't bother with interrupts */ +#define spin_lock_irq(lock) spin_lock(lock) +#define spin_unlock_irq(lock) spin_unlock(lock) +#define spin_lock_irqsave(lock, flags) spin_lock(lock) +#define spin_unlock_irqrestore(lock, flags) spin_unlock(lock) + +/* + * This is supposed to return an int, but I think that a bool should work as + * well. + */ +static inline bool spin_trylock(spinlock_t *lock) +{ +#ifndef NO_SYNC_SMP_MB + preempt_disable(); + return lock_impl_trylock(&lock->internal_lock); +#else + return true; +#endif +} + +struct completion { + /* Hopefuly this won't overflow. */ + unsigned int count; +}; + +#define COMPLETION_INITIALIZER(x) {.count = 0} +#define DECLARE_COMPLETION(x) struct completion x = COMPLETION_INITIALIZER(x) +#define DECLARE_COMPLETION_ONSTACK(x) DECLARE_COMPLETION(x) + +static inline void init_completion(struct completion *c) +{ + c->count = 0; +} + +static inline void wait_for_completion(struct completion *c) +{ + unsigned int prev_count = __sync_fetch_and_sub(&c->count, 1); + + assume(prev_count); +} + +static inline void complete(struct completion *c) +{ + unsigned int prev_count = __sync_fetch_and_add(&c->count, 1); + + BUG_ON(prev_count == UINT_MAX); +} + +/* This function probably isn't very useful for CBMC. */ +static inline bool try_wait_for_completion(struct completion *c) +{ + BUG(); +} + +static inline bool completion_done(struct completion *c) +{ + return c->count; +} + +/* TODO: Implement complete_all */ +static inline void complete_all(struct completion *c) +{ + BUG(); +} + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c new file mode 100644 index 000000000000..ca892e3b2351 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.c @@ -0,0 +1,11 @@ +#include <config.h> + +#include "misc.h" +#include "bug_on.h" + +struct rcu_head; + +void wakeme_after_rcu(struct rcu_head *head) +{ + BUG(); +} diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h new file mode 100644 index 000000000000..aca50030f954 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/misc.h @@ -0,0 +1,58 @@ +#ifndef MISC_H +#define MISC_H + +#include "assume.h" +#include "int_typedefs.h" +#include "locks.h" + +#include <linux/types.h> + +/* Probably won't need to deal with bottom halves. */ +static inline void local_bh_disable(void) {} +static inline void local_bh_enable(void) {} + +#define MODULE_ALIAS(X) +#define module_param(...) +#define EXPORT_SYMBOL_GPL(x) + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ +}) + +#ifndef USE_SIMPLE_SYNC_SRCU +/* Abuse udelay to make sure that busy loops terminate. */ +#define udelay(x) assume(0) + +#else + +/* The simple custom synchronize_srcu is ok with try_check_zero failing. */ +#define udelay(x) do { } while (0) +#endif + +#define trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ + do { } while (0) + +#define notrace + +/* Avoid including rcupdate.h */ +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; + +void wakeme_after_rcu(struct rcu_head *head); + +#define rcu_lock_acquire(a) do { } while (0) +#define rcu_lock_release(a) do { } while (0) +#define rcu_lockdep_assert(c, s) do { } while (0) +#define RCU_LOCKDEP_WARN(c, s) do { } while (0) + +/* Let CBMC non-deterministically choose switch between normal and expedited. */ +bool rcu_gp_is_normal(void); +bool rcu_gp_is_expedited(void); + +/* Do the same for old versions of rcu. */ +#define rcu_expedited (rcu_gp_is_expedited()) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h new file mode 100644 index 000000000000..3de5a49de49b --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/percpu.h @@ -0,0 +1,92 @@ +#ifndef PERCPU_H +#define PERCPU_H + +#include <stddef.h> +#include "bug_on.h" +#include "preempt.h" + +#define __percpu + +/* Maximum size of any percpu data. */ +#define PERCPU_OFFSET (4 * sizeof(long)) + +/* Ignore alignment, as CBMC doesn't care about false sharing. */ +#define alloc_percpu(type) __alloc_percpu(sizeof(type), 1) + +static inline void *__alloc_percpu(size_t size, size_t align) +{ + BUG(); + return NULL; +} + +static inline void free_percpu(void *ptr) +{ + BUG(); +} + +#define per_cpu_ptr(ptr, cpu) \ + ((typeof(ptr)) ((char *) (ptr) + PERCPU_OFFSET * cpu)) + +#define __this_cpu_inc(pcp) __this_cpu_add(pcp, 1) +#define __this_cpu_dec(pcp) __this_cpu_sub(pcp, 1) +#define __this_cpu_sub(pcp, n) __this_cpu_add(pcp, -(typeof(pcp)) (n)) + +#define this_cpu_inc(pcp) this_cpu_add(pcp, 1) +#define this_cpu_dec(pcp) this_cpu_sub(pcp, 1) +#define this_cpu_sub(pcp, n) this_cpu_add(pcp, -(typeof(pcp)) (n)) + +/* Make CBMC use atomics to work around bug. */ +#ifdef RUN +#define THIS_CPU_ADD_HELPER(ptr, x) (*(ptr) += (x)) +#else +/* + * Split the atomic into a read and a write so that it has the least + * possible ordering. + */ +#define THIS_CPU_ADD_HELPER(ptr, x) \ + do { \ + typeof(ptr) this_cpu_add_helper_ptr = (ptr); \ + typeof(ptr) this_cpu_add_helper_x = (x); \ + typeof(*ptr) this_cpu_add_helper_temp; \ + __CPROVER_atomic_begin(); \ + this_cpu_add_helper_temp = *(this_cpu_add_helper_ptr); \ + __CPROVER_atomic_end(); \ + this_cpu_add_helper_temp += this_cpu_add_helper_x; \ + __CPROVER_atomic_begin(); \ + *(this_cpu_add_helper_ptr) = this_cpu_add_helper_temp; \ + __CPROVER_atomic_end(); \ + } while (0) +#endif + +/* + * For some reason CBMC needs an atomic operation even though this is percpu + * data. + */ +#define __this_cpu_add(pcp, n) \ + do { \ + BUG_ON(preemptible()); \ + THIS_CPU_ADD_HELPER(per_cpu_ptr(&(pcp), thread_cpu_id), \ + (typeof(pcp)) (n)); \ + } while (0) + +#define this_cpu_add(pcp, n) \ + do { \ + int this_cpu_add_impl_cpu = get_cpu(); \ + THIS_CPU_ADD_HELPER(per_cpu_ptr(&(pcp), this_cpu_add_impl_cpu), \ + (typeof(pcp)) (n)); \ + put_cpu(); \ + } while (0) + +/* + * This will cause a compiler warning because of the cast from char[][] to + * type*. This will cause a compile time error if type is too big. + */ +#define DEFINE_PER_CPU(type, name) \ + char name[NR_CPUS][PERCPU_OFFSET]; \ + typedef char percpu_too_big_##name \ + [sizeof(type) > PERCPU_OFFSET ? -1 : 1] + +#define for_each_possible_cpu(cpu) \ + for ((cpu) = 0; (cpu) < NR_CPUS; ++(cpu)) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c new file mode 100644 index 000000000000..4f1b068e9b7a --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.c @@ -0,0 +1,78 @@ +#include <config.h> + +#include "preempt.h" + +#include "assume.h" +#include "locks.h" + +/* Support NR_CPUS of at most 64 */ +#define CPU_PREEMPTION_LOCKS_INIT0 LOCK_IMPL_INITIALIZER +#define CPU_PREEMPTION_LOCKS_INIT1 \ + CPU_PREEMPTION_LOCKS_INIT0, CPU_PREEMPTION_LOCKS_INIT0 +#define CPU_PREEMPTION_LOCKS_INIT2 \ + CPU_PREEMPTION_LOCKS_INIT1, CPU_PREEMPTION_LOCKS_INIT1 +#define CPU_PREEMPTION_LOCKS_INIT3 \ + CPU_PREEMPTION_LOCKS_INIT2, CPU_PREEMPTION_LOCKS_INIT2 +#define CPU_PREEMPTION_LOCKS_INIT4 \ + CPU_PREEMPTION_LOCKS_INIT3, CPU_PREEMPTION_LOCKS_INIT3 +#define CPU_PREEMPTION_LOCKS_INIT5 \ + CPU_PREEMPTION_LOCKS_INIT4, CPU_PREEMPTION_LOCKS_INIT4 + +/* + * Simulate disabling preemption by locking a particular cpu. NR_CPUS + * should be the actual number of cpus, not just the maximum. + */ +struct lock_impl cpu_preemption_locks[NR_CPUS] = { + CPU_PREEMPTION_LOCKS_INIT0 +#if (NR_CPUS - 1) & 1 + , CPU_PREEMPTION_LOCKS_INIT0 +#endif +#if (NR_CPUS - 1) & 2 + , CPU_PREEMPTION_LOCKS_INIT1 +#endif +#if (NR_CPUS - 1) & 4 + , CPU_PREEMPTION_LOCKS_INIT2 +#endif +#if (NR_CPUS - 1) & 8 + , CPU_PREEMPTION_LOCKS_INIT3 +#endif +#if (NR_CPUS - 1) & 16 + , CPU_PREEMPTION_LOCKS_INIT4 +#endif +#if (NR_CPUS - 1) & 32 + , CPU_PREEMPTION_LOCKS_INIT5 +#endif +}; + +#undef CPU_PREEMPTION_LOCKS_INIT0 +#undef CPU_PREEMPTION_LOCKS_INIT1 +#undef CPU_PREEMPTION_LOCKS_INIT2 +#undef CPU_PREEMPTION_LOCKS_INIT3 +#undef CPU_PREEMPTION_LOCKS_INIT4 +#undef CPU_PREEMPTION_LOCKS_INIT5 + +__thread int thread_cpu_id; +__thread int preempt_disable_count; + +void preempt_disable(void) +{ + BUG_ON(preempt_disable_count < 0 || preempt_disable_count == INT_MAX); + + if (preempt_disable_count++) + return; + + thread_cpu_id = nondet_int(); + assume(thread_cpu_id >= 0); + assume(thread_cpu_id < NR_CPUS); + lock_impl_lock(&cpu_preemption_locks[thread_cpu_id]); +} + +void preempt_enable(void) +{ + BUG_ON(preempt_disable_count < 1); + + if (--preempt_disable_count) + return; + + lock_impl_unlock(&cpu_preemption_locks[thread_cpu_id]); +} diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h new file mode 100644 index 000000000000..2f95ee0e4dd5 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/preempt.h @@ -0,0 +1,58 @@ +#ifndef PREEMPT_H +#define PREEMPT_H + +#include <stdbool.h> + +#include "bug_on.h" + +/* This flag contains garbage if preempt_disable_count is 0. */ +extern __thread int thread_cpu_id; + +/* Support recursive preemption disabling. */ +extern __thread int preempt_disable_count; + +void preempt_disable(void); +void preempt_enable(void); + +static inline void preempt_disable_notrace(void) +{ + preempt_disable(); +} + +static inline void preempt_enable_no_resched(void) +{ + preempt_enable(); +} + +static inline void preempt_enable_notrace(void) +{ + preempt_enable(); +} + +static inline int preempt_count(void) +{ + return preempt_disable_count; +} + +static inline bool preemptible(void) +{ + return !preempt_count(); +} + +static inline int get_cpu(void) +{ + preempt_disable(); + return thread_cpu_id; +} + +static inline void put_cpu(void) +{ + preempt_enable(); +} + +static inline void might_sleep(void) +{ + BUG_ON(preempt_disable_count); +} + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c new file mode 100644 index 000000000000..ac9cbc62b411 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/simple_sync_srcu.c @@ -0,0 +1,50 @@ +#include <config.h> + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <pthread.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> + +#include "int_typedefs.h" + +#include "barriers.h" +#include "bug_on.h" +#include "locks.h" +#include "misc.h" +#include "preempt.h" +#include "percpu.h" +#include "workqueues.h" + +#include <linux/srcu.h> + +/* Functions needed from modify_srcu.c */ +bool try_check_zero(struct srcu_struct *sp, int idx, int trycount); +void srcu_flip(struct srcu_struct *sp); + +/* Simpler implementation of synchronize_srcu that ignores batching. */ +void synchronize_srcu(struct srcu_struct *sp) +{ + int idx; + /* + * This code assumes that try_check_zero will succeed anyway, + * so there is no point in multiple tries. + */ + const int trycount = 1; + + might_sleep(); + + /* Ignore the lock, as multiple writers aren't working yet anyway. */ + + idx = 1 ^ (sp->completed & 1); + + /* For comments see srcu_advance_batches. */ + + assume(try_check_zero(sp, idx, trycount)); + + srcu_flip(sp); + + assume(try_check_zero(sp, idx^1, trycount)); +} diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h new file mode 100644 index 000000000000..e58c8dfd3e90 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/workqueues.h @@ -0,0 +1,102 @@ +#ifndef WORKQUEUES_H +#define WORKQUEUES_H + +#include <stdbool.h> + +#include "barriers.h" +#include "bug_on.h" +#include "int_typedefs.h" + +#include <linux/types.h> + +/* Stub workqueue implementation. */ + +struct work_struct; +typedef void (*work_func_t)(struct work_struct *work); +void delayed_work_timer_fn(unsigned long __data); + +struct work_struct { +/* atomic_long_t data; */ + unsigned long data; + + struct list_head entry; + work_func_t func; +#ifdef CONFIG_LOCKDEP + struct lockdep_map lockdep_map; +#endif +}; + +struct timer_list { + struct hlist_node entry; + unsigned long expires; + void (*function)(unsigned long); + unsigned long data; + u32 flags; + int slack; +}; + +struct delayed_work { + struct work_struct work; + struct timer_list timer; + + /* target workqueue and CPU ->timer uses to queue ->work */ + struct workqueue_struct *wq; + int cpu; +}; + + +static inline bool schedule_work(struct work_struct *work) +{ + BUG(); + return true; +} + +static inline bool schedule_work_on(int cpu, struct work_struct *work) +{ + BUG(); + return true; +} + +static inline bool queue_work(struct workqueue_struct *wq, + struct work_struct *work) +{ + BUG(); + return true; +} + +static inline bool queue_delayed_work(struct workqueue_struct *wq, + struct delayed_work *dwork, + unsigned long delay) +{ + BUG(); + return true; +} + +#define INIT_WORK(w, f) \ + do { \ + (w)->data = 0; \ + (w)->func = (f); \ + } while (0) + +#define INIT_DELAYED_WORK(w, f) INIT_WORK(&(w)->work, (f)) + +#define __WORK_INITIALIZER(n, f) { \ + .data = 0, \ + .entry = { &(n).entry, &(n).entry }, \ + .func = f \ + } + +/* Don't bother initializing timer. */ +#define __DELAYED_WORK_INITIALIZER(n, f, tflags) { \ + .work = __WORK_INITIALIZER((n).work, (f)), \ + } + +#define DECLARE_WORK(n, f) \ + struct workqueue_struct n = __WORK_INITIALIZER + +#define DECLARE_DELAYED_WORK(n, f) \ + struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0) + +#define system_power_efficient_wq ((struct workqueue_struct *) NULL) + +#endif diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore new file mode 100644 index 000000000000..f47cb2045f13 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/.gitignore @@ -0,0 +1 @@ +*.out diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile new file mode 100644 index 000000000000..3a3aee149225 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/Makefile @@ -0,0 +1,11 @@ +CBMC_FLAGS = -I../.. -I../../src -I../../include -I../../empty_includes -32 -pointer-check -mm pso + +all: + for i in ./*.pass; do \ + echo $$i ; \ + CBMC_FLAGS="$(CBMC_FLAGS)" sh ../test_script.sh --should-pass $$i > $$i.out 2>&1 ; \ + done + for i in ./*.fail; do \ + echo $$i ; \ + CBMC_FLAGS="$(CBMC_FLAGS)" sh ../test_script.sh --should-fail $$i > $$i.out 2>&1 ; \ + done diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail new file mode 100644 index 000000000000..40c8075919d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/assert_end.fail @@ -0,0 +1 @@ +test_cbmc_options="-DASSERT_END" diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail new file mode 100644 index 000000000000..ada5baf0b60d --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force.fail @@ -0,0 +1 @@ +test_cbmc_options="-DFORCE_FAILURE" diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail new file mode 100644 index 000000000000..8fe00c8db466 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force2.fail @@ -0,0 +1 @@ +test_cbmc_options="-DFORCE_FAILURE_2" diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail new file mode 100644 index 000000000000..612ed6772844 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/force3.fail @@ -0,0 +1 @@ +test_cbmc_options="-DFORCE_FAILURE_3" diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/main.pass diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c new file mode 100644 index 000000000000..470b1105a112 --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/store_buffering/test.c @@ -0,0 +1,72 @@ +#include <src/combined_source.c> + +int x; +int y; + +int __unbuffered_tpr_x; +int __unbuffered_tpr_y; + +DEFINE_SRCU(ss); + +void rcu_reader(void) +{ + int idx; + +#ifndef FORCE_FAILURE_3 + idx = srcu_read_lock(&ss); +#endif + might_sleep(); + + __unbuffered_tpr_y = READ_ONCE(y); +#ifdef FORCE_FAILURE + srcu_read_unlock(&ss, idx); + idx = srcu_read_lock(&ss); +#endif + WRITE_ONCE(x, 1); + +#ifndef FORCE_FAILURE_3 + srcu_read_unlock(&ss, idx); +#endif + might_sleep(); +} + +void *thread_update(void *arg) +{ + WRITE_ONCE(y, 1); +#ifndef FORCE_FAILURE_2 + synchronize_srcu(&ss); +#endif + might_sleep(); + __unbuffered_tpr_x = READ_ONCE(x); + + return NULL; +} + +void *thread_process_reader(void *arg) +{ + rcu_reader(); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + pthread_t tu; + pthread_t tpr; + + if (pthread_create(&tu, NULL, thread_update, NULL)) + abort(); + if (pthread_create(&tpr, NULL, thread_process_reader, NULL)) + abort(); + if (pthread_join(tu, NULL)) + abort(); + if (pthread_join(tpr, NULL)) + abort(); + assert(__unbuffered_tpr_y != 0 || __unbuffered_tpr_x != 0); + +#ifdef ASSERT_END + assert(0); +#endif + + return 0; +} diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh new file mode 100755 index 000000000000..d1545972a0fa --- /dev/null +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/tests/test_script.sh @@ -0,0 +1,102 @@ +#!/bin/sh + +# This script expects a mode (either --should-pass or --should-fail) followed by +# an input file. The script uses the following environment variables. The test C +# source file is expected to be named test.c in the directory containing the +# input file. +# +# CBMC: The command to run CBMC. Default: cbmc +# CBMC_FLAGS: Additional flags to pass to CBMC +# NR_CPUS: Number of cpus to run tests with. Default specified by the test +# SYNC_SRCU_MODE: Choose implementation of synchronize_srcu. Defaults to simple. +# kernel: Version included in the linux kernel source. +# simple: Use try_check_zero directly. +# +# The input file is a script that is sourced by this file. It can define any of +# the following variables to configure the test. +# +# test_cbmc_options: Extra options to pass to CBMC. +# min_cpus_fail: Minimum number of CPUs (NR_CPUS) for verification to fail. +# The test is expected to pass if it is run with fewer. (Only +# useful for .fail files) +# default_cpus: Quantity of CPUs to use for the test, if not specified on the +# command line. Default: Larger of 2 and MIN_CPUS_FAIL. + +set -e + +if test "$#" -ne 2; then + echo "Expected one option followed by an input file" 1>&2 + exit 99 +fi + +if test "x$1" = "x--should-pass"; then + should_pass="yes" +elif test "x$1" = "x--should-fail"; then + should_pass="no" +else + echo "Unrecognized argument '$1'" 1>&2 + + # Exit code 99 indicates a hard error. + exit 99 +fi + +CBMC=${CBMC:-cbmc} + +SYNC_SRCU_MODE=${SYNC_SRCU_MODE:-simple} + +case ${SYNC_SRCU_MODE} in +kernel) sync_srcu_mode_flags="" ;; +simple) sync_srcu_mode_flags="-DUSE_SIMPLE_SYNC_SRCU" ;; + +*) + echo "Unrecognized argument '${SYNC_SRCU_MODE}'" 1>&2 + exit 99 + ;; +esac + +min_cpus_fail=1 + +c_file=`dirname "$2"`/test.c + +# Source the input file. +. $2 + +if test ${min_cpus_fail} -gt 2; then + default_default_cpus=${min_cpus_fail} +else + default_default_cpus=2 +fi +default_cpus=${default_cpus:-${default_default_cpus}} +cpus=${NR_CPUS:-${default_cpus}} + +# Check if there are two few cpus to make the test fail. +if test $cpus -lt ${min_cpus_fail:-0}; then + should_pass="yes" +fi + +cbmc_opts="-DNR_CPUS=${cpus} ${sync_srcu_mode_flags} ${test_cbmc_options} ${CBMC_FLAGS}" + +echo "Running CBMC: ${CBMC} ${cbmc_opts} ${c_file}" +if ${CBMC} ${cbmc_opts} "${c_file}"; then + # Verification successful. Make sure that it was supposed to verify. + test "x${should_pass}" = xyes +else + cbmc_exit_status=$? + + # An exit status of 10 indicates a failed verification. + # (see cbmc_parse_optionst::do_bmc in the CBMC source code) + if test ${cbmc_exit_status} -eq 10 && test "x${should_pass}" = xno; then + : + else + echo "CBMC returned ${cbmc_exit_status} exit status" 1>&2 + + # Parse errors have exit status 6. Any other type of error + # should be considered a hard error. + if test ${cbmc_exit_status} -ne 6 && \ + test ${cbmc_exit_status} -ne 10; then + exit 99 + else + exit 1 + fi + fi +fi diff --git a/tools/testing/selftests/seccomp/Makefile b/tools/testing/selftests/seccomp/Makefile index 8401e87e34e1..5fa6fd2246b1 100644 --- a/tools/testing/selftests/seccomp/Makefile +++ b/tools/testing/selftests/seccomp/Makefile @@ -1,10 +1,6 @@ -TEST_PROGS := seccomp_bpf +TEST_GEN_PROGS := seccomp_bpf CFLAGS += -Wl,-no-as-needed -Wall LDFLAGS += -lpthread -all: $(TEST_PROGS) - include ../lib.mk -clean: - $(RM) $(TEST_PROGS) diff --git a/tools/testing/selftests/sigaltstack/Makefile b/tools/testing/selftests/sigaltstack/Makefile index 56af56eda6fa..f68fbf80d8be 100644 --- a/tools/testing/selftests/sigaltstack/Makefile +++ b/tools/testing/selftests/sigaltstack/Makefile @@ -1,8 +1,5 @@ CFLAGS = -Wall -BINARIES = sas -all: $(BINARIES) +TEST_GEN_PROGS = sas include ../lib.mk -clean: - rm -rf $(BINARIES) diff --git a/tools/testing/selftests/sigaltstack/sas.c b/tools/testing/selftests/sigaltstack/sas.c index 1bb01258e559..ccd07343d418 100644 --- a/tools/testing/selftests/sigaltstack/sas.c +++ b/tools/testing/selftests/sigaltstack/sas.c @@ -57,7 +57,7 @@ void my_usr1(int sig, siginfo_t *si, void *u) exit(EXIT_FAILURE); } if (stk.ss_flags != SS_DISABLE) - printf("[FAIL]\tss_flags=%i, should be SS_DISABLE\n", + printf("[FAIL]\tss_flags=%x, should be SS_DISABLE\n", stk.ss_flags); else printf("[OK]\tsigaltstack is disabled in sighandler\n"); @@ -122,7 +122,8 @@ int main(void) if (stk.ss_flags == SS_DISABLE) { printf("[OK]\tInitial sigaltstack state was SS_DISABLE\n"); } else { - printf("[FAIL]\tInitial sigaltstack state was %i; should have been SS_DISABLE\n", stk.ss_flags); + printf("[FAIL]\tInitial sigaltstack state was %x; " + "should have been SS_DISABLE\n", stk.ss_flags); return EXIT_FAILURE; } @@ -165,7 +166,7 @@ int main(void) exit(EXIT_FAILURE); } if (stk.ss_flags != SS_AUTODISARM) { - printf("[FAIL]\tss_flags=%i, should be SS_AUTODISARM\n", + printf("[FAIL]\tss_flags=%x, should be SS_AUTODISARM\n", stk.ss_flags); exit(EXIT_FAILURE); } diff --git a/tools/testing/selftests/size/Makefile b/tools/testing/selftests/size/Makefile index bbd0b5398b61..4685b3e421fc 100644 --- a/tools/testing/selftests/size/Makefile +++ b/tools/testing/selftests/size/Makefile @@ -1,11 +1,5 @@ -all: get_size +CFLAGS := -static -ffreestanding -nostartfiles -s -get_size: get_size.c - $(CC) -static -ffreestanding -nostartfiles -s $< -o $@ - -TEST_PROGS := get_size +TEST_GEN_PROGS := get_size include ../lib.mk - -clean: - $(RM) get_size diff --git a/tools/testing/selftests/splice/Makefile b/tools/testing/selftests/splice/Makefile new file mode 100644 index 000000000000..de51f439d4a6 --- /dev/null +++ b/tools/testing/selftests/splice/Makefile @@ -0,0 +1,8 @@ +TEST_PROGS := default_file_splice_read.sh +EXTRA := default_file_splice_read +all: $(TEST_PROGS) $(EXTRA) + +include ../lib.mk + +clean: + rm -fr $(TEST_PROGS) $(EXTRA) diff --git a/tools/testing/selftests/splice/default_file_splice_read.c b/tools/testing/selftests/splice/default_file_splice_read.c new file mode 100644 index 000000000000..01dd6091554c --- /dev/null +++ b/tools/testing/selftests/splice/default_file_splice_read.c @@ -0,0 +1,8 @@ +#define _GNU_SOURCE +#include <fcntl.h> + +int main(int argc, char **argv) +{ + splice(0, 0, 1, 0, 1<<30, 0); + return 0; +} diff --git a/tools/testing/selftests/splice/default_file_splice_read.sh b/tools/testing/selftests/splice/default_file_splice_read.sh new file mode 100755 index 000000000000..1ea2adeabc94 --- /dev/null +++ b/tools/testing/selftests/splice/default_file_splice_read.sh @@ -0,0 +1,7 @@ +#!/bin/sh +n=`./default_file_splice_read </dev/null | wc -c` + +test "$n" = 0 && exit 0 + +echo "default_file_splice_read broken: leaked $n" +exit 1 diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile index 1d5556869137..b90e50c36f9f 100644 --- a/tools/testing/selftests/timers/Makefile +++ b/tools/testing/selftests/timers/Makefile @@ -1,20 +1,16 @@ -CC = $(CROSS_COMPILE)gcc BUILD_FLAGS = -DKTEST CFLAGS += -O3 -Wl,-no-as-needed -Wall $(BUILD_FLAGS) LDFLAGS += -lrt -lpthread # these are all "safe" tests that don't modify # system time or require escalated privledges -TEST_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \ +TEST_GEN_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \ inconsistency-check raw_skew threadtest rtctest -TEST_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \ +TEST_GEN_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \ skew_consistency clocksource-switch leap-a-day \ leapcrash set-tai set-2038 set-tz -bins = $(TEST_PROGS) $(TEST_PROGS_EXTENDED) - -all: ${bins} include ../lib.mk @@ -34,5 +30,3 @@ run_destructive_tests: run_tests ./set-tai ./set-2038 -clean: - rm -f ${bins} diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index bbab7f4664ac..41642ba5e318 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -1,33 +1,37 @@ # Makefile for vm selftests -CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS) -BINARIES = compaction_test -BINARIES += hugepage-mmap -BINARIES += hugepage-shm -BINARIES += map_hugetlb -BINARIES += mlock2-tests -BINARIES += on-fault-limit -BINARIES += thuge-gen -BINARIES += transhuge-stress -BINARIES += userfaultfd -BINARIES += mlock-random-test - -all: $(BINARIES) -%: %.c - $(CC) $(CFLAGS) -o $@ $^ -lrt -userfaultfd: userfaultfd.c ../../../../usr/include/linux/kernel.h - $(CC) $(CFLAGS) -O2 -o $@ $< -lpthread - -mlock-random-test: mlock-random-test.c - $(CC) $(CFLAGS) -o $@ $< -lcap +ifndef OUTPUT + OUTPUT := $(shell pwd) +endif -../../../../usr/include/linux/kernel.h: - make -C ../../../.. headers_install +CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS) +LDLIBS = -lrt +TEST_GEN_FILES = compaction_test +TEST_GEN_FILES += hugepage-mmap +TEST_GEN_FILES += hugepage-shm +TEST_GEN_FILES += map_hugetlb +TEST_GEN_FILES += mlock2-tests +TEST_GEN_FILES += on-fault-limit +TEST_GEN_FILES += thuge-gen +TEST_GEN_FILES += transhuge-stress +TEST_GEN_FILES += userfaultfd +TEST_GEN_FILES += userfaultfd_hugetlb +TEST_GEN_FILES += userfaultfd_shmem +TEST_GEN_FILES += mlock-random-test TEST_PROGS := run_vmtests -TEST_FILES := $(BINARIES) include ../lib.mk -clean: - $(RM) $(BINARIES) +$(OUTPUT)/userfaultfd: LDLIBS += -lpthread ../../../../usr/include/linux/kernel.h + +$(OUTPUT)/userfaultfd_hugetlb: userfaultfd.c ../../../../usr/include/linux/kernel.h + $(CC) $(CFLAGS) -DHUGETLB_TEST -O2 -o $@ $< -lpthread + +$(OUTPUT)/userfaultfd_shmem: userfaultfd.c ../../../../usr/include/linux/kernel.h + $(CC) $(CFLAGS) -DSHMEM_TEST -O2 -o $@ $< -lpthread + +$(OUTPUT)/mlock-random-test: LDLIBS += -lcap + +../../../../usr/include/linux/kernel.h: + make -C ../../../.. headers_install diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests index e11968b3677e..c92f6cf31d0a 100755 --- a/tools/testing/selftests/vm/run_vmtests +++ b/tools/testing/selftests/vm/run_vmtests @@ -103,6 +103,30 @@ else echo "[PASS]" fi +echo "----------------------------" +echo "running userfaultfd_hugetlb" +echo "----------------------------" +# 258MB total huge pages == 128MB src and 128MB dst +./userfaultfd_hugetlb 128 32 $mnt/ufd_test_file +if [ $? -ne 0 ]; then + echo "[FAIL]" + exitcode=1 +else + echo "[PASS]" +fi +rm -f $mnt/ufd_test_file + +echo "----------------------------" +echo "running userfaultfd_shmem" +echo "----------------------------" +./userfaultfd_shmem 128 32 +if [ $? -ne 0 ]; then + echo "[FAIL]" + exitcode=1 +else + echo "[PASS]" +fi + #cleanup umount $mnt rm -rf $mnt diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index d77ed41b2094..e9449c801888 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -63,6 +63,7 @@ #include <sys/mman.h> #include <sys/syscall.h> #include <sys/ioctl.h> +#include <sys/wait.h> #include <pthread.h> #include <linux/userfaultfd.h> @@ -76,8 +77,12 @@ static unsigned long nr_cpus, nr_pages, nr_pages_per_cpu, page_size; #define BOUNCE_POLL (1<<3) static int bounces; +#ifdef HUGETLB_TEST +static int huge_fd; +static char *huge_fd_off0; +#endif static unsigned long long *count_verify; -static int uffd, finished, *pipefd; +static int uffd, uffd_flags, finished, *pipefd; static char *area_src, *area_dst; static char *zeropage; pthread_attr_t attr; @@ -97,6 +102,102 @@ pthread_attr_t attr; ~(unsigned long)(sizeof(unsigned long long) \ - 1))) +#if !defined(HUGETLB_TEST) && !defined(SHMEM_TEST) + +/* Anonymous memory */ +#define EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \ + (1 << _UFFDIO_COPY) | \ + (1 << _UFFDIO_ZEROPAGE)) + +static int release_pages(char *rel_area) +{ + int ret = 0; + + if (madvise(rel_area, nr_pages * page_size, MADV_DONTNEED)) { + perror("madvise"); + ret = 1; + } + + return ret; +} + +static void allocate_area(void **alloc_area) +{ + if (posix_memalign(alloc_area, page_size, nr_pages * page_size)) { + fprintf(stderr, "out of memory\n"); + *alloc_area = NULL; + } +} + +#else /* HUGETLB_TEST or SHMEM_TEST */ + +#define EXPECTED_IOCTLS UFFD_API_RANGE_IOCTLS_BASIC + +#ifdef HUGETLB_TEST + +/* HugeTLB memory */ +static int release_pages(char *rel_area) +{ + int ret = 0; + + if (fallocate(huge_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + rel_area == huge_fd_off0 ? 0 : + nr_pages * page_size, + nr_pages * page_size)) { + perror("fallocate"); + ret = 1; + } + + return ret; +} + + +static void allocate_area(void **alloc_area) +{ + *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_HUGETLB, huge_fd, + *alloc_area == area_src ? 0 : + nr_pages * page_size); + if (*alloc_area == MAP_FAILED) { + fprintf(stderr, "mmap of hugetlbfs file failed\n"); + *alloc_area = NULL; + } + + if (*alloc_area == area_src) + huge_fd_off0 = *alloc_area; +} + +#elif defined(SHMEM_TEST) + +/* Shared memory */ +static int release_pages(char *rel_area) +{ + int ret = 0; + + if (madvise(rel_area, nr_pages * page_size, MADV_REMOVE)) { + perror("madvise"); + ret = 1; + } + + return ret; +} + +static void allocate_area(void **alloc_area) +{ + *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (*alloc_area == MAP_FAILED) { + fprintf(stderr, "shared memory mmap failed\n"); + *alloc_area = NULL; + } +} + +#else /* SHMEM_TEST */ +#error "Undefined test type" +#endif /* HUGETLB_TEST */ + +#endif /* !defined(HUGETLB_TEST) && !defined(SHMEM_TEST) */ + static int my_bcmp(char *str1, char *str2, size_t n) { unsigned long i; @@ -217,7 +318,7 @@ static void *locking_thread(void *arg) return NULL; } -static int copy_page(unsigned long offset) +static int copy_page(int ufd, unsigned long offset) { struct uffdio_copy uffdio_copy; @@ -229,7 +330,7 @@ static int copy_page(unsigned long offset) uffdio_copy.len = page_size; uffdio_copy.mode = 0; uffdio_copy.copy = 0; - if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy)) { + if (ioctl(ufd, UFFDIO_COPY, &uffdio_copy)) { /* real retval in ufdio_copy.copy */ if (uffdio_copy.copy != -EEXIST) fprintf(stderr, "UFFDIO_COPY error %Ld\n", @@ -247,6 +348,7 @@ static void *uffd_poll_thread(void *arg) unsigned long cpu = (unsigned long) arg; struct pollfd pollfd[2]; struct uffd_msg msg; + struct uffdio_register uffd_reg; int ret; unsigned long offset; char tmp_chr; @@ -278,16 +380,35 @@ static void *uffd_poll_thread(void *arg) continue; perror("nonblocking read error"), exit(1); } - if (msg.event != UFFD_EVENT_PAGEFAULT) + switch (msg.event) { + default: fprintf(stderr, "unexpected msg event %u\n", msg.event), exit(1); - if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) - fprintf(stderr, "unexpected write fault\n"), exit(1); - offset = (char *)(unsigned long)msg.arg.pagefault.address - - area_dst; - offset &= ~(page_size-1); - if (copy_page(offset)) - userfaults++; + break; + case UFFD_EVENT_PAGEFAULT: + if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) + fprintf(stderr, "unexpected write fault\n"), exit(1); + offset = (char *)(unsigned long)msg.arg.pagefault.address - + area_dst; + offset &= ~(page_size-1); + if (copy_page(uffd, offset)) + userfaults++; + break; + case UFFD_EVENT_FORK: + uffd = msg.arg.fork.ufd; + pollfd[0].fd = uffd; + break; + case UFFD_EVENT_REMOVE: + uffd_reg.range.start = msg.arg.remove.start; + uffd_reg.range.len = msg.arg.remove.end - + msg.arg.remove.start; + if (ioctl(uffd, UFFDIO_UNREGISTER, &uffd_reg.range)) + fprintf(stderr, "remove failure\n"), exit(1); + break; + case UFFD_EVENT_REMAP: + area_dst = (char *)(unsigned long)msg.arg.remap.to; + break; + } } return (void *)userfaults; } @@ -324,7 +445,7 @@ static void *uffd_read_thread(void *arg) offset = (char *)(unsigned long)msg.arg.pagefault.address - area_dst; offset &= ~(page_size-1); - if (copy_page(offset)) + if (copy_page(uffd, offset)) (*this_cpu_userfaults)++; } return (void *)NULL; @@ -338,7 +459,7 @@ static void *background_thread(void *arg) for (page_nr = cpu * nr_pages_per_cpu; page_nr < (cpu+1) * nr_pages_per_cpu; page_nr++) - copy_page(page_nr * page_size); + copy_page(uffd, page_nr * page_size); return NULL; } @@ -384,10 +505,8 @@ static int stress(unsigned long *userfaults) * UFFDIO_COPY without writing zero pages into area_dst * because the background threads already completed). */ - if (madvise(area_src, nr_pages * page_size, MADV_DONTNEED)) { - perror("madvise"); + if (release_pages(area_src)) return 1; - } for (cpu = 0; cpu < nr_cpus; cpu++) { char c; @@ -414,27 +533,9 @@ static int stress(unsigned long *userfaults) return 0; } -static int userfaultfd_stress(void) +static int userfaultfd_open(int features) { - void *area; - char *tmp_area; - unsigned long nr; - struct uffdio_register uffdio_register; struct uffdio_api uffdio_api; - unsigned long cpu; - int uffd_flags, err; - unsigned long userfaults[nr_cpus]; - - if (posix_memalign(&area, page_size, nr_pages * page_size)) { - fprintf(stderr, "out of memory\n"); - return 1; - } - area_src = area; - if (posix_memalign(&area, page_size, nr_pages * page_size)) { - fprintf(stderr, "out of memory\n"); - return 1; - } - area_dst = area; uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd < 0) { @@ -445,7 +546,7 @@ static int userfaultfd_stress(void) uffd_flags = fcntl(uffd, F_GETFD, NULL); uffdio_api.api = UFFD_API; - uffdio_api.features = 0; + uffdio_api.features = features; if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { fprintf(stderr, "UFFDIO_API\n"); return 1; @@ -455,6 +556,233 @@ static int userfaultfd_stress(void) return 1; } + return 0; +} + +/* + * For non-cooperative userfaultfd test we fork() a process that will + * generate pagefaults, will mremap the area monitored by the + * userfaultfd and at last this process will release the monitored + * area. + * For the anonymous and shared memory the area is divided into two + * parts, the first part is accessed before mremap, and the second + * part is accessed after mremap. Since hugetlbfs does not support + * mremap, the entire monitored area is accessed in a single pass for + * HUGETLB_TEST. + * The release of the pages currently generates event for shmem and + * anonymous memory (UFFD_EVENT_REMOVE), hence it is not checked + * for hugetlb. + */ +static int faulting_process(void) +{ + unsigned long nr; + unsigned long long count; + +#ifndef HUGETLB_TEST + unsigned long split_nr_pages = (nr_pages + 1) / 2; +#else + unsigned long split_nr_pages = nr_pages; +#endif + + for (nr = 0; nr < split_nr_pages; nr++) { + count = *area_count(area_dst, nr); + if (count != count_verify[nr]) { + fprintf(stderr, + "nr %lu memory corruption %Lu %Lu\n", + nr, count, + count_verify[nr]), exit(1); + } + } + +#ifndef HUGETLB_TEST + area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, area_src); + if (area_dst == MAP_FAILED) + perror("mremap"), exit(1); + + for (; nr < nr_pages; nr++) { + count = *area_count(area_dst, nr); + if (count != count_verify[nr]) { + fprintf(stderr, + "nr %lu memory corruption %Lu %Lu\n", + nr, count, + count_verify[nr]), exit(1); + } + } + + if (release_pages(area_dst)) + return 1; + + for (nr = 0; nr < nr_pages; nr++) { + if (my_bcmp(area_dst + nr * page_size, zeropage, page_size)) + fprintf(stderr, "nr %lu is not zero\n", nr), exit(1); + } + +#endif /* HUGETLB_TEST */ + + return 0; +} + +static int uffdio_zeropage(int ufd, unsigned long offset) +{ + struct uffdio_zeropage uffdio_zeropage; + int ret; + unsigned long has_zeropage = EXPECTED_IOCTLS & (1 << _UFFDIO_ZEROPAGE); + + if (offset >= nr_pages * page_size) + fprintf(stderr, "unexpected offset %lu\n", + offset), exit(1); + uffdio_zeropage.range.start = (unsigned long) area_dst + offset; + uffdio_zeropage.range.len = page_size; + uffdio_zeropage.mode = 0; + ret = ioctl(ufd, UFFDIO_ZEROPAGE, &uffdio_zeropage); + if (ret) { + /* real retval in ufdio_zeropage.zeropage */ + if (has_zeropage) { + if (uffdio_zeropage.zeropage == -EEXIST) + fprintf(stderr, "UFFDIO_ZEROPAGE -EEXIST\n"), + exit(1); + else + fprintf(stderr, "UFFDIO_ZEROPAGE error %Ld\n", + uffdio_zeropage.zeropage), exit(1); + } else { + if (uffdio_zeropage.zeropage != -EINVAL) + fprintf(stderr, + "UFFDIO_ZEROPAGE not -EINVAL %Ld\n", + uffdio_zeropage.zeropage), exit(1); + } + } else if (has_zeropage) { + if (uffdio_zeropage.zeropage != page_size) { + fprintf(stderr, "UFFDIO_ZEROPAGE unexpected %Ld\n", + uffdio_zeropage.zeropage), exit(1); + } else + return 1; + } else { + fprintf(stderr, + "UFFDIO_ZEROPAGE succeeded %Ld\n", + uffdio_zeropage.zeropage), exit(1); + } + + return 0; +} + +/* exercise UFFDIO_ZEROPAGE */ +static int userfaultfd_zeropage_test(void) +{ + struct uffdio_register uffdio_register; + unsigned long expected_ioctls; + + printf("testing UFFDIO_ZEROPAGE: "); + fflush(stdout); + + if (release_pages(area_dst)) + return 1; + + if (userfaultfd_open(0) < 0) + return 1; + uffdio_register.range.start = (unsigned long) area_dst; + uffdio_register.range.len = nr_pages * page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) + fprintf(stderr, "register failure\n"), exit(1); + + expected_ioctls = EXPECTED_IOCTLS; + if ((uffdio_register.ioctls & expected_ioctls) != + expected_ioctls) + fprintf(stderr, + "unexpected missing ioctl for anon memory\n"), + exit(1); + + if (uffdio_zeropage(uffd, 0)) { + if (my_bcmp(area_dst, zeropage, page_size)) + fprintf(stderr, "zeropage is not zero\n"), exit(1); + } + + close(uffd); + printf("done.\n"); + return 0; +} + +static int userfaultfd_events_test(void) +{ + struct uffdio_register uffdio_register; + unsigned long expected_ioctls; + unsigned long userfaults; + pthread_t uffd_mon; + int err, features; + pid_t pid; + char c; + + printf("testing events (fork, remap, remove): "); + fflush(stdout); + + if (release_pages(area_dst)) + return 1; + + features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | + UFFD_FEATURE_EVENT_REMOVE; + if (userfaultfd_open(features) < 0) + return 1; + fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); + + uffdio_register.range.start = (unsigned long) area_dst; + uffdio_register.range.len = nr_pages * page_size; + uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) + fprintf(stderr, "register failure\n"), exit(1); + + expected_ioctls = EXPECTED_IOCTLS; + if ((uffdio_register.ioctls & expected_ioctls) != + expected_ioctls) + fprintf(stderr, + "unexpected missing ioctl for anon memory\n"), + exit(1); + + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) + perror("uffd_poll_thread create"), exit(1); + + pid = fork(); + if (pid < 0) + perror("fork"), exit(1); + + if (!pid) + return faulting_process(); + + waitpid(pid, &err, 0); + if (err) + fprintf(stderr, "faulting process failed\n"), exit(1); + + if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) + perror("pipe write"), exit(1); + if (pthread_join(uffd_mon, (void **)&userfaults)) + return 1; + + close(uffd); + printf("userfaults: %ld\n", userfaults); + + return userfaults != nr_pages; +} + +static int userfaultfd_stress(void) +{ + void *area; + char *tmp_area; + unsigned long nr; + struct uffdio_register uffdio_register; + unsigned long cpu; + int err; + unsigned long userfaults[nr_cpus]; + + allocate_area((void **)&area_src); + if (!area_src) + return 1; + allocate_area((void **)&area_dst); + if (!area_dst) + return 1; + + if (userfaultfd_open(0) < 0) + return 1; + count_verify = malloc(nr_pages * sizeof(unsigned long long)); if (!count_verify) { perror("count_verify"); @@ -528,9 +856,7 @@ static int userfaultfd_stress(void) fprintf(stderr, "register failure\n"); return 1; } - expected_ioctls = (1 << _UFFDIO_WAKE) | - (1 << _UFFDIO_COPY) | - (1 << _UFFDIO_ZEROPAGE); + expected_ioctls = EXPECTED_IOCTLS; if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) { fprintf(stderr, @@ -562,10 +888,8 @@ static int userfaultfd_stress(void) * MADV_DONTNEED only after the UFFDIO_REGISTER, so it's * required to MADV_DONTNEED here. */ - if (madvise(area_dst, nr_pages * page_size, MADV_DONTNEED)) { - perror("madvise 2"); + if (release_pages(area_dst)) return 1; - } /* bounce pass */ if (stress(userfaults)) @@ -603,9 +927,15 @@ static int userfaultfd_stress(void) printf("\n"); } - return err; + if (err) + return err; + + close(uffd); + return userfaultfd_zeropage_test() || userfaultfd_events_test(); } +#ifndef HUGETLB_TEST + int main(int argc, char **argv) { if (argc < 3) @@ -632,6 +962,74 @@ int main(int argc, char **argv) return userfaultfd_stress(); } +#else /* HUGETLB_TEST */ + +/* + * Copied from mlock2-tests.c + */ +unsigned long default_huge_page_size(void) +{ + unsigned long hps = 0; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen("/proc/meminfo", "r"); + + if (!f) + return 0; + while (getline(&line, &linelen, f) > 0) { + if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { + hps <<= 10; + break; + } + } + + free(line); + fclose(f); + return hps; +} + +int main(int argc, char **argv) +{ + if (argc < 4) + fprintf(stderr, "Usage: <MiB> <bounces> <hugetlbfs_file>\n"), + exit(1); + nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); + page_size = default_huge_page_size(); + if (!page_size) + fprintf(stderr, "Unable to determine huge page size\n"), + exit(2); + if ((unsigned long) area_count(NULL, 0) + sizeof(unsigned long long) * 2 + > page_size) + fprintf(stderr, "Impossible to run this test\n"), exit(2); + nr_pages_per_cpu = atol(argv[1]) * 1024*1024 / page_size / + nr_cpus; + if (!nr_pages_per_cpu) { + fprintf(stderr, "invalid MiB\n"); + fprintf(stderr, "Usage: <MiB> <bounces>\n"), exit(1); + } + bounces = atoi(argv[2]); + if (bounces <= 0) { + fprintf(stderr, "invalid bounces\n"); + fprintf(stderr, "Usage: <MiB> <bounces>\n"), exit(1); + } + nr_pages = nr_pages_per_cpu * nr_cpus; + huge_fd = open(argv[3], O_CREAT | O_RDWR, 0755); + if (huge_fd < 0) { + fprintf(stderr, "Open of %s failed", argv[3]); + perror("open"); + exit(1); + } + if (ftruncate(huge_fd, 0)) { + fprintf(stderr, "ftruncate %s to size 0 failed", argv[3]); + perror("ftruncate"); + exit(1); + } + printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", + nr_pages, nr_pages_per_cpu); + return userfaultfd_stress(); +} + +#endif #else /* __NR_userfaultfd */ #warning "missing __NR_userfaultfd definition" diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 8c1cb423cfe6..38e0a9ca5d71 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -5,18 +5,21 @@ include ../lib.mk .PHONY: all all_32 all_64 warn_32bit_failure clean TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ - check_initial_reg_state sigreturn ldt_gdt iopl \ + check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test ioperm \ protection_keys test_vdso TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer -TARGETS_C_64BIT_ONLY := fsgsbase +TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64) +BINARIES_32 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_32)) +BINARIES_64 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_64)) + CFLAGS := -O2 -g -std=gnu99 -pthread -Wall UNAME_M := $(shell uname -m) @@ -40,10 +43,10 @@ all_64: $(BINARIES_64) clean: $(RM) $(BINARIES_32) $(BINARIES_64) -$(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c +$(BINARIES_32): $(OUTPUT)/%_32: %.c $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm -$(TARGETS_C_64BIT_ALL:%=%_64): %_64: %.c +$(BINARIES_64): $(OUTPUT)/%_64: %.c $(CC) -m64 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl # x86_64 users should be encouraged to install 32-bit libraries @@ -65,12 +68,12 @@ warn_32bit_failure: endif # Some tests have additional dependencies. -sysret_ss_attrs_64: thunks.S -ptrace_syscall_32: raw_syscall_helper_32.S -test_syscall_vdso_32: thunks_32.S +$(OUTPUT)/sysret_ss_attrs_64: thunks.S +$(OUTPUT)/ptrace_syscall_32: raw_syscall_helper_32.S +$(OUTPUT)/test_syscall_vdso_32: thunks_32.S # check_initial_reg_state is special: it needs a custom entry, and it # needs to be static so that its interpreter doesn't destroy its initial # state. -check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static -check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static +$(OUTPUT)/check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static +$(OUTPUT)/check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c index 5b2b4b3c634c..b4967d875236 100644 --- a/tools/testing/selftests/x86/fsgsbase.c +++ b/tools/testing/selftests/x86/fsgsbase.c @@ -245,7 +245,7 @@ void do_unexpected_base(void) long ret; asm volatile ("int $0x80" : "=a" (ret) : "a" (243), "b" (low_desc) - : "flags"); + : "r8", "r9", "r10", "r11"); memcpy(&desc, low_desc, sizeof(desc)); munmap(low_desc, sizeof(desc)); diff --git a/tools/testing/selftests/x86/ioperm.c b/tools/testing/selftests/x86/ioperm.c new file mode 100644 index 000000000000..b77313ba2ab1 --- /dev/null +++ b/tools/testing/selftests/x86/ioperm.c @@ -0,0 +1,170 @@ +/* + * ioperm.c - Test case for ioperm(2) + * Copyright (c) 2015 Andrew Lutomirski + */ + +#define _GNU_SOURCE +#include <err.h> +#include <stdio.h> +#include <stdint.h> +#include <signal.h> +#include <setjmp.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdbool.h> +#include <sched.h> +#include <sys/io.h> + +static int nerrs = 0; + +static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), + int flags) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = handler; + sa.sa_flags = SA_SIGINFO | flags; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, 0)) + err(1, "sigaction"); + +} + +static void clearhandler(int sig) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, 0)) + err(1, "sigaction"); +} + +static jmp_buf jmpbuf; + +static void sigsegv(int sig, siginfo_t *si, void *ctx_void) +{ + siglongjmp(jmpbuf, 1); +} + +static bool try_outb(unsigned short port) +{ + sethandler(SIGSEGV, sigsegv, SA_RESETHAND); + if (sigsetjmp(jmpbuf, 1) != 0) { + return false; + } else { + asm volatile ("outb %%al, %w[port]" + : : [port] "Nd" (port), "a" (0)); + return true; + } + clearhandler(SIGSEGV); +} + +static void expect_ok(unsigned short port) +{ + if (!try_outb(port)) { + printf("[FAIL]\toutb to 0x%02hx failed\n", port); + exit(1); + } + + printf("[OK]\toutb to 0x%02hx worked\n", port); +} + +static void expect_gp(unsigned short port) +{ + if (try_outb(port)) { + printf("[FAIL]\toutb to 0x%02hx worked\n", port); + exit(1); + } + + printf("[OK]\toutb to 0x%02hx failed\n", port); +} + +int main(void) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) + err(1, "sched_setaffinity to CPU 0"); + + expect_gp(0x80); + expect_gp(0xed); + + /* + * Probe for ioperm support. Note that clearing ioperm bits + * works even as nonroot. + */ + printf("[RUN]\tenable 0x80\n"); + if (ioperm(0x80, 1, 1) != 0) { + printf("[OK]\tioperm(0x80, 1, 1) failed (%d) -- try running as root\n", + errno); + return 0; + } + expect_ok(0x80); + expect_gp(0xed); + + printf("[RUN]\tdisable 0x80\n"); + if (ioperm(0x80, 1, 0) != 0) { + printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); + return 1; + } + expect_gp(0x80); + expect_gp(0xed); + + /* Make sure that fork() preserves ioperm. */ + if (ioperm(0x80, 1, 1) != 0) { + printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); + return 1; + } + + pid_t child = fork(); + if (child == -1) + err(1, "fork"); + + if (child == 0) { + printf("[RUN]\tchild: check that we inherited permissions\n"); + expect_ok(0x80); + expect_gp(0xed); + return 0; + } else { + int status; + if (waitpid(child, &status, 0) != child || + !WIFEXITED(status)) { + printf("[FAIL]\tChild died\n"); + nerrs++; + } else if (WEXITSTATUS(status) != 0) { + printf("[FAIL]\tChild failed\n"); + nerrs++; + } else { + printf("[OK]\tChild succeeded\n"); + } + } + + /* Test the capability checks. */ + + printf("\tDrop privileges\n"); + if (setresuid(1, 1, 1) != 0) { + printf("[WARN]\tDropping privileges failed\n"); + return 0; + } + + printf("[RUN]\tdisable 0x80\n"); + if (ioperm(0x80, 1, 0) != 0) { + printf("[FAIL]\tioperm(0x80, 1, 0) failed (%d)", errno); + return 1; + } + printf("[OK]\tit worked\n"); + + printf("[RUN]\tenable 0x80 again\n"); + if (ioperm(0x80, 1, 1) == 0) { + printf("[FAIL]\tit succeeded but should have failed.\n"); + return 1; + } + printf("[OK]\tit failed\n"); + return 0; +} diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c index 4af47079cf04..f6121612e769 100644 --- a/tools/testing/selftests/x86/ldt_gdt.c +++ b/tools/testing/selftests/x86/ldt_gdt.c @@ -45,6 +45,12 @@ #define AR_DB (1 << 22) #define AR_G (1 << 23) +#ifdef __x86_64__ +# define INT80_CLOBBERS "r8", "r9", "r10", "r11" +#else +# define INT80_CLOBBERS +#endif + static int nerrs; /* Points to an array of 1024 ints, each holding its own index. */ @@ -588,7 +594,7 @@ static int invoke_set_thread_area(void) asm volatile ("int $0x80" : "=a" (ret), "+m" (low_user_desc) : "a" (243), "b" (low_user_desc) - : "flags"); + : INT80_CLOBBERS); return ret; } @@ -657,7 +663,7 @@ static void test_gdt_invalidation(void) "+a" (eax) : "m" (low_user_desc_clear), [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) - : "flags"); + : INT80_CLOBBERS); if (sel != 0) { result = "FAIL"; @@ -688,7 +694,7 @@ static void test_gdt_invalidation(void) "+a" (eax) : "m" (low_user_desc_clear), [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) - : "flags"); + : INT80_CLOBBERS); if (sel != 0) { result = "FAIL"; @@ -721,7 +727,7 @@ static void test_gdt_invalidation(void) "+a" (eax) : "m" (low_user_desc_clear), [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) - : "flags"); + : INT80_CLOBBERS); #ifdef __x86_64__ syscall(SYS_arch_prctl, ARCH_GET_FS, &new_base); @@ -774,7 +780,7 @@ static void test_gdt_invalidation(void) "+a" (eax) : "m" (low_user_desc_clear), [arg1] "r" ((unsigned int)(unsigned long)low_user_desc_clear) - : "flags"); + : INT80_CLOBBERS); #ifdef __x86_64__ syscall(SYS_arch_prctl, ARCH_GET_GS, &new_base); diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c index df9e0a0cdf29..3237bc010e1c 100644 --- a/tools/testing/selftests/x86/protection_keys.c +++ b/tools/testing/selftests/x86/protection_keys.c @@ -192,7 +192,7 @@ void lots_o_noops_around_write(int *write_to_me) #define SYS_pkey_alloc 381 #define SYS_pkey_free 382 #define REG_IP_IDX REG_EIP -#define si_pkey_offset 0x18 +#define si_pkey_offset 0x14 #else #define SYS_mprotect_key 329 #define SYS_pkey_alloc 330 @@ -462,7 +462,7 @@ void pkey_disable_set(int pkey, int flags) unsigned long syscall_flags = 0; int ret; int pkey_rights; - u32 orig_pkru; + u32 orig_pkru = rdpkru(); dprintf1("START->%s(%d, 0x%x)\n", __func__, pkey, flags); @@ -812,8 +812,6 @@ void setup_hugetlbfs(void) { int err; int fd; - int validated_nr_pages; - int i; char buf[] = "123"; if (geteuid() != 0) { @@ -1116,11 +1114,6 @@ void test_pkey_syscalls_on_non_allocated_pkey(int *ptr, u16 pkey) err = sys_pkey_free(i); pkey_assert(err); - /* not enforced when pkey_get() is not a syscall - err = pkey_get(i, 0); - pkey_assert(err < 0); - */ - err = sys_pkey_free(i); pkey_assert(err); @@ -1133,14 +1126,8 @@ void test_pkey_syscalls_on_non_allocated_pkey(int *ptr, u16 pkey) void test_pkey_syscalls_bad_args(int *ptr, u16 pkey) { int err; - int bad_flag = (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) + 1; int bad_pkey = NR_PKEYS+99; - /* not enforced when pkey_get() is not a syscall - err = pkey_get(bad_pkey, bad_flag); - pkey_assert(err < 0); - */ - /* pass a known-invalid pkey in: */ err = sys_mprotect_pkey(ptr, PAGE_SIZE, PROT_READ, bad_pkey); pkey_assert(err); @@ -1149,8 +1136,6 @@ void test_pkey_syscalls_bad_args(int *ptr, u16 pkey) /* Assumes that all pkeys other than 'pkey' are unallocated */ void test_pkey_alloc_exhaust(int *ptr, u16 pkey) { - unsigned long flags; - unsigned long init_val; int err; int allocated_pkeys[NR_PKEYS] = {0}; int nr_allocated_pkeys = 0; diff --git a/tools/testing/selftests/x86/ptrace_syscall.c b/tools/testing/selftests/x86/ptrace_syscall.c index b037ce9cf116..eaea92439708 100644 --- a/tools/testing/selftests/x86/ptrace_syscall.c +++ b/tools/testing/selftests/x86/ptrace_syscall.c @@ -58,7 +58,8 @@ static void do_full_int80(struct syscall_args32 *args) asm volatile ("int $0x80" : "+a" (args->nr), "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2), - "+S" (args->arg3), "+D" (args->arg4), "+r" (bp)); + "+S" (args->arg3), "+D" (args->arg4), "+r" (bp) + : : "r8", "r9", "r10", "r11"); args->arg5 = bp; #else sys32_helper(args, int80_and_ret); diff --git a/tools/testing/selftests/x86/single_step_syscall.c b/tools/testing/selftests/x86/single_step_syscall.c index 50c26358e8b7..a48da95c18fd 100644 --- a/tools/testing/selftests/x86/single_step_syscall.c +++ b/tools/testing/selftests/x86/single_step_syscall.c @@ -56,9 +56,11 @@ static volatile sig_atomic_t sig_traps; #ifdef __x86_64__ # define REG_IP REG_RIP # define WIDTH "q" +# define INT80_CLOBBERS "r8", "r9", "r10", "r11" #else # define REG_IP REG_EIP # define WIDTH "l" +# define INT80_CLOBBERS #endif static unsigned long get_eflags(void) @@ -140,7 +142,8 @@ int main() printf("[RUN]\tSet TF and check int80\n"); set_eflags(get_eflags() | X86_EFLAGS_TF); - asm volatile ("int $0x80" : "=a" (tmp) : "a" (SYS_getpid)); + asm volatile ("int $0x80" : "=a" (tmp) : "a" (SYS_getpid) + : INT80_CLOBBERS); check_result(); /* diff --git a/tools/testing/selftests/x86/sysret_rip.c b/tools/testing/selftests/x86/sysret_rip.c new file mode 100644 index 000000000000..d85ec5b3671c --- /dev/null +++ b/tools/testing/selftests/x86/sysret_rip.c @@ -0,0 +1,195 @@ +/* + * sigreturn.c - tests that x86 avoids Intel SYSRET pitfalls + * Copyright (c) 2014-2016 Andrew Lutomirski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <sys/signal.h> +#include <sys/ucontext.h> +#include <sys/syscall.h> +#include <err.h> +#include <stddef.h> +#include <stdbool.h> +#include <setjmp.h> +#include <sys/user.h> +#include <sys/mman.h> +#include <assert.h> + + +asm ( + ".pushsection \".text\", \"ax\"\n\t" + ".balign 4096\n\t" + "test_page: .globl test_page\n\t" + ".fill 4094,1,0xcc\n\t" + "test_syscall_insn:\n\t" + "syscall\n\t" + ".ifne . - test_page - 4096\n\t" + ".error \"test page is not one page long\"\n\t" + ".endif\n\t" + ".popsection" + ); + +extern const char test_page[]; +static void const *current_test_page_addr = test_page; + +static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), + int flags) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = handler; + sa.sa_flags = SA_SIGINFO | flags; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, 0)) + err(1, "sigaction"); +} + +static void clearhandler(int sig) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, 0)) + err(1, "sigaction"); +} + +/* State used by our signal handlers. */ +static gregset_t initial_regs; + +static volatile unsigned long rip; + +static void sigsegv_for_sigreturn_test(int sig, siginfo_t *info, void *ctx_void) +{ + ucontext_t *ctx = (ucontext_t*)ctx_void; + + if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { + printf("[FAIL]\tRequested RIP=0x%lx but got RIP=0x%lx\n", + rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); + fflush(stdout); + _exit(1); + } + + memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t)); + + printf("[OK]\tGot SIGSEGV at RIP=0x%lx\n", rip); +} + +static void sigusr1(int sig, siginfo_t *info, void *ctx_void) +{ + ucontext_t *ctx = (ucontext_t*)ctx_void; + + memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t)); + + /* Set IP and CX to match so that SYSRET can happen. */ + ctx->uc_mcontext.gregs[REG_RIP] = rip; + ctx->uc_mcontext.gregs[REG_RCX] = rip; + + /* R11 and EFLAGS should already match. */ + assert(ctx->uc_mcontext.gregs[REG_EFL] == + ctx->uc_mcontext.gregs[REG_R11]); + + sethandler(SIGSEGV, sigsegv_for_sigreturn_test, SA_RESETHAND); + + return; +} + +static void test_sigreturn_to(unsigned long ip) +{ + rip = ip; + printf("[RUN]\tsigreturn to 0x%lx\n", ip); + raise(SIGUSR1); +} + +static jmp_buf jmpbuf; + +static void sigsegv_for_fallthrough(int sig, siginfo_t *info, void *ctx_void) +{ + ucontext_t *ctx = (ucontext_t*)ctx_void; + + if (rip != ctx->uc_mcontext.gregs[REG_RIP]) { + printf("[FAIL]\tExpected SIGSEGV at 0x%lx but got RIP=0x%lx\n", + rip, (unsigned long)ctx->uc_mcontext.gregs[REG_RIP]); + fflush(stdout); + _exit(1); + } + + siglongjmp(jmpbuf, 1); +} + +static void test_syscall_fallthrough_to(unsigned long ip) +{ + void *new_address = (void *)(ip - 4096); + void *ret; + + printf("[RUN]\tTrying a SYSCALL that falls through to 0x%lx\n", ip); + + ret = mremap((void *)current_test_page_addr, 4096, 4096, + MREMAP_MAYMOVE | MREMAP_FIXED, new_address); + if (ret == MAP_FAILED) { + if (ip <= (1UL << 47) - PAGE_SIZE) { + err(1, "mremap to %p", new_address); + } else { + printf("[OK]\tmremap to %p failed\n", new_address); + return; + } + } + + if (ret != new_address) + errx(1, "mremap malfunctioned: asked for %p but got %p\n", + new_address, ret); + + current_test_page_addr = new_address; + rip = ip; + + if (sigsetjmp(jmpbuf, 1) == 0) { + asm volatile ("call *%[syscall_insn]" :: "a" (SYS_getpid), + [syscall_insn] "rm" (ip - 2)); + errx(1, "[FAIL]\tSyscall trampoline returned"); + } + + printf("[OK]\tWe survived\n"); +} + +int main() +{ + /* + * When the kernel returns from a slow-path syscall, it will + * detect whether SYSRET is appropriate. If it incorrectly + * thinks that SYSRET is appropriate when RIP is noncanonical, + * it'll crash on Intel CPUs. + */ + sethandler(SIGUSR1, sigusr1, 0); + for (int i = 47; i < 64; i++) + test_sigreturn_to(1UL<<i); + + clearhandler(SIGUSR1); + + sethandler(SIGSEGV, sigsegv_for_fallthrough, 0); + + /* One extra test to check that we didn't screw up the mremap logic. */ + test_syscall_fallthrough_to((1UL << 47) - 2*PAGE_SIZE); + + /* These are the interesting cases. */ + for (int i = 47; i < 64; i++) { + test_syscall_fallthrough_to((1UL<<i) - PAGE_SIZE); + test_syscall_fallthrough_to(1UL<<i); + } + + return 0; +} diff --git a/tools/testing/selftests/zram/Makefile b/tools/testing/selftests/zram/Makefile index 29d80346e3eb..c3a87e5f9d36 100644 --- a/tools/testing/selftests/zram/Makefile +++ b/tools/testing/selftests/zram/Makefile @@ -2,8 +2,7 @@ all: TEST_PROGS := zram.sh TEST_FILES := zram01.sh zram02.sh zram_lib.sh +EXTRA_CLEAN := err.log include ../lib.mk -clean: - $(RM) err.log diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index 88d5e71be044..95dd14648ba5 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c @@ -22,7 +22,7 @@ /* $(CROSS_COMPILE)cc -Wall -Wextra -g -o ffs-test ffs-test.c -lpthread */ -#define _BSD_SOURCE /* for endian.h */ +#define _DEFAULT_SOURCE /* for endian.h */ #include <endian.h> #include <errno.h> @@ -110,16 +110,25 @@ static const struct { struct usb_functionfs_descs_head_v2 header; __le32 fs_count; __le32 hs_count; + __le32 ss_count; struct { struct usb_interface_descriptor intf; struct usb_endpoint_descriptor_no_audio sink; struct usb_endpoint_descriptor_no_audio source; } __attribute__((packed)) fs_descs, hs_descs; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio sink; + struct usb_ss_ep_comp_descriptor sink_comp; + struct usb_endpoint_descriptor_no_audio source; + struct usb_ss_ep_comp_descriptor source_comp; + } ss_descs; } __attribute__((packed)) descriptors = { .header = { .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | - FUNCTIONFS_HAS_HS_DESC), + FUNCTIONFS_HAS_HS_DESC | + FUNCTIONFS_HAS_SS_DESC), .length = cpu_to_le32(sizeof descriptors), }, .fs_count = cpu_to_le32(3), @@ -171,6 +180,45 @@ static const struct { .bInterval = 1, /* NAK every 1 uframe */ }, }, + .ss_count = cpu_to_le32(5), + .ss_descs = { + .intf = { + .bLength = sizeof descriptors.fs_descs.intf, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = 1, + }, + .sink = { + .bLength = sizeof descriptors.hs_descs.sink, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), + }, + .sink_comp = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, + }, + .source = { + .bLength = sizeof descriptors.hs_descs.source, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 1, /* NAK every 1 uframe */ + }, + .source_comp = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = 0, + }, + }, }; static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) diff --git a/tools/usb/usbip/README b/tools/usb/usbip/README index 831f49fea3ce..5eb2b6c7722b 100644 --- a/tools/usb/usbip/README +++ b/tools/usb/usbip/README @@ -4,10 +4,33 @@ # Copyright (C) 2011 matt mooney <mfm@muteddisk.com> # 2005-2008 Takahiro Hirofuchi +[Overview] +USB/IP protocol allows to pass USB device from server to client over the +network. Server is a machine which provides (shares) a USB device. Client is +a machine which uses USB device provided by server over the network. +The USB device may be either physical device connected to a server or +software entity created on a server using USB gadget subsystem. +Whole project consists of four parts: + + - usbip-vhci + A client side kernel module which provides a virtual USB Host Controller + and allows to import a USB device from a remote machine. + + - usbip-host (stub driver) + A server side module which provides a USB device driver which can be + bound to a physical USB device to make it exportable. + + - usbip-vudc + A server side module which provides a virtual USB Device Controller and allows + to export a USB device created using USB Gadget Subsystem. + + - usbip-utils + A set of userspace tools used to handle connection and management. + Used on both sides. [Requirements] - USB/IP device drivers - Found in the staging directory of the Linux kernel. + Found in the drivers/usb/usbip/ directory of the Linux kernel tree. - libudev >= 2.0 libudev library @@ -36,6 +59,10 @@ [Usage] +On a server side there are two entities which can be shared. +First of them is physical usb device connected to the machine. +To make it available below steps should be executed: + server:# (Physically attach your USB device.) server:# insmod usbip-core.ko @@ -52,6 +79,30 @@ - The USB device 1-2 is now exportable to other hosts! - Use `usbip unbind --busid 1-2' to stop exporting the device. +Second of shareable entities is USB Gadget created using USB Gadget Subsystem +on a server machine. To make it available below steps should be executed: + + server:# (Create your USB gadget) + - Currently the most preferable way of creating a new USB gadget + is ConfigFS Composite Gadget. Please refer to its documentation + for details. + - See vudc_server_example.sh for a short example of USB gadget creation + + server:# insmod usbip-core.ko + server:# insmod usbip-vudc.ko + - To create more than one instance of vudc use num module param + + server:# (Bind gadget to one of available vudc) + - Assign your new gadget to USB/IP UDC + - Using ConfigFS interface you may do this simply by: + server:# cd /sys/kernel/config/usb_gadget/<gadget_name> + server:# echo "usbip-vudc.0" > UDC + + server:# usbipd -D --device + - Start usbip daemon. + +To attach new device to client machine below commands should be used: + client:# insmod usbip-core.ko client:# insmod vhci-hcd.ko @@ -60,6 +111,8 @@ client:# usbip attach --remote <host> --busid 1-2 - Connect the remote USB device. + - When using vudc on a server side busid is really vudc instance name. + For example: usbip-vudc.0 client:# usbip port - Show virtual port status. @@ -192,6 +245,8 @@ Detach the imported device: - http://usbip.wiki.sourceforge.net/how-to-debug-usbip - usbip-host.ko must be bound to the target device. - See /proc/bus/usb/devices and find "Driver=..." lines of the device. + - Target USB gadget must be bound to vudc + (using USB gadget susbsys, not usbip bind command) - Shutdown firewall. - usbip now uses TCP port 3240. - Disable SELinux. diff --git a/tools/usb/usbip/vudc/vudc_server_example.sh b/tools/usb/usbip/vudc/vudc_server_example.sh new file mode 100755 index 000000000000..2736be64f203 --- /dev/null +++ b/tools/usb/usbip/vudc/vudc_server_example.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +################################################################################ +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to <http://unlicense.org/> +################################################################################ + +################################################################################ +# This is a sample script which shows how to use vUDC with ConfigFS gadgets +################################################################################ + +# Stop script on error +set -e + +################################################################################ +# Create your USB gadget +# You may use bare ConfigFS interface (as below) +# or libusbgx or gt toool +# Instead of ConfigFS gadgets you may use any of legacy gadgets. +################################################################################ +CONFIGFS_MOUNT_POINT="/sys/kernel/config" +GADGET_NAME="g1" +ID_VENDOR="0x1d6b" +ID_PRODUCT="0x0104" + +cd ${CONFIGFS_MOUNT_POINT}/usb_gadget +# Create a new USB gadget +mkdir ${GADGET_NAME} +cd ${GADGET_NAME} + +# This gadget contains one function - ACM (serial port over USB) +FUNC_DIR="functions/acm.ser0" +mkdir ${FUNC_DIR} + +# Just one configuration +mkdir configs/c.1 +ln -s ${FUNC_DIR} configs/c.1 + +# Set our gadget identity +echo ${ID_VENDOR} > idVendor +echo ${ID_PRODUCT} > idProduct + +################################################################################ +# Load vudc-module if vudc is not available +# You may change value of num param to get more than one vUDC instance +################################################################################ +[[ -d /sys/class/udc/usbip-vudc.0 ]] || modprobe usbip-vudc num=1 + +################################################################################ +# Bind gadget to our vUDC +# By default we bind to first one but you may change this if you would like +# to use more than one instance +################################################################################ +echo "usbip-vudc.0" > UDC + +################################################################################ +# Let's now run our usbip daemon in a USB device mode +################################################################################ +usbipd --device & + +################################################################################ +# Now your USB gadget is available using USB/IP protocol. +# To prepare your client, you should ensure that usbip-vhci module is inside +# your kernel. If it's not then you can load it: +# +# $ modprobe usbip-vhci +# +# To check availability of your gadget you may try to list devices exported +# on a remote server: +# +# $ modprobe usbip-vhci +# $ usbip list -r $SERVER_IP +# Exportable USB devices +# ====================== +# usbipd: info: request 0x8005(6): complete +# - 127.0.0.1 +# usbip-vudc.0: Linux Foundation : unknown product (1d6b:0104) +# : /sys/devices/platform/usbip-vudc.0 +# : (Defined at Interface level) (00/00/00) +# +# To attach this device to your client you may use: +# +# $ usbip attach -r $SERVER_IP -d usbip-vudc.0 +# +################################################################################ diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 93aadaf7ff63..006029456988 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile @@ -9,6 +9,8 @@ CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall -Wextra -I../lib/ LDFLAGS = $(LIBS) +all: $(TARGETS) + $(TARGETS): $(LIBS) $(LIBS): @@ -20,3 +22,9 @@ $(LIBS): clean: $(RM) page-types slabinfo page_owner_sort make -C $(LIB_DIR) clean + +sbindir ?= /usr/sbin + +install: all + install -d $(DESTDIR)$(sbindir) + install -m 755 -p $(TARGETS) $(DESTDIR)$(sbindir) |