summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung@kernel.org>2026-07-28 22:11:23 -0700
committerNamhyung Kim <namhyung@kernel.org>2026-07-28 22:11:23 -0700
commitb56de9f26245c45bb5cb140fefd287b371b7e28d (patch)
tree994214b735002c56f641071fc437601a33a1a0b9 /tools
parent0d40fedcd1b9e809f966c97d40f32943fb89e0d4 (diff)
parentfac520e43a60230b24026f462a2b63e4d170566e (diff)
Merge tag 'perf-tools-fixes-for-v7.2-1-2026-07-25' into perf-tools-next
perf tools fixes for v7.2. - Update header copies of kernel headers, including const.h, fs.h, perf_event.h, gfp_types.h, kvm.h, cpufeatures.h, rtnetlink.hp, msr-index.h, drm.h and socket.h. - Add some build files related to BPF skels to .gitignore. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/arch/s390/include/uapi/asm/kvm.h1
-rw-r--r--tools/arch/x86/include/asm/cpufeatures.h3
-rw-r--r--tools/arch/x86/include/asm/msr-index.h11
-rw-r--r--tools/arch/x86/include/uapi/asm/kvm.h14
-rw-r--r--tools/arch/x86/include/uapi/asm/svm.h32
-rw-r--r--tools/include/linux/gfp_types.h16
-rw-r--r--tools/include/linux/overflow.h1
-rw-r--r--tools/include/uapi/linux/const.h18
-rw-r--r--tools/include/uapi/linux/kvm.h20
-rw-r--r--tools/include/uapi/linux/rtnetlink.h1
-rw-r--r--tools/perf/bench/bpf_skel/.gitignore4
-rw-r--r--tools/perf/trace/beauty/include/linux/socket.h2
-rw-r--r--tools/perf/trace/beauty/include/uapi/drm/drm.h27
-rw-r--r--tools/perf/trace/beauty/include/uapi/linux/fs.h11
-rw-r--r--tools/sched_ext/include/scx/cid.bpf.h88
-rw-r--r--tools/testing/cxl/test/mem.c1
-rw-r--r--tools/testing/selftests/alsa/mixer-test.c5
-rw-r--r--tools/testing/selftests/arm64/gcs/libc-gcs.c2
-rw-r--r--tools/testing/selftests/arm64/pauth/pac.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c36
-rw-r--r--tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c57
-rw-r--r--tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c43
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_basic.c45
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_listen.c21
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_strp.c31
-rw-r--r--tools/testing/selftests/bpf/prog_tests/verifier.c2
-rw-r--r--tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c77
-rw-r--r--tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c34
-rw-r--r--tools/testing/selftests/bpf/progs/sockmap_parse_prog.c22
-rw-r--r--tools/testing/selftests/bpf/progs/test_sockmap_strp.c7
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c27
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c16
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_unpriv.c19
-rw-r--r--tools/testing/selftests/bpf/test_maps.c13
-rwxr-xr-xtools/testing/selftests/drivers/net/hw/tso.py16
-rwxr-xr-xtools/testing/selftests/drivers/net/netconsole/netcons_resume.sh3
-rw-r--r--tools/testing/selftests/filesystems/.gitignore1
-rw-r--r--tools/testing/selftests/filesystems/Makefile4
-rw-r--r--tools/testing/selftests/filesystems/idmapped_tmpfile.c168
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc27
-rw-r--r--tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc4
-rw-r--r--tools/testing/selftests/gpio/.gitignore1
-rw-r--r--tools/testing/selftests/hid/Makefile2
-rw-r--r--tools/testing/selftests/hid/hid_bpf.c36
-rw-r--r--tools/testing/selftests/hid/progs/hid.c15
-rw-r--r--tools/testing/selftests/hid/tests/test_multitouch.py114
-rw-r--r--tools/testing/selftests/kvm/Makefile.kvm1
-rw-r--r--tools/testing/selftests/kvm/arm64/mmio_sign_ext.c255
-rw-r--r--tools/testing/selftests/kvm/x86/sev_init2_tests.c16
-rw-r--r--tools/testing/selftests/kvm/x86/sev_migrate_tests.c47
-rw-r--r--tools/testing/selftests/kvm/x86/sev_smoke_test.c9
-rw-r--r--tools/testing/selftests/landlock/net_test.c97
-rw-r--r--tools/testing/selftests/landlock/scoped_signal_test.c67
-rw-r--r--tools/testing/selftests/mm/hmm-tests.c1
-rwxr-xr-xtools/testing/selftests/mm/ksft_process_madv.sh2
-rw-r--r--tools/testing/selftests/mm/pagemap_ioctl.c12
-rw-r--r--tools/testing/selftests/net/lib.sh25
-rw-r--r--tools/testing/selftests/net/lib/py/utils.py4
-rwxr-xr-xtools/testing/selftests/net/netfilter/nft_flowtable.sh55
-rw-r--r--tools/testing/selftests/net/tcp_mmap.c4
-rw-r--r--tools/testing/selftests/riscv/vector/validate_v_ptrace.c15
-rw-r--r--tools/testing/selftests/rseq/Makefile6
-rw-r--r--tools/testing/selftests/sched_ext/Makefile1
-rw-r--r--tools/testing/selftests/sched_ext/nohz_tick.bpf.c65
-rw-r--r--tools/testing/selftests/sched_ext/nohz_tick.c347
-rw-r--r--tools/testing/selftests/user_events/abi_test.c29
-rw-r--r--tools/testing/selftests/user_events/perf_test.c26
-rw-r--r--tools/testing/selftests/vfio/Makefile2
-rw-r--r--tools/testing/selftests/vfio/lib/libvfio.mk2
-rw-r--r--tools/testing/selftests/x86/test_shadow_stack.c86
-rw-r--r--tools/tracing/rtla/Makefile14
-rw-r--r--tools/tracing/rtla/src/common.c1
-rw-r--r--tools/usb/usbip/libsrc/usbip_common.c2
-rw-r--r--tools/usb/usbip/libsrc/usbip_device_driver.c4
-rw-r--r--tools/usb/usbip/libsrc/vhci_driver.c1
-rw-r--r--tools/virtio/asm/percpu_types.h7
-rw-r--r--tools/virtio/linux/completion.h9
-rw-r--r--tools/virtio/linux/device.h1
-rw-r--r--tools/virtio/linux/dma-mapping.h1
-rw-r--r--tools/virtio/linux/mod_devicetable.h14
-rw-r--r--tools/virtio/linux/virtio_features.h79
81 files changed, 2140 insertions, 267 deletions
diff --git a/tools/arch/s390/include/uapi/asm/kvm.h b/tools/arch/s390/include/uapi/asm/kvm.h
index 60345dd2cba2..4192769b5ce0 100644
--- a/tools/arch/s390/include/uapi/asm/kvm.h
+++ b/tools/arch/s390/include/uapi/asm/kvm.h
@@ -444,6 +444,7 @@ struct kvm_s390_vm_cpu_machine {
#define KVM_S390_VM_CPU_FEAT_PFMFI 11
#define KVM_S390_VM_CPU_FEAT_SIGPIF 12
#define KVM_S390_VM_CPU_FEAT_KSS 13
+#define KVM_S390_VM_CPU_FEAT_ASTFLEIE2 14
struct kvm_s390_vm_cpu_feat {
__u64 feat[16];
};
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 86d17b195e79..1b4a48bff18f 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -379,6 +379,7 @@
#define X86_FEATURE_AVIC (15*32+13) /* "avic" Virtual Interrupt Controller */
#define X86_FEATURE_V_VMSAVE_VMLOAD (15*32+15) /* "v_vmsave_vmload" Virtual VMSAVE VMLOAD */
#define X86_FEATURE_VGIF (15*32+16) /* "vgif" Virtual GIF */
+#define X86_FEATURE_GMET (15*32+17) /* Guest Mode Execution Trap */
#define X86_FEATURE_X2AVIC (15*32+18) /* "x2avic" Virtual x2apic */
#define X86_FEATURE_V_SPEC_CTRL (15*32+20) /* "v_spec_ctrl" Virtual SPEC_CTRL */
#define X86_FEATURE_VNMI (15*32+25) /* "vnmi" Virtual NMI */
@@ -473,6 +474,7 @@
#define X86_FEATURE_GP_ON_USER_CPUID (20*32+17) /* User CPUID faulting */
#define X86_FEATURE_PREFETCHI (20*32+20) /* Prefetch Data/Instruction to Cache Level */
+#define X86_FEATURE_AVX512_BMM (20*32+23) /* AVX512 Bit Matrix Multiply instructions */
#define X86_FEATURE_ERAPS (20*32+24) /* Enhanced Return Address Predictor Security */
#define X86_FEATURE_SBPB (20*32+27) /* Selective Branch Prediction Barrier */
#define X86_FEATURE_IBPB_BRTYPE (20*32+28) /* MSR_PRED_CMD[IBPB] flushes all branch type predictions */
@@ -572,4 +574,5 @@
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */
#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */
+#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET from P-SEAMLDR clears the current VMCS */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index eff29645719b..18c4be75e927 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -674,6 +674,9 @@
#define MSR_AMD64_DC_CFG 0xc0011022
#define MSR_AMD64_TW_CFG 0xc0011023
+#define MSR_AMD64_FP_CFG 0xc0011028
+#define MSR_AMD64_FP_CFG_ZEN1_DENORM_FIX_BIT 9
+
#define MSR_AMD64_DE_CFG 0xc0011029
#define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT 1
#define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE BIT_ULL(MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT)
@@ -698,6 +701,8 @@
#define MSR_AMD64_IBSBRTARGET 0xc001103b
#define MSR_AMD64_ICIBSEXTDCTL 0xc001103c
#define MSR_AMD64_IBSOPDATA4 0xc001103d
+#define MSR_AMD64_IBSOPCTL2 0xc001103e
+#define MSR_AMD64_IBSFETCHCTL2 0xc001103f
#define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */
#define MSR_AMD64_SVM_AVIC_DOORBELL 0xc001011b
#define MSR_AMD64_VM_PAGE_FLUSH 0xc001011e
@@ -765,12 +770,14 @@
#define MSR_AMD_CPPC_CAP2 0xc00102b2
#define MSR_AMD_CPPC_REQ 0xc00102b3
#define MSR_AMD_CPPC_STATUS 0xc00102b4
+#define MSR_AMD_CPPC_REQ2 0xc00102b5
/* Masks for use with MSR_AMD_CPPC_CAP1 */
#define AMD_CPPC_LOWEST_PERF_MASK GENMASK(7, 0)
#define AMD_CPPC_LOWNONLIN_PERF_MASK GENMASK(15, 8)
#define AMD_CPPC_NOMINAL_PERF_MASK GENMASK(23, 16)
#define AMD_CPPC_HIGHEST_PERF_MASK GENMASK(31, 24)
+#define AMD_CPPC_FLOOR_PERF_CNT_MASK GENMASK_ULL(39, 32)
/* Masks for use with MSR_AMD_CPPC_REQ */
#define AMD_CPPC_MAX_PERF_MASK GENMASK(7, 0)
@@ -778,6 +785,9 @@
#define AMD_CPPC_DES_PERF_MASK GENMASK(23, 16)
#define AMD_CPPC_EPP_PERF_MASK GENMASK(31, 24)
+/* Masks for use with MSR_AMD_CPPC_REQ2 */
+#define AMD_CPPC_FLOOR_PERF_MASK GENMASK(7, 0)
+
/* AMD Performance Counter Global Status and Control MSRs */
#define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS 0xc0000300
#define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301
@@ -889,6 +899,7 @@
#define MSR_K7_HWCR_IRPERF_EN_BIT 30
#define MSR_K7_HWCR_IRPERF_EN BIT_ULL(MSR_K7_HWCR_IRPERF_EN_BIT)
#define MSR_K7_HWCR_CPUID_USER_DIS_BIT 35
+#define MSR_K7_HWCR_CPUID_USER_DIS BIT_ULL(MSR_K7_HWCR_CPUID_USER_DIS_BIT)
#define MSR_K7_FID_VID_CTL 0xc0010041
#define MSR_K7_FID_VID_STATUS 0xc0010042
#define MSR_K7_HWCR_CPB_DIS_BIT 25
diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h
index 0d4538fa6c31..1585ec804066 100644
--- a/tools/arch/x86/include/uapi/asm/kvm.h
+++ b/tools/arch/x86/include/uapi/asm/kvm.h
@@ -197,13 +197,13 @@ struct kvm_msrs {
__u32 nmsrs; /* number of msrs in entries */
__u32 pad;
- struct kvm_msr_entry entries[];
+ __DECLARE_FLEX_ARRAY(struct kvm_msr_entry, entries);
};
/* for KVM_GET_MSR_INDEX_LIST */
struct kvm_msr_list {
__u32 nmsrs; /* number of msrs in entries */
- __u32 indices[];
+ __DECLARE_FLEX_ARRAY(__u32, indices);
};
/* Maximum size of any access bitmap in bytes */
@@ -245,7 +245,7 @@ struct kvm_cpuid_entry {
struct kvm_cpuid {
__u32 nent;
__u32 padding;
- struct kvm_cpuid_entry entries[];
+ __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry, entries);
};
struct kvm_cpuid_entry2 {
@@ -267,7 +267,7 @@ struct kvm_cpuid_entry2 {
struct kvm_cpuid2 {
__u32 nent;
__u32 padding;
- struct kvm_cpuid_entry2 entries[];
+ __DECLARE_FLEX_ARRAY(struct kvm_cpuid_entry2, entries);
};
/* for KVM_GET_PIT and KVM_SET_PIT */
@@ -398,7 +398,7 @@ struct kvm_xsave {
* the contents of CPUID leaf 0xD on the host.
*/
__u32 region[1024];
- __u32 extra[];
+ __DECLARE_FLEX_ARRAY(__u32, extra);
};
#define KVM_MAX_XCRS 16
@@ -477,6 +477,7 @@ struct kvm_sync_regs {
#define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8)
#define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9)
#define KVM_X86_QUIRK_VMCS12_ALLOW_FREEZE_IN_SMM (1 << 10)
+#define KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT (1 << 11)
#define KVM_STATE_NESTED_FORMAT_VMX 0
#define KVM_STATE_NESTED_FORMAT_SVM 1
@@ -532,6 +533,7 @@ struct kvm_svm_nested_state_data {
struct kvm_svm_nested_state_hdr {
__u64 vmcb_pa;
+ __u64 gpat;
};
/* for KVM_CAP_NESTED_STATE */
@@ -566,7 +568,7 @@ struct kvm_pmu_event_filter {
__u32 fixed_counter_bitmap;
__u32 flags;
__u32 pad[4];
- __u64 events[];
+ __DECLARE_FLEX_ARRAY(__u64, events);
};
#define KVM_PMU_EVENT_ALLOW 0
diff --git a/tools/arch/x86/include/uapi/asm/svm.h b/tools/arch/x86/include/uapi/asm/svm.h
index 650e3256ea7d..010a45c9f614 100644
--- a/tools/arch/x86/include/uapi/asm/svm.h
+++ b/tools/arch/x86/include/uapi/asm/svm.h
@@ -103,38 +103,38 @@
#define SVM_EXIT_VMGEXIT 0x403
/* SEV-ES software-defined VMGEXIT events */
-#define SVM_VMGEXIT_MMIO_READ 0x80000001
-#define SVM_VMGEXIT_MMIO_WRITE 0x80000002
-#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003
-#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004
-#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005
+#define SVM_VMGEXIT_MMIO_READ 0x80000001ull
+#define SVM_VMGEXIT_MMIO_WRITE 0x80000002ull
+#define SVM_VMGEXIT_NMI_COMPLETE 0x80000003ull
+#define SVM_VMGEXIT_AP_HLT_LOOP 0x80000004ull
+#define SVM_VMGEXIT_AP_JUMP_TABLE 0x80000005ull
#define SVM_VMGEXIT_SET_AP_JUMP_TABLE 0
#define SVM_VMGEXIT_GET_AP_JUMP_TABLE 1
-#define SVM_VMGEXIT_PSC 0x80000010
-#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011
-#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012
-#define SVM_VMGEXIT_AP_CREATION 0x80000013
+#define SVM_VMGEXIT_PSC 0x80000010ull
+#define SVM_VMGEXIT_GUEST_REQUEST 0x80000011ull
+#define SVM_VMGEXIT_EXT_GUEST_REQUEST 0x80000012ull
+#define SVM_VMGEXIT_AP_CREATION 0x80000013ull
#define SVM_VMGEXIT_AP_CREATE_ON_INIT 0
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
-#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018
-#define SVM_VMGEXIT_SAVIC 0x8000001a
+#define SVM_VMGEXIT_SNP_RUN_VMPL 0x80000018ull
+#define SVM_VMGEXIT_SAVIC 0x8000001aull
#define SVM_VMGEXIT_SAVIC_REGISTER_GPA 0
#define SVM_VMGEXIT_SAVIC_UNREGISTER_GPA 1
#define SVM_VMGEXIT_SAVIC_SELF_GPA ~0ULL
-#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
-#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
+#define SVM_VMGEXIT_HV_FEATURES 0x8000fffdull
+#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffeull
#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
/* SW_EXITINFO1[3:0] */ \
(((((u64)reason_set) & 0xf)) | \
/* SW_EXITINFO1[11:4] */ \
((((u64)reason_code) & 0xff) << 4))
-#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
+#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffffull
/* Exit code reserved for hypervisor/software use */
-#define SVM_EXIT_SW 0xf0000000
+#define SVM_EXIT_SW 0xf0000000ull
-#define SVM_EXIT_ERR -1
+#define SVM_EXIT_ERR -1ull
#define SVM_EXIT_REASONS \
{ SVM_EXIT_READ_CR0, "read_cr0" }, \
diff --git a/tools/include/linux/gfp_types.h b/tools/include/linux/gfp_types.h
index 6c75df30a281..54ca0c88bab6 100644
--- a/tools/include/linux/gfp_types.h
+++ b/tools/include/linux/gfp_types.h
@@ -273,17 +273,17 @@ enum {
*
* %__GFP_ZERO returns a zeroed page on success.
*
- * %__GFP_ZEROTAGS zeroes memory tags at allocation time if the memory itself
- * is being zeroed (either via __GFP_ZERO or via init_on_alloc, provided that
- * __GFP_SKIP_ZERO is not set). This flag is intended for optimization: setting
- * memory tags at the same time as zeroing memory has minimal additional
- * performance impact.
+ * %__GFP_ZEROTAGS zeroes memory tags at allocation time. Setting memory tags at
+ * the same time as zeroing memory (e.g., with __GFP_ZERO) has minimal
+ * additional performance impact. However, __GFP_ZEROTAGS also zeroes the tags
+ * even if memory is not getting zeroed at allocation time (e.g.,
+ * with init_on_free).
*
* %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation.
* Used for userspace and vmalloc pages; the latter are unpoisoned by
- * kasan_unpoison_vmalloc instead. For userspace pages, results in
- * poisoning being skipped as well, see should_skip_kasan_poison for
- * details. Only effective in HW_TAGS mode.
+ * kasan_unpoison_vmalloc instead. If passed to vmalloc, kasan_unpoison_vmalloc
+ * is skipped too. For userspace pages, results in poisoning being skipped as
+ * well, see should_skip_kasan_poison for details. Only effective in HW_TAGS mode.
*/
#define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN)
#define __GFP_COMP ((__force gfp_t)___GFP_COMP)
diff --git a/tools/include/linux/overflow.h b/tools/include/linux/overflow.h
index 3427d7880326..98963688143f 100644
--- a/tools/include/linux/overflow.h
+++ b/tools/include/linux/overflow.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+#include <stdint.h>
#ifndef __LINUX_OVERFLOW_H
#define __LINUX_OVERFLOW_H
diff --git a/tools/include/uapi/linux/const.h b/tools/include/uapi/linux/const.h
index b8f629ef135f..565f309b9df8 100644
--- a/tools/include/uapi/linux/const.h
+++ b/tools/include/uapi/linux/const.h
@@ -50,4 +50,22 @@
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+/*
+ * Divide positive or negative dividend by positive or negative divisor
+ * and round to closest integer. Result is undefined for negative
+ * divisors if the dividend variable type is unsigned and for negative
+ * dividends if the divisor variable type is unsigned.
+ */
+#define __KERNEL_DIV_ROUND_CLOSEST(x, divisor) \
+({ \
+ __typeof__(x) __x = x; \
+ __typeof__(divisor) __d = divisor; \
+ \
+ (((__typeof__(x))-1) > 0 || \
+ ((__typeof__(divisor))-1) > 0 || \
+ (((__x) > 0) == ((__d) > 0))) ? \
+ (((__x) + ((__d) / 2)) / (__d)) : \
+ (((__x) - ((__d) / 2)) / (__d)); \
+})
+
#endif /* _UAPI_LINUX_CONST_H */
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index d0c0c8605976..419011097fa8 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -11,6 +11,7 @@
#include <linux/const.h>
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/stddef.h>
#include <linux/ioctl.h>
#include <asm/kvm.h>
@@ -542,7 +543,7 @@ struct kvm_coalesced_mmio {
struct kvm_coalesced_mmio_ring {
__u32 first, last;
- struct kvm_coalesced_mmio coalesced_mmio[];
+ __DECLARE_FLEX_ARRAY(struct kvm_coalesced_mmio, coalesced_mmio);
};
#define KVM_COALESCED_MMIO_MAX \
@@ -592,7 +593,7 @@ struct kvm_clear_dirty_log {
/* for KVM_SET_SIGNAL_MASK */
struct kvm_signal_mask {
__u32 len;
- __u8 sigset[];
+ __DECLARE_FLEX_ARRAY(__u8, sigset);
};
/* for KVM_TPR_ACCESS_REPORTING */
@@ -703,6 +704,11 @@ struct kvm_enable_cap {
#define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL
#define KVM_VM_TYPE_ARM_IPA_SIZE(x) \
((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK)
+
+#define KVM_VM_TYPE_ARM_PROTECTED (1UL << 31)
+#define KVM_VM_TYPE_ARM_MASK (KVM_VM_TYPE_ARM_IPA_SIZE_MASK | \
+ KVM_VM_TYPE_ARM_PROTECTED)
+
/*
* ioctls for /dev/kvm fds:
*/
@@ -989,6 +995,8 @@ struct kvm_enable_cap {
#define KVM_CAP_ARM_SEA_TO_USER 245
#define KVM_CAP_S390_USER_OPEREXEC 246
#define KVM_CAP_S390_KEYOP 247
+#define KVM_CAP_S390_VSIE_ESAMODE 248
+#define KVM_CAP_S390_HPAGE_2G 249
struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1051,7 +1059,7 @@ struct kvm_irq_routing_entry {
struct kvm_irq_routing {
__u32 nr;
__u32 flags;
- struct kvm_irq_routing_entry entries[];
+ __DECLARE_FLEX_ARRAY(struct kvm_irq_routing_entry, entries);
};
#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0)
@@ -1142,7 +1150,7 @@ struct kvm_dirty_tlb {
struct kvm_reg_list {
__u64 n; /* number of regs */
- __u64 reg[];
+ __DECLARE_FLEX_ARRAY(__u64, reg);
};
struct kvm_one_reg {
@@ -1224,6 +1232,8 @@ enum kvm_device_type {
#define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC
KVM_DEV_TYPE_LOONGARCH_PCHPIC,
#define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC
+ KVM_DEV_TYPE_LOONGARCH_DMSINTC,
+#define KVM_DEV_TYPE_LOONGARCH_DMSINTC KVM_DEV_TYPE_LOONGARCH_DMSINTC
KVM_DEV_TYPE_ARM_VGIC_V5,
#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5
@@ -1610,7 +1620,7 @@ struct kvm_stats_desc {
#ifdef __KERNEL__
char name[KVM_STATS_NAME_SIZE];
#else
- char name[];
+ __DECLARE_FLEX_ARRAY(char, name);
#endif
};
diff --git a/tools/include/uapi/linux/rtnetlink.h b/tools/include/uapi/linux/rtnetlink.h
index dab9493c791b..27265fd31e5f 100644
--- a/tools/include/uapi/linux/rtnetlink.h
+++ b/tools/include/uapi/linux/rtnetlink.h
@@ -840,6 +840,7 @@ enum {
#define RTEXT_FILTER_CFM_CONFIG (1 << 5)
#define RTEXT_FILTER_CFM_STATUS (1 << 6)
#define RTEXT_FILTER_MST (1 << 7)
+#define RTEXT_FILTER_NAME_ONLY (1 << 8)
/* End of information exported to user level */
diff --git a/tools/perf/bench/bpf_skel/.gitignore b/tools/perf/bench/bpf_skel/.gitignore
new file mode 100644
index 000000000000..cd01455e1b53
--- /dev/null
+++ b/tools/perf/bench/bpf_skel/.gitignore
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+.tmp
+*.skel.h
+vmlinux.h
diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h
index 3ffdfe184b23..2a8d7b14f1d1 100644
--- a/tools/perf/trace/beauty/include/linux/socket.h
+++ b/tools/perf/trace/beauty/include/linux/socket.h
@@ -356,7 +356,7 @@ struct ucred {
/* Flags to be cleared on entry by sendmsg and sendmmsg syscalls */
#define MSG_INTERNAL_SENDMSG_FLAGS \
- (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED)
+ (MSG_SPLICE_PAGES | MSG_SENDPAGE_NOPOLICY | MSG_SENDPAGE_DECRYPTED | MSG_NO_SHARED_FRAGS)
/* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
#define SOL_IP 0
diff --git a/tools/perf/trace/beauty/include/uapi/drm/drm.h b/tools/perf/trace/beauty/include/uapi/drm/drm.h
index 27cc159c1d27..bc7ef7684099 100644
--- a/tools/perf/trace/beauty/include/uapi/drm/drm.h
+++ b/tools/perf/trace/beauty/include/uapi/drm/drm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: MIT */
/*
* Header for the Direct Rendering Manager
*
@@ -11,25 +12,6 @@
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * 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
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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.
*/
#ifndef _DRM_H_
@@ -1323,6 +1305,13 @@ extern "C" {
*/
#define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2)
+/**
+ * DRM_IOCTL_SYNCOBJ_EVENTFD - Register an eventfd to be signalled by a syncobj.
+ *
+ * This can be used to integrate a syncobj in an event loop.
+ *
+ * The IOCTL argument is a struct drm_syncobj_eventfd.
+ */
#define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd)
/**
diff --git a/tools/perf/trace/beauty/include/uapi/linux/fs.h b/tools/perf/trace/beauty/include/uapi/linux/fs.h
index 2ea4c81df08f..bd87262f2e34 100644
--- a/tools/perf/trace/beauty/include/uapi/linux/fs.h
+++ b/tools/perf/trace/beauty/include/uapi/linux/fs.h
@@ -395,7 +395,16 @@ struct file_attr {
#define FS_DAX_FL 0x02000000 /* Inode is DAX */
#define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */
#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
-#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */
+/*
+ * FS_CASEFOLD_FL indicates case-insensitive name lookup. The
+ * bit is most often reported on directories, where it controls
+ * lookups of entries within. Filesystems that derive
+ * case-insensitivity from mount or volume state may also report
+ * it on non-directory inodes; userspace must not assume the bit
+ * is directory-only. FS_XFLAG_CASEFOLD reports the same
+ * information read-only via FS_IOC_FSGETXATTR.
+ */
+#define FS_CASEFOLD_FL 0x40000000
#define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
diff --git a/tools/sched_ext/include/scx/cid.bpf.h b/tools/sched_ext/include/scx/cid.bpf.h
index db247e42fb45..6b0b4e41b288 100644
--- a/tools/sched_ext/include/scx/cid.bpf.h
+++ b/tools/sched_ext/include/scx/cid.bpf.h
@@ -391,7 +391,9 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a,
if (a->base != b->base || a->nr_cids != b->nr_cids)
return false;
- nr_words = CMASK_NR_WORDS(a->nr_cids);
+ if (a->nr_cids == 0)
+ return true;
+ nr_words = (a->base + a->nr_cids - 1) / 64 - a->base / 64 + 1;
bpf_for(i, 0, CMASK_MAX_WORDS) {
if (i >= nr_words)
@@ -402,36 +404,6 @@ static __always_inline bool cmask_equal(const struct scx_cmask __arena *a,
return true;
}
-/*
- * True iff every bit set in @a is also set in @b over the intersection of
- * their ranges. Bits of @a outside @b's range fail the test.
- */
-static __always_inline bool cmask_subset(const struct scx_cmask __arena *a,
- const struct scx_cmask __arena *b)
-{
- u32 a_end = a->base + a->nr_cids;
- u32 b_end = b->base + b->nr_cids;
- u32 a_wbase = a->base / 64;
- u32 b_wbase = b->base / 64;
- u32 nr_words, i;
-
- /* any bit of @a outside @b's range is a subset violation */
- if (a->base < b->base || a_end > b_end)
- return false;
-
- nr_words = CMASK_NR_WORDS(a->nr_cids);
- bpf_for(i, 0, CMASK_MAX_WORDS) {
- u32 wi_b;
-
- if (i >= nr_words)
- break;
- wi_b = a_wbase + i - b_wbase;
- if (a->bits[i] & ~b->bits[wi_b])
- return false;
- }
- return true;
-}
-
/**
* cmask_next_set - find the first set bit at or after @cid
* @m: cmask to search
@@ -489,15 +461,65 @@ static __always_inline u32 cmask_first_set(const struct scx_cmask __arena *m)
(cid) = cmask_next_set((m), (cid) + 1))
/*
+ * True iff every bit set in @a is also set in @b. Matches the kernel-side
+ * scx_cmask_subset(): ranges don't need to nest, and set bits of @a outside
+ * @b's range fail the test.
+ */
+static __always_inline bool cmask_subset(const struct scx_cmask __arena *a,
+ const struct scx_cmask __arena *b)
+{
+ u32 a_end = a->base + a->nr_cids;
+ u32 b_end = b->base + b->nr_cids;
+ u32 a_wbase = a->base / 64;
+ u32 b_wbase = b->base / 64;
+ u32 lo = a->base > b->base ? a->base : b->base;
+ u32 hi = a_end < b_end ? a_end : b_end;
+ u32 lo_word, hi_word, i;
+
+ /* set bits of @a outside @b's range can't be in @b */
+ if (a->base < b->base &&
+ cmask_next_set(a, a->base) < (b->base < a_end ? b->base : a_end))
+ return false;
+ if (a_end > b_end &&
+ cmask_next_set(a, a->base > b_end ? a->base : b_end) < a_end)
+ return false;
+
+ if (lo >= hi)
+ return true;
+
+ /*
+ * Walk the words the range intersection spans. Plain word tests
+ * suffice: the scans above guarantee @a has no set bit outside @b's
+ * range and padding bits are kept clear by all cmask helpers.
+ */
+ lo_word = lo / 64;
+ hi_word = (hi - 1) / 64;
+
+ bpf_for(i, 0, CMASK_MAX_WORDS) {
+ u32 w = lo_word + i;
+
+ if (w > hi_word)
+ break;
+ if (a->bits[w - a_wbase] & ~b->bits[w - b_wbase])
+ return false;
+ }
+ return true;
+}
+
+/*
* Population count over [base, base + nr_cids). Padding bits in the head/tail
* words are guaranteed zero by the mutating helpers, so a flat popcount over
- * all words is correct.
+ * the words the range spans is correct.
*/
static __always_inline u32 cmask_weight(const struct scx_cmask __arena *m)
{
- u32 nr_words = CMASK_NR_WORDS(m->nr_cids), i;
+ u32 nr_words, i;
u32 count = 0;
+ if (!m->nr_cids)
+ return 0;
+ nr_words = (m->base + m->nr_cids - 1) / 64 - m->base / 64 + 1;
+
bpf_for(i, 0, CMASK_MAX_WORDS) {
if (i >= nr_words)
break;
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index a1d170f88fee..a7da279aa3ef 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -2,7 +2,6 @@
// Copyright(c) 2021 Intel Corporation. All rights reserved.
#include <linux/platform_device.h>
-#include <linux/mod_devicetable.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/delay.h>
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index d4f845c32804..a329f901c5ed 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -84,6 +84,7 @@ static void find_controls(void)
if (err < 0) {
ksft_print_msg("Failed to get hctl for card %d: %s\n",
card, snd_strerror(err));
+ free(card_data);
goto next_card;
}
@@ -339,9 +340,9 @@ static bool ctl_value_index_valid(struct ctl_data *ctl,
}
if (int64_val > snd_ctl_elem_info_get_max64(ctl->info)) {
- ksft_print_msg("%s.%d value %lld more than maximum %ld\n",
+ ksft_print_msg("%s.%d value %lld more than maximum %lld\n",
ctl->name, index, int64_val,
- snd_ctl_elem_info_get_max(ctl->info));
+ snd_ctl_elem_info_get_max64(ctl->info));
return false;
}
diff --git a/tools/testing/selftests/arm64/gcs/libc-gcs.c b/tools/testing/selftests/arm64/gcs/libc-gcs.c
index 72e82bfbecc9..ddb0b2b20155 100644
--- a/tools/testing/selftests/arm64/gcs/libc-gcs.c
+++ b/tools/testing/selftests/arm64/gcs/libc-gcs.c
@@ -130,7 +130,7 @@ TEST(gcs_find_terminator)
* We can access a GCS via ptrace
*
* This could usefully have a fixture but note that each test is
- * fork()ed into a new child whcih causes issues. Might be better to
+ * fork()ed into a new child which causes issues. Might be better to
* lift at least some of this out into a separate, non-harness, test
* program.
*/
diff --git a/tools/testing/selftests/arm64/pauth/pac.c b/tools/testing/selftests/arm64/pauth/pac.c
index 67d138057707..f4b859c75a5a 100644
--- a/tools/testing/selftests/arm64/pauth/pac.c
+++ b/tools/testing/selftests/arm64/pauth/pac.c
@@ -292,7 +292,7 @@ TEST(single_thread_different_keys)
/*
* fork() does not change keys. Only exec() does so call a worker program.
- * Its only job is to sign a value and report back the resutls
+ * Its only job is to sign a value and report back the results
*/
TEST(exec_changed_keys)
{
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..61cc5aaba025
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args_tracing.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "kfunc_implicit_args_tracing.skel.h"
+
+void test_kfunc_implicit_args_tracing(void)
+{
+ struct kfunc_implicit_args_tracing *skel;
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+ int err, fd;
+
+ skel = kfunc_implicit_args_tracing__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ err = kfunc_implicit_args_tracing__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto cleanup;
+
+ fd = bpf_program__fd(skel->progs.trigger_implicit_arg);
+ err = bpf_prog_test_run_opts(fd, &topts);
+ if (!ASSERT_OK(err, "test_run"))
+ goto cleanup;
+
+ ASSERT_EQ(topts.retval, 5, "kfunc_retval");
+ ASSERT_EQ(skel->bss->fentry_arg_cnt, 2, "fentry_arg_cnt");
+ ASSERT_NEQ(skel->bss->fentry_aux_arg, 0, "fentry_aux_arg");
+ ASSERT_EQ(skel->bss->fentry_result, 1, "fentry_result");
+ ASSERT_EQ(skel->bss->fexit_arg_cnt, 2, "fexit_arg_cnt");
+ ASSERT_NEQ(skel->bss->fexit_aux_arg, 0, "fexit_aux_arg");
+ ASSERT_EQ(skel->bss->fexit_result, 1, "fexit_result");
+
+cleanup:
+ kfunc_implicit_args_tracing__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c
new file mode 100644
index 000000000000..b8538fc4fc3f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_bad_access.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "test_kmods/bpf_testmod.h"
+#include "bpf_util.h"
+
+static void check_attach_reject(const struct bpf_insn *program, size_t prog_len)
+{
+ LIBBPF_OPTS(bpf_prog_load_opts, opts);
+ char error[4096];
+ int bpf_fd, tp_fd;
+
+ opts.log_level = 2;
+ opts.log_buf = error;
+ opts.log_size = sizeof(error);
+
+ bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
+ program, prog_len, &opts);
+ if (!ASSERT_GE(bpf_fd, 0, "prog_load"))
+ return;
+
+ tp_fd = bpf_raw_tracepoint_open("bpf_testmod_test_writable_bare_tp", bpf_fd);
+ ASSERT_EQ(tp_fd, -EINVAL, "bpf_raw_tracepoint_open");
+ if (tp_fd >= 0)
+ close(tp_fd);
+
+ close(bpf_fd);
+}
+
+void test_raw_tp_writable_reject_bad_access(void)
+{
+ const struct bpf_insn program[] = {
+ /* r6 is our tp buffer */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
+ /* one byte beyond the end of the writable context */
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
+ sizeof(struct bpf_testmod_test_writable_ctx)),
+ BPF_EXIT_INSN(),
+ };
+
+ const struct bpf_insn negative_var_off_program[] = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
+ /* make var_off negative, but keep the effective access offset non-negative */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8),
+ /* one byte beyond the end of the writable context */
+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
+ sizeof(struct bpf_testmod_test_writable_ctx) + 8),
+ BPF_EXIT_INSN(),
+ };
+
+ if (test__start_subtest("past_end"))
+ check_attach_reject(program, ARRAY_SIZE(program));
+
+ if (test__start_subtest("negative_var_off_past_end"))
+ check_attach_reject(negative_var_off_program,
+ ARRAY_SIZE(negative_var_off_program));
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
deleted file mode 100644
index 216b0dfac0fe..000000000000
--- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_reject_nbd_invalid.c
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <test_progs.h>
-#include <linux/nbd.h>
-#include "bpf_util.h"
-
-void test_raw_tp_writable_reject_nbd_invalid(void)
-{
- __u32 duration = 0;
- char error[4096];
- int bpf_fd = -1, tp_fd = -1;
-
- const struct bpf_insn program[] = {
- /* r6 is our tp buffer */
- BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
- /* one byte beyond the end of the nbd_request struct */
- BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_6,
- sizeof(struct nbd_request)),
- BPF_EXIT_INSN(),
- };
-
- LIBBPF_OPTS(bpf_prog_load_opts, opts,
- .log_level = 2,
- .log_buf = error,
- .log_size = sizeof(error),
- );
-
- bpf_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, NULL, "GPL v2",
- program, ARRAY_SIZE(program),
- &opts);
- if (CHECK(bpf_fd < 0, "bpf_raw_tracepoint_writable load",
- "failed: %d errno %d\n", bpf_fd, errno))
- return;
-
- tp_fd = bpf_raw_tracepoint_open("nbd_send_request", bpf_fd);
- if (CHECK(tp_fd >= 0, "bpf_raw_tracepoint_writable open",
- "erroneously succeeded\n"))
- goto out_bpffd;
-
- close(tp_fd);
-out_bpffd:
- close(bpf_fd);
-}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index cb3229711f93..e5fc038d747b 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -853,7 +853,7 @@ static void test_sockmap_many_socket(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -922,7 +922,7 @@ static void test_sockmap_many_maps(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -993,7 +993,7 @@ static void test_sockmap_same_sock(void)
return;
}
- udp = xsocket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+ udp = socket_loopback(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK);
if (udp < 0) {
close(dgram);
close(tcp);
@@ -1373,6 +1373,43 @@ end:
test_sockmap_pass_prog__destroy(skel);
}
+/* A socket in a sockmap without a verdict program keeps its ingress data
+ * in sk_receive_queue: FIONREAD must account for it.
+ */
+static void test_sockmap_no_verdict_fionread(void)
+{
+ int err, map, zero = 0, sent, avail;
+ int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
+ struct test_sockmap_pass_prog *skel;
+ char buf[256] = "0123456789";
+
+ skel = test_sockmap_pass_prog__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+ map = bpf_map__fd(skel->maps.sock_map_rx);
+
+ err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
+ if (!ASSERT_OK(err, "create_socket_pairs()"))
+ goto out;
+
+ err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
+ if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
+ goto out_close;
+
+ sent = xsend(p1, &buf, sizeof(buf), 0);
+ ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
+ avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
+ ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
+
+out_close:
+ close(c0);
+ close(p0);
+ close(c1);
+ close(p1);
+out:
+ test_sockmap_pass_prog__destroy(skel);
+}
+
void test_sockmap_basic(void)
{
if (test__start_subtest("sockmap create_update_free"))
@@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
test_sockmap_skb_verdict_shutdown();
if (test__start_subtest("sockmap skb_verdict fionread"))
test_sockmap_skb_verdict_fionread(true);
+ if (test__start_subtest("sockmap no_verdict fionread"))
+ test_sockmap_no_verdict_fionread();
if (test__start_subtest("sockmap skb_verdict fionread on drop"))
test_sockmap_skb_verdict_fionread(false);
if (test__start_subtest("sockmap skb_verdict change tail"))
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
index cc0c68bab907..1c96a3cf4b97 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
@@ -53,8 +53,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
int family, int sotype, int mapfd)
{
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
s = xsocket(family, sotype, 0);
if (s == -1)
@@ -63,11 +63,8 @@ static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (sotype == SOCK_STREAM) {
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
- } else if (err)
- FAIL_ERRNO("map_update: expected success");
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
xclose(s);
}
@@ -77,8 +74,8 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
struct sockaddr_storage addr;
socklen_t len = 0;
u32 key = 0;
- u64 value;
int err, s;
+ u64 value;
init_addr_loopback(family, &addr, &len);
@@ -93,8 +90,12 @@ static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
errno = 0;
value = s;
err = bpf_map_update_elem(mapfd, &key, &value, BPF_NOEXIST);
- if (!err || errno != EOPNOTSUPP)
- FAIL_ERRNO("map_update: expected EOPNOTSUPP");
+ if (sotype == SOCK_STREAM) {
+ ASSERT_ERR(err, "map_update");
+ ASSERT_EQ(errno, EOPNOTSUPP, "errno");
+ } else {
+ ASSERT_OK(err, "map_update");
+ }
close:
xclose(s);
}
@@ -1289,7 +1290,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
/* insert */
TEST(test_insert_invalid),
TEST(test_insert_opened),
- TEST(test_insert_bound, SOCK_STREAM),
+ TEST(test_insert_bound),
TEST(test_insert),
/* delete */
TEST(test_delete_after_insert),
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
index 621b3b71888e..1d7231728eaf 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_strp.c
@@ -431,6 +431,35 @@ out:
test_sockmap_strp__destroy(strp);
}
+static void test_sockmap_strp_parser_reject(void)
+{
+ struct test_sockmap_strp *strp = NULL;
+ int parser_mod, parser_ro, link;
+ int err, map;
+
+ strp = test_sockmap_strp__open_and_load();
+ if (!ASSERT_OK_PTR(strp, "test_sockmap_strp__open_and_load"))
+ return;
+
+ map = bpf_map__fd(strp->maps.sock_map);
+ parser_mod = bpf_program__fd(strp->progs.prog_skb_parser_resize);
+ parser_ro = bpf_program__fd(strp->progs.prog_skb_parser);
+
+ err = bpf_prog_attach(parser_mod, map, BPF_SK_SKB_STREAM_PARSER, 0);
+ ASSERT_ERR(err, "bpf_prog_attach parser_mod");
+
+ link = bpf_link_create(parser_ro, map, BPF_SK_SKB_STREAM_PARSER, NULL);
+ if (!ASSERT_GE(link, 0, "bpf_link_create parser_ro"))
+ goto out;
+
+ err = bpf_link_update(link, parser_mod, NULL);
+ ASSERT_ERR(err, "bpf_link_update parser_mod");
+out:
+ if (link >= 0)
+ close(link);
+ test_sockmap_strp__destroy(strp);
+}
+
void test_sockmap_strp(void)
{
if (test__start_subtest("sockmap strp tcp pass"))
@@ -451,4 +480,6 @@ void test_sockmap_strp(void)
test_sockmap_strp_multiple_pkt(AF_INET, SOCK_STREAM);
if (test__start_subtest("sockmap strp tcp dispatch"))
test_sockmap_strp_dispatch_pkt(AF_INET, SOCK_STREAM);
+ if (test__start_subtest("sockmap strp parser reject pkt mod"))
+ test_sockmap_strp_parser_reject();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 8a3d69e2453c..be97f6887f0e 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -78,6 +78,7 @@
#include "verifier_precision.skel.h"
#include "verifier_prevent_map_lookup.skel.h"
#include "verifier_private_stack.skel.h"
+#include "verifier_ptr_to_buf.skel.h"
#include "verifier_raw_stack.skel.h"
#include "verifier_raw_tp_writable.skel.h"
#include "verifier_reg_equal.skel.h"
@@ -230,6 +231,7 @@ void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); }
void test_verifier_precision(void) { RUN(verifier_precision); }
void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); }
void test_verifier_private_stack(void) { RUN(verifier_private_stack); }
+void test_verifier_ptr_to_buf(void) { RUN(verifier_ptr_to_buf); }
void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); }
void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); }
void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); }
diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
new file mode 100644
index 000000000000..995f8b8b5b9e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args_tracing.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <errno.h>
+
+extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
+
+char _license[] SEC("license") = "GPL";
+
+/* Shared arg checks; reports arg count and aux, returns 1 on success. */
+static __always_inline __u64
+check_implicit_args(void *ctx, __u64 *arg_cnt, __u64 *aux_arg)
+{
+ __u64 a = 0, aux = 0, z = 0;
+ __u64 result;
+ __s64 err;
+
+ *arg_cnt = bpf_get_func_arg_cnt(ctx);
+ result = *arg_cnt == 2;
+
+ err = bpf_get_func_arg(ctx, 0, &a);
+ result &= err == 0 && (int)a == 5;
+
+ err = bpf_get_func_arg(ctx, 1, &aux);
+ *aux_arg = aux;
+ result &= err == 0 && aux != 0;
+
+ err = bpf_get_func_arg(ctx, 2, &z);
+ result &= err == -EINVAL;
+
+ return result;
+}
+
+__u64 fentry_result;
+__u64 fentry_arg_cnt;
+__u64 fentry_aux_arg;
+
+SEC("fentry/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fentry)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fentry_result = check_implicit_args(ctx, &fentry_arg_cnt, &fentry_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fentry_result &= err == -EOPNOTSUPP;
+
+ return 0;
+}
+
+__u64 fexit_result;
+__u64 fexit_arg_cnt;
+__u64 fexit_aux_arg;
+
+SEC("fexit/bpf_kfunc_implicit_arg")
+int BPF_PROG(trace_implicit_arg_fexit)
+{
+ __u64 ret = 0;
+ __s64 err;
+
+ fexit_result = check_implicit_args(ctx, &fexit_arg_cnt, &fexit_aux_arg);
+
+ err = bpf_get_func_ret(ctx, &ret);
+ fexit_result &= err == 0 && ret == 5;
+
+ return 0;
+}
+
+SEC("syscall")
+int trigger_implicit_arg(void *ctx)
+{
+ return bpf_kfunc_implicit_arg(5);
+}
diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
index 7247a20c0a3b..024ef2aae200 100644
--- a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
@@ -13,12 +13,20 @@ struct node_acquire {
struct bpf_refcount refcount;
};
+struct node_refcounted {
+ long key;
+ struct bpf_list_node list;
+ struct bpf_refcount refcount;
+};
+
extern void bpf_rcu_read_lock(void) __ksym;
extern void bpf_rcu_read_unlock(void) __ksym;
#define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8)))
private(A) struct bpf_spin_lock glock;
private(A) struct bpf_rb_root groot __contains(node_acquire, node);
+private(B) struct bpf_spin_lock lock;
+private(B) struct bpf_list_head head __contains(node_refcounted, list);
static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
{
@@ -93,6 +101,32 @@ long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
return 0;
}
+SEC("?tc")
+__failure __msg("dereference of modified ptr_ ptr R1")
+long refcount_acquire_list_node_offset(void *ctx)
+{
+ struct node_refcounted *node, *base, *ref;
+ struct bpf_list_node *list_node;
+
+ node = bpf_obj_new(typeof(*node));
+ if (!node)
+ return 1;
+
+ bpf_spin_lock(&lock);
+ bpf_list_push_front(&head, &node->list);
+ list_node = bpf_list_pop_front(&head);
+ bpf_spin_unlock(&lock);
+ if (!list_node)
+ return 2;
+
+ base = container_of(list_node, struct node_refcounted, list);
+ ref = bpf_refcount_acquire(list_node);
+ if (ref)
+ bpf_obj_drop(ref);
+ bpf_obj_drop(base);
+ return 0;
+}
+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("function calls are not allowed while holding a lock")
int BPF_PROG(rbtree_fail_sleepable_lock_across_rcu,
diff --git a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
index c9abfe3a11af..56e9aebf05f2 100644
--- a/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
+++ b/tools/testing/selftests/bpf/progs/sockmap_parse_prog.c
@@ -5,28 +5,6 @@
SEC("sk_skb1")
int bpf_prog1(struct __sk_buff *skb)
{
- void *data_end = (void *)(long) skb->data_end;
- void *data = (void *)(long) skb->data;
- __u8 *d = data;
- int err;
-
- if (data + 10 > data_end) {
- err = bpf_skb_pull_data(skb, 10);
- if (err)
- return SK_DROP;
-
- data_end = (void *)(long)skb->data_end;
- data = (void *)(long)skb->data;
- if (data + 10 > data_end)
- return SK_DROP;
- }
-
- /* This write/read is a bit pointless but tests the verifier and
- * strparser handler for read/write pkt data and access into sk
- * fields.
- */
- d = data;
- d[7] = 1;
return skb->len;
}
diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_strp.c b/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
index dde3d5bec515..fe88fa6d40bc 100644
--- a/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
+++ b/tools/testing/selftests/bpf/progs/test_sockmap_strp.c
@@ -50,4 +50,11 @@ int prog_skb_parser_partial(struct __sk_buff *skb)
return 10;
}
+SEC("sk_skb/stream_parser")
+int prog_skb_parser_resize(struct __sk_buff *skb)
+{
+ bpf_skb_change_tail(skb, skb->len, 0);
+ return skb->len;
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
new file mode 100644
index 000000000000..12cf24db46a0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_ptr_to_buf.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+SEC("iter/bpf_map_elem")
+__description("PTR_TO_BUF: reject negative const offset")
+__failure
+__msg("invalid negative rdwr buffer offset")
+__naked void ptr_to_buf_reject_negative_const_offset(void)
+{
+ asm volatile ("r0 = 0; \
+ r2 = *(u64 *)(r1 + %[value_off]); \
+ if r2 == 0 goto l0_%=; \
+ r2 += -8; \
+ r0 = *(u64 *)(r2 + 0); \
+l0_%=: \
+ exit; \
+ "
+ :
+ : __imm_const(value_off,
+ offsetof(struct bpf_iter__bpf_map_elem, value))
+ : __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
index 14a0172e2141..4055a6443bc2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
+++ b/tools/testing/selftests/bpf/progs/verifier_raw_tp_writable.c
@@ -47,4 +47,20 @@ l0_%=: /* shift the buffer pointer to a variable location */\
: __clobber_all);
}
+SEC("raw_tracepoint.w")
+__description("raw_tracepoint_writable: reject negative const offset")
+__failure
+__msg("invalid negative tracepoint buffer offset")
+__naked void tracepoint_writable_reject_negative_const_offset(void)
+{
+ asm volatile (" \
+ r6 = *(u64 *)(r1 + 0); \
+ r6 += -8; \
+ r0 = *(u64 *)(r6 + 0); \
+ exit; \
+" :
+ :
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c
index 49f7bd05edad..42de5cff7e52 100644
--- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c
+++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c
@@ -6,6 +6,8 @@
#include "../../../include/linux/filter.h"
#include "bpf_misc.h"
+extern const int bpf_prog_active __ksym;
+
#define BPF_SK_LOOKUP(func) \
/* struct bpf_sock_tuple tuple = {} */ \
"r2 = 0;" \
@@ -78,6 +80,23 @@ __naked void dummy_prog_loop1_socket(void)
}
SEC("socket")
+__description("unpriv: pseudo btf id log masks address")
+__success_unpriv
+__msg_unpriv("0: (18) r1 = 0x0")
+__not_msg_unpriv("0: (18) r1 = 0x{{[1-9a-f][0-9a-f]*}}")
+__retval_unpriv(0)
+__log_level(2)
+__naked void pseudo_btf_id_log_masks_address(void)
+{
+ asm volatile ("r1 = %[bpf_prog_active] ll;"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_addr(bpf_prog_active)
+ : __clobber_all);
+}
+
+SEC("socket")
__description("unpriv: return pointer")
__success __failure_unpriv __msg_unpriv("R0 leaks addr")
__retval(POINTER_VALUE)
diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
index c32da7bd8be2..6a2641ee7897 100644
--- a/tools/testing/selftests/bpf/test_maps.c
+++ b/tools/testing/selftests/bpf/test_maps.c
@@ -759,16 +759,15 @@ static void test_sockmap(unsigned int tasks, void *data)
goto out_sockmap;
}
- /* Test update with unsupported UDP socket */
+ /* Test update with unsupported unbound UDP socket */
udp = socket(AF_INET, SOCK_DGRAM, 0);
- i = 0;
- err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY);
- if (err) {
- printf("Failed socket update SOCK_DGRAM '%i:%i'\n",
- i, udp);
+ CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno);
+ err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY);
+ close(udp);
+ if (!err) {
+ printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp);
goto out_sockmap;
}
- close(udp);
/* Test update without programs */
for (i = 0; i < 6; i++) {
diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py
index 1b789fea8929..802bb4868046 100755
--- a/tools/testing/selftests/drivers/net/hw/tso.py
+++ b/tools/testing/selftests/drivers/net/hw/tso.py
@@ -187,28 +187,24 @@ def query_nic_features(cfg) -> None:
cfg.wanted_features.add(f["name"])
cfg.hw_features = set()
- hw_all_features_cmd = ""
for f in features["hw"]["bits"]["bit"]:
if f.get("value", False):
- feature = f["name"]
- cfg.hw_features.add(feature)
- hw_all_features_cmd += f" {feature} on"
- try:
- ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}")
- except Exception as e:
- ksft_pr(f"WARNING: failure enabling all hw features: {e}")
- ksft_pr("partial gso feature detection may be impacted")
+ cfg.hw_features.add(f["name"])
# Check which features are supported via GSO partial
cfg.partial_features = set()
if 'tx-gso-partial' in cfg.hw_features:
+ seg_features = {f for f in cfg.hw_features if "segmentation" in f}
+ ethtool(f"-K {cfg.ifname} " +
+ " ".join(f"{f} on" for f in seg_features))
+
ethtool(f"-K {cfg.ifname} tx-gso-partial off")
no_partial = set()
features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}})
for f in features["active"]["bits"]["bit"]:
no_partial.add(f["name"])
- cfg.partial_features = cfg.hw_features - no_partial
+ cfg.partial_features = seg_features - no_partial
ethtool(f"-K {cfg.ifname} tx-gso-partial on")
restore_wanted_features(cfg)
diff --git a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
index cb59cf436dd0..d9111f2102bc 100755
--- a/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
+++ b/tools/testing/selftests/drivers/net/netconsole/netcons_resume.sh
@@ -44,7 +44,8 @@ function trigger_reactivation() {
# Restore MACs
ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" \
address "${SAVED_DSTMAC}"
- if [ "${BINDMODE}" == "mac" ]; then
+ if [ "${BINDMODE}" == "mac" ] &&
+ [ "$(mac_get "${SRCIF}")" != "${SAVED_SRCMAC}" ]; then
ip link set dev "${SRCIF}" down
ip link set dev "${SRCIF}" address "${SAVED_SRCMAC}"
# Rename device in order to trigger target resume, as initial
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore
index 64ac0dfa46b7..a78f894157de 100644
--- a/tools/testing/selftests/filesystems/.gitignore
+++ b/tools/testing/selftests/filesystems/.gitignore
@@ -5,3 +5,4 @@ fclog
file_stressor
anon_inode_test
kernfs_test
+idmapped_tmpfile
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
index 85427d7f19b9..a7ec2ba2dd83 100644
--- a/tools/testing/selftests/filesystems/Makefile
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -2,6 +2,10 @@
CFLAGS += $(KHDR_INCLUDES)
TEST_GEN_PROGS := devpts_pts file_stressor anon_inode_test kernfs_test fclog
+TEST_GEN_PROGS += idmapped_tmpfile
TEST_GEN_PROGS_EXTENDED := dnotify_test
include ../lib.mk
+
+$(OUTPUT)/idmapped_tmpfile: LDLIBS += -lcap
+$(OUTPUT)/idmapped_tmpfile: utils.c
diff --git a/tools/testing/selftests/filesystems/idmapped_tmpfile.c b/tools/testing/selftests/filesystems/idmapped_tmpfile.c
new file mode 100644
index 000000000000..bc411ab8281e
--- /dev/null
+++ b/tools/testing/selftests/filesystems/idmapped_tmpfile.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/fsuid.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+
+#include <linux/mount.h>
+#include <linux/types.h>
+
+#include "kselftest_harness.h"
+#include "wrappers.h"
+#include "utils.h"
+
+/*
+ * The test mount maps caller-visible ids [0, MAP_RANGE) onto the on-disk range
+ * [MAP_HOST, MAP_HOST + MAP_RANGE). An id outside [0, MAP_RANGE) therefore has
+ * no mapping in the mount and is not representable in the filesystem.
+ */
+#define MAP_HOST 10000
+#define MAP_RANGE 10000
+#define UNMAPPED 50000
+
+#ifndef MOUNT_ATTR_IDMAP
+#define MOUNT_ATTR_IDMAP 0x00100000
+#endif
+
+#ifndef __NR_mount_setattr
+#define __NR_mount_setattr 442
+#endif
+
+static inline int sys_mount_setattr(int dfd, const char *path,
+ unsigned int flags,
+ struct mount_attr *attr, size_t size)
+{
+ return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
+}
+
+/*
+ * Clone @path into a detached mount idmapped so that caller-visible ids
+ * [0, MAP_RANGE) map onto the on-disk ids [MAP_HOST, MAP_HOST + MAP_RANGE).
+ * Returns the mount fd, or -1 if idmapped mounts are not available.
+ */
+static int idmapped_clone(const char *path)
+{
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ };
+ int fd_tree, userns_fd, ret;
+
+ fd_tree = sys_open_tree(AT_FDCWD, path,
+ OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
+ if (fd_tree < 0)
+ return -1;
+
+ userns_fd = get_userns_fd(MAP_HOST, 0, MAP_RANGE);
+ if (userns_fd < 0) {
+ close(fd_tree);
+ return -1;
+ }
+
+ attr.userns_fd = userns_fd;
+ ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr, sizeof(attr));
+ close(userns_fd);
+ if (ret) {
+ close(fd_tree);
+ return -1;
+ }
+
+ return fd_tree;
+}
+
+FIXTURE(idmapped_tmpfile) {
+ char dir[64]; /* non-idmapped path to the layer directory */
+};
+
+FIXTURE_SETUP(idmapped_tmpfile)
+{
+ /* Private mount namespace so test mounts need no cleanup. */
+ ASSERT_EQ(unshare(CLONE_NEWNS), 0);
+ ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
+ ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0);
+
+ snprintf(self->dir, sizeof(self->dir), "/tmp/d");
+ ASSERT_EQ(mkdir(self->dir, 0777), 0);
+ /* World-writable so an unmapped caller still passes permission(). */
+ ASSERT_EQ(chmod(self->dir, 0777), 0);
+}
+
+FIXTURE_TEARDOWN(idmapped_tmpfile)
+{
+}
+
+/*
+ * A caller whose fsuid/fsgid have no mapping in the idmapped mount must not be
+ * able to create an O_TMPFILE. Without the check in vfs_tmpfile() the inode
+ * would be created owned by (uid_t)-1 and could then be linked into the
+ * namespace.
+ */
+TEST_F(idmapped_tmpfile, unmapped_caller_is_refused)
+{
+ int mfd, fd;
+
+ mfd = idmapped_clone(self->dir);
+ if (mfd < 0)
+ SKIP(return, "idmapped mounts not supported");
+
+ /* Become a caller outside the mount's [0, MAP_RANGE) range. */
+ setfsgid(UNMAPPED);
+ setfsuid(UNMAPPED);
+ ASSERT_EQ(setfsuid(-1), UNMAPPED);
+
+ fd = openat(mfd, ".", O_TMPFILE | O_WRONLY, 0644);
+ ASSERT_LT(fd, 0);
+ EXPECT_EQ(errno, EOVERFLOW);
+ if (fd >= 0)
+ close(fd);
+
+ EXPECT_EQ(close(mfd), 0);
+}
+
+/*
+ * A mapped caller can create an O_TMPFILE and link it into the namespace; the
+ * ownership round-trips through the mount idmap. This is what makes refusing
+ * the unmapped case above necessary in the first place.
+ */
+TEST_F(idmapped_tmpfile, mapped_caller_creates_and_links)
+{
+ char path[PATH_MAX];
+ struct stat st;
+ int mfd, fd;
+
+ mfd = idmapped_clone(self->dir);
+ if (mfd < 0)
+ SKIP(return, "idmapped mounts not supported");
+
+ /* Caller is uid/gid 0, which maps to MAP_HOST through the mount. */
+ fd = openat(mfd, ".", O_TMPFILE | O_RDWR, 0600);
+ ASSERT_GE(fd, 0);
+
+ ASSERT_EQ(fstat(fd, &st), 0);
+ EXPECT_EQ(st.st_uid, 0);
+ EXPECT_EQ(st.st_gid, 0);
+
+ /* The tmpfile is linkable: splice it into the directory. */
+ ASSERT_EQ(linkat(fd, "", mfd, "linked", AT_EMPTY_PATH), 0);
+ EXPECT_EQ(close(fd), 0);
+
+ ASSERT_EQ(fstatat(mfd, "linked", &st, 0), 0);
+ EXPECT_EQ(st.st_uid, 0);
+ EXPECT_EQ(st.st_gid, 0);
+
+ /* On the underlying, non-idmapped tmpfs it is stored as MAP_HOST. */
+ snprintf(path, sizeof(path), "%s/linked", self->dir);
+ ASSERT_EQ(stat(path, &st), 0);
+ EXPECT_EQ(st.st_uid, MAP_HOST);
+ EXPECT_EQ(st.st_gid, MAP_HOST);
+
+ EXPECT_EQ(close(mfd), 0);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
index 2915206777b6..89660a9adf44 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe_module.tc
@@ -16,23 +16,32 @@ echo > dynamic_events
FUNC1='foo_bar*'
FUNC2='vfs_read'
+:;: "Save enabled functions count" ;:
+ecount=`cat enabled_functions | wc -l`
+
+count_enabled_functions() {
+ count=`cat enabled_functions | wc -l`
+ count=$(($count-$ecount))
+ echo $count
+}
+
:;: "Add an event on the test module" ;:
echo "f:test1 $FUNC1" >> dynamic_events
echo 1 > events/fprobes/test1/enable
:;: "Ensure it is enabled" ;:
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -ne 0
:;: "Check the enabled_functions is cleared on unloading" ;:
rmmod trace-events-sample
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
:;: "Check it is kept clean" ;:
modprobe trace-events-sample
echo 1 > events/fprobes/test1/enable || echo "OK"
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
:;: "Add another event not on the test module" ;:
@@ -40,19 +49,19 @@ echo "f:test2 $FUNC2" >> dynamic_events
echo 1 > events/fprobes/test2/enable
:;: "Ensure it is enabled" ;:
-ofuncs=`cat enabled_functions | wc -l`
+ofuncs=`count_enabled_functions`
test $ofuncs -ne 0
:;: "Disable and remove the first event"
echo 0 > events/fprobes/test1/enable
echo "-:fprobes/test1" >> dynamic_events
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $ofuncs -eq $funcs
:;: "Disable and remove other events" ;:
echo 0 > events/fprobes/enable
echo > dynamic_events
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
rmmod trace-events-sample
@@ -63,12 +72,12 @@ echo "f:test1 $FUNC1" >> dynamic_events
echo 1 > events/fprobes/test1/enable
echo "f:test2 $FUNC2" >> dynamic_events
echo 1 > events/fprobes/test2/enable
-ofuncs=`cat enabled_functions | wc -l`
+ofuncs=`count_enabled_functions`
test $ofuncs -ne 0
:;: "Unload module (ftrace entry should be removed)" ;:
rmmod trace-events-sample
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -ne 0
test $ofuncs -ne $funcs
@@ -77,7 +86,7 @@ echo 0 > events/fprobes/test2/enable
echo "-:fprobes/test2" >> dynamic_events
:;: "Ensure ftrace is disabled." ;:
-funcs=`cat enabled_functions | wc -l`
+funcs=`count_enabled_functions`
test $funcs -eq 0
echo 0 > events/fprobes/enable
diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
index 8d275e3238d9..04eb8546fc07 100644
--- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
+++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-poll.tc
@@ -1,7 +1,7 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: event trigger - test poll wait on histogram
-# requires: set_event events/sched/sched_process_free/trigger events/sched/sched_process_free/hist
+# requires: set_event events/sched/sched_process_exit/trigger events/sched/sched_process_exit/hist
# flags: instance
POLL=${FTRACETEST_ROOT}/poll
@@ -11,7 +11,7 @@ if [ ! -x ${POLL} ]; then
exit_unresolved
fi
-EVENT=events/sched/sched_process_free/
+EVENT=events/sched/sched_process_exit/
# Check poll ops is supported. Before implementing poll on hist file, it
# returns soon with POLLIN | POLLOUT, but not POLLPRI.
diff --git a/tools/testing/selftests/gpio/.gitignore b/tools/testing/selftests/gpio/.gitignore
index ededb077a3a6..16f74de479f1 100644
--- a/tools/testing/selftests/gpio/.gitignore
+++ b/tools/testing/selftests/gpio/.gitignore
@@ -2,3 +2,4 @@
gpio-mockup-cdev
gpio-chip-info
gpio-line-name
+gpio-cdev-uaf
diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 96071b4800e8..2f423de83147 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -24,7 +24,7 @@ CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
-CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT)
+CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT) $(KHDR_INCLUDES)
CFLAGS += -I$(OUTPUT)/tools/include
LDLIBS += -lelf -lz -lrt -lpthread
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 1e979fb3542b..b851339308c2 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -86,6 +86,20 @@ static void load_programs(const struct test_program programs[],
self->skel = hid__open();
ASSERT_OK_PTR(self->skel) TEARDOWN_LOG("Error while calling hid__open");
+ /*
+ * Disable all struct_ops maps by default so libbpf does not autoload
+ * programs referenced by maps that are unrelated to the current test.
+ */
+ bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj) {
+ if (bpf_map__type(iter_map) == BPF_MAP_TYPE_STRUCT_OPS) {
+ err = bpf_map__set_autocreate(iter_map, false);
+ ASSERT_OK(err) TH_LOG("can not disable struct_ops map '%s'",
+ bpf_map__name(iter_map));
+ }
+
+ bpf_map__set_autoattach(iter_map, false);
+ }
+
for (int i = 0; i < progs_count; i++) {
struct bpf_program *prog;
struct bpf_map *map;
@@ -102,6 +116,10 @@ static void load_programs(const struct test_program programs[],
ASSERT_OK_PTR(map) TH_LOG("can not find struct_ops by name '%s'",
programs[i].name + 4);
+ err = bpf_map__set_autocreate(map, true);
+ ASSERT_OK(err) TH_LOG("can not enable struct_ops map '%s'",
+ programs[i].name + 4);
+
/* hid_id is the first field of struct hid_bpf_ops */
ops_hid_id = bpf_map__initial_value(map, NULL);
ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data");
@@ -109,13 +127,6 @@ static void load_programs(const struct test_program programs[],
*ops_hid_id = self->hid.hid_id;
}
- /* we disable the auto-attach feature of all maps because we
- * only want the tested one to be manually attached in the next
- * call to bpf_map__attach_struct_ops()
- */
- bpf_object__for_each_map(iter_map, *self->skel->skeleton->obj)
- bpf_map__set_autoattach(iter_map, false);
-
err = hid__load(self->skel);
ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
@@ -887,6 +898,17 @@ TEST_F(hid_bpf, test_rdesc_fixup)
ASSERT_EQ(rpt_desc.value[4], 0x42);
}
+TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_rdesc_fixup_get_data_overflow" },
+ };
+
+ LOAD_PROGRAMS(progs);
+
+ ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
+}
+
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index 5ecc845ef792..b21fbb13c926 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -13,6 +13,7 @@ struct attach_prog_args {
__u64 callback_check = 52;
__u64 callback2_check = 52;
+__u64 get_data_overflow_check;
SEC("?struct_ops/hid_device_event")
int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
@@ -240,6 +241,20 @@ struct hid_bpf_ops rdesc_fixup = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup,
};
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx)
+{
+ if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */))
+ get_data_overflow_check = 1;
+
+ return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
+};
+
SEC("?struct_ops/hid_device_event")
int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py
index fa4fb2054bd4..7897340118b4 100644
--- a/tools/testing/selftests/hid/tests/test_multitouch.py
+++ b/tools/testing/selftests/hid/tests/test_multitouch.py
@@ -513,6 +513,79 @@ class SmartTechDigitizer(Digitizer):
return absinfo is not None and absinfo.resolution == 3
+class MinWin8TSParallelBigContactMax(Digitizer):
+ """A parallel Win8 touchscreen that advertises a ContactCountMaximum much
+ larger than the number of contacts it actually reports.
+
+ Such firmware makes the driver allocate that many input slots (up to 255)
+ while the input report only carries a few contacts. This is what used to
+ drive the per-slot bit operations on mt_io_flags out of bounds. The number
+ of contacts a HID report can describe is limited by the descriptor size,
+ so a large ContactCountMaximum can only be expressed this way, decoupled
+ from the number of finger collections."""
+
+ def __init__(self, n_fingers=5, contact_max=250):
+ self.phys_max = 120, 90
+ rdesc_finger_str = f"""
+ Usage Page (Digitizers)
+ Usage (Finger)
+ Collection (Logical)
+ Report Size (1)
+ Report Count (1)
+ Logical Minimum (0)
+ Logical Maximum (1)
+ Usage (Tip Switch)
+ Input (Data,Var,Abs)
+ Report Size (7)
+ Logical Maximum (127)
+ Input (Cnst,Var,Abs)
+ Report Size (8)
+ Logical Maximum (255)
+ Usage (Contact Id)
+ Input (Data,Var,Abs)
+ Report Size (16)
+ Unit Exponent (-1)
+ Unit (SILinear: cm)
+ Logical Maximum (4095)
+ Physical Minimum (0)
+ Physical Maximum ({self.phys_max[0]})
+ Usage Page (Generic Desktop)
+ Usage (X)
+ Input (Data,Var,Abs)
+ Physical Maximum ({self.phys_max[1]})
+ Usage (Y)
+ Input (Data,Var,Abs)
+ End Collection
+"""
+ rdesc_str = f"""
+ Usage Page (Digitizers)
+ Usage (Touch Screen)
+ Collection (Application)
+ Report ID (1)
+ {rdesc_finger_str * n_fingers}
+ Unit Exponent (-4)
+ Unit (SILinear: s)
+ Logical Maximum (65535)
+ Physical Maximum (65535)
+ Usage Page (Digitizers)
+ Usage (Scan Time)
+ Input (Data,Var,Abs)
+ Report Size (8)
+ Logical Maximum (255)
+ Usage (Contact Count)
+ Input (Data,Var,Abs)
+ Report ID (2)
+ Logical Maximum ({contact_max})
+ Usage (Contact Max)
+ Feature (Data,Var,Abs)
+ End Collection
+ {Digitizer.msCertificationBlob(68)}
+"""
+ super().__init__(
+ f"uhid test parallel big contact max {contact_max}", rdesc_str
+ )
+
+
class BaseTest:
class TestMultitouch(base.BaseTestCase.TestUhid):
kernel_modules = [KERNEL_MODULE]
@@ -1735,6 +1808,47 @@ class TestMinWin8TSParallel(BaseTest.TestWin8Multitouch):
return MinWin8TSParallel(10)
+class TestMinWin8TSParallelBigContactMax(base.BaseTestCase.TestUhid):
+ """Regression test for the out-of-bounds bit operations on
+ struct mt_device.mt_io_flags.
+
+ A Win8 touchscreen may advertise a ContactCountMaximum much larger than
+ the number of contacts it reports. The driver used to keep the per-slot
+ active state in the bits of a single unsigned long while indexing
+ set_bit()/clear_bit() by the slot number, so such a device drove those bit
+ operations out of bounds. The sticky-fingers release timer made it fatal:
+ mt_release_contacts() cleared one bit per slot, overwrote the adjacent
+ struct mt_device members and panicked the kernel.
+
+ Send a single contact, let the 100ms sticky-fingers timer release it, and
+ check that the kernel reports the release cleanly instead of crashing."""
+
+ kernel_modules = [KERNEL_MODULE]
+
+ def create_device(self):
+ return MinWin8TSParallelBigContactMax()
+
+ def test_sticky_fingers_release_big_contact_max(self):
+ uhdev = self.uhdev
+ evdev = uhdev.get_evdev()
+
+ assert evdev.num_slots == uhdev.max_contacts
+
+ t0 = Touch(1, 5, 10)
+ r = uhdev.event([t0])
+ events = uhdev.next_sync_events()
+ self.debug_reports(r, uhdev, events)
+ assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
+
+ # do not release the contact; the sticky-fingers timer must do it
+ # after 100ms, which is where the out-of-bounds release used to hit
+ time.sleep(0.2)
+ events = uhdev.next_sync_events()
+ self.debug_reports(r, uhdev, events)
+ assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
+ assert evdev.slots[0][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
+
+
class TestMinWin8TSHybrid(BaseTest.TestWin8Multitouch):
def create_device(self):
return MinWin8TSHybrid()
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..6fc34e9bf8e1 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -174,6 +174,7 @@ TEST_GEN_PROGS_arm64 += arm64/hello_el2
TEST_GEN_PROGS_arm64 += arm64/host_sve
TEST_GEN_PROGS_arm64 += arm64/hypercalls
TEST_GEN_PROGS_arm64 += arm64/external_aborts
+TEST_GEN_PROGS_arm64 += arm64/mmio_sign_ext
TEST_GEN_PROGS_arm64 += arm64/page_fault_test
TEST_GEN_PROGS_arm64 += arm64/psci_test
TEST_GEN_PROGS_arm64 += arm64/sea_to_user
diff --git a/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c
new file mode 100644
index 000000000000..25196f1e6322
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/mmio_sign_ext.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mmio_sign_ext - Test sign-extending MMIO load emulation (LDRSB/LDRSH/LDRSW)
+ *
+ * Copyright (c) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+
+#include <asm/ptrace.h>
+
+#include "processor.h"
+#include "test_util.h"
+
+#define MMIO_ADDR 0x8000000ULL
+
+/* AP[1]: allow unprivileged (EL0) access to a mapping. */
+#define PTE_USER BIT(6)
+
+/* SPSR for ERET to EL0t with DAIF masked. */
+#define SPSR_EL0 (PSR_MODE_EL0t | PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
+
+struct mmio_test {
+ const char *name;
+ uint64_t data; /* access-width value, host byte order */
+ uint8_t len;
+ uint64_t expected; /* sign-extended result; same for LE and BE */
+};
+
+/* Paired 1:1, in order, with the loads in guest_loads_le() and el0_be_loads. */
+static const struct mmio_test tests[] = {
+ /* LDRSB Xt: byte sign-extended to 64 bits */
+ { "LDRSB Xt 0xFF", 0xFF, 1, 0xFFFFFFFFFFFFFFFFULL },
+ { "LDRSB Xt 0x7F", 0x7F, 1, 0x7FULL },
+
+ /* LDRSB Wt: byte sign-extended to 32 bits, upper 32 bits zeroed */
+ { "LDRSB Wt 0xFF", 0xFF, 1, 0xFFFFFFFFULL },
+ { "LDRSB Wt 0x7F", 0x7F, 1, 0x7FULL },
+
+ /* LDRSH Xt: halfword sign-extended to 64 bits */
+ { "LDRSH Xt 0x8001", 0x8001, 2, 0xFFFFFFFFFFFF8001ULL },
+ { "LDRSH Xt 0x7FFF", 0x7FFF, 2, 0x7FFFULL },
+
+ /* LDRSH Wt: halfword sign-extended to 32 bits, upper 32 bits zeroed */
+ { "LDRSH Wt 0x8001", 0x8001, 2, 0xFFFF8001ULL },
+ { "LDRSH Wt 0x7FFF", 0x7FFF, 2, 0x7FFFULL },
+
+ /* LDRSW Xt: word sign-extended to 64 bits (no Wt form) */
+ { "LDRSW Xt 0x80000001", 0x80000001, 4, 0xFFFFFFFF80000001ULL },
+ { "LDRSW Xt 0x7FFFFFFF", 0x7FFFFFFF, 4, 0x7FFFFFFFULL },
+};
+
+/* Issue one sign-extending load from MMIO and report the result. */
+#define GUEST_LDRS(load) do { \
+ uint64_t val; \
+ \
+ asm volatile(load : "=r"(val) : "r"(MMIO_ADDR) : "memory"); \
+ GUEST_SYNC(val); \
+} while (0)
+
+/* Little-endian pass: loads issued at EL1. */
+static void guest_loads_le(void)
+{
+ GUEST_LDRS("ldrsb %0, [%1]");
+ GUEST_LDRS("ldrsb %0, [%1]");
+ GUEST_LDRS("ldrsb %w0, [%1]");
+ GUEST_LDRS("ldrsb %w0, [%1]");
+ GUEST_LDRS("ldrsh %0, [%1]");
+ GUEST_LDRS("ldrsh %0, [%1]");
+ GUEST_LDRS("ldrsh %w0, [%1]");
+ GUEST_LDRS("ldrsh %w0, [%1]");
+ GUEST_LDRS("ldrsw %0, [%1]");
+ GUEST_LDRS("ldrsw %0, [%1]");
+}
+
+/*
+ * Run the big-endian loads at EL0, where SCTLR_EL1.E0E flips only the data
+ * endianness; at EL1, SCTLR_EL1.EE would also flip the page-table walk and
+ * fault on the little-endian tables. x0 holds MMIO_ADDR; results return in
+ * x19..x28 (tests[] order) via a single SVC.
+ */
+extern char el0_be_loads[];
+asm(
+" .pushsection .text, \"ax\"\n"
+" .global el0_be_loads\n"
+"el0_be_loads:\n"
+" ldrsb x19, [x0]\n"
+" ldrsb x20, [x0]\n"
+" ldrsb w21, [x0]\n"
+" ldrsb w22, [x0]\n"
+" ldrsh x23, [x0]\n"
+" ldrsh x24, [x0]\n"
+" ldrsh w25, [x0]\n"
+" ldrsh w26, [x0]\n"
+" ldrsw x27, [x0]\n"
+" ldrsw x28, [x0]\n"
+" svc #0\n"
+" .popsection\n"
+);
+
+/* EL1 handler for the EL0 SVC: report the results, then finish. */
+static void el0_svc_handler(struct ex_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ GUEST_SYNC(regs->regs[19 + i]);
+
+ GUEST_DONE();
+}
+
+static bool guest_mixed_endian_el0(void)
+{
+ uint64_t mmfr0 = read_sysreg(id_aa64mmfr0_el1);
+
+ return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) ||
+ SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0);
+}
+
+static void guest_code(void)
+{
+ guest_loads_le();
+
+ if (guest_mixed_endian_el0()) {
+ write_sysreg(read_sysreg(sctlr_el1) | SCTLR_EL1_E0E, sctlr_el1);
+ isb();
+
+ asm volatile(
+ " msr elr_el1, %[pc]\n"
+ " msr spsr_el1, %[spsr]\n"
+ " mov x0, %[mmio]\n"
+ " isb\n"
+ " eret\n"
+ :
+ : [pc] "r"(el0_be_loads),
+ [spsr] "r"((uint64_t)SPSR_EL0),
+ [mmio] "r"(MMIO_ADDR)
+ : "x0", "memory");
+ __builtin_unreachable(); /* el0_svc_handler ends the test */
+ }
+
+ GUEST_DONE();
+}
+
+static void handle_mmio(struct kvm_run *run, const struct mmio_test *t, bool be)
+{
+ int i;
+
+ TEST_ASSERT_EQ(run->mmio.phys_addr, MMIO_ADDR);
+ TEST_ASSERT(!run->mmio.is_write, "Expected MMIO read for %s", t->name);
+ TEST_ASSERT_EQ(run->mmio.len, t->len);
+
+ memset(run->mmio.data, 0, sizeof(run->mmio.data));
+ if (be) {
+ /* The guest reads the device bytes most-significant first. */
+ for (i = 0; i < t->len; i++)
+ run->mmio.data[i] = t->data >> (8 * (t->len - 1 - i));
+ } else {
+ /* Works because arm64 KVM hosts are always little-endian. */
+ memcpy(run->mmio.data, &t->data, t->len);
+ }
+}
+
+static void expect_sync(struct kvm_vcpu *vcpu, struct ucall *uc,
+ const struct mmio_test *t)
+{
+ switch (get_ucall(vcpu, uc)) {
+ case UCALL_SYNC:
+ TEST_ASSERT(uc->args[1] == t->expected,
+ "%s: got %#lx, want %#lx", t->name,
+ (unsigned long)uc->args[1], (unsigned long)t->expected);
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(*uc);
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall for %s", t->name);
+ }
+}
+
+/* OR PTE_USER into the leaf descriptors covering [gva, gva + len). */
+static void make_el0_accessible(struct kvm_vm *vm, uint64_t gva, uint64_t len)
+{
+ uint64_t addr;
+
+ for (addr = gva & ~((uint64_t)vm->page_size - 1); addr < gva + len;
+ addr += vm->page_size)
+ *virt_get_pte_hva(vm, addr) |= PTE_USER;
+}
+
+static bool vcpu_mixed_endian_el0(struct kvm_vcpu *vcpu)
+{
+ uint64_t mmfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1));
+
+ return SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGEND, mmfr0) ||
+ SYS_FIELD_GET(ID_AA64MMFR0_EL1, BIGENDEL0, mmfr0);
+}
+
+int main(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ unsigned int i;
+ bool be;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ virt_map(vm, MMIO_ADDR, MMIO_ADDR, 1);
+
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+ vm_install_sync_handler(vm, VECTOR_SYNC_LOWER_64, ESR_ELx_EC_SVC64,
+ el0_svc_handler);
+
+ be = vcpu_mixed_endian_el0(vcpu);
+ if (be)
+ make_el0_accessible(vm, MMIO_ADDR, vm->page_size);
+
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(tests) * (be ? 2 : 1));
+
+ /* Little-endian pass: one load and one result per iteration. */
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ const struct mmio_test *t = &tests[i];
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ handle_mmio(vcpu->run, t, false);
+
+ vcpu_run(vcpu);
+ expect_sync(vcpu, &uc, t);
+
+ ksft_test_result_pass("%s\n", t->name);
+ }
+
+ if (be) {
+ /* The EL0 stub issues all the loads, then reports the results. */
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ handle_mmio(vcpu->run, &tests[i], true);
+ }
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ vcpu_run(vcpu);
+ expect_sync(vcpu, &uc, &tests[i]);
+ ksft_test_result_pass("BE %s\n", tests[i].name);
+ }
+ }
+
+ vcpu_run(vcpu);
+ TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE, "Expected UCALL_DONE");
+
+ kvm_vm_free(vm);
+
+ ksft_finished();
+}
diff --git a/tools/testing/selftests/kvm/x86/sev_init2_tests.c b/tools/testing/selftests/kvm/x86/sev_init2_tests.c
index 8db88c355f16..689390c10f7c 100644
--- a/tools/testing/selftests/kvm/x86/sev_init2_tests.c
+++ b/tools/testing/selftests/kvm/x86/sev_init2_tests.c
@@ -130,12 +130,18 @@ int main(int argc, char *argv[])
KVM_X86_SEV_VMSA_FEATURES,
&supported_vmsa_features);
- have_sev = kvm_cpu_has(X86_FEATURE_SEV);
- TEST_ASSERT(have_sev == !!(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM)),
- "sev: KVM_CAP_VM_TYPES (%x) does not match cpuid (checking %x)",
- kvm_check_cap(KVM_CAP_VM_TYPES), 1 << KVM_X86_SEV_VM);
+ /*
+ * Whether a VM type is available depends on KVM, not just CPUID: e.g.
+ * when all SEV ASIDs are assigned to SEV-SNP, KVM does not offer the
+ * SEV VM type even though X86_FEATURE_SEV is set. Derive availability
+ * from KVM_CAP_VM_TYPES and only assert the one-way implication that a
+ * type offered by KVM must also be reported in CPUID.
+ */
+ have_sev = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM);
+ TEST_ASSERT(!have_sev || kvm_cpu_has(X86_FEATURE_SEV),
+ "sev: SEV_VM supported without SEV in CPUID");
- TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM));
+ TEST_REQUIRE(have_sev);
have_sev_es = kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM);
TEST_ASSERT(!have_sev_es || kvm_cpu_has(X86_FEATURE_SEV_ES),
diff --git a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
index 42bc023d5193..d59abb198d86 100644
--- a/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
+++ b/tools/testing/selftests/kvm/x86/sev_migrate_tests.c
@@ -313,6 +313,49 @@ out:
kvm_vm_free(vm_no_vcpu);
}
+static void test_sev_snp_migrate_reject(void)
+{
+ struct kvm_vm *src_vm, *dst_vm;
+ int ret;
+
+ src_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ snp_vm_init(src_vm);
+ __vm_vcpu_add(src_vm, 0);
+ vm_sev_launch(src_vm, snp_default_policy(), NULL);
+
+ dst_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ __vm_vcpu_add(dst_vm, 0);
+
+ ret = __sev_migrate_from(dst_vm, src_vm);
+ TEST_ASSERT(ret == -1 && errno == EINVAL,
+ "SNP VM migration should be rejected. ret: %d, errno: %d",
+ ret, errno);
+
+ kvm_vm_free(src_vm);
+ kvm_vm_free(dst_vm);
+}
+
+static void test_sev_snp_mirror_reject(void)
+{
+ struct kvm_vm *src_vm, *dst_vm;
+ int ret;
+
+ src_vm = vm_create_barebones_type(KVM_X86_SNP_VM);
+ snp_vm_init(src_vm);
+ __vm_vcpu_add(src_vm, 0);
+ vm_sev_launch(src_vm, snp_default_policy(), NULL);
+
+ dst_vm = aux_vm_create(false);
+
+ ret = __sev_mirror_create(dst_vm, src_vm);
+ TEST_ASSERT(ret == -1 && errno == EINVAL,
+ "SNP VM mirroring should be rejected. ret: %d, errno: %d",
+ ret, errno);
+
+ kvm_vm_free(src_vm);
+ kvm_vm_free(dst_vm);
+}
+
static void test_sev_move_copy(void)
{
struct kvm_vm *dst_vm, *dst2_vm, *dst3_vm, *sev_vm, *mirror_vm,
@@ -384,12 +427,16 @@ int main(int argc, char *argv[])
test_sev_migrate_parameters();
if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM))
test_sev_move_copy();
+ if (kvm_cpu_has(X86_FEATURE_SEV_SNP))
+ test_sev_snp_migrate_reject();
}
if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) {
test_sev_mirror(/* es= */ false);
if (have_sev_es)
test_sev_mirror(/* es= */ true);
test_sev_mirror_parameters();
+ if (kvm_cpu_has(X86_FEATURE_SEV_SNP))
+ test_sev_snp_mirror_reject();
}
return 0;
}
diff --git a/tools/testing/selftests/kvm/x86/sev_smoke_test.c b/tools/testing/selftests/kvm/x86/sev_smoke_test.c
index 6b2cbe2a90b7..bf27b6187afa 100644
--- a/tools/testing/selftests/kvm/x86/sev_smoke_test.c
+++ b/tools/testing/selftests/kvm/x86/sev_smoke_test.c
@@ -247,7 +247,14 @@ int main(int argc, char *argv[])
{
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));
- test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);
+ /*
+ * Only exercise VM types the host actually offers. CPUID reporting
+ * SEV does not guarantee KVM offers the SEV VM type: when all SEV
+ * ASIDs are assigned to SEV-SNP, KVM_X86_SEV_VM is unavailable even
+ * though X86_FEATURE_SEV is set. Gate every type on KVM_CAP_VM_TYPES.
+ */
+ if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_VM))
+ test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);
if (kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SEV_ES_VM))
test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES);
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 2ed1f76b7a8b..be2eb88092fb 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -1281,6 +1281,103 @@ TEST_F(protocol, connect_unspec)
EXPECT_EQ(0, close(bind_fd));
}
+TEST_F(protocol, tcp_fastopen)
+{
+ const bool restricted = variant->sandbox == TCP_SANDBOX &&
+ variant->prot.type == SOCK_STREAM &&
+ (variant->prot.protocol == IPPROTO_TCP ||
+ variant->prot.protocol == IPPROTO_IP) &&
+ (variant->prot.domain == AF_INET ||
+ variant->prot.domain == AF_INET6);
+ const struct landlock_ruleset_attr ruleset_attr = {
+ .handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+ };
+ int bind_fd, client_fd, status;
+ char buf;
+ pid_t child;
+
+ bind_fd = socket_variant(&self->srv0);
+ ASSERT_LE(0, bind_fd);
+ EXPECT_EQ(0, bind_variant(bind_fd, &self->srv0));
+ if (self->srv0.protocol.type == SOCK_STREAM)
+ EXPECT_EQ(0, listen(bind_fd, backlog));
+
+ child = fork();
+ ASSERT_LE(0, child);
+ if (child == 0) {
+ int connect_fd, ret;
+
+ /* Closes listening socket for the child. */
+ EXPECT_EQ(0, close(bind_fd));
+
+ connect_fd = socket_variant(&self->srv0);
+ ASSERT_LE(0, connect_fd);
+
+ if (variant->sandbox == TCP_SANDBOX) {
+ const int ruleset_fd = landlock_create_ruleset(
+ &ruleset_attr, sizeof(ruleset_attr), 0);
+ ASSERT_LE(0, ruleset_fd);
+
+ enforce_ruleset(_metadata, ruleset_fd);
+ EXPECT_EQ(0, close(ruleset_fd));
+ }
+
+ /* Fast Open with no address. */
+ ret = sendto_variant(connect_fd, NULL, NULL, 0, MSG_FASTOPEN);
+ if (self->srv0.protocol.domain == AF_UNIX) {
+ EXPECT_EQ(-ENOTCONN, ret);
+ } else if (self->srv0.protocol.type == SOCK_DGRAM) {
+ EXPECT_EQ(-EDESTADDRREQ, ret);
+ } else {
+ EXPECT_EQ(-EINVAL, ret);
+ }
+
+ /* Fast Open to a denied address. */
+ ret = sendto_variant(connect_fd, &self->srv0, "A", 1,
+ MSG_FASTOPEN);
+ if (restricted) {
+ EXPECT_EQ(-EACCES, ret);
+ } else if (self->srv0.protocol.domain == AF_UNIX &&
+ self->srv0.protocol.type == SOCK_STREAM) {
+ EXPECT_EQ(-EOPNOTSUPP, ret);
+ } else {
+ EXPECT_EQ(0, ret);
+ }
+
+ EXPECT_EQ(0, close(connect_fd));
+ _exit(_metadata->exit_code);
+ return;
+ }
+
+ client_fd = bind_fd;
+ if (!restricted && self->srv0.protocol.type == SOCK_STREAM &&
+ self->srv0.protocol.domain != AF_UNIX) {
+ client_fd = accept(bind_fd, NULL, 0);
+ ASSERT_LE(0, client_fd);
+ }
+
+ if (restricted) {
+ EXPECT_EQ(-1, read(client_fd, &buf, 1));
+ EXPECT_EQ(ENOTCONN, errno);
+ } else if (self->srv0.protocol.domain == AF_UNIX &&
+ self->srv0.protocol.type == SOCK_STREAM) {
+ EXPECT_EQ(-1, read(client_fd, &buf, 1));
+ EXPECT_EQ(EINVAL, errno);
+ } else {
+ EXPECT_EQ(1, read(client_fd, &buf, 1));
+ EXPECT_EQ('A', buf);
+ }
+
+ EXPECT_EQ(child, waitpid(child, &status, 0));
+ EXPECT_EQ(1, WIFEXITED(status));
+ EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ if (client_fd != bind_fd)
+ EXPECT_LE(0, close(client_fd));
+
+ EXPECT_EQ(0, close(bind_fd));
+}
+
TEST_F(protocol, sendmsg_stream)
{
int srv0_fd, tmp_fd, client_fd, res;
diff --git a/tools/testing/selftests/landlock/scoped_signal_test.c b/tools/testing/selftests/landlock/scoped_signal_test.c
index f24f2c28f62e..2d37d0c06c06 100644
--- a/tools/testing/selftests/landlock/scoped_signal_test.c
+++ b/tools/testing/selftests/landlock/scoped_signal_test.c
@@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal)
_metadata->exit_code = KSFT_FAIL;
}
-enum thread_return {
- THREAD_INVALID = 0,
- THREAD_SUCCESS = 1,
- THREAD_ERROR = 2,
- THREAD_TEST_FAILED = 3,
-};
+/* clang-format off */
+#define THREAD_INVALID ((void *)0)
+#define THREAD_SUCCESS ((void *)1)
+#define THREAD_ERROR ((void *)2)
+#define THREAD_TEST_FAILED ((void *)3)
+/* clang-format on */
static void *thread_sync(void *arg)
{
@@ -262,15 +262,15 @@ static void *thread_sync(void *arg)
char buf;
if (read(pipe_read, &buf, 1) != 1)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
TEST(signal_scoping_thread_before)
{
pthread_t no_sandbox_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
@@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before)
EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
- EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
@@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before)
TEST(signal_scoping_thread_after)
{
pthread_t scoped_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
@@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after)
EXPECT_EQ(0, pthread_kill(scoped_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
- EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(scoped_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
@@ -327,20 +327,20 @@ void *thread_setuid(void *ptr)
char buf;
if (read(arg->pipe_read, &buf, 1) != 1)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
/* libc's setuid() should update all thread's credentials. */
if (getuid() != arg->new_uid)
- return (void *)THREAD_TEST_FAILED;
+ return THREAD_TEST_FAILED;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
TEST(signal_scoping_thread_setuid)
{
struct thread_setuid_args arg;
pthread_t no_sandbox_thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int pipe_parent[2];
int prev_uid;
@@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid)
EXPECT_EQ(arg.new_uid, getuid());
EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
- EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
+ EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
clear_cap(_metadata, CAP_SETUID);
@@ -400,6 +400,24 @@ static int setup_signal_handler(int signal)
return sigaction(SIGURG, &sa, NULL);
}
+/*
+ * MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB
+ * switch, so this function can be used for probing for its availability.
+ */
+static bool has_af_unix_oob(void)
+{
+ bool available = false;
+ int sp[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) {
+ available = (send(sp[0], ".", 1, MSG_OOB) == 1);
+ close(sp[0]);
+ close(sp[1]);
+ }
+
+ return available;
+}
+
/* clang-format off */
FIXTURE(fown) {};
/* clang-format on */
@@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket)
int pipe_parent[2], pipe_child[2];
pid_t child;
+ if (!has_af_unix_oob())
+ SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available");
+
memset(&server_address, 0, sizeof(server_address));
set_unix_address(&server_address, 0);
@@ -667,20 +688,20 @@ static void *thread_setown_scoped(void *arg)
ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (ruleset_fd < 0)
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
landlock_restrict_self(ruleset_fd, 0)) {
close(ruleset_fd);
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
}
close(ruleset_fd);
/* Makes this process group own the SIGIO source. */
if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) ||
fcntl(fd, F_SETFL, O_ASYNC))
- return (void *)THREAD_ERROR;
+ return THREAD_ERROR;
- return (void *)THREAD_SUCCESS;
+ return THREAD_SUCCESS;
}
/*
@@ -702,7 +723,7 @@ TEST(sigio_to_pgid_self)
{
int trigger[2];
pthread_t thread;
- enum thread_return ret = THREAD_INVALID;
+ void *ret = THREAD_INVALID;
int i;
drop_caps(_metadata);
@@ -722,7 +743,7 @@ TEST(sigio_to_pgid_self)
*/
ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped,
&trigger[0]));
- ASSERT_EQ(0, pthread_join(thread, (void **)&ret));
+ ASSERT_EQ(0, pthread_join(thread, &ret));
ASSERT_EQ(THREAD_SUCCESS, ret);
/* Fans SIGURG out to the process group. */
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index e4c49699f3f7..2f2b9879d100 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -23,6 +23,7 @@
#include <time.h>
#include <pthread.h>
#include <limits.h>
+#include <linux/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
diff --git a/tools/testing/selftests/mm/ksft_process_madv.sh b/tools/testing/selftests/mm/ksft_process_madv.sh
index 2c3137ae8bc8..edad2d2d888f 100755
--- a/tools/testing/selftests/mm/ksft_process_madv.sh
+++ b/tools/testing/selftests/mm/ksft_process_madv.sh
@@ -1,4 +1,4 @@
#!/bin/sh -e
# SPDX-License-Identifier: GPL-2.0
-./run_vmtests.sh -t mmap
+./run_vmtests.sh -t process_madv
diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c
index 762306177ad8..6f8971d5b3ce 100644
--- a/tools/testing/selftests/mm/pagemap_ioctl.c
+++ b/tools/testing/selftests/mm/pagemap_ioctl.c
@@ -1368,7 +1368,7 @@ void *thread_proc(void *mem)
ksft_exit_fail_msg("pthread_barrier_wait\n");
for (i = 0; i < access_per_thread; ++i)
- __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST);
+ __atomic_add_fetch(m + i * (page_size / sizeof(*m)), 1, __ATOMIC_SEQ_CST);
ret = pthread_barrier_wait(&end_barrier);
if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD)
@@ -1403,15 +1403,15 @@ static void transact_test(int page_size)
if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1))
ksft_exit_fail_msg("pthread_barrier_init\n");
- mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE,
+ mem = mmap(NULL, page_size * nthreads * pages_per_thread, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (mem == MAP_FAILED)
ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno));
- wp_init(mem, 0x1000 * nthreads * pages_per_thread);
- wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread);
+ wp_init(mem, page_size * nthreads * pages_per_thread);
+ wp_addr_range(mem, page_size * nthreads * pages_per_thread);
- memset(mem, 0, 0x1000 * nthreads * pages_per_thread);
+ memset(mem, 0, page_size * nthreads * pages_per_thread);
count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size);
ksft_test_result(count > 0, "%s count %u\n", __func__, count);
@@ -1420,7 +1420,7 @@ static void transact_test(int page_size)
finish = 0;
for (i = 0; i < nthreads; ++i)
- pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread);
+ pthread_create(&th, NULL, thread_proc, mem + page_size * i * pages_per_thread);
extra_pages = 0;
for (i = 0; i < iter_count; ++i) {
diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh
index b3827b43782b..d46d2cec89e4 100644
--- a/tools/testing/selftests/net/lib.sh
+++ b/tools/testing/selftests/net/lib.sh
@@ -70,12 +70,33 @@ ksft_exit_status_merge()
$ksft_xfail $ksft_pass $ksft_skip $ksft_fail
}
+timestamp_ms()
+{
+ local now
+ local seconds
+ local nanoseconds
+
+ now=$(date -u +%s:%N) || return
+ seconds=${now%:*}
+ nanoseconds=${now#*:}
+
+ if [[ $nanoseconds =~ ^[0-9]+$ ]]; then
+ nanoseconds=${nanoseconds:0:9}
+ else
+ nanoseconds=0
+ fi
+
+ echo $((seconds * 1000 + 10#$nanoseconds / 1000000))
+}
+
loopy_wait()
{
local sleep_cmd=$1; shift
local timeout_ms=$1; shift
+ local start_time
+ local current_time
- local start_time="$(date -u +%s%3N)"
+ start_time=$(timestamp_ms) || return
while true
do
local out
@@ -84,7 +105,7 @@ loopy_wait()
return 0
fi
- local current_time="$(date -u +%s%3N)"
+ current_time=$(timestamp_ms) || return
if ((current_time - start_time > timeout_ms)); then
echo -n "$out"
return 1
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index 87eae79d01c1..184bb04343f6 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -44,7 +44,7 @@ class cmd:
Use bkg() instead to run a command in the background.
"""
def __init__(self, comm, shell=None, fail=True, expect_fail=False, ns=None,
- background=False, host=None, timeout=5, ksft_ready=None,
+ background=False, host=None, timeout=20, ksft_ready=None,
ksft_wait=None):
if ns:
if hasattr(ns, 'user_ns_path'):
@@ -113,7 +113,7 @@ class cmd:
return stdout, stderr
- def process(self, terminate=True, fail=None, expect_fail=False, timeout=5):
+ def process(self, terminate=True, fail=None, expect_fail=False, timeout=20):
if fail is None:
fail = not terminate
diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
index 08ad07500e8a..fb1c59d45567 100755
--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
@@ -736,6 +736,61 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "on bridge"; then
ret=1
fi
+if ip -net "$nsr1" link show tun0 > /dev/null 2>&1 &&
+ ip -net "$nsr2" link show tun0 > /dev/null 2>&1; then
+ ip -net "$nsr1" route change default via 192.168.100.2
+ ip -net "$nsr2" route change default via 192.168.100.1
+ ip -6 -net "$nsr1" route delete default
+ ip -6 -net "$nsr1" route add default via fee1:3::2
+ ip -6 -net "$nsr2" route delete default
+ ip -6 -net "$nsr2" route add default via fee1:3::1
+ ip -net "$ns2" route add default via 10.0.2.1
+ ip -6 -net "$ns2" route add default via dead:2::1
+
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "tun0" tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "tun6" tcp dport 12345 ct mark set 1 flow add @f1 counter name routed_orig accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "br0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "tun0" accept'
+ ip netns exec "$nsr1" nft -a insert rule inet filter forward \
+ 'meta oif "tun6" accept'
+
+ ip netns exec "$nsr1" nft reset counters table inet filter >/dev/null
+
+ if test_tcp_forwarding "$ns1" "$ns2" 1 4 10.0.2.99 12345; then
+ check_counters "bridge + IPIP tunnel"
+ else
+ echo "FAIL: flow offload for ns1/ns2 with bridge + IPIP tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+ fi
+
+ if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+ check_counters "bridge + IP6IP6 tunnel"
+ else
+ echo "FAIL: flow offload for ns1/ns2 with bridge + IP6IP6 tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+ fi
+
+ ip -net "$nsr1" route change default via 192.168.10.2
+ ip -net "$nsr2" route change default via 192.168.10.1
+ ip -net "$ns2" route del default via 10.0.2.1
+ ip -6 -net "$nsr1" route delete default
+ ip -6 -net "$nsr1" route add default via fee1:2::2
+ ip -6 -net "$nsr2" route delete default
+ ip -6 -net "$nsr2" route add default via fee1:2::1
+ ip -6 -net "$ns2" route del default via dead:2::1
+else
+ echo "SKIP: bridge + tunnel flowtable regression (tun0 missing)"
+ [ "$ret" -eq 0 ] && ret=$ksft_skip
+fi
+
# Another test:
# Add bridge interface br0 to Router1, with NAT and VLAN.
diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c
index 4fcce5150850..2544ae35d07a 100644
--- a/tools/testing/selftests/net/tcp_mmap.c
+++ b/tools/testing/selftests/net/tcp_mmap.c
@@ -313,6 +313,8 @@ end:
tcp_info_get_rcv_mss(fd));
}
error:
+ if (ctx)
+ EVP_MD_CTX_free(ctx);
munmap(buffer, buffer_sz);
close(fd);
if (zflg)
@@ -606,6 +608,8 @@ int main(int argc, char *argv[])
EVP_DigestFinal_ex(ctx, digest, &digest_len);
send(fd, digest, (size_t)SHA256_DIGEST_LENGTH, 0);
}
+ if (ctx)
+ EVP_MD_CTX_free(ctx);
close(fd);
munmap(buffer, buffer_sz);
return 0;
diff --git a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c
index 74b6f6bcf067..a388b7963d47 100644
--- a/tools/testing/selftests/riscv/vector/validate_v_ptrace.c
+++ b/tools/testing/selftests/riscv/vector/validate_v_ptrace.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <sys/ptrace.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h>
@@ -25,9 +26,9 @@ TEST(ptrace_v_not_enabled)
SKIP(return, "Vector not supported");
chld_lock = 1;
- pid = fork();
+ pid = (pid_t)syscall(SYS_clone, SIGCHLD, 0, NULL, 0, NULL);
ASSERT_LE(0, pid)
- TH_LOG("fork: %m");
+ TH_LOG("clone: %m");
if (pid == 0) {
while (chld_lock == 1)
@@ -74,7 +75,7 @@ TEST(ptrace_v_not_enabled)
ASSERT_EQ(-1, ret);
/* cleanup */
-
+ free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
@@ -206,7 +207,7 @@ TEST(ptrace_v_early_debug)
EXPECT_EQ(vl_csr, regset_data->vl);
/* cleanup */
-
+ free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
@@ -330,7 +331,7 @@ TEST(ptrace_v_syscall_clobbering)
EXPECT_EQ(0UL, regset_data->vl);
/* cleanup */
-
+ free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
@@ -648,7 +649,7 @@ TEST_F(v_csr_invalid, ptrace_v_invalid_values)
ASSERT_EQ(ret, -1);
/* cleanup */
-
+ free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
@@ -910,7 +911,7 @@ TEST_F(v_csr_valid, ptrace_v_valid_values)
EXPECT_EQ(regset_data->vlenb, vlenb);
/* cleanup */
-
+ free(regset_data);
ASSERT_EQ(0, kill(pid, SIGKILL));
}
}
diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile
index 50d69e22ee7a..aba6317f6cb8 100644
--- a/tools/testing/selftests/rseq/Makefile
+++ b/tools/testing/selftests/rseq/Makefile
@@ -5,9 +5,13 @@ CLANG_FLAGS += -no-integrated-as
endif
top_srcdir = ../../../..
+include $(top_srcdir)/scripts/subarch.include
+ARCH ?= $(SUBARCH)
+LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \
- $(CLANG_FLAGS) -I$(top_srcdir)/tools/include
+ $(CLANG_FLAGS) -I$(top_srcdir)/tools/include \
+ -I$(LINUX_TOOL_ARCH_INCLUDE)
LDLIBS += -lpthread -ldl
# Own dependencies because we only want to build against 1st prerequisite, but
diff --git a/tools/testing/selftests/sched_ext/Makefile b/tools/testing/selftests/sched_ext/Makefile
index 5d2dffca0e91..3cfe90e0f34f 100644
--- a/tools/testing/selftests/sched_ext/Makefile
+++ b/tools/testing/selftests/sched_ext/Makefile
@@ -176,6 +176,7 @@ auto-test-targets := \
maybe_null \
minimal \
non_scx_kfunc_deny \
+ nohz_tick \
numa \
allowed_cpus \
peek_dsq \
diff --git a/tools/testing/selftests/sched_ext/nohz_tick.bpf.c b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c
new file mode 100644
index 000000000000..6998c5dd6bcb
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/nohz_tick.bpf.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
+ *
+ * Exercise tick dependency transitions between infinite and finite slices.
+ */
+#include <scx/common.bpf.h>
+
+char _license[] SEC("license") = "GPL";
+
+const volatile s32 test_cpu;
+bool finite_phase;
+u64 nr_inf_running;
+u64 nr_finite_running;
+u64 nr_finite_ticks;
+
+UEI_DEFINE(uei);
+
+s32 BPF_STRUCT_OPS(nohz_tick_select_cpu, struct task_struct *p, s32 prev_cpu,
+ u64 wake_flags)
+{
+ return prev_cpu;
+}
+
+void BPF_STRUCT_OPS(nohz_tick_enqueue, struct task_struct *p, u64 enq_flags)
+{
+ u64 slice = finite_phase ? 1000000ULL : SCX_SLICE_INF;
+
+ scx_bpf_dsq_insert(p, SCX_DSQ_GLOBAL, slice, enq_flags);
+ if (enq_flags & SCX_ENQ_LAST)
+ scx_bpf_kick_cpu(test_cpu, SCX_KICK_IDLE);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_running, struct task_struct *p)
+{
+ if (bpf_get_smp_processor_id() != test_cpu)
+ return;
+
+ if (finite_phase)
+ __sync_fetch_and_add(&nr_finite_running, 1);
+ else
+ __sync_fetch_and_add(&nr_inf_running, 1);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_tick, struct task_struct *p)
+{
+ if (bpf_get_smp_processor_id() == test_cpu && finite_phase)
+ __sync_fetch_and_add(&nr_finite_ticks, 1);
+}
+
+void BPF_STRUCT_OPS(nohz_tick_exit, struct scx_exit_info *ei)
+{
+ UEI_RECORD(uei, ei);
+}
+
+SEC(".struct_ops.link")
+struct sched_ext_ops nohz_tick_ops = {
+ .select_cpu = (void *)nohz_tick_select_cpu,
+ .enqueue = (void *)nohz_tick_enqueue,
+ .running = (void *)nohz_tick_running,
+ .tick = (void *)nohz_tick_tick,
+ .exit = (void *)nohz_tick_exit,
+ .name = "nohz_tick",
+ .timeout_ms = 1000U,
+};
diff --git a/tools/testing/selftests/sched_ext/nohz_tick.c b/tools/testing/selftests/sched_ext/nohz_tick.c
new file mode 100644
index 000000000000..028f54391c2c
--- /dev/null
+++ b/tools/testing/selftests/sched_ext/nohz_tick.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES
+ *
+ * Validate that a finite-slice EXT task restarts the scheduler tick when it
+ * follows an infinite-slice EXT task and an idle interval on a NOHZ_FULL CPU.
+ */
+#define _GNU_SOURCE
+
+#include <bpf/bpf.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <scx/common.h>
+
+#include "nohz_tick.bpf.skel.h"
+#include "scx_test.h"
+
+#ifndef SCHED_EXT
+#define SCHED_EXT 7
+#endif
+
+#define MIN_FINITE_TICKS 3
+#define PHASE_TIMEOUT_MS 1000
+
+struct nohz_tick_ctx {
+ struct nohz_tick *skel;
+ cpu_set_t original_mask;
+ int test_cpu;
+};
+
+static int first_allowed_cpu(const cpu_set_t *mask, int first, int last)
+{
+ int cpu;
+
+ for (cpu = first; cpu <= last && cpu < CPU_SETSIZE; cpu++)
+ if (CPU_ISSET(cpu, mask))
+ return cpu;
+
+ return -1;
+}
+
+static int find_nohz_full_cpu(const cpu_set_t *allowed)
+{
+ char buf[4096], *cur, *end;
+ FILE *file;
+
+ file = fopen("/sys/devices/system/cpu/nohz_full", "r");
+ if (!file)
+ return -1;
+ if (!fgets(buf, sizeof(buf), file)) {
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+
+ cur = buf;
+ while (*cur) {
+ long first, last;
+ int cpu;
+
+ while (*cur == ' ' || *cur == '\t' || *cur == ',')
+ cur++;
+ if (*cur < '0' || *cur > '9')
+ break;
+
+ errno = 0;
+ first = strtol(cur, &end, 10);
+ if (errno || end == cur || first < 0 || first >= CPU_SETSIZE)
+ return -1;
+ cur = end;
+ last = first;
+ if (*cur == '-') {
+ cur++;
+ errno = 0;
+ last = strtol(cur, &end, 10);
+ if (errno || end == cur || last < first)
+ return -1;
+ cur = end;
+ }
+
+ cpu = first_allowed_cpu(allowed, first, last);
+ if (cpu >= 0)
+ return cpu;
+ }
+
+ return -1;
+}
+
+static pid_t start_worker(int cpu)
+{
+ struct sched_param param = {};
+ cpu_set_t mask;
+ pid_t parent;
+ pid_t pid;
+
+ parent = getpid();
+ pid = fork();
+ if (pid != 0)
+ return pid;
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) || getppid() != parent)
+ _exit(1);
+
+ /*
+ * Become EXT before touching the target so it stays idle until wakeup.
+ */
+ if (sched_setscheduler(0, SCHED_EXT, &param))
+ _exit(1);
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+ if (sched_setaffinity(0, sizeof(mask), &mask))
+ _exit(1);
+
+ for (;;)
+ asm volatile("" ::: "memory");
+}
+
+static void stop_worker(pid_t pid)
+{
+ if (pid <= 0)
+ return;
+
+ kill(pid, SIGKILL);
+ waitpid(pid, NULL, 0);
+}
+
+static int pause_worker(pid_t pid)
+{
+ int status;
+
+ if (kill(pid, SIGSTOP))
+ return -errno;
+ if (waitpid(pid, &status, WUNTRACED) != pid)
+ return -errno;
+ if (!WIFSTOPPED(status))
+ return -ECHILD;
+
+ return 0;
+}
+
+static bool wait_for_counter(const u64 *counter, u64 value, int timeout_ms)
+{
+ int elapsed;
+
+ for (elapsed = 0; elapsed < timeout_ms; elapsed++) {
+ if (__atomic_load_n(counter, __ATOMIC_RELAXED) >= value)
+ return true;
+ usleep(1000);
+ }
+
+ return false;
+}
+
+static enum scx_test_status setup(void **ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx;
+ cpu_set_t controller_mask;
+ int cpu;
+
+ ctx = calloc(1, sizeof(*ctx));
+ SCX_FAIL_IF(!ctx, "Failed to allocate context");
+ if (sched_getaffinity(0, sizeof(ctx->original_mask),
+ &ctx->original_mask)) {
+ free(ctx);
+ SCX_FAIL("Failed to get affinity (%d)", errno);
+ }
+
+ cpu = find_nohz_full_cpu(&ctx->original_mask);
+ if (cpu < 0) {
+ fprintf(stderr, "SKIP: no allowed NOHZ_FULL CPU\n");
+ free(ctx);
+ return SCX_TEST_SKIP;
+ }
+
+ controller_mask = ctx->original_mask;
+ CPU_CLR(cpu, &controller_mask);
+ if (CPU_COUNT(&controller_mask) == 0) {
+ fprintf(stderr, "SKIP: no housekeeping CPU available\n");
+ free(ctx);
+ return SCX_TEST_SKIP;
+ }
+
+ ctx->test_cpu = cpu;
+ ctx->skel = nohz_tick__open();
+ if (!ctx->skel) {
+ free(ctx);
+ SCX_FAIL("Failed to open skeleton");
+ }
+
+ SCX_ENUM_INIT(ctx->skel);
+ ctx->skel->rodata->test_cpu = cpu;
+ ctx->skel->struct_ops.nohz_tick_ops->flags |= SCX_OPS_SWITCH_PARTIAL |
+ SCX_OPS_ENQ_LAST;
+ if (nohz_tick__load(ctx->skel)) {
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+ SCX_FAIL("Failed to load skeleton");
+ }
+
+ if (sched_setaffinity(0, sizeof(controller_mask), &controller_mask)) {
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+ SCX_FAIL("Failed to move controller off CPU %d (%d)", cpu, errno);
+ }
+
+ *ctx_ptr = ctx;
+ return SCX_TEST_PASS;
+}
+
+static enum scx_test_status run(void *ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx = ctx_ptr;
+ struct nohz_tick *skel = ctx->skel;
+ struct bpf_link *link = NULL;
+ enum scx_test_status status = SCX_TEST_FAIL;
+ pid_t finite_worker = -1;
+ pid_t inf_worker = -1;
+ u64 finite_running;
+ u64 finite_ticks;
+ int ret;
+
+ link = bpf_map__attach_struct_ops(skel->maps.nohz_tick_ops);
+ if (!link) {
+ SCX_ERR("Failed to attach scheduler");
+ goto out;
+ }
+
+ /*
+ * Establish SCX_RQ_CAN_STOP_TICK with an infinite-slice task.
+ */
+ inf_worker = start_worker(ctx->test_cpu);
+ if (inf_worker < 0) {
+ SCX_ERR("Failed to start infinite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_inf_running, 1,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Infinite-slice worker was not scheduled");
+ goto out;
+ }
+
+ /* Block without exiting so the rq retains the infinite-slice state. */
+ ret = pause_worker(inf_worker);
+ if (ret) {
+ SCX_ERR("Failed to stop infinite-slice worker (%d)", ret);
+ goto out;
+ }
+
+ /* Let the target enter idle with its tick stopped. */
+ usleep(100000);
+
+ /*
+ * The next EXT task receives a finite slice and must restart the tick.
+ */
+ __atomic_store_n(&skel->bss->finite_phase, true, __ATOMIC_RELEASE);
+ finite_worker = start_worker(ctx->test_cpu);
+ if (finite_worker < 0) {
+ SCX_ERR("Failed to start finite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_running, 1,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Finite-slice worker was not scheduled");
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_ticks, MIN_FINITE_TICKS,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Finite-slice worker received only %llu scheduler ticks",
+ (unsigned long long)skel->bss->nr_finite_ticks);
+ goto out;
+ }
+ stop_worker(finite_worker);
+ finite_worker = -1;
+
+ /*
+ * Leave the CPU idle after a finite-slice task. The next finite-slice
+ * task must restart the tick even though the slice type is unchanged.
+ */
+ usleep(100000);
+ finite_running = __atomic_load_n(&skel->bss->nr_finite_running,
+ __ATOMIC_RELAXED);
+ finite_ticks = __atomic_load_n(&skel->bss->nr_finite_ticks,
+ __ATOMIC_RELAXED);
+
+ finite_worker = start_worker(ctx->test_cpu);
+ if (finite_worker < 0) {
+ SCX_ERR("Failed to start second finite-slice worker (%d)", errno);
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_running,
+ finite_running + 1, PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Second finite-slice worker was not scheduled");
+ goto out;
+ }
+ if (!wait_for_counter(&skel->bss->nr_finite_ticks,
+ finite_ticks + MIN_FINITE_TICKS,
+ PHASE_TIMEOUT_MS)) {
+ SCX_ERR("Second finite-slice worker received only %llu scheduler ticks",
+ (unsigned long long)(skel->bss->nr_finite_ticks -
+ finite_ticks));
+ goto out;
+ }
+
+ if (skel->data->uei.kind != EXIT_KIND(SCX_EXIT_NONE)) {
+ SCX_ERR("Scheduler exited unexpectedly (kind=%llu code=%lld)",
+ (unsigned long long)skel->data->uei.kind,
+ (long long)skel->data->uei.exit_code);
+ goto out;
+ }
+
+ fprintf(stderr, "CPU %d received %llu finite-slice ticks\n",
+ ctx->test_cpu,
+ (unsigned long long)skel->bss->nr_finite_ticks);
+ status = SCX_TEST_PASS;
+out:
+ stop_worker(finite_worker);
+ stop_worker(inf_worker);
+ if (link)
+ bpf_link__destroy(link);
+ return status;
+}
+
+static void cleanup(void *ctx_ptr)
+{
+ struct nohz_tick_ctx *ctx = ctx_ptr;
+
+ sched_setaffinity(0, sizeof(ctx->original_mask), &ctx->original_mask);
+ nohz_tick__destroy(ctx->skel);
+ free(ctx);
+}
+
+struct scx_test nohz_tick = {
+ .name = "nohz_tick",
+ .description = "Verify finite EXT slices restart the NOHZ_FULL tick",
+ .setup = setup,
+ .run = run,
+ .cleanup = cleanup,
+};
+REGISTER_SCX_TEST(&nohz_tick)
diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c
index 85892b3b719c..b71813eaf5c0 100644
--- a/tools/testing/selftests/user_events/abi_test.c
+++ b/tools/testing/selftests/user_events/abi_test.c
@@ -132,6 +132,33 @@ static int event_delete(void)
return ret;
}
+/*
+ * Deleting an event drops its last reference, but an unregister may defer
+ * that put (and the freeing of the associated enabler) past an RCU grace
+ * period. The delete can therefore transiently fail with -EBUSY while the
+ * previous reference is still being dropped. Retry only on that transient
+ * failure; treat an already-deleted event (-ENOENT) as success and return
+ * any other error immediately rather than spinning for the full timeout.
+ */
+static int wait_for_event_delete(void)
+{
+ int i, ret;
+
+ for (i = 0; i < 10000; ++i) {
+ ret = event_delete();
+
+ if (ret == 0 || errno == ENOENT)
+ return 0;
+
+ if (errno != EBUSY)
+ return ret;
+
+ usleep(1000);
+ }
+
+ return ret;
+}
+
static int reg_enable_multi(void *enable, int size, int bit, int flags,
char *args)
{
@@ -262,7 +289,7 @@ TEST_F(user, flags) {
ASSERT_TRUE(event_exists());
/* Ensure we can delete it */
- ASSERT_EQ(0, event_delete());
+ ASSERT_EQ(0, wait_for_event_delete());
/* USER_EVENT_REG_MAX or above is not allowed */
ASSERT_EQ(-1, reg_enable_flags(&self->check, sizeof(int), 0,
diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c
index cafec0e52eb3..5727cb5b914c 100644
--- a/tools/testing/selftests/user_events/perf_test.c
+++ b/tools/testing/selftests/user_events/perf_test.c
@@ -85,6 +85,7 @@ static int get_offset(void)
static int clear(int *check)
{
struct user_unreg unreg = {0};
+ int i, ret;
unreg.size = sizeof(unreg);
unreg.disable_bit = 31;
@@ -99,13 +100,32 @@ static int clear(int *check)
if (errno != ENOENT)
return -1;
- if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
- if (errno != ENOENT)
+ /*
+ * Deleting the event drops its last reference, but the unregister
+ * above defers that put (and the freeing of the enabler) past an RCU
+ * grace period. The delete can therefore transiently fail with -EBUSY
+ * until that reference is dropped. Retry for up to ~10 seconds so the
+ * event is actually gone before the next test registers the same name.
+ */
+ for (i = 0; i < 10000; ++i) {
+ ret = ioctl(fd, DIAG_IOCSDEL, "__test_event");
+
+ if (ret == 0 || errno == ENOENT) {
+ ret = 0;
+ break;
+ }
+
+ if (errno != EBUSY) {
+ close(fd);
return -1;
+ }
+
+ usleep(1000);
+ }
close(fd);
- return 0;
+ return ret;
}
FIXTURE(user) {
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index e6e8cb52ab03..2c32c48db509 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -29,8 +29,6 @@ CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += -pthread
-LDLIBS += -luuid
-
$(TEST_GEN_PROGS): $(OUTPUT)/%: $(OUTPUT)/%.o $(LIBVFIO_O)
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LIBVFIO_O) $(LDLIBS) -o $@
diff --git a/tools/testing/selftests/vfio/lib/libvfio.mk b/tools/testing/selftests/vfio/lib/libvfio.mk
index 2b8d73b7d329..67942b085068 100644
--- a/tools/testing/selftests/vfio/lib/libvfio.mk
+++ b/tools/testing/selftests/vfio/lib/libvfio.mk
@@ -26,6 +26,8 @@ $(LIBVFIO_O_DIRS):
CFLAGS += -I$(LIBVFIO_SRCDIR)/include
+LDLIBS += -luuid
+
$(LIBVFIO_O): $(LIBVFIO_OUTPUT)/%.o : $(LIBVFIO_SRCDIR)/%.c | $(LIBVFIO_O_DIRS)
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
diff --git a/tools/testing/selftests/x86/test_shadow_stack.c b/tools/testing/selftests/x86/test_shadow_stack.c
index 21af54d5f4ea..3d6ca33edba4 100644
--- a/tools/testing/selftests/x86/test_shadow_stack.c
+++ b/tools/testing/selftests/x86/test_shadow_stack.c
@@ -873,6 +873,86 @@ out:
return err;
}
+/* Keep the CALL first so the function address is exactly the probed CALL. */
+extern void uprobe_call_trigger(void);
+asm (".pushsection .text\n"
+ ".global uprobe_call_target\n"
+ ".type uprobe_call_target, @function\n"
+ "uprobe_call_target:\n"
+ " ret\n"
+ ".size uprobe_call_target, .-uprobe_call_target\n"
+
+ ".global uprobe_call_trigger\n"
+ ".type uprobe_call_trigger, @function\n"
+ "uprobe_call_trigger:\n"
+ " call uprobe_call_target\n"
+ " ret\n"
+ ".size uprobe_call_trigger, .-uprobe_call_trigger\n"
+ ".popsection\n"
+);
+
+/* If CALL emulation misses the shadow stack update, this exits via SIGSEGV. */
+static int test_uprobe_call(void)
+{
+ const size_t attr_sz = sizeof(struct perf_event_attr);
+ const char *file = "/proc/self/exe";
+ int fd = -1, type, err = 1;
+ struct perf_event_attr attr;
+ struct sigaction sa = {};
+ ssize_t offset;
+
+ type = determine_uprobe_perf_type();
+ if (type < 0) {
+ if (type == -ENOENT)
+ printf("[SKIP]\tUprobe on CALL test, uprobes are not available\n");
+ return 0;
+ }
+
+ offset = get_uprobe_offset(uprobe_call_trigger);
+ if (offset < 0)
+ return 1;
+
+ sa.sa_sigaction = segv_gp_handler;
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGSEGV, &sa, NULL))
+ return 1;
+
+ /* Setup entry uprobe through perf event interface. */
+ memset(&attr, 0, attr_sz);
+ attr.size = attr_sz;
+ attr.type = type;
+ attr.config = 0;
+ attr.config1 = (__u64)(unsigned long)file;
+ attr.config2 = offset;
+
+ fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, -1 /* cpu */,
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (fd < 0)
+ goto out;
+
+ if (sigsetjmp(jmp_buffer, 1))
+ goto out;
+
+ if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK))
+ goto out;
+
+ /*
+ * This either segfaults and goes through sigsetjmp above
+ * or succeeds and we're good.
+ */
+ uprobe_call_trigger();
+
+ printf("[OK]\tUprobe on CALL test\n");
+ err = 0;
+
+out:
+ ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK);
+ signal(SIGSEGV, SIG_DFL);
+ if (fd >= 0)
+ close(fd);
+ return err;
+}
+
void segv_handler_ptrace(int signum, siginfo_t *si, void *uc)
{
/* The SSP adjustment caused a segfault. */
@@ -1071,6 +1151,12 @@ int main(int argc, char *argv[])
goto out;
}
+ if (test_uprobe_call()) {
+ ret = 1;
+ printf("[FAIL]\tuprobe on CALL test\n");
+ goto out;
+ }
+
return ret;
out:
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile
index 60a102538988..387bc6cc18f0 100644
--- a/tools/tracing/rtla/Makefile
+++ b/tools/tracing/rtla/Makefile
@@ -45,6 +45,9 @@ else
LIB_OUTPUT = $(CURDIR)/lib
endif
+LIB_CTYPE = $(LIB_OUTPUT)/ctype.o
+LIB_CTYPE_SRC = $(srctree)/tools/lib/ctype.c
+
LIB_STRING = $(LIB_OUTPUT)/string.o
LIB_STRING_SRC = $(srctree)/tools/lib/string.c
@@ -117,12 +120,12 @@ tests/bpf/bpf_action_map.o: tests/bpf/bpf_action_map.c
$(Q)echo "BPF skeleton support is disabled, skipping tests/bpf/bpf_action_map.o"
endif
-$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
- $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
+$(RTLA): $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R)
+ $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RTLA) $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
-static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R)
+static: $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R)
$(eval LDFLAGS += -static)
- $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
+ $(QUIET_LINK)$(CC) -static $(LDFLAGS) -o $(RTLA)-static $(RTLA_IN) $(LIBSUBCMD) $(LIB_CTYPE) $(LIB_STRING) $(LIB_STR_ERROR_R) $(EXTLIBS)
rtla.%: fixdep FORCE
make -f $(srctree)/tools/build/Makefile.build dir=. $@
@@ -150,6 +153,9 @@ $(LIB_STR_ERROR_R): $(LIB_STR_ERROR_R_SRC) | $(LIB_OUTPUT)
$(LIB_STRING): $(LIB_STRING_SRC) | $(LIB_OUTPUT)
$(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
+$(LIB_CTYPE): $(LIB_CTYPE_SRC) | $(LIB_OUTPUT)
+ $(QUIET_CC)$(CC) $(CFLAGS) -c -o $@ $<
+
libsubcmd-clean:
$(call QUIET_CLEAN, libsubcmd)
$(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT)
diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c
index d0a8a6edbf0c..8c7f5e75b2ec 100644
--- a/tools/tracing/rtla/src/common.c
+++ b/tools/tracing/rtla/src/common.c
@@ -5,6 +5,7 @@
#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <sys/sysinfo.h>
#include "common.h"
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c
index b8d7d480595a..f4734f552d31 100644
--- a/tools/usb/usbip/libsrc/usbip_common.c
+++ b/tools/usb/usbip/libsrc/usbip_common.c
@@ -29,6 +29,8 @@ static const struct speed_string speed_strings[] = {
{ USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
{ USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
{ USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "10000", "Super Speed Plus(10000Mbps)" },
+ { USB_SPEED_SUPER_PLUS, "20000", "Super Speed Plus(20000Mbps)" },
{ 0, NULL, NULL }
};
diff --git a/tools/usb/usbip/libsrc/usbip_device_driver.c b/tools/usb/usbip/libsrc/usbip_device_driver.c
index 1dfbb76ab26c..c9b3619d86f3 100644
--- a/tools/usb/usbip/libsrc/usbip_device_driver.c
+++ b/tools/usb/usbip/libsrc/usbip_device_driver.c
@@ -57,6 +57,10 @@ static struct {
.speed = USB_SPEED_SUPER,
.name = "super-speed",
},
+ {
+ .speed = USB_SPEED_SUPER_PLUS,
+ .name = "super-speed-plus",
+ },
};
static
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c
index 8159fd98680b..4ca3783ee5b7 100644
--- a/tools/usb/usbip/libsrc/vhci_driver.c
+++ b/tools/usb/usbip/libsrc/vhci_driver.c
@@ -338,6 +338,7 @@ int usbip_vhci_get_free_port(uint32_t speed)
switch (speed) {
case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
if (vhci_driver->idev[i].hub != HUB_SPEED_SUPER)
continue;
break;
diff --git a/tools/virtio/asm/percpu_types.h b/tools/virtio/asm/percpu_types.h
new file mode 100644
index 000000000000..4eb53d93c099
--- /dev/null
+++ b/tools/virtio/asm/percpu_types.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_PERCPU_TYPES_H
+#define _ASM_PERCPU_TYPES_H
+
+#define __percpu_qual
+
+#endif /* _ASM_PERCPU_TYPES_H */
diff --git a/tools/virtio/linux/completion.h b/tools/virtio/linux/completion.h
new file mode 100644
index 000000000000..5e54b679721b
--- /dev/null
+++ b/tools/virtio/linux/completion.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_COMPLETION_H
+#define _LINUX_COMPLETION_H
+
+struct completion {
+ unsigned int done;
+};
+
+#endif /* _LINUX_COMPLETION_H */
diff --git a/tools/virtio/linux/device.h b/tools/virtio/linux/device.h
index 075c2140d975..abf100cb0023 100644
--- a/tools/virtio/linux/device.h
+++ b/tools/virtio/linux/device.h
@@ -1,4 +1,5 @@
#ifndef LINUX_DEVICE_H
+#define LINUX_DEVICE_H
struct device {
void *parent;
diff --git a/tools/virtio/linux/dma-mapping.h b/tools/virtio/linux/dma-mapping.h
index 8d1a16cb20db..b9fc5e8338e3 100644
--- a/tools/virtio/linux/dma-mapping.h
+++ b/tools/virtio/linux/dma-mapping.h
@@ -61,5 +61,6 @@ enum dma_data_direction {
#define DMA_MAPPING_ERROR (~(dma_addr_t)0)
#define DMA_ATTR_CPU_CACHE_CLEAN (1UL << 11)
+#define DMA_ATTR_DEBUGGING_IGNORE_CACHELINES 0
#endif
diff --git a/tools/virtio/linux/mod_devicetable.h b/tools/virtio/linux/mod_devicetable.h
new file mode 100644
index 000000000000..3ba594b8229d
--- /dev/null
+++ b/tools/virtio/linux/mod_devicetable.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_MOD_DEVICETABLE_H
+#define _LINUX_MOD_DEVICETABLE_H
+
+#include <linux/types.h>
+
+struct virtio_device_id {
+ __u32 device;
+ __u32 vendor;
+};
+
+#define VIRTIO_DEV_ANY_ID 0xffffffff
+
+#endif /* _LINUX_MOD_DEVICETABLE_H */
diff --git a/tools/virtio/linux/virtio_features.h b/tools/virtio/linux/virtio_features.h
new file mode 100644
index 000000000000..04cbb9622ec7
--- /dev/null
+++ b/tools/virtio/linux/virtio_features.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VIRTIO_FEATURES_H
+#define _LINUX_VIRTIO_FEATURES_H
+
+#include <linux/bug.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define VIRTIO_FEATURES_U64S 2
+#define VIRTIO_FEATURES_BITS (VIRTIO_FEATURES_U64S * 64)
+
+#define VIRTIO_BIT(b) (1ULL << ((b) & 0x3f))
+#define VIRTIO_U64(b) ((b) >> 6)
+
+#define VIRTIO_DECLARE_FEATURES(name) \
+ union { \
+ u64 name; \
+ u64 name##_array[VIRTIO_FEATURES_U64S];\
+ }
+
+static inline bool virtio_features_chk_bit(unsigned int bit)
+{
+ return bit < VIRTIO_FEATURES_BITS;
+}
+
+static inline bool virtio_features_test_bit(const u64 *features,
+ unsigned int bit)
+{
+ return virtio_features_chk_bit(bit) &&
+ !!(features[VIRTIO_U64(bit)] & VIRTIO_BIT(bit));
+}
+
+static inline void virtio_features_set_bit(u64 *features, unsigned int bit)
+{
+ if (virtio_features_chk_bit(bit))
+ features[VIRTIO_U64(bit)] |= VIRTIO_BIT(bit);
+}
+
+static inline void virtio_features_clear_bit(u64 *features, unsigned int bit)
+{
+ if (virtio_features_chk_bit(bit))
+ features[VIRTIO_U64(bit)] &= ~VIRTIO_BIT(bit);
+}
+
+static inline void virtio_features_zero(u64 *features)
+{
+ memset(features, 0, sizeof(features[0]) * VIRTIO_FEATURES_U64S);
+}
+
+static inline void virtio_features_from_u64(u64 *features, u64 from)
+{
+ virtio_features_zero(features);
+ features[0] = from;
+}
+
+static inline bool virtio_features_equal(const u64 *f1, const u64 *f2)
+{
+ int i;
+
+ for (i = 0; i < VIRTIO_FEATURES_U64S; ++i)
+ if (f1[i] != f2[i])
+ return false;
+ return true;
+}
+
+static inline void virtio_features_copy(u64 *to, const u64 *from)
+{
+ memcpy(to, from, sizeof(to[0]) * VIRTIO_FEATURES_U64S);
+}
+
+static inline void virtio_features_andnot(u64 *to, const u64 *f1, const u64 *f2)
+{
+ int i;
+
+ for (i = 0; i < VIRTIO_FEATURES_U64S; i++)
+ to[i] = f1[i] & ~f2[i];
+}
+
+#endif /* _LINUX_VIRTIO_FEATURES_H */