kernel: backport bpf_loop helper for dae
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
parent
79e9020dbe
commit
7c31d7dd83
@ -40,7 +40,7 @@ Link: https://lore.kernel.org/bpf/20220223012814.1898677-1-connoro@google.com
|
||||
err = btf_alloc_id(btf);
|
||||
--- a/lib/Kconfig.debug
|
||||
+++ b/lib/Kconfig.debug
|
||||
@@ -336,6 +336,16 @@ config DEBUG_INFO_BTF_MODULES
|
||||
@@ -337,6 +337,16 @@ config DEBUG_INFO_BTF_MODULES
|
||||
help
|
||||
Generate compact split BTF type information for kernel modules.
|
||||
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
From 3d717fad5081b8e3bda76d86907fad95398cbde8 Mon Sep 17 00:00:00 2001
|
||||
From: Kees Cook <keescook@chromium.org>
|
||||
Date: Tue, 28 Sep 2021 16:09:45 -0700
|
||||
Subject: [PATCH] bpf: Replace "want address" users of BPF_CAST_CALL with
|
||||
BPF_CALL_IMM
|
||||
|
||||
In order to keep ahead of cases in the kernel where Control Flow
|
||||
Integrity (CFI) may trip over function call casts, enabling
|
||||
-Wcast-function-type is helpful. To that end, BPF_CAST_CALL causes
|
||||
various warnings and is one of the last places in the kernel triggering
|
||||
this warning.
|
||||
|
||||
Most places using BPF_CAST_CALL actually just want a void * to perform
|
||||
math on. It's not actually performing a call, so just use a different
|
||||
helper to get the void *, by way of the new BPF_CALL_IMM() helper, which
|
||||
can clean up a common copy/paste idiom as well.
|
||||
|
||||
This change results in no object code difference.
|
||||
|
||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
|
||||
Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org>
|
||||
Acked-by: Andrii Nakryiko <andrii@kernel.org>
|
||||
Link: https://github.com/KSPP/linux/issues/20
|
||||
Link: https://lore.kernel.org/lkml/CAEf4Bzb46=-J5Fxc3mMZ8JQPtK1uoE0q6+g6WPz53Cvx=CBEhw@mail.gmail.com
|
||||
Link: https://lore.kernel.org/bpf/20210928230946.4062144-2-keescook@chromium.org
|
||||
---
|
||||
include/linux/filter.h | 6 +++++-
|
||||
kernel/bpf/hashtab.c | 6 +++---
|
||||
kernel/bpf/verifier.c | 26 +++++++++-----------------
|
||||
lib/test_bpf.c | 2 +-
|
||||
4 files changed, 18 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/include/linux/filter.h
|
||||
+++ b/include/linux/filter.h
|
||||
@@ -365,13 +365,17 @@ static inline bool insn_is_zext(const st
|
||||
#define BPF_CAST_CALL(x) \
|
||||
((u64 (*)(u64, u64, u64, u64, u64))(x))
|
||||
|
||||
+/* Convert function address to BPF immediate */
|
||||
+
|
||||
+#define BPF_CALL_IMM(x) ((void *)(x) - (void *)__bpf_call_base)
|
||||
+
|
||||
#define BPF_EMIT_CALL(FUNC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_CALL, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
- .imm = ((FUNC) - __bpf_call_base) })
|
||||
+ .imm = BPF_CALL_IMM(FUNC) })
|
||||
|
||||
/* Raw code statement block */
|
||||
|
||||
--- a/kernel/bpf/hashtab.c
|
||||
+++ b/kernel/bpf/hashtab.c
|
||||
@@ -681,7 +681,7 @@ static int htab_map_gen_lookup(struct bp
|
||||
|
||||
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
|
||||
(void *(*)(struct bpf_map *map, void *key))NULL));
|
||||
- *insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
|
||||
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 1);
|
||||
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
|
||||
offsetof(struct htab_elem, key) +
|
||||
@@ -722,7 +722,7 @@ static int htab_lru_map_gen_lookup(struc
|
||||
|
||||
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
|
||||
(void *(*)(struct bpf_map *map, void *key))NULL));
|
||||
- *insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
|
||||
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 4);
|
||||
*insn++ = BPF_LDX_MEM(BPF_B, ref_reg, ret,
|
||||
offsetof(struct htab_elem, lru_node) +
|
||||
@@ -2417,7 +2417,7 @@ static int htab_of_map_gen_lookup(struct
|
||||
|
||||
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
|
||||
(void *(*)(struct bpf_map *map, void *key))NULL));
|
||||
- *insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
|
||||
+ *insn++ = BPF_EMIT_CALL(__htab_map_lookup_elem);
|
||||
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 2);
|
||||
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
|
||||
offsetof(struct htab_elem, key) +
|
||||
--- a/kernel/bpf/verifier.c
|
||||
+++ b/kernel/bpf/verifier.c
|
||||
@@ -1736,7 +1736,7 @@ static int add_kfunc_call(struct bpf_ver
|
||||
|
||||
desc = &tab->descs[tab->nr_descs++];
|
||||
desc->func_id = func_id;
|
||||
- desc->imm = BPF_CAST_CALL(addr) - __bpf_call_base;
|
||||
+ desc->imm = BPF_CALL_IMM(addr);
|
||||
err = btf_distill_func_proto(&env->log, btf_vmlinux,
|
||||
func_proto, func_name,
|
||||
&desc->func_model);
|
||||
@@ -12778,8 +12778,7 @@ static int jit_subprogs(struct bpf_verif
|
||||
if (!bpf_pseudo_call(insn))
|
||||
continue;
|
||||
subprog = insn->off;
|
||||
- insn->imm = BPF_CAST_CALL(func[subprog]->bpf_func) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(func[subprog]->bpf_func);
|
||||
}
|
||||
|
||||
/* we use the aux data to keep a list of the start addresses
|
||||
@@ -13267,32 +13266,25 @@ static int do_misc_fixups(struct bpf_ver
|
||||
patch_map_ops_generic:
|
||||
switch (insn->imm) {
|
||||
case BPF_FUNC_map_lookup_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_lookup_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_lookup_elem);
|
||||
continue;
|
||||
case BPF_FUNC_map_update_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_update_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_update_elem);
|
||||
continue;
|
||||
case BPF_FUNC_map_delete_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_delete_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_delete_elem);
|
||||
continue;
|
||||
case BPF_FUNC_map_push_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_push_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_push_elem);
|
||||
continue;
|
||||
case BPF_FUNC_map_pop_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_pop_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_pop_elem);
|
||||
continue;
|
||||
case BPF_FUNC_map_peek_elem:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_peek_elem) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_peek_elem);
|
||||
continue;
|
||||
case BPF_FUNC_redirect_map:
|
||||
- insn->imm = BPF_CAST_CALL(ops->map_redirect) -
|
||||
- __bpf_call_base;
|
||||
+ insn->imm = BPF_CALL_IMM(ops->map_redirect);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
From 102acbacfd9a96d101abd96d1a7a5bf92b7c3e8e Mon Sep 17 00:00:00 2001
|
||||
From: Kees Cook <keescook@chromium.org>
|
||||
Date: Tue, 28 Sep 2021 16:09:46 -0700
|
||||
Subject: [PATCH] bpf: Replace callers of BPF_CAST_CALL with proper function
|
||||
typedef
|
||||
|
||||
In order to keep ahead of cases in the kernel where Control Flow
|
||||
Integrity (CFI) may trip over function call casts, enabling
|
||||
-Wcast-function-type is helpful. To that end, BPF_CAST_CALL causes
|
||||
various warnings and is one of the last places in the kernel
|
||||
triggering this warning.
|
||||
|
||||
For actual function calls, replace BPF_CAST_CALL() with a typedef, which
|
||||
captures the same details about the given function pointers.
|
||||
|
||||
This change results in no object code difference.
|
||||
|
||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
|
||||
Acked-by: Andrii Nakryiko <andrii@kernel.org>
|
||||
Acked-by: Gustavo A. R. Silva <gustavoars@kernel.org>
|
||||
Link: https://github.com/KSPP/linux/issues/20
|
||||
Link: https://lore.kernel.org/lkml/CAEf4Bzb46=-J5Fxc3mMZ8JQPtK1uoE0q6+g6WPz53Cvx=CBEhw@mail.gmail.com
|
||||
Link: https://lore.kernel.org/bpf/20210928230946.4062144-3-keescook@chromium.org
|
||||
---
|
||||
include/linux/bpf.h | 4 +++-
|
||||
include/linux/filter.h | 5 -----
|
||||
kernel/bpf/arraymap.c | 7 +++----
|
||||
kernel/bpf/hashtab.c | 7 +++----
|
||||
kernel/bpf/helpers.c | 5 ++---
|
||||
5 files changed, 11 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/include/linux/bpf.h
|
||||
+++ b/include/linux/bpf.h
|
||||
@@ -48,6 +48,7 @@ extern struct idr btf_idr;
|
||||
extern spinlock_t btf_idr_lock;
|
||||
extern struct kobject *btf_kobj;
|
||||
|
||||
+typedef u64 (*bpf_callback_t)(u64, u64, u64, u64, u64);
|
||||
typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
|
||||
struct bpf_iter_aux_info *aux);
|
||||
typedef void (*bpf_iter_fini_seq_priv_t)(void *private_data);
|
||||
@@ -146,7 +147,8 @@ struct bpf_map_ops {
|
||||
int (*map_set_for_each_callback_args)(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *caller,
|
||||
struct bpf_func_state *callee);
|
||||
- int (*map_for_each_callback)(struct bpf_map *map, void *callback_fn,
|
||||
+ int (*map_for_each_callback)(struct bpf_map *map,
|
||||
+ bpf_callback_t callback_fn,
|
||||
void *callback_ctx, u64 flags);
|
||||
|
||||
/* BTF name and id of struct allocated by map_alloc */
|
||||
--- a/include/linux/filter.h
|
||||
+++ b/include/linux/filter.h
|
||||
@@ -360,11 +360,6 @@ static inline bool insn_is_zext(const st
|
||||
.off = 0, \
|
||||
.imm = TGT })
|
||||
|
||||
-/* Function call */
|
||||
-
|
||||
-#define BPF_CAST_CALL(x) \
|
||||
- ((u64 (*)(u64, u64, u64, u64, u64))(x))
|
||||
-
|
||||
/* Convert function address to BPF immediate */
|
||||
|
||||
#define BPF_CALL_IMM(x) ((void *)(x) - (void *)__bpf_call_base)
|
||||
--- a/kernel/bpf/arraymap.c
|
||||
+++ b/kernel/bpf/arraymap.c
|
||||
@@ -651,7 +651,7 @@ static const struct bpf_iter_seq_info it
|
||||
.seq_priv_size = sizeof(struct bpf_iter_seq_array_map_info),
|
||||
};
|
||||
|
||||
-static int bpf_for_each_array_elem(struct bpf_map *map, void *callback_fn,
|
||||
+static int bpf_for_each_array_elem(struct bpf_map *map, bpf_callback_t callback_fn,
|
||||
void *callback_ctx, u64 flags)
|
||||
{
|
||||
u32 i, key, num_elems = 0;
|
||||
@@ -674,9 +674,8 @@ static int bpf_for_each_array_elem(struc
|
||||
val = array->value + array->elem_size * i;
|
||||
num_elems++;
|
||||
key = i;
|
||||
- ret = BPF_CAST_CALL(callback_fn)((u64)(long)map,
|
||||
- (u64)(long)&key, (u64)(long)val,
|
||||
- (u64)(long)callback_ctx, 0);
|
||||
+ ret = callback_fn((u64)(long)map, (u64)(long)&key,
|
||||
+ (u64)(long)val, (u64)(long)callback_ctx, 0);
|
||||
/* return value: 0 - continue, 1 - stop and return */
|
||||
if (ret)
|
||||
break;
|
||||
--- a/kernel/bpf/hashtab.c
|
||||
+++ b/kernel/bpf/hashtab.c
|
||||
@@ -2069,7 +2069,7 @@ static const struct bpf_iter_seq_info it
|
||||
.seq_priv_size = sizeof(struct bpf_iter_seq_hash_map_info),
|
||||
};
|
||||
|
||||
-static int bpf_for_each_hash_elem(struct bpf_map *map, void *callback_fn,
|
||||
+static int bpf_for_each_hash_elem(struct bpf_map *map, bpf_callback_t callback_fn,
|
||||
void *callback_ctx, u64 flags)
|
||||
{
|
||||
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
|
||||
@@ -2109,9 +2109,8 @@ static int bpf_for_each_hash_elem(struct
|
||||
val = elem->key + roundup_key_size;
|
||||
}
|
||||
num_elems++;
|
||||
- ret = BPF_CAST_CALL(callback_fn)((u64)(long)map,
|
||||
- (u64)(long)key, (u64)(long)val,
|
||||
- (u64)(long)callback_ctx, 0);
|
||||
+ ret = callback_fn((u64)(long)map, (u64)(long)key,
|
||||
+ (u64)(long)val, (u64)(long)callback_ctx, 0);
|
||||
/* return value: 0 - continue, 1 - stop and return */
|
||||
if (ret) {
|
||||
rcu_read_unlock();
|
||||
--- a/kernel/bpf/helpers.c
|
||||
+++ b/kernel/bpf/helpers.c
|
||||
@@ -1068,7 +1068,7 @@ static enum hrtimer_restart bpf_timer_cb
|
||||
struct bpf_hrtimer *t = container_of(hrtimer, struct bpf_hrtimer, timer);
|
||||
struct bpf_map *map = t->map;
|
||||
void *value = t->value;
|
||||
- void *callback_fn;
|
||||
+ bpf_callback_t callback_fn;
|
||||
void *key;
|
||||
u32 idx;
|
||||
|
||||
@@ -1093,8 +1093,7 @@ static enum hrtimer_restart bpf_timer_cb
|
||||
key = value - round_up(map->key_size, 8);
|
||||
}
|
||||
|
||||
- BPF_CAST_CALL(callback_fn)((u64)(long)map, (u64)(long)key,
|
||||
- (u64)(long)value, 0, 0);
|
||||
+ callback_fn((u64)(long)map, (u64)(long)key, (u64)(long)value, 0, 0);
|
||||
/* The verifier checked that return value is zero. */
|
||||
|
||||
this_cpu_write(hrtimer_running, NULL);
|
||||
@ -0,0 +1,297 @@
|
||||
From e6f2dd0f80674e9d5960337b3e9c2a242441b326 Mon Sep 17 00:00:00 2001
|
||||
From: Joanne Koong <joannekoong@fb.com>
|
||||
Date: Mon, 29 Nov 2021 19:06:19 -0800
|
||||
Subject: [PATCH] bpf: Add bpf_loop helper
|
||||
|
||||
This patch adds the kernel-side and API changes for a new helper
|
||||
function, bpf_loop:
|
||||
|
||||
long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx,
|
||||
u64 flags);
|
||||
|
||||
where long (*callback_fn)(u32 index, void *ctx);
|
||||
|
||||
bpf_loop invokes the "callback_fn" **nr_loops** times or until the
|
||||
callback_fn returns 1. The callback_fn can only return 0 or 1, and
|
||||
this is enforced by the verifier. The callback_fn index is zero-indexed.
|
||||
|
||||
A few things to please note:
|
||||
~ The "u64 flags" parameter is currently unused but is included in
|
||||
case a future use case for it arises.
|
||||
~ In the kernel-side implementation of bpf_loop (kernel/bpf/bpf_iter.c),
|
||||
bpf_callback_t is used as the callback function cast.
|
||||
~ A program can have nested bpf_loop calls but the program must
|
||||
still adhere to the verifier constraint of its stack depth (the stack depth
|
||||
cannot exceed MAX_BPF_STACK))
|
||||
~ Recursive callback_fns do not pass the verifier, due to the call stack
|
||||
for these being too deep.
|
||||
~ The next patch will include the tests and benchmark
|
||||
|
||||
Signed-off-by: Joanne Koong <joannekoong@fb.com>
|
||||
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
|
||||
Acked-by: Andrii Nakryiko <andrii@kernel.org>
|
||||
Link: https://lore.kernel.org/bpf/20211130030622.4131246-2-joannekoong@fb.com
|
||||
---
|
||||
include/linux/bpf.h | 1 +
|
||||
include/uapi/linux/bpf.h | 25 ++++++++++
|
||||
kernel/bpf/bpf_iter.c | 35 ++++++++++++++
|
||||
kernel/bpf/helpers.c | 2 +
|
||||
kernel/bpf/verifier.c | 88 +++++++++++++++++++++-------------
|
||||
tools/include/uapi/linux/bpf.h | 25 ++++++++++
|
||||
6 files changed, 142 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/include/linux/bpf.h
|
||||
+++ b/include/linux/bpf.h
|
||||
@@ -2184,6 +2184,7 @@ extern const struct bpf_func_proto bpf_f
|
||||
extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto;
|
||||
extern const struct bpf_func_proto bpf_sk_setsockopt_proto;
|
||||
extern const struct bpf_func_proto bpf_sk_getsockopt_proto;
|
||||
+extern const struct bpf_func_proto bpf_loop_proto;
|
||||
|
||||
const struct bpf_func_proto *tracing_prog_func_proto(
|
||||
enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||
--- a/include/uapi/linux/bpf.h
|
||||
+++ b/include/uapi/linux/bpf.h
|
||||
@@ -4896,6 +4896,30 @@ union bpf_attr {
|
||||
* Get the struct pt_regs associated with **task**.
|
||||
* Return
|
||||
* A pointer to struct pt_regs.
|
||||
+ *
|
||||
+ * long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
|
||||
+ * Description
|
||||
+ * For **nr_loops**, call **callback_fn** function
|
||||
+ * with **callback_ctx** as the context parameter.
|
||||
+ * The **callback_fn** should be a static function and
|
||||
+ * the **callback_ctx** should be a pointer to the stack.
|
||||
+ * The **flags** is used to control certain aspects of the helper.
|
||||
+ * Currently, the **flags** must be 0. Currently, nr_loops is
|
||||
+ * limited to 1 << 23 (~8 million) loops.
|
||||
+ *
|
||||
+ * long (\*callback_fn)(u32 index, void \*ctx);
|
||||
+ *
|
||||
+ * where **index** is the current index in the loop. The index
|
||||
+ * is zero-indexed.
|
||||
+ *
|
||||
+ * If **callback_fn** returns 0, the helper will continue to the next
|
||||
+ * loop. If return value is 1, the helper will skip the rest of
|
||||
+ * the loops and return. Other return values are not used now,
|
||||
+ * and will be rejected by the verifier.
|
||||
+ *
|
||||
+ * Return
|
||||
+ * The number of loops performed, **-EINVAL** for invalid **flags**,
|
||||
+ * **-E2BIG** if **nr_loops** exceeds the maximum number of loops.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@@ -5074,6 +5098,7 @@ union bpf_attr {
|
||||
FN(get_func_ip), \
|
||||
FN(get_attach_cookie), \
|
||||
FN(task_pt_regs), \
|
||||
+ FN(loop), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
--- a/kernel/bpf/bpf_iter.c
|
||||
+++ b/kernel/bpf/bpf_iter.c
|
||||
@@ -714,3 +714,38 @@ const struct bpf_func_proto bpf_for_each
|
||||
.arg3_type = ARG_PTR_TO_STACK_OR_NULL,
|
||||
.arg4_type = ARG_ANYTHING,
|
||||
};
|
||||
+
|
||||
+/* maximum number of loops */
|
||||
+#define MAX_LOOPS BIT(23)
|
||||
+
|
||||
+BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,
|
||||
+ u64, flags)
|
||||
+{
|
||||
+ bpf_callback_t callback = (bpf_callback_t)callback_fn;
|
||||
+ u64 ret;
|
||||
+ u32 i;
|
||||
+
|
||||
+ if (flags)
|
||||
+ return -EINVAL;
|
||||
+ if (nr_loops > MAX_LOOPS)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
+ for (i = 0; i < nr_loops; i++) {
|
||||
+ ret = callback((u64)i, (u64)(long)callback_ctx, 0, 0, 0);
|
||||
+ /* return value: 0 - continue, 1 - stop and return */
|
||||
+ if (ret)
|
||||
+ return i + 1;
|
||||
+ }
|
||||
+
|
||||
+ return i;
|
||||
+}
|
||||
+
|
||||
+const struct bpf_func_proto bpf_loop_proto = {
|
||||
+ .func = bpf_loop,
|
||||
+ .gpl_only = false,
|
||||
+ .ret_type = RET_INTEGER,
|
||||
+ .arg1_type = ARG_ANYTHING,
|
||||
+ .arg2_type = ARG_PTR_TO_FUNC,
|
||||
+ .arg3_type = ARG_PTR_TO_STACK_OR_NULL,
|
||||
+ .arg4_type = ARG_ANYTHING,
|
||||
+};
|
||||
--- a/kernel/bpf/helpers.c
|
||||
+++ b/kernel/bpf/helpers.c
|
||||
@@ -1397,6 +1397,8 @@ bpf_base_func_proto(enum bpf_func_id fun
|
||||
return &bpf_ringbuf_query_proto;
|
||||
case BPF_FUNC_for_each_map_elem:
|
||||
return &bpf_for_each_map_elem_proto;
|
||||
+ case BPF_FUNC_loop:
|
||||
+ return &bpf_loop_proto;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
--- a/kernel/bpf/verifier.c
|
||||
+++ b/kernel/bpf/verifier.c
|
||||
@@ -6217,6 +6217,27 @@ static int set_map_elem_callback_state(s
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int set_loop_callback_state(struct bpf_verifier_env *env,
|
||||
+ struct bpf_func_state *caller,
|
||||
+ struct bpf_func_state *callee,
|
||||
+ int insn_idx)
|
||||
+{
|
||||
+ /* bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx,
|
||||
+ * u64 flags);
|
||||
+ * callback_fn(u32 index, void *callback_ctx);
|
||||
+ */
|
||||
+ callee->regs[BPF_REG_1].type = SCALAR_VALUE;
|
||||
+ callee->regs[BPF_REG_2] = caller->regs[BPF_REG_3];
|
||||
+
|
||||
+ /* unused */
|
||||
+ __mark_reg_not_init(env, &callee->regs[BPF_REG_3]);
|
||||
+ __mark_reg_not_init(env, &callee->regs[BPF_REG_4]);
|
||||
+ __mark_reg_not_init(env, &callee->regs[BPF_REG_5]);
|
||||
+
|
||||
+ callee->in_callback_fn = true;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int set_timer_callback_state(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *caller,
|
||||
struct bpf_func_state *callee,
|
||||
@@ -6582,13 +6603,7 @@ static int check_helper_call(struct bpf_
|
||||
return err;
|
||||
}
|
||||
|
||||
- if (func_id == BPF_FUNC_tail_call) {
|
||||
- err = check_reference_leak(env);
|
||||
- if (err) {
|
||||
- verbose(env, "tail_call would lead to reference leak\n");
|
||||
- return err;
|
||||
- }
|
||||
- } else if (is_release_function(func_id)) {
|
||||
+ if (is_release_function(func_id)) {
|
||||
err = release_reference(env, meta.ref_obj_id);
|
||||
if (err) {
|
||||
verbose(env, "func %s#%d reference has not been acquired before\n",
|
||||
@@ -6599,35 +6614,43 @@ static int check_helper_call(struct bpf_
|
||||
|
||||
regs = cur_regs(env);
|
||||
|
||||
- /* check that flags argument in get_local_storage(map, flags) is 0,
|
||||
- * this is required because get_local_storage() can't return an error.
|
||||
- */
|
||||
- if (func_id == BPF_FUNC_get_local_storage &&
|
||||
- !register_is_null(®s[BPF_REG_2])) {
|
||||
- verbose(env, "get_local_storage() doesn't support non-zero flags\n");
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- if (func_id == BPF_FUNC_for_each_map_elem) {
|
||||
+ switch (func_id) {
|
||||
+ case BPF_FUNC_tail_call:
|
||||
+ err = check_reference_leak(env);
|
||||
+ if (err) {
|
||||
+ verbose(env, "tail_call would lead to reference leak\n");
|
||||
+ return err;
|
||||
+ }
|
||||
+ break;
|
||||
+ case BPF_FUNC_get_local_storage:
|
||||
+ /* check that flags argument in get_local_storage(map, flags) is 0,
|
||||
+ * this is required because get_local_storage() can't return an error.
|
||||
+ */
|
||||
+ if (!register_is_null(®s[BPF_REG_2])) {
|
||||
+ verbose(env, "get_local_storage() doesn't support non-zero flags\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ break;
|
||||
+ case BPF_FUNC_for_each_map_elem:
|
||||
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
|
||||
set_map_elem_callback_state);
|
||||
- if (err < 0)
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- if (func_id == BPF_FUNC_timer_set_callback) {
|
||||
+ break;
|
||||
+ case BPF_FUNC_timer_set_callback:
|
||||
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
|
||||
set_timer_callback_state);
|
||||
- if (err < 0)
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- if (func_id == BPF_FUNC_snprintf) {
|
||||
+ break;
|
||||
+ case BPF_FUNC_snprintf:
|
||||
err = check_bpf_snprintf_call(env, regs);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
+ break;
|
||||
+ case BPF_FUNC_loop:
|
||||
+ err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
|
||||
+ set_loop_callback_state);
|
||||
+ break;
|
||||
}
|
||||
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
/* reset caller saved regs */
|
||||
for (i = 0; i < CALLER_SAVED_REGS; i++) {
|
||||
mark_reg_not_init(env, regs, caller_saved[i]);
|
||||
--- a/tools/include/uapi/linux/bpf.h
|
||||
+++ b/tools/include/uapi/linux/bpf.h
|
||||
@@ -4896,6 +4896,30 @@ union bpf_attr {
|
||||
* Get the struct pt_regs associated with **task**.
|
||||
* Return
|
||||
* A pointer to struct pt_regs.
|
||||
+ *
|
||||
+ * long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
|
||||
+ * Description
|
||||
+ * For **nr_loops**, call **callback_fn** function
|
||||
+ * with **callback_ctx** as the context parameter.
|
||||
+ * The **callback_fn** should be a static function and
|
||||
+ * the **callback_ctx** should be a pointer to the stack.
|
||||
+ * The **flags** is used to control certain aspects of the helper.
|
||||
+ * Currently, the **flags** must be 0. Currently, nr_loops is
|
||||
+ * limited to 1 << 23 (~8 million) loops.
|
||||
+ *
|
||||
+ * long (\*callback_fn)(u32 index, void \*ctx);
|
||||
+ *
|
||||
+ * where **index** is the current index in the loop. The index
|
||||
+ * is zero-indexed.
|
||||
+ *
|
||||
+ * If **callback_fn** returns 0, the helper will continue to the next
|
||||
+ * loop. If return value is 1, the helper will skip the rest of
|
||||
+ * the loops and return. Other return values are not used now,
|
||||
+ * and will be rejected by the verifier.
|
||||
+ *
|
||||
+ * Return
|
||||
+ * The number of loops performed, **-EINVAL** for invalid **flags**,
|
||||
+ * **-E2BIG** if **nr_loops** exceeds the maximum number of loops.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@@ -5074,6 +5098,7 @@ union bpf_attr {
|
||||
FN(get_func_ip), \
|
||||
FN(get_attach_cookie), \
|
||||
FN(task_pt_regs), \
|
||||
+ FN(loop), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
@ -0,0 +1,330 @@
|
||||
From 1ade23711971b0eececf0d7fedc29d3c1d2fce01 Mon Sep 17 00:00:00 2001
|
||||
From: Eduard Zingerman <eddyz87@gmail.com>
|
||||
Date: Tue, 21 Jun 2022 02:53:42 +0300
|
||||
Subject: [PATCH] bpf: Inline calls to bpf_loop when callback is known
|
||||
|
||||
Calls to `bpf_loop` are replaced with direct loops to avoid
|
||||
indirection. E.g. the following:
|
||||
|
||||
bpf_loop(10, foo, NULL, 0);
|
||||
|
||||
Is replaced by equivalent of the following:
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
foo(i, NULL);
|
||||
|
||||
This transformation could be applied when:
|
||||
- callback is known and does not change during program execution;
|
||||
- flags passed to `bpf_loop` are always zero.
|
||||
|
||||
Inlining logic works as follows:
|
||||
|
||||
- During execution simulation function `update_loop_inline_state`
|
||||
tracks the following information for each `bpf_loop` call
|
||||
instruction:
|
||||
- is callback known and constant?
|
||||
- are flags constant and zero?
|
||||
- Function `optimize_bpf_loop` increases stack depth for functions
|
||||
where `bpf_loop` calls can be inlined and invokes `inline_bpf_loop`
|
||||
to apply the inlining. The additional stack space is used to spill
|
||||
registers R6, R7 and R8. These registers are used as loop counter,
|
||||
loop maximal bound and callback context parameter;
|
||||
|
||||
Measurements using `benchs/run_bench_bpf_loop.sh` inside QEMU / KVM on
|
||||
i7-4710HQ CPU show a drop in latency from 14 ns/op to 2 ns/op.
|
||||
|
||||
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
|
||||
Acked-by: Song Liu <songliubraving@fb.com>
|
||||
Link: https://lore.kernel.org/r/20220620235344.569325-4-eddyz87@gmail.com
|
||||
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
|
||||
---
|
||||
include/linux/bpf.h | 3 +
|
||||
include/linux/bpf_verifier.h | 12 +++
|
||||
kernel/bpf/bpf_iter.c | 9 +-
|
||||
kernel/bpf/verifier.c | 180 ++++++++++++++++++++++++++++++++++-
|
||||
4 files changed, 195 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/include/linux/bpf.h
|
||||
+++ b/include/linux/bpf.h
|
||||
@@ -1127,6 +1127,9 @@ struct bpf_array {
|
||||
#define BPF_COMPLEXITY_LIMIT_INSNS 1000000 /* yes. 1M insns */
|
||||
#define MAX_TAIL_CALL_CNT 32
|
||||
|
||||
+/* Maximum number of loops for bpf_loop */
|
||||
+#define BPF_MAX_LOOPS BIT(23)
|
||||
+
|
||||
#define BPF_F_ACCESS_MASK (BPF_F_RDONLY | \
|
||||
BPF_F_RDONLY_PROG | \
|
||||
BPF_F_WRONLY | \
|
||||
--- a/include/linux/bpf_verifier.h
|
||||
+++ b/include/linux/bpf_verifier.h
|
||||
@@ -356,6 +356,14 @@ struct bpf_verifier_state_list {
|
||||
int miss_cnt, hit_cnt;
|
||||
};
|
||||
|
||||
+struct bpf_loop_inline_state {
|
||||
+ int initialized:1; /* set to true upon first entry */
|
||||
+ int fit_for_inline:1; /* true if callback function is the same
|
||||
+ * at each call and flags are always zero
|
||||
+ */
|
||||
+ u32 callback_subprogno; /* valid when fit_for_inline is true */
|
||||
+};
|
||||
+
|
||||
/* Possible states for alu_state member. */
|
||||
#define BPF_ALU_SANITIZE_SRC (1U << 0)
|
||||
#define BPF_ALU_SANITIZE_DST (1U << 1)
|
||||
@@ -385,6 +393,10 @@ struct bpf_insn_aux_data {
|
||||
u32 mem_size; /* mem_size for non-struct typed var */
|
||||
};
|
||||
} btf_var;
|
||||
+ /* if instruction is a call to bpf_loop this field tracks
|
||||
+ * the state of the relevant registers to make decision about inlining
|
||||
+ */
|
||||
+ struct bpf_loop_inline_state loop_inline_state;
|
||||
};
|
||||
u64 map_key_state; /* constant (32 bit) key tracking for maps */
|
||||
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
|
||||
--- a/kernel/bpf/bpf_iter.c
|
||||
+++ b/kernel/bpf/bpf_iter.c
|
||||
@@ -715,9 +715,6 @@ const struct bpf_func_proto bpf_for_each
|
||||
.arg4_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
-/* maximum number of loops */
|
||||
-#define MAX_LOOPS BIT(23)
|
||||
-
|
||||
BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,
|
||||
u64, flags)
|
||||
{
|
||||
@@ -725,9 +722,13 @@ BPF_CALL_4(bpf_loop, u32, nr_loops, void
|
||||
u64 ret;
|
||||
u32 i;
|
||||
|
||||
+ /* Note: these safety checks are also verified when bpf_loop
|
||||
+ * is inlined, be careful to modify this code in sync. See
|
||||
+ * function verifier.c:inline_bpf_loop.
|
||||
+ */
|
||||
if (flags)
|
||||
return -EINVAL;
|
||||
- if (nr_loops > MAX_LOOPS)
|
||||
+ if (nr_loops > BPF_MAX_LOOPS)
|
||||
return -E2BIG;
|
||||
|
||||
for (i = 0; i < nr_loops; i++) {
|
||||
--- a/kernel/bpf/verifier.c
|
||||
+++ b/kernel/bpf/verifier.c
|
||||
@@ -6520,6 +6520,41 @@ static int check_get_func_ip(struct bpf_
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
+static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
|
||||
+{
|
||||
+ return &env->insn_aux_data[env->insn_idx];
|
||||
+}
|
||||
+
|
||||
+static bool loop_flag_is_zero(struct bpf_verifier_env *env)
|
||||
+{
|
||||
+ struct bpf_reg_state *regs = cur_regs(env);
|
||||
+ struct bpf_reg_state *reg = ®s[BPF_REG_4];
|
||||
+ bool reg_is_null = register_is_null(reg);
|
||||
+
|
||||
+ if (reg_is_null)
|
||||
+ mark_chain_precision(env, BPF_REG_4);
|
||||
+
|
||||
+ return reg_is_null;
|
||||
+}
|
||||
+
|
||||
+static void update_loop_inline_state(struct bpf_verifier_env *env, u32 subprogno)
|
||||
+{
|
||||
+ struct bpf_loop_inline_state *state = &cur_aux(env)->loop_inline_state;
|
||||
+
|
||||
+ if (!state->initialized) {
|
||||
+ state->initialized = 1;
|
||||
+ state->fit_for_inline = loop_flag_is_zero(env);
|
||||
+ state->callback_subprogno = subprogno;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (!state->fit_for_inline)
|
||||
+ return;
|
||||
+
|
||||
+ state->fit_for_inline = (loop_flag_is_zero(env) &&
|
||||
+ state->callback_subprogno == subprogno);
|
||||
+}
|
||||
+
|
||||
static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int *insn_idx_p)
|
||||
{
|
||||
@@ -6643,6 +6678,7 @@ static int check_helper_call(struct bpf_
|
||||
err = check_bpf_snprintf_call(env, regs);
|
||||
break;
|
||||
case BPF_FUNC_loop:
|
||||
+ update_loop_inline_state(env, meta.subprogno);
|
||||
err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
|
||||
set_loop_callback_state);
|
||||
break;
|
||||
@@ -6979,11 +7015,6 @@ static bool check_reg_sane_offset(struct
|
||||
return true;
|
||||
}
|
||||
|
||||
-static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
|
||||
-{
|
||||
- return &env->insn_aux_data[env->insn_idx];
|
||||
-}
|
||||
-
|
||||
enum {
|
||||
REASON_BOUNDS = -1,
|
||||
REASON_TYPE = -2,
|
||||
@@ -13390,6 +13421,142 @@ patch_call_imm:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,
|
||||
+ int position,
|
||||
+ s32 stack_base,
|
||||
+ u32 callback_subprogno,
|
||||
+ u32 *cnt)
|
||||
+{
|
||||
+ s32 r6_offset = stack_base + 0 * BPF_REG_SIZE;
|
||||
+ s32 r7_offset = stack_base + 1 * BPF_REG_SIZE;
|
||||
+ s32 r8_offset = stack_base + 2 * BPF_REG_SIZE;
|
||||
+ int reg_loop_max = BPF_REG_6;
|
||||
+ int reg_loop_cnt = BPF_REG_7;
|
||||
+ int reg_loop_ctx = BPF_REG_8;
|
||||
+
|
||||
+ struct bpf_prog *new_prog;
|
||||
+ u32 callback_start;
|
||||
+ u32 call_insn_offset;
|
||||
+ s32 callback_offset;
|
||||
+
|
||||
+ /* This represents an inlined version of bpf_iter.c:bpf_loop,
|
||||
+ * be careful to modify this code in sync.
|
||||
+ */
|
||||
+ struct bpf_insn insn_buf[] = {
|
||||
+ /* Return error and jump to the end of the patch if
|
||||
+ * expected number of iterations is too big.
|
||||
+ */
|
||||
+ BPF_JMP_IMM(BPF_JLE, BPF_REG_1, BPF_MAX_LOOPS, 2),
|
||||
+ BPF_MOV32_IMM(BPF_REG_0, -E2BIG),
|
||||
+ BPF_JMP_IMM(BPF_JA, 0, 0, 16),
|
||||
+ /* spill R6, R7, R8 to use these as loop vars */
|
||||
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, r6_offset),
|
||||
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_7, r7_offset),
|
||||
+ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_8, r8_offset),
|
||||
+ /* initialize loop vars */
|
||||
+ BPF_MOV64_REG(reg_loop_max, BPF_REG_1),
|
||||
+ BPF_MOV32_IMM(reg_loop_cnt, 0),
|
||||
+ BPF_MOV64_REG(reg_loop_ctx, BPF_REG_3),
|
||||
+ /* loop header,
|
||||
+ * if reg_loop_cnt >= reg_loop_max skip the loop body
|
||||
+ */
|
||||
+ BPF_JMP_REG(BPF_JGE, reg_loop_cnt, reg_loop_max, 5),
|
||||
+ /* callback call,
|
||||
+ * correct callback offset would be set after patching
|
||||
+ */
|
||||
+ BPF_MOV64_REG(BPF_REG_1, reg_loop_cnt),
|
||||
+ BPF_MOV64_REG(BPF_REG_2, reg_loop_ctx),
|
||||
+ BPF_CALL_REL(0),
|
||||
+ /* increment loop counter */
|
||||
+ BPF_ALU64_IMM(BPF_ADD, reg_loop_cnt, 1),
|
||||
+ /* jump to loop header if callback returned 0 */
|
||||
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -6),
|
||||
+ /* return value of bpf_loop,
|
||||
+ * set R0 to the number of iterations
|
||||
+ */
|
||||
+ BPF_MOV64_REG(BPF_REG_0, reg_loop_cnt),
|
||||
+ /* restore original values of R6, R7, R8 */
|
||||
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, r6_offset),
|
||||
+ BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_10, r7_offset),
|
||||
+ BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_10, r8_offset),
|
||||
+ };
|
||||
+
|
||||
+ *cnt = ARRAY_SIZE(insn_buf);
|
||||
+ new_prog = bpf_patch_insn_data(env, position, insn_buf, *cnt);
|
||||
+ if (!new_prog)
|
||||
+ return new_prog;
|
||||
+
|
||||
+ /* callback start is known only after patching */
|
||||
+ callback_start = env->subprog_info[callback_subprogno].start;
|
||||
+ /* Note: insn_buf[12] is an offset of BPF_CALL_REL instruction */
|
||||
+ call_insn_offset = position + 12;
|
||||
+ callback_offset = callback_start - call_insn_offset - 1;
|
||||
+ env->prog->insnsi[call_insn_offset].imm = callback_offset;
|
||||
+
|
||||
+ return new_prog;
|
||||
+}
|
||||
+
|
||||
+static bool is_bpf_loop_call(struct bpf_insn *insn)
|
||||
+{
|
||||
+ return insn->code == (BPF_JMP | BPF_CALL) &&
|
||||
+ insn->src_reg == 0 &&
|
||||
+ insn->imm == BPF_FUNC_loop;
|
||||
+}
|
||||
+
|
||||
+/* For all sub-programs in the program (including main) check
|
||||
+ * insn_aux_data to see if there are bpf_loop calls that require
|
||||
+ * inlining. If such calls are found the calls are replaced with a
|
||||
+ * sequence of instructions produced by `inline_bpf_loop` function and
|
||||
+ * subprog stack_depth is increased by the size of 3 registers.
|
||||
+ * This stack space is used to spill values of the R6, R7, R8. These
|
||||
+ * registers are used to store the loop bound, counter and context
|
||||
+ * variables.
|
||||
+ */
|
||||
+static int optimize_bpf_loop(struct bpf_verifier_env *env)
|
||||
+{
|
||||
+ struct bpf_subprog_info *subprogs = env->subprog_info;
|
||||
+ int i, cur_subprog = 0, cnt, delta = 0;
|
||||
+ struct bpf_insn *insn = env->prog->insnsi;
|
||||
+ int insn_cnt = env->prog->len;
|
||||
+ u16 stack_depth = subprogs[cur_subprog].stack_depth;
|
||||
+ u16 stack_depth_roundup = round_up(stack_depth, 8) - stack_depth;
|
||||
+ u16 stack_depth_extra = 0;
|
||||
+
|
||||
+ for (i = 0; i < insn_cnt; i++, insn++) {
|
||||
+ struct bpf_loop_inline_state *inline_state =
|
||||
+ &env->insn_aux_data[i + delta].loop_inline_state;
|
||||
+
|
||||
+ if (is_bpf_loop_call(insn) && inline_state->fit_for_inline) {
|
||||
+ struct bpf_prog *new_prog;
|
||||
+
|
||||
+ stack_depth_extra = BPF_REG_SIZE * 3 + stack_depth_roundup;
|
||||
+ new_prog = inline_bpf_loop(env,
|
||||
+ i + delta,
|
||||
+ -(stack_depth + stack_depth_extra),
|
||||
+ inline_state->callback_subprogno,
|
||||
+ &cnt);
|
||||
+ if (!new_prog)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ delta += cnt - 1;
|
||||
+ env->prog = new_prog;
|
||||
+ insn = new_prog->insnsi + i + delta;
|
||||
+ }
|
||||
+
|
||||
+ if (subprogs[cur_subprog + 1].start == i + delta + 1) {
|
||||
+ subprogs[cur_subprog].stack_depth += stack_depth_extra;
|
||||
+ cur_subprog++;
|
||||
+ stack_depth = subprogs[cur_subprog].stack_depth;
|
||||
+ stack_depth_roundup = round_up(stack_depth, 8) - stack_depth;
|
||||
+ stack_depth_extra = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static void free_states(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_verifier_state_list *sl, *sln;
|
||||
@@ -14131,6 +14298,9 @@ skip_full_check:
|
||||
ret = check_max_stack_depth(env);
|
||||
|
||||
/* instruction rewrites happen after this point */
|
||||
+ if (ret == 0)
|
||||
+ ret = optimize_bpf_loop(env);
|
||||
+
|
||||
if (is_priv) {
|
||||
if (ret == 0)
|
||||
opt_hard_wire_dead_code_branches(env);
|
||||
@ -22,7 +22,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
--- a/drivers/usb/dwc3/core.c
|
||||
+++ b/drivers/usb/dwc3/core.c
|
||||
@@ -1090,6 +1090,11 @@ static int dwc3_core_init(struct dwc3 *d
|
||||
@@ -1076,6 +1076,11 @@ static int dwc3_core_init(struct dwc3 *d
|
||||
if (dwc->parkmode_disable_ss_quirk)
|
||||
reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@@ -5320,12 +5321,16 @@ static int rtl_init_one(struct pci_dev *
|
||||
@@ -5319,12 +5320,16 @@ static int rtl_init_one(struct pci_dev *
|
||||
int jumbo_max, region, rc;
|
||||
enum mac_version chipset;
|
||||
struct net_device *dev;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user