aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 09:18:14 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 09:18:14 +0100
commit9c87e61e3c5797277407ba5eae4eac8a52be3fa3 (patch)
treee3f902cb5363b5b90ab74a4b7e26fafbc15aaeaf /lib
parentb85966adbf5de0668a815c6e3527f87e0c387fb4 (diff)
parente4287bf34f97a88c7d9322f5bde828724c073a6b (diff)
Merge tag 'bpf-next-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov: "Major changes: - Recover from BPF arena page faults using a scratch page and add ptep_try_set() for lockless empty-slot installs on x86 and arm64. This allows BPF kfuncs to access arena pointers directly. The 'arena_direct_access' stable branch was created for this work and was pulled into sched-ext and bpf-next trees (Tejun Heo, Kumar Kartikeya Dwivedi) - Lift old restriction and support 6+ arguments in BPF programs and kfuncs on x86 and arm64 (Yonghong Song, Puranjay Mohan) Other features and fixes: - Add 24-bit BTF vlen and reclaim unused bits in the BTF UAPI to ease addition of new BTF kinds (Alan Maguire) - Raise the maximum BPF call chain depth from 8 to 16 frames (Alexei Starovoitov) - Refactor object relationship tracking in the verifier and fix a dynptr use-after-free bug (Amery Hung) - Harden the signed program loader and reject exclusive maps as inner maps (Daniel Borkmann) - Replace the verifier min/max bounds fields with a circular number (cnum) representation and improve 32->64 bit range refinements (Eduard Zingerman) - Introduce the arena library and runtime (libarena) with a buddy allocator, rbtree and SPMC queue data structures, ASAN support and a parallel test harness. Allow subprograms to return arena pointers and switch to a BTF type-tag based __arena annotation (Emil Tsalapatis) - Cache build IDs in the sleepable stackmap path and avoid faultable build ID reads under mm locks (Ihor Solodrai) - Introduce the tracing_multi link to attach a single BPF program to many kernel functions at once. Allow specifying the uprobe_multi target via FD (Jiri Olsa) - Extend the bpf_list family of kfuncs with bpf_list_add/del(), and bpf_list_is_first/is_last/empty() (Kaitao Cheng) - Extend the BPF syscall with common attributes support for prog_load, btf_load and map_create (Leon Hwang) - Wrap rhashtable as BPF map (Mykyta Yatsenko, Herbert Xu) - Add sleepable support for tracepoint programs and fix deadlocks in LRU map due to NMI reentry (Mykyta Yatsenko) - Fix OOB access in bpf_flow_keys, fix nullness analysis of inner arrays, enforce write checks for global subprograms (Nuoqi Gui) - Report the maximum combined stack depth and print a breakdown of instructions processed per subprogram (Paul Chaignon) - Add an XDP load-balancer benchmark and arm64 JIT support for stack arguments (Puranjay Mohan) - Add kfuncs to traverse over wakeup_sources (Samuel Wu) - Allow sleepable BPF programs to use LPM trie maps directly (Vlad Poenaru) - Many more fixes and cleanups across the verifier, BTF, sockmap, devmap, bpffs, security hooks, s390/riscv/loongarch JITs, rqspinlock, libbpf, bpftool, selftests" * tag 'bpf-next-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (336 commits) selftests/bpf: Work around llvm stack overflow in crypto progs selftests/bpf: add test for bpf_msg_pop_data() overflow bpf, sockmap: fix integer overflow in bpf_msg_pop_data() bounds check sockmap: Fix use-after-free in udp_bpf_recvmsg() bpf, sockmap: keep sk_msg copy state in sync bpf, sockmap: Fix wrong rsge offset in bpf_msg_push_data() bpf, sockmap: reject overflowing copy + len in bpf_msg_push_data() selftsets/bpf: Retry map update on helper_fill_hashmap() selftests/bpf: Add test for sleepable lsm_cgroup rejection selftests/bpf: Add test to verify the fix for bpf_setsockopt() helper bpf: Fix bpf_get/setsockopt to tos for ipv4-mapped ipv6 socket selftests/bpf: Avoid static LLVM linking for cross builds selftests/bpf: Use common CFLAGS for urandom_read selftests/bpf: Initialize operation name before use tools/bpf: build: Append extra cflags libbpf: Initialize CFLAGS before including Makefile.include bpftool: Append extra host flags bpftool: Avoid adding EXTRA_CFLAGS to HOST_CFLAGS bpftool: Pass host flags to bootstrap libbpf selftests/bpf: correct CONFIG_PPC64 macro name in comment ...
Diffstat (limited to 'lib')
-rw-r--r--lib/rhashtable.c102
-rw-r--r--lib/test_bpf.c363
-rw-r--r--lib/test_rhashtable.c75
3 files changed, 440 insertions, 100 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index c0ba34eadb39..40cfb38ac919 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -687,6 +687,108 @@ void *rhashtable_insert_slow(struct rhashtable *ht, const void *key,
}
EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
+/* Scan one element forward from prev_key's position in @tbl.
+ * Returns first rhash_head whose bucket > prev_key's bucket, or the
+ * element immediately after prev_key inside prev_key's bucket.
+ * Returns the first element if prev_key is NULL, NULL when @tbl is
+ * exhausted, or ERR_PTR(-ENOENT) if prev_key is not found in @tbl.
+ */
+static struct rhash_head *__rhashtable_next_in_table(
+ struct rhashtable *ht, struct bucket_table *tbl,
+ const void *prev_key)
+{
+ struct rhashtable_compare_arg arg = { .ht = ht, .key = prev_key };
+ const struct rhashtable_params params = ht->p;
+ struct rhash_head *he;
+ unsigned int b = 0;
+ bool found = false;
+
+ if (prev_key) {
+ b = rht_key_hashfn(ht, tbl, prev_key, params);
+ rht_for_each_rcu(he, tbl, b) {
+ bool match = params.obj_cmpfn
+ ? !params.obj_cmpfn(&arg, rht_obj(ht, he))
+ : !rhashtable_compare(&arg, rht_obj(ht, he));
+ if (found) {
+ if (match)
+ continue;
+ return he;
+ }
+ if (match)
+ found = true;
+ }
+ if (!found)
+ return ERR_PTR(-ENOENT);
+ b++;
+ }
+
+ for (; b < tbl->size; b++)
+ rht_for_each_rcu(he, tbl, b)
+ return he;
+ return NULL;
+}
+
+/**
+ * rhashtable_next_key - return next element after a given key
+ * @ht: hash table
+ * @prev_key: pointer to previous key, or NULL for the first element
+ *
+ * WARNING: this walk is highly unstable. Unlike rhashtable_walk_*(),
+ * it cannot detect a concurrent resize or rehash, so a full iteration
+ * is NOT guaranteed to terminate under adversarial or sustained
+ * rehashing. Callers MUST tolerate skipped and duplicated elements and
+ * SHOULD bound their loop externally.
+ *
+ * Returns the next element in best-effort iteration order, walking the
+ * @tbl chain (including any future_tbl in flight). Caller must hold RCU.
+ *
+ * Pass @prev_key == NULL to obtain the first element. To iterate, set
+ * @prev_key to the key of the previously returned element on each call,
+ * and stop when NULL is returned.
+ *
+ * Best-effort semantics:
+ * - Across the tbl->future_tbl chain, an element being migrated may
+ * transiently appear in both tables and be observed twice.
+ * - Concurrent inserts may or may not be observed.
+ * - Termination of a full iteration loop is NOT guaranteed under
+ * adversarial continuous rehash; callers MUST tolerate skips and
+ * repeats and SHOULD bound their loop externally.
+ * - Behavior on tables that contain duplicate keys is undefined:
+ * duplicates may be skipped, repeated, or trap the walk in a
+ * cycle. Callers requiring duplicate-key iteration must use
+ * rhashtable_walk_*() instead.
+ * - rhltable instances are not supported and return
+ * ERR_PTR(-EOPNOTSUPP).
+ * - If prev_key was concurrently deleted and is not present in any
+ * in-flight table, returns ERR_PTR(-ENOENT).
+ *
+ * Returns entry of the next element, or NULL when iteration is exhausted,
+ * or ERR_PTR(-ENOENT) if prev_key is not found, or
+ * ERR_PTR(-EOPNOTSUPP) if @ht is an rhltable.
+ */
+void *rhashtable_next_key(struct rhashtable *ht, const void *prev_key)
+{
+ struct bucket_table *tbl;
+ struct rhash_head *he;
+
+ if (unlikely(ht->rhlist))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+ do {
+ he = __rhashtable_next_in_table(ht, tbl, prev_key);
+ if (!IS_ERR_OR_NULL(he))
+ return rht_obj(ht, he);
+ if (!he)
+ prev_key = NULL;
+ /* See any new future_tbl attached during a rehash. */
+ smp_rmb();
+ tbl = rht_dereference_rcu(tbl->future_tbl, ht);
+ } while (tbl);
+ return he; /* NULL or -ENOENT */
+}
+EXPORT_SYMBOL_GPL(rhashtable_next_key);
+
/**
* rhashtable_walk_enter - Initialise an iterator
* @ht: Table to walk over
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 5892c0f17ddc..af6f3340c034 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -560,8 +560,23 @@ static int bpf_fill_max_jmp_never_taken(struct bpf_test *self)
}
/* ALU result computation used in tests */
-static bool __bpf_alu_result(u64 *res, u64 v1, u64 v2, u8 op)
+enum { F_ALU32 = 1, F_SIGNED = 2 };
+
+static bool __bpf_alu_result(u64 *res, u64 v1, u64 v2, u8 op, u32 flags)
{
+ bool is_signed = flags & F_SIGNED;
+
+ /* Narrow operands for ALU32 */
+ if (flags & F_ALU32) {
+ if (is_signed) {
+ v1 = (u64)(s32)v1;
+ v2 = (u64)(s32)v2;
+ } else {
+ v1 = (u32)v1;
+ v2 = (u32)v2;
+ }
+ }
+
*res = 0;
switch (op) {
case BPF_MOV:
@@ -599,12 +614,28 @@ static bool __bpf_alu_result(u64 *res, u64 v1, u64 v2, u8 op)
case BPF_DIV:
if (v2 == 0)
return false;
- *res = div64_u64(v1, v2);
+ if (!is_signed) {
+ *res = div64_u64(v1, v2);
+ } else {
+ if ((s64)v2 == -1) /* Handled by verifier */
+ return false;
+ *res = (u64)div64_s64(v1, v2);
+ }
break;
case BPF_MOD:
if (v2 == 0)
return false;
- div64_u64_rem(v1, v2, res);
+ if (!is_signed) {
+ div64_u64_rem(v1, v2, res);
+ } else {
+ if ((s64)v2 == -1)
+ return false;
+ /*
+ * Avoid s64 % s64 which generates __moddi3 on
+ * 32-bit architectures. Use div64_s64 instead.
+ */
+ *res = (u64)((s64)v1 - div64_s64(v1, v2) * (s64)v2);
+ }
break;
}
return true;
@@ -612,7 +643,7 @@ static bool __bpf_alu_result(u64 *res, u64 v1, u64 v2, u8 op)
/* Test an ALU shift operation for all valid shift values */
static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op,
- u8 mode, bool alu32)
+ u8 mode, u32 flags)
{
static const s64 regs[] = {
0x0123456789abcdefLL, /* dword > 0, word < 0 */
@@ -620,7 +651,7 @@ static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op,
0xfedcba0198765432LL, /* dword < 0, word < 0 */
0x0123458967abcdefLL, /* dword > 0, word > 0 */
};
- int bits = alu32 ? 32 : 64;
+ int bits = (flags & F_ALU32) ? 32 : 64;
int len = (2 + 7 * bits) * ARRAY_SIZE(regs) + 3;
struct bpf_insn *insn;
int imm, k;
@@ -643,7 +674,7 @@ static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op,
/* Perform operation */
insn[i++] = BPF_ALU64_REG(BPF_MOV, R1, R3);
insn[i++] = BPF_ALU64_IMM(BPF_MOV, R2, imm);
- if (alu32) {
+ if (flags & F_ALU32) {
if (mode == BPF_K)
insn[i++] = BPF_ALU32_IMM(op, R1, imm);
else
@@ -653,14 +684,14 @@ static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op,
reg = (s32)reg;
else
reg = (u32)reg;
- __bpf_alu_result(&val, reg, imm, op);
+ __bpf_alu_result(&val, reg, imm, op, 0);
val = (u32)val;
} else {
if (mode == BPF_K)
insn[i++] = BPF_ALU64_IMM(op, R1, imm);
else
insn[i++] = BPF_ALU64_REG(op, R1, R2);
- __bpf_alu_result(&val, reg, imm, op);
+ __bpf_alu_result(&val, reg, imm, op, 0);
}
/*
@@ -688,62 +719,62 @@ static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op,
static int bpf_fill_alu64_lsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, false);
+ return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, 0);
}
static int bpf_fill_alu64_rsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, false);
+ return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, 0);
}
static int bpf_fill_alu64_arsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, false);
+ return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, 0);
}
static int bpf_fill_alu64_lsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, false);
+ return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, 0);
}
static int bpf_fill_alu64_rsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, false);
+ return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, 0);
}
static int bpf_fill_alu64_arsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, false);
+ return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, 0);
}
static int bpf_fill_alu32_lsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, true);
+ return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, F_ALU32);
}
static int bpf_fill_alu32_rsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, true);
+ return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, F_ALU32);
}
static int bpf_fill_alu32_arsh_imm(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, true);
+ return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, F_ALU32);
}
static int bpf_fill_alu32_lsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, true);
+ return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, F_ALU32);
}
static int bpf_fill_alu32_rsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, true);
+ return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, F_ALU32);
}
static int bpf_fill_alu32_arsh_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, true);
+ return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, F_ALU32);
}
/*
@@ -751,9 +782,9 @@ static int bpf_fill_alu32_arsh_reg(struct bpf_test *self)
* for the case when the source and destination are the same.
*/
static int __bpf_fill_alu_shift_same_reg(struct bpf_test *self, u8 op,
- bool alu32)
+ u32 flags)
{
- int bits = alu32 ? 32 : 64;
+ int bits = (flags & F_ALU32) ? 32 : 64;
int len = 3 + 6 * bits;
struct bpf_insn *insn;
int i = 0;
@@ -770,14 +801,14 @@ static int __bpf_fill_alu_shift_same_reg(struct bpf_test *self, u8 op,
/* Perform operation */
insn[i++] = BPF_ALU64_IMM(BPF_MOV, R1, val);
- if (alu32)
+ if (flags & F_ALU32)
insn[i++] = BPF_ALU32_REG(op, R1, R1);
else
insn[i++] = BPF_ALU64_REG(op, R1, R1);
/* Compute the reference result */
- __bpf_alu_result(&res, val, val, op);
- if (alu32)
+ __bpf_alu_result(&res, val, val, op, 0);
+ if (flags & F_ALU32)
res = (u32)res;
i += __bpf_ld_imm64(&insn[i], R2, res);
@@ -798,32 +829,32 @@ static int __bpf_fill_alu_shift_same_reg(struct bpf_test *self, u8 op,
static int bpf_fill_alu64_lsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, false);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, 0);
}
static int bpf_fill_alu64_rsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, false);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, 0);
}
static int bpf_fill_alu64_arsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, false);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, 0);
}
static int bpf_fill_alu32_lsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, true);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, F_ALU32);
}
static int bpf_fill_alu32_rsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, true);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, F_ALU32);
}
static int bpf_fill_alu32_arsh_same_reg(struct bpf_test *self)
{
- return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, true);
+ return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, F_ALU32);
}
/*
@@ -936,17 +967,20 @@ static int __bpf_fill_pattern(struct bpf_test *self, void *arg,
static int __bpf_emit_alu64_imm(struct bpf_test *self, void *arg,
struct bpf_insn *insns, s64 dst, s64 imm)
{
- int op = *(int *)arg;
+ int *a = arg;
+ int op = a[0];
+ u32 flags = a[1];
+ s16 off = (flags & F_SIGNED) ? 1 : 0;
int i = 0;
u64 res;
if (!insns)
return 7;
- if (__bpf_alu_result(&res, dst, (s32)imm, op)) {
+ if (__bpf_alu_result(&res, dst, (s32)imm, op, flags)) {
i += __bpf_ld_imm64(&insns[i], R1, dst);
i += __bpf_ld_imm64(&insns[i], R3, res);
- insns[i++] = BPF_ALU64_IMM(op, R1, imm);
+ insns[i++] = BPF_ALU64_IMM_OFF(op, R1, imm, off);
insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1);
insns[i++] = BPF_EXIT_INSN();
}
@@ -957,17 +991,20 @@ static int __bpf_emit_alu64_imm(struct bpf_test *self, void *arg,
static int __bpf_emit_alu32_imm(struct bpf_test *self, void *arg,
struct bpf_insn *insns, s64 dst, s64 imm)
{
- int op = *(int *)arg;
+ int *a = arg;
+ int op = a[0];
+ u32 flags = a[1];
+ s16 off = (flags & F_SIGNED) ? 1 : 0;
int i = 0;
u64 res;
if (!insns)
return 7;
- if (__bpf_alu_result(&res, (u32)dst, (u32)imm, op)) {
+ if (__bpf_alu_result(&res, dst, (s32)imm, op, flags | F_ALU32)) {
i += __bpf_ld_imm64(&insns[i], R1, dst);
i += __bpf_ld_imm64(&insns[i], R3, (u32)res);
- insns[i++] = BPF_ALU32_IMM(op, R1, imm);
+ insns[i++] = BPF_ALU32_IMM_OFF(op, R1, imm, off);
insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1);
insns[i++] = BPF_EXIT_INSN();
}
@@ -985,7 +1022,7 @@ static int __bpf_emit_alu64_reg(struct bpf_test *self, void *arg,
if (!insns)
return 9;
- if (__bpf_alu_result(&res, dst, src, op)) {
+ if (__bpf_alu_result(&res, dst, src, op, 0)) {
i += __bpf_ld_imm64(&insns[i], R1, dst);
i += __bpf_ld_imm64(&insns[i], R2, src);
i += __bpf_ld_imm64(&insns[i], R3, res);
@@ -1007,7 +1044,7 @@ static int __bpf_emit_alu32_reg(struct bpf_test *self, void *arg,
if (!insns)
return 9;
- if (__bpf_alu_result(&res, (u32)dst, (u32)src, op)) {
+ if (__bpf_alu_result(&res, (u32)dst, (u32)src, op, 0)) {
i += __bpf_ld_imm64(&insns[i], R1, dst);
i += __bpf_ld_imm64(&insns[i], R2, src);
i += __bpf_ld_imm64(&insns[i], R3, (u32)res);
@@ -1019,16 +1056,20 @@ static int __bpf_emit_alu32_reg(struct bpf_test *self, void *arg,
return i;
}
-static int __bpf_fill_alu64_imm(struct bpf_test *self, int op)
+static int __bpf_fill_alu64_imm(struct bpf_test *self, int op, u32 flags)
{
- return __bpf_fill_pattern(self, &op, 64, 32,
+ int arg[2] = {op, flags};
+
+ return __bpf_fill_pattern(self, &arg, 64, 32,
PATTERN_BLOCK1, PATTERN_BLOCK2,
&__bpf_emit_alu64_imm);
}
-static int __bpf_fill_alu32_imm(struct bpf_test *self, int op)
+static int __bpf_fill_alu32_imm(struct bpf_test *self, int op, u32 flags)
{
- return __bpf_fill_pattern(self, &op, 64, 32,
+ int arg[2] = {op, flags};
+
+ return __bpf_fill_pattern(self, &arg, 64, 32,
PATTERN_BLOCK1, PATTERN_BLOCK2,
&__bpf_emit_alu32_imm);
}
@@ -1050,93 +1091,115 @@ static int __bpf_fill_alu32_reg(struct bpf_test *self, int op)
/* ALU64 immediate operations */
static int bpf_fill_alu64_mov_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_MOV);
+ return __bpf_fill_alu64_imm(self, BPF_MOV, 0);
}
static int bpf_fill_alu64_and_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_AND);
+ return __bpf_fill_alu64_imm(self, BPF_AND, 0);
}
static int bpf_fill_alu64_or_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_OR);
+ return __bpf_fill_alu64_imm(self, BPF_OR, 0);
}
static int bpf_fill_alu64_xor_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_XOR);
+ return __bpf_fill_alu64_imm(self, BPF_XOR, 0);
}
static int bpf_fill_alu64_add_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_ADD);
+ return __bpf_fill_alu64_imm(self, BPF_ADD, 0);
}
static int bpf_fill_alu64_sub_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_SUB);
+ return __bpf_fill_alu64_imm(self, BPF_SUB, 0);
}
static int bpf_fill_alu64_mul_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_MUL);
+ return __bpf_fill_alu64_imm(self, BPF_MUL, 0);
}
static int bpf_fill_alu64_div_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_DIV);
+ return __bpf_fill_alu64_imm(self, BPF_DIV, 0);
}
static int bpf_fill_alu64_mod_imm(struct bpf_test *self)
{
- return __bpf_fill_alu64_imm(self, BPF_MOD);
+ return __bpf_fill_alu64_imm(self, BPF_MOD, 0);
+}
+
+/* Signed ALU64 immediate operations */
+static int bpf_fill_alu64_sdiv_imm(struct bpf_test *self)
+{
+ return __bpf_fill_alu64_imm(self, BPF_DIV, F_SIGNED);
+}
+
+static int bpf_fill_alu64_smod_imm(struct bpf_test *self)
+{
+ return __bpf_fill_alu64_imm(self, BPF_MOD, F_SIGNED);
+}
+
+/* Signed ALU32 immediate operations */
+static int bpf_fill_alu32_sdiv_imm(struct bpf_test *self)
+{
+ return __bpf_fill_alu32_imm(self, BPF_DIV, F_SIGNED);
+}
+
+static int bpf_fill_alu32_smod_imm(struct bpf_test *self)
+{
+ return __bpf_fill_alu32_imm(self, BPF_MOD, F_SIGNED);
}
/* ALU32 immediate operations */
static int bpf_fill_alu32_mov_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_MOV);
+ return __bpf_fill_alu32_imm(self, BPF_MOV, 0);
}
static int bpf_fill_alu32_and_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_AND);
+ return __bpf_fill_alu32_imm(self, BPF_AND, 0);
}
static int bpf_fill_alu32_or_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_OR);
+ return __bpf_fill_alu32_imm(self, BPF_OR, 0);
}
static int bpf_fill_alu32_xor_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_XOR);
+ return __bpf_fill_alu32_imm(self, BPF_XOR, 0);
}
static int bpf_fill_alu32_add_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_ADD);
+ return __bpf_fill_alu32_imm(self, BPF_ADD, 0);
}
static int bpf_fill_alu32_sub_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_SUB);
+ return __bpf_fill_alu32_imm(self, BPF_SUB, 0);
}
static int bpf_fill_alu32_mul_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_MUL);
+ return __bpf_fill_alu32_imm(self, BPF_MUL, 0);
}
static int bpf_fill_alu32_div_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_DIV);
+ return __bpf_fill_alu32_imm(self, BPF_DIV, 0);
}
static int bpf_fill_alu32_mod_imm(struct bpf_test *self)
{
- return __bpf_fill_alu32_imm(self, BPF_MOD);
+ return __bpf_fill_alu32_imm(self, BPF_MOD, 0);
}
/* ALU64 register operations */
@@ -1235,7 +1298,8 @@ static int bpf_fill_alu32_mod_reg(struct bpf_test *self)
* Test JITs that implement complex ALU operations as function
* calls, and must re-arrange operands for argument passing.
*/
-static int __bpf_fill_alu_imm_regs(struct bpf_test *self, u8 op, bool alu32)
+static int __bpf_fill_alu_imm_regs(struct bpf_test *self, u8 op,
+ u32 flags)
{
int len = 2 + 10 * 10;
struct bpf_insn *insns;
@@ -1249,28 +1313,37 @@ static int __bpf_fill_alu_imm_regs(struct bpf_test *self, u8 op, bool alu32)
return -ENOMEM;
/* Operand and result values according to operation */
- if (alu32)
- dst = 0x76543210U;
- else
- dst = 0x7edcba9876543210ULL;
+ if (flags & F_SIGNED) {
+ if (flags & F_ALU32)
+ dst = -76543210;
+ else
+ dst = -7654321076543210LL;
+ } else {
+ if (flags & F_ALU32)
+ dst = 0x76543210U;
+ else
+ dst = 0x7edcba9876543210ULL;
+ }
imm = 0x01234567U;
if (op == BPF_LSH || op == BPF_RSH || op == BPF_ARSH)
imm &= 31;
- __bpf_alu_result(&res, dst, imm, op);
+ __bpf_alu_result(&res, dst, imm, op, flags);
- if (alu32)
+ if (flags & F_ALU32)
res = (u32)res;
/* Check all operand registers */
for (rd = R0; rd <= R9; rd++) {
i += __bpf_ld_imm64(&insns[i], rd, dst);
- if (alu32)
- insns[i++] = BPF_ALU32_IMM(op, rd, imm);
+ s16 off = (flags & F_SIGNED) ? 1 : 0;
+
+ if (flags & F_ALU32)
+ insns[i++] = BPF_ALU32_IMM_OFF(op, rd, imm, off);
else
- insns[i++] = BPF_ALU64_IMM(op, rd, imm);
+ insns[i++] = BPF_ALU64_IMM_OFF(op, rd, imm, off);
insns[i++] = BPF_JMP32_IMM(BPF_JEQ, rd, res, 2);
insns[i++] = BPF_MOV64_IMM(R0, __LINE__);
@@ -1295,123 +1368,145 @@ static int __bpf_fill_alu_imm_regs(struct bpf_test *self, u8 op, bool alu32)
/* ALU64 K registers */
static int bpf_fill_alu64_mov_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MOV, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_MOV, 0);
}
static int bpf_fill_alu64_and_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_AND, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_AND, 0);
}
static int bpf_fill_alu64_or_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_OR, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_OR, 0);
}
static int bpf_fill_alu64_xor_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_XOR, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_XOR, 0);
}
static int bpf_fill_alu64_lsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_LSH, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_LSH, 0);
}
static int bpf_fill_alu64_rsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_RSH, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_RSH, 0);
}
static int bpf_fill_alu64_arsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_ARSH, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_ARSH, 0);
}
static int bpf_fill_alu64_add_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_ADD, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_ADD, 0);
}
static int bpf_fill_alu64_sub_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_SUB, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_SUB, 0);
}
static int bpf_fill_alu64_mul_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MUL, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_MUL, 0);
}
static int bpf_fill_alu64_div_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_DIV, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_DIV, 0);
}
static int bpf_fill_alu64_mod_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MOD, false);
+ return __bpf_fill_alu_imm_regs(self, BPF_MOD, 0);
+}
+
+/* Signed ALU64 K registers */
+static int bpf_fill_alu64_sdiv_imm_regs(struct bpf_test *self)
+{
+ return __bpf_fill_alu_imm_regs(self, BPF_DIV, F_SIGNED);
+}
+
+static int bpf_fill_alu64_smod_imm_regs(struct bpf_test *self)
+{
+ return __bpf_fill_alu_imm_regs(self, BPF_MOD, F_SIGNED);
}
/* ALU32 K registers */
static int bpf_fill_alu32_mov_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MOV, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_MOV, F_ALU32);
}
static int bpf_fill_alu32_and_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_AND, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_AND, F_ALU32);
}
static int bpf_fill_alu32_or_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_OR, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_OR, F_ALU32);
}
static int bpf_fill_alu32_xor_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_XOR, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_XOR, F_ALU32);
}
static int bpf_fill_alu32_lsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_LSH, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_LSH, F_ALU32);
}
static int bpf_fill_alu32_rsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_RSH, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_RSH, F_ALU32);
}
static int bpf_fill_alu32_arsh_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_ARSH, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_ARSH, F_ALU32);
}
static int bpf_fill_alu32_add_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_ADD, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_ADD, F_ALU32);
}
static int bpf_fill_alu32_sub_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_SUB, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_SUB, F_ALU32);
}
static int bpf_fill_alu32_mul_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MUL, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_MUL, F_ALU32);
}
static int bpf_fill_alu32_div_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_DIV, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_DIV, F_ALU32);
}
static int bpf_fill_alu32_mod_imm_regs(struct bpf_test *self)
{
- return __bpf_fill_alu_imm_regs(self, BPF_MOD, true);
+ return __bpf_fill_alu_imm_regs(self, BPF_MOD, F_ALU32);
+}
+
+/* Signed ALU32 K registers */
+static int bpf_fill_alu32_sdiv_imm_regs(struct bpf_test *self)
+{
+ return __bpf_fill_alu_imm_regs(self, BPF_DIV, F_ALU32 | F_SIGNED);
+}
+
+static int bpf_fill_alu32_smod_imm_regs(struct bpf_test *self)
+{
+ return __bpf_fill_alu_imm_regs(self, BPF_MOD, F_ALU32 | F_SIGNED);
}
/*
@@ -1442,8 +1537,8 @@ static int __bpf_fill_alu_reg_pairs(struct bpf_test *self, u8 op, bool alu32)
if (op == BPF_LSH || op == BPF_RSH || op == BPF_ARSH)
src &= 31;
- __bpf_alu_result(&res, dst, src, op);
- __bpf_alu_result(&same, src, src, op);
+ __bpf_alu_result(&res, dst, src, op, 0);
+ __bpf_alu_result(&same, src, src, op, 0);
if (alu32) {
res = (u32)res;
@@ -1626,7 +1721,7 @@ static int __bpf_emit_atomic64(struct bpf_test *self, void *arg,
res = src;
break;
default:
- __bpf_alu_result(&res, dst, src, BPF_OP(op));
+ __bpf_alu_result(&res, dst, src, BPF_OP(op), 0);
}
keep = 0x0123456789abcdefULL;
@@ -1673,7 +1768,7 @@ static int __bpf_emit_atomic32(struct bpf_test *self, void *arg,
res = src;
break;
default:
- __bpf_alu_result(&res, (u32)dst, (u32)src, BPF_OP(op));
+ __bpf_alu_result(&res, (u32)dst, (u32)src, BPF_OP(op), 0);
}
keep = 0x0123456789abcdefULL;
@@ -1939,7 +2034,7 @@ static int __bpf_fill_atomic_reg_pairs(struct bpf_test *self, u8 width, u8 op)
res = mem;
break;
default:
- __bpf_alu_result(&res, mem, upd, BPF_OP(op));
+ __bpf_alu_result(&res, mem, upd, BPF_OP(op), 0);
}
/* Test all operand registers */
@@ -12354,6 +12449,22 @@ static struct bpf_test tests[] = {
{ { 0, 1 } },
.fill_helper = bpf_fill_alu64_mod_imm_regs,
},
+ {
+ "ALU64_SDIV_K: registers",
+ { },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu64_sdiv_imm_regs,
+ },
+ {
+ "ALU64_SMOD_K: registers",
+ { },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu64_smod_imm_regs,
+ },
/* ALU32 K registers */
{
"ALU32_MOV_K: registers",
@@ -12451,6 +12562,22 @@ static struct bpf_test tests[] = {
{ { 0, 1 } },
.fill_helper = bpf_fill_alu32_mod_imm_regs,
},
+ {
+ "ALU32_SDIV_K: registers",
+ { },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu32_sdiv_imm_regs,
+ },
+ {
+ "ALU32_SMOD_K: registers",
+ { },
+ INTERNAL,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu32_smod_imm_regs,
+ },
/* ALU64 X register combinations */
{
"ALU64_MOV_X: register combinations",
@@ -12881,6 +13008,24 @@ static struct bpf_test tests[] = {
.fill_helper = bpf_fill_alu64_mod_imm,
.nr_testruns = NR_PATTERN_RUNS,
},
+ {
+ "ALU64_SDIV_K: all immediate value magnitudes",
+ { },
+ INTERNAL | FLAG_NO_DATA,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu64_sdiv_imm,
+ .nr_testruns = NR_PATTERN_RUNS,
+ },
+ {
+ "ALU64_SMOD_K: all immediate value magnitudes",
+ { },
+ INTERNAL | FLAG_NO_DATA,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu64_smod_imm,
+ .nr_testruns = NR_PATTERN_RUNS,
+ },
/* ALU32 immediate magnitudes */
{
"ALU32_MOV_K: all immediate value magnitudes",
@@ -12963,6 +13108,24 @@ static struct bpf_test tests[] = {
.fill_helper = bpf_fill_alu32_mod_imm,
.nr_testruns = NR_PATTERN_RUNS,
},
+ {
+ "ALU32_SDIV_K: all immediate value magnitudes",
+ { },
+ INTERNAL | FLAG_NO_DATA,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu32_sdiv_imm,
+ .nr_testruns = NR_PATTERN_RUNS,
+ },
+ {
+ "ALU32_SMOD_K: all immediate value magnitudes",
+ { },
+ INTERNAL | FLAG_NO_DATA,
+ { },
+ { { 0, 1 } },
+ .fill_helper = bpf_fill_alu32_smod_imm,
+ .nr_testruns = NR_PATTERN_RUNS,
+ },
/* ALU64 register magnitudes */
{
"ALU64_MOV_X: all register value magnitudes",
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c
index 0b33559a910b..b767a38a74f9 100644
--- a/lib/test_rhashtable.c
+++ b/lib/test_rhashtable.c
@@ -679,6 +679,78 @@ out:
return err;
}
+static int __init test_rhashtable_next_key(void)
+{
+ struct rhashtable_params params = test_rht_params;
+ struct test_obj_val key_missing = { .id = 99999, .tid = 0 };
+ struct test_obj_val *prev_key = NULL;
+ struct rhashtable ht;
+ struct test_obj *objs, *cur;
+ int i, count = 0, err;
+ int visited_keys[8] = { 0 };
+ const int n = ARRAY_SIZE(visited_keys);
+
+ params.nelem_hint = n;
+
+ err = rhashtable_init(&ht, &params);
+ if (err)
+ return err;
+
+ objs = kcalloc(n, sizeof(*objs), GFP_KERNEL);
+ if (!objs) {
+ rhashtable_destroy(&ht);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < n; i++) {
+ objs[i].value.id = i;
+ err = rhashtable_insert_fast(&ht, &objs[i].node, params);
+ if (err)
+ goto out;
+ }
+
+ rcu_read_lock();
+
+ /* NULL prev_key: walk from the beginning, expect all n elements. */
+ while ((cur = rhashtable_next_key(&ht, prev_key))) {
+ if (IS_ERR(cur)) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ count++;
+ prev_key = &cur->value;
+ visited_keys[cur->value.id] = 1;
+ if (count > n)
+ break;
+ }
+
+ if (count != n) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (!visited_keys[i]) {
+ err = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ /* Non-existing prev_key: must return ERR_PTR(-ENOENT). */
+