mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'bpf-next-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov:
- Fix and improve BTF deduplication of identical BTF types (Alan
Maguire and Andrii Nakryiko)
- Support up to 12 arguments in BPF trampoline on arm64 (Xu Kuohai and
Alexis Lothoré)
- Support load-acquire and store-release instructions in BPF JIT on
riscv64 (Andrea Parri)
- Fix uninitialized values in BPF_{CORE,PROBE}_READ macros (Anton
Protopopov)
- Streamline allowed helpers across program types (Feng Yang)
- Support atomic update for hashtab of BPF maps (Hou Tao)
- Implement json output for BPF helpers (Ihor Solodrai)
- Several s390 JIT fixes (Ilya Leoshkevich)
- Various sockmap fixes (Jiayuan Chen)
- Support mmap of vmlinux BTF data (Lorenz Bauer)
- Support BPF rbtree traversal and list peeking (Martin KaFai Lau)
- Tests for sockmap/sockhash redirection (Michal Luczaj)
- Introduce kfuncs for memory reads into dynptrs (Mykyta Yatsenko)
- Add support for dma-buf iterators in BPF (T.J. Mercier)
- The verifier support for __bpf_trap() (Yonghong Song)
* tag 'bpf-next-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (135 commits)
bpf, arm64: Remove unused-but-set function and variable.
selftests/bpf: Add tests with stack ptr register in conditional jmp
bpf: Do not include stack ptr register in precision backtracking bookkeeping
selftests/bpf: enable many-args tests for arm64
bpf, arm64: Support up to 12 function arguments
bpf: Check rcu_read_lock_trace_held() in bpf_map_lookup_percpu_elem()
bpf: Avoid __bpf_prog_ret0_warn when jit fails
bpftool: Add support for custom BTF path in prog load/loadall
selftests/bpf: Add unit tests with __bpf_trap() kfunc
bpf: Warn with __bpf_trap() kfunc maybe due to uninitialized variable
bpf: Remove special_kfunc_set from verifier
selftests/bpf: Add test for open coded dmabuf_iter
selftests/bpf: Add test for dmabuf_iter
bpf: Add open coded dmabuf iterator
bpf: Add dmabuf iterator
dma-buf: Rename debugfs symbols
bpf: Fix error return value in bpf_copy_from_user_dynptr
libbpf: Use mmap to parse vmlinux BTF from sysfs
selftests: bpf: Add a test for mmapable vmlinux BTF
btf: Allow mmap of vmlinux btf
...
This commit is contained in:
@@ -2,10 +2,117 @@
|
||||
BPF Iterators
|
||||
=============
|
||||
|
||||
--------
|
||||
Overview
|
||||
--------
|
||||
|
||||
----------
|
||||
Motivation
|
||||
----------
|
||||
BPF supports two separate entities collectively known as "BPF iterators": BPF
|
||||
iterator *program type* and *open-coded* BPF iterators. The former is
|
||||
a stand-alone BPF program type which, when attached and activated by user,
|
||||
will be called once for each entity (task_struct, cgroup, etc) that is being
|
||||
iterated. The latter is a set of BPF-side APIs implementing iterator
|
||||
functionality and available across multiple BPF program types. Open-coded
|
||||
iterators provide similar functionality to BPF iterator programs, but gives
|
||||
more flexibility and control to all other BPF program types. BPF iterator
|
||||
programs, on the other hand, can be used to implement anonymous or BPF
|
||||
FS-mounted special files, whose contents are generated by attached BPF iterator
|
||||
program, backed by seq_file functionality. Both are useful depending on
|
||||
specific needs.
|
||||
|
||||
When adding a new BPF iterator program, it is expected that similar
|
||||
functionality will be added as open-coded iterator for maximum flexibility.
|
||||
It's also expected that iteration logic and code will be maximally shared and
|
||||
reused between two iterator API surfaces.
|
||||
|
||||
------------------------
|
||||
Open-coded BPF Iterators
|
||||
------------------------
|
||||
|
||||
Open-coded BPF iterators are implemented as tightly-coupled trios of kfuncs
|
||||
(constructor, next element fetch, destructor) and iterator-specific type
|
||||
describing on-the-stack iterator state, which is guaranteed by the BPF
|
||||
verifier to not be tampered with outside of the corresponding
|
||||
constructor/destructor/next APIs.
|
||||
|
||||
Each kind of open-coded BPF iterator has its own associated
|
||||
struct bpf_iter_<type>, where <type> denotes a specific type of iterator.
|
||||
bpf_iter_<type> state needs to live on BPF program stack, so make sure it's
|
||||
small enough to fit on BPF stack. For performance reasons its best to avoid
|
||||
dynamic memory allocation for iterator state and size the state struct big
|
||||
enough to fit everything necessary. But if necessary, dynamic memory
|
||||
allocation is a way to bypass BPF stack limitations. Note, state struct size
|
||||
is part of iterator's user-visible API, so changing it will break backwards
|
||||
compatibility, so be deliberate about designing it.
|
||||
|
||||
All kfuncs (constructor, next, destructor) have to be named consistently as
|
||||
bpf_iter_<type>_{new,next,destroy}(), respectively. <type> represents iterator
|
||||
type, and iterator state should be represented as a matching
|
||||
`struct bpf_iter_<type>` state type. Also, all iter kfuncs should have
|
||||
a pointer to this `struct bpf_iter_<type>` as the very first argument.
|
||||
|
||||
Additionally:
|
||||
- Constructor, i.e., `bpf_iter_<type>_new()`, can have arbitrary extra
|
||||
number of arguments. Return type is not enforced either.
|
||||
- Next method, i.e., `bpf_iter_<type>_next()`, has to return a pointer
|
||||
type and should have exactly one argument: `struct bpf_iter_<type> *`
|
||||
(const/volatile/restrict and typedefs are ignored).
|
||||
- Destructor, i.e., `bpf_iter_<type>_destroy()`, should return void and
|
||||
should have exactly one argument, similar to the next method.
|
||||
- `struct bpf_iter_<type>` size is enforced to be positive and
|
||||
a multiple of 8 bytes (to fit stack slots correctly).
|
||||
|
||||
Such strictness and consistency allows to build generic helpers abstracting
|
||||
important, but boilerplate, details to be able to use open-coded iterators
|
||||
effectively and ergonomically (see libbpf's bpf_for_each() macro). This is
|
||||
enforced at kfunc registration point by the kernel.
|
||||
|
||||
Constructor/next/destructor implementation contract is as follows:
|
||||
- constructor, `bpf_iter_<type>_new()`, always initializes iterator state on
|
||||
the stack. If any of the input arguments are invalid, constructor should
|
||||
make sure to still initialize it such that subsequent next() calls will
|
||||
return NULL. I.e., on error, *return error and construct empty iterator*.
|
||||
Constructor kfunc is marked with KF_ITER_NEW flag.
|
||||
|
||||
- next method, `bpf_iter_<type>_next()`, accepts pointer to iterator state
|
||||
and produces an element. Next method should always return a pointer. The
|
||||
contract between BPF verifier is that next method *guarantees* that it
|
||||
will eventually return NULL when elements are exhausted. Once NULL is
|
||||
returned, subsequent next calls *should keep returning NULL*. Next method
|
||||
is marked with KF_ITER_NEXT (and should also have KF_RET_NULL as
|
||||
NULL-returning kfunc, of course).
|
||||
|
||||
- destructor, `bpf_iter_<type>_destroy()`, is always called once. Even if
|
||||
constructor failed or next returned nothing. Destructor frees up any
|
||||
resources and marks stack space used by `struct bpf_iter_<type>` as usable
|
||||
for something else. Destructor is marked with KF_ITER_DESTROY flag.
|
||||
|
||||
Any open-coded BPF iterator implementation has to implement at least these
|
||||
three methods. It is enforced that for any given type of iterator only
|
||||
applicable constructor/destructor/next are callable. I.e., verifier ensures
|
||||
you can't pass number iterator state into, say, cgroup iterator's next method.
|
||||
|
||||
From a 10,000-feet BPF verification point of view, next methods are the points
|
||||
of forking a verification state, which are conceptually similar to what
|
||||
verifier is doing when validating conditional jumps. Verifier is branching out
|
||||
`call bpf_iter_<type>_next` instruction and simulates two outcomes: NULL
|
||||
(iteration is done) and non-NULL (new element is returned). NULL is simulated
|
||||
first and is supposed to reach exit without looping. After that non-NULL case
|
||||
is validated and it either reaches exit (for trivial examples with no real
|
||||
loop), or reaches another `call bpf_iter_<type>_next` instruction with the
|
||||
state equivalent to already (partially) validated one. State equivalency at
|
||||
that point means we technically are going to be looping forever without
|
||||
"breaking out" out of established "state envelope" (i.e., subsequent
|
||||
iterations don't add any new knowledge or constraints to the verifier state,
|
||||
so running 1, 2, 10, or a million of them doesn't matter). But taking into
|
||||
account the contract stating that iterator next method *has to* return NULL
|
||||
eventually, we can conclude that loop body is safe and will eventually
|
||||
terminate. Given we validated logic outside of the loop (NULL case), and
|
||||
concluded that loop body is safe (though potentially looping many times),
|
||||
verifier can claim safety of the overall program logic.
|
||||
|
||||
------------------------
|
||||
BPF Iterators Motivation
|
||||
------------------------
|
||||
|
||||
There are a few existing ways to dump kernel data into user space. The most
|
||||
popular one is the ``/proc`` system. For example, ``cat /proc/net/tcp6`` dumps
|
||||
@@ -323,8 +430,8 @@ Now, in the userspace program, pass the pointer of struct to the
|
||||
|
||||
::
|
||||
|
||||
link = bpf_program__attach_iter(prog, &opts); iter_fd =
|
||||
bpf_iter_create(bpf_link__fd(link));
|
||||
link = bpf_program__attach_iter(prog, &opts);
|
||||
iter_fd = bpf_iter_create(bpf_link__fd(link));
|
||||
|
||||
If both *tid* and *pid* are zero, an iterator created from this struct
|
||||
``bpf_iter_attach_opts`` will include every opened file of every task in the
|
||||
|
||||
@@ -160,6 +160,23 @@ Or::
|
||||
...
|
||||
}
|
||||
|
||||
2.2.6 __prog Annotation
|
||||
---------------------------
|
||||
This annotation is used to indicate that the argument needs to be fixed up to
|
||||
the bpf_prog_aux of the caller BPF program. Any value passed into this argument
|
||||
is ignored, and rewritten by the verifier.
|
||||
|
||||
An example is given below::
|
||||
|
||||
__bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq,
|
||||
int (callback_fn)(void *map, int *key, void *value),
|
||||
unsigned int flags,
|
||||
void *aux__prog)
|
||||
{
|
||||
struct bpf_prog_aux *aux = aux__prog;
|
||||
...
|
||||
}
|
||||
|
||||
.. _BPF_kfunc_nodef:
|
||||
|
||||
2.3 Using an existing kernel function
|
||||
|
||||
+172
-72
@@ -2113,7 +2113,7 @@ bool bpf_jit_supports_subprog_tailcalls(void)
|
||||
}
|
||||
|
||||
static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
int args_off, int retval_off, int run_ctx_off,
|
||||
int bargs_off, int retval_off, int run_ctx_off,
|
||||
bool save_ret)
|
||||
{
|
||||
__le32 *branch;
|
||||
@@ -2155,7 +2155,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
branch = ctx->image + ctx->idx;
|
||||
emit(A64_NOP, ctx);
|
||||
|
||||
emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx);
|
||||
emit(A64_ADD_I(1, A64_R(0), A64_SP, bargs_off), ctx);
|
||||
if (!p->jited)
|
||||
emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx);
|
||||
|
||||
@@ -2180,7 +2180,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l,
|
||||
}
|
||||
|
||||
static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
|
||||
int args_off, int retval_off, int run_ctx_off,
|
||||
int bargs_off, int retval_off, int run_ctx_off,
|
||||
__le32 **branches)
|
||||
{
|
||||
int i;
|
||||
@@ -2190,7 +2190,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
|
||||
*/
|
||||
emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx);
|
||||
for (i = 0; i < tl->nr_links; i++) {
|
||||
invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off,
|
||||
invoke_bpf_prog(ctx, tl->links[i], bargs_off, retval_off,
|
||||
run_ctx_off, true);
|
||||
/* if (*(u64 *)(sp + retval_off) != 0)
|
||||
* goto do_fexit;
|
||||
@@ -2204,23 +2204,125 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl,
|
||||
}
|
||||
}
|
||||
|
||||
static void save_args(struct jit_ctx *ctx, int args_off, int nregs)
|
||||
{
|
||||
int i;
|
||||
struct arg_aux {
|
||||
/* how many args are passed through registers, the rest of the args are
|
||||
* passed through stack
|
||||
*/
|
||||
int args_in_regs;
|
||||
/* how many registers are used to pass arguments */
|
||||
int regs_for_args;
|
||||
/* how much stack is used for additional args passed to bpf program
|
||||
* that did not fit in original function registers
|
||||
*/
|
||||
int bstack_for_args;
|
||||
/* home much stack is used for additional args passed to the
|
||||
* original function when called from trampoline (this one needs
|
||||
* arguments to be properly aligned)
|
||||
*/
|
||||
int ostack_for_args;
|
||||
};
|
||||
|
||||
for (i = 0; i < nregs; i++) {
|
||||
emit(A64_STR64I(i, A64_SP, args_off), ctx);
|
||||
args_off += 8;
|
||||
static int calc_arg_aux(const struct btf_func_model *m,
|
||||
struct arg_aux *a)
|
||||
{
|
||||
int stack_slots, nregs, slots, i;
|
||||
|
||||
/* verifier ensures m->nr_args <= MAX_BPF_FUNC_ARGS */
|
||||
for (i = 0, nregs = 0; i < m->nr_args; i++) {
|
||||
slots = (m->arg_size[i] + 7) / 8;
|
||||
if (nregs + slots <= 8) /* passed through register ? */
|
||||
nregs += slots;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
a->args_in_regs = i;
|
||||
a->regs_for_args = nregs;
|
||||
a->ostack_for_args = 0;
|
||||
a->bstack_for_args = 0;
|
||||
|
||||
/* the rest arguments are passed through stack */
|
||||
for (; i < m->nr_args; i++) {
|
||||
/* We can not know for sure about exact alignment needs for
|
||||
* struct passed on stack, so deny those
|
||||
*/
|
||||
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
|
||||
return -ENOTSUPP;
|
||||
stack_slots = (m->arg_size[i] + 7) / 8;
|
||||
a->bstack_for_args += stack_slots * 8;
|
||||
a->ostack_for_args = a->ostack_for_args + stack_slots * 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clear_garbage(struct jit_ctx *ctx, int reg, int effective_bytes)
|
||||
{
|
||||
if (effective_bytes) {
|
||||
int garbage_bits = 64 - 8 * effective_bytes;
|
||||
#ifdef CONFIG_CPU_BIG_ENDIAN
|
||||
/* garbage bits are at the right end */
|
||||
emit(A64_LSR(1, reg, reg, garbage_bits), ctx);
|
||||
emit(A64_LSL(1, reg, reg, garbage_bits), ctx);
|
||||
#else
|
||||
/* garbage bits are at the left end */
|
||||
emit(A64_LSL(1, reg, reg, garbage_bits), ctx);
|
||||
emit(A64_LSR(1, reg, reg, garbage_bits), ctx);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_args(struct jit_ctx *ctx, int args_off, int nregs)
|
||||
static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off,
|
||||
const struct btf_func_model *m,
|
||||
const struct arg_aux *a,
|
||||
bool for_call_origin)
|
||||
{
|
||||
int i;
|
||||
int reg;
|
||||
int doff;
|
||||
int soff;
|
||||
int slots;
|
||||
u8 tmp = bpf2a64[TMP_REG_1];
|
||||
|
||||
for (i = 0; i < nregs; i++) {
|
||||
emit(A64_LDR64I(i, A64_SP, args_off), ctx);
|
||||
args_off += 8;
|
||||
/* store arguments to the stack for the bpf program, or restore
|
||||
* arguments from stack for the original function
|
||||
*/
|
||||
for (reg = 0; reg < a->regs_for_args; reg++) {
|
||||
emit(for_call_origin ?
|
||||
A64_LDR64I(reg, A64_SP, bargs_off) :
|
||||
A64_STR64I(reg, A64_SP, bargs_off),
|
||||
ctx);
|
||||
bargs_off += 8;
|
||||
}
|
||||
|
||||
soff = 32; /* on stack arguments start from FP + 32 */
|
||||
doff = (for_call_origin ? oargs_off : bargs_off);
|
||||
|
||||
/* save on stack arguments */
|
||||
for (i = a->args_in_regs; i < m->nr_args; i++) {
|
||||
slots = (m->arg_size[i] + 7) / 8;
|
||||
/* verifier ensures arg_size <= 16, so slots equals 1 or 2 */
|
||||
while (slots-- > 0) {
|
||||
emit(A64_LDR64I(tmp, A64_FP, soff), ctx);
|
||||
/* if there is unused space in the last slot, clear
|
||||
* the garbage contained in the space.
|
||||
*/
|
||||
if (slots == 0 && !for_call_origin)
|
||||
clear_garbage(ctx, tmp, m->arg_size[i] % 8);
|
||||
emit(A64_STR64I(tmp, A64_SP, doff), ctx);
|
||||
soff += 8;
|
||||
doff += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_args(struct jit_ctx *ctx, int bargs_off, int nregs)
|
||||
{
|
||||
int reg;
|
||||
|
||||
for (reg = 0; reg < nregs; reg++) {
|
||||
emit(A64_LDR64I(reg, A64_SP, bargs_off), ctx);
|
||||
bargs_off += 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2243,17 +2345,21 @@ static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links)
|
||||
*/
|
||||
static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
struct bpf_tramp_links *tlinks, void *func_addr,
|
||||
int nregs, u32 flags)
|
||||
const struct btf_func_model *m,
|
||||
const struct arg_aux *a,
|
||||
u32 flags)
|
||||
{
|
||||
int i;
|
||||
int stack_size;
|
||||
int retaddr_off;
|
||||
int regs_off;
|
||||
int retval_off;
|
||||
int args_off;
|
||||
int nregs_off;
|
||||
int bargs_off;
|
||||
int nfuncargs_off;
|
||||
int ip_off;
|
||||
int run_ctx_off;
|
||||
int oargs_off;
|
||||
int nfuncargs;
|
||||
struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
|
||||
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
|
||||
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
|
||||
@@ -2262,31 +2368,38 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
bool is_struct_ops = is_struct_ops_tramp(fentry);
|
||||
|
||||
/* trampoline stack layout:
|
||||
* [ parent ip ]
|
||||
* [ FP ]
|
||||
* SP + retaddr_off [ self ip ]
|
||||
* [ FP ]
|
||||
* [ parent ip ]
|
||||
* [ FP ]
|
||||
* SP + retaddr_off [ self ip ]
|
||||
* [ FP ]
|
||||
*
|
||||
* [ padding ] align SP to multiples of 16
|
||||
* [ padding ] align SP to multiples of 16
|
||||
*
|
||||
* [ x20 ] callee saved reg x20
|
||||
* SP + regs_off [ x19 ] callee saved reg x19
|
||||
* [ x20 ] callee saved reg x20
|
||||
* SP + regs_off [ x19 ] callee saved reg x19
|
||||
*
|
||||
* SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
|
||||
* BPF_TRAMP_F_RET_FENTRY_RET
|
||||
* SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
|
||||
* BPF_TRAMP_F_RET_FENTRY_RET
|
||||
* [ arg reg N ]
|
||||
* [ ... ]
|
||||
* SP + bargs_off [ arg reg 1 ] for bpf
|
||||
*
|
||||
* [ arg reg N ]
|
||||
* [ ... ]
|
||||
* SP + args_off [ arg reg 1 ]
|
||||
* SP + nfuncargs_off [ arg regs count ]
|
||||
*
|
||||
* SP + nregs_off [ arg regs count ]
|
||||
* SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
|
||||
*
|
||||
* SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
|
||||
* SP + run_ctx_off [ bpf_tramp_run_ctx ]
|
||||
*
|
||||
* SP + run_ctx_off [ bpf_tramp_run_ctx ]
|
||||
* [ stack arg N ]
|
||||
* [ ... ]
|
||||
* SP + oargs_off [ stack arg 1 ] for original func
|
||||
*/
|
||||
|
||||
stack_size = 0;
|
||||
oargs_off = stack_size;
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG)
|
||||
stack_size += a->ostack_for_args;
|
||||
|
||||
run_ctx_off = stack_size;
|
||||
/* room for bpf_tramp_run_ctx */
|
||||
stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8);
|
||||
@@ -2296,13 +2409,14 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
if (flags & BPF_TRAMP_F_IP_ARG)
|
||||
stack_size += 8;
|
||||
|
||||
nregs_off = stack_size;
|
||||
nfuncargs_off = stack_size;
|
||||
/* room for args count */
|
||||
stack_size += 8;
|
||||
|
||||
args_off = stack_size;
|
||||
bargs_off = stack_size;
|
||||
/* room for args */
|
||||
stack_size += nregs * 8;
|
||||
nfuncargs = a->regs_for_args + a->bstack_for_args / 8;
|
||||
stack_size += 8 * nfuncargs;
|
||||
|
||||
/* room for return value */
|
||||
retval_off = stack_size;
|
||||
@@ -2349,11 +2463,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
}
|
||||
|
||||
/* save arg regs count*/
|
||||
emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx);
|
||||
emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx);
|
||||
emit(A64_MOVZ(1, A64_R(10), nfuncargs, 0), ctx);
|
||||
emit(A64_STR64I(A64_R(10), A64_SP, nfuncargs_off), ctx);
|
||||
|
||||
/* save arg regs */
|
||||
save_args(ctx, args_off, nregs);
|
||||
/* save args for bpf */
|
||||
save_args(ctx, bargs_off, oargs_off, m, a, false);
|
||||
|
||||
/* save callee saved registers */
|
||||
emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx);
|
||||
@@ -2369,7 +2483,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
}
|
||||
|
||||
for (i = 0; i < fentry->nr_links; i++)
|
||||
invoke_bpf_prog(ctx, fentry->links[i], args_off,
|
||||
invoke_bpf_prog(ctx, fentry->links[i], bargs_off,
|
||||
retval_off, run_ctx_off,
|
||||
flags & BPF_TRAMP_F_RET_FENTRY_RET);
|
||||
|
||||
@@ -2379,12 +2493,13 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
if (!branches)
|
||||
return -ENOMEM;
|
||||
|
||||
invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off,
|
||||
invoke_bpf_mod_ret(ctx, fmod_ret, bargs_off, retval_off,
|
||||
run_ctx_off, branches);
|
||||
}
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
restore_args(ctx, args_off, nregs);
|
||||
/* save args for original func */
|
||||
save_args(ctx, bargs_off, oargs_off, m, a, true);
|
||||
/* call original func */
|
||||
emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx);
|
||||
emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx);
|
||||
@@ -2403,7 +2518,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
}
|
||||
|
||||
for (i = 0; i < fexit->nr_links; i++)
|
||||
invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off,
|
||||
invoke_bpf_prog(ctx, fexit->links[i], bargs_off, retval_off,
|
||||
run_ctx_off, false);
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
@@ -2417,7 +2532,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
}
|
||||
|
||||
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
||||
restore_args(ctx, args_off, nregs);
|
||||
restore_args(ctx, bargs_off, a->regs_for_args);
|
||||
|
||||
/* restore callee saved register x19 and x20 */
|
||||
emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx);
|
||||
@@ -2454,21 +2569,6 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
|
||||
return ctx->idx;
|
||||
}
|
||||
|
||||
static int btf_func_model_nregs(const struct btf_func_model *m)
|
||||
{
|
||||
int nregs = m->nr_args;
|
||||
int i;
|
||||
|
||||
/* extra registers needed for struct argument */
|
||||
for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) {
|
||||
/* The arg_size is at most 16 bytes, enforced by the verifier. */
|
||||
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
|
||||
nregs += (m->arg_size[i] + 7) / 8 - 1;
|
||||
}
|
||||
|
||||
return nregs;
|
||||
}
|
||||
|
||||
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
|
||||
struct bpf_tramp_links *tlinks, void *func_addr)
|
||||
{
|
||||
@@ -2477,14 +2577,14 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
|
||||
.idx = 0,
|
||||
};
|
||||
struct bpf_tramp_image im;
|
||||
int nregs, ret;
|
||||
struct arg_aux aaux;
|
||||
int ret;
|
||||
|
||||
nregs = btf_func_model_nregs(m);
|
||||
/* the first 8 registers are used for arguments */
|
||||
if (nregs > 8)
|
||||
return -ENOTSUPP;
|
||||
ret = calc_arg_aux(m, &aaux);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags);
|
||||
ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, m, &aaux, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -2511,9 +2611,10 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image,
|
||||
u32 flags, struct bpf_tramp_links *tlinks,
|
||||
void *func_addr)
|
||||
{
|
||||
int ret, nregs;
|
||||
void *image, *tmp;
|
||||
u32 size = ro_image_end - ro_image;
|
||||
struct arg_aux aaux;
|
||||
void *image, *tmp;
|
||||
int ret;
|
||||
|
||||
/* image doesn't need to be in module memory range, so we can
|
||||
* use kvmalloc.
|
||||
@@ -2529,13 +2630,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image,
|
||||
.write = true,
|
||||
};
|
||||
|
||||
nregs = btf_func_model_nregs(m);
|
||||
/* the first 8 registers are used for arguments */
|
||||
if (nregs > 8)
|
||||
return -ENOTSUPP;
|
||||
|
||||
jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image));
|
||||
ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags);
|
||||
ret = calc_arg_aux(m, &aaux);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = prepare_trampoline(&ctx, im, tlinks, func_addr, m, &aaux, flags);
|
||||
|
||||
if (ret > 0 && validate_code(&ctx) < 0) {
|
||||
ret = -EINVAL;
|
||||
|
||||
@@ -608,6 +608,21 @@ static inline u32 rv_fence(u8 pred, u8 succ)
|
||||
return rv_i_insn(imm11_0, 0, 0, 0, 0xf);
|
||||
}
|
||||
|
||||
static inline void emit_fence_r_rw(struct rv_jit_context *ctx)
|
||||
{
|
||||
emit(rv_fence(0x2, 0x3), ctx);
|
||||
}
|
||||
|
||||
static inline void emit_fence_rw_w(struct rv_jit_context *ctx)
|
||||
{
|
||||
emit(rv_fence(0x3, 0x1), ctx);
|
||||
}
|
||||
|
||||
static inline void emit_fence_rw_rw(struct rv_jit_context *ctx)
|
||||
{
|
||||
emit(rv_fence(0x3, 0x3), ctx);
|
||||
}
|
||||
|
||||
static inline u32 rv_nop(void)
|
||||
{
|
||||
return rv_i_insn(0, 0, 0, 0, 0x13);
|
||||
|
||||
+227
-105
@@ -473,11 +473,212 @@ static inline void emit_kcfi(u32 hash, struct rv_jit_context *ctx)
|
||||
emit(hash, ctx);
|
||||
}
|
||||
|
||||
static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
|
||||
struct rv_jit_context *ctx)
|
||||
static int emit_load_8(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
u8 r0;
|
||||
int insns_start;
|
||||
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lb(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lbu(rd, off, rs), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lb(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
static int emit_load_16(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
int insns_start;
|
||||
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lh(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lhu(rd, off, rs), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lh(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
static int emit_load_32(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
int insns_start;
|
||||
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lw(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lwu(rd, off, rs), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lw(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
static int emit_load_64(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
int insns_start;
|
||||
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
emit_ld(rd, off, rs, ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
emit_ld(rd, 0, RV_REG_T1, ctx);
|
||||
return ctx->ninsns - insns_start;
|
||||
}
|
||||
|
||||
static void emit_store_8(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (is_12b_int(off)) {
|
||||
emit(rv_sb(rd, off, rs), ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit(rv_sb(RV_REG_T1, 0, rs), ctx);
|
||||
}
|
||||
|
||||
static void emit_store_16(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (is_12b_int(off)) {
|
||||
emit(rv_sh(rd, off, rs), ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit(rv_sh(RV_REG_T1, 0, rs), ctx);
|
||||
}
|
||||
|
||||
static void emit_store_32(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (is_12b_int(off)) {
|
||||
emit_sw(rd, off, rs, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit_sw(RV_REG_T1, 0, rs, ctx);
|
||||
}
|
||||
|
||||
static void emit_store_64(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (is_12b_int(off)) {
|
||||
emit_sd(rd, off, rs, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit_sd(RV_REG_T1, 0, rs, ctx);
|
||||
}
|
||||
|
||||
static int emit_atomic_ld_st(u8 rd, u8 rs, const struct bpf_insn *insn,
|
||||
struct rv_jit_context *ctx)
|
||||
{
|
||||
u8 code = insn->code;
|
||||
s32 imm = insn->imm;
|
||||
s16 off = insn->off;
|
||||
|
||||
switch (imm) {
|
||||
/* dst_reg = load_acquire(src_reg + off16) */
|
||||
case BPF_LOAD_ACQ:
|
||||
switch (BPF_SIZE(code)) {
|
||||
case BPF_B:
|
||||
emit_load_8(false, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_H:
|
||||
emit_load_16(false, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_W:
|
||||
emit_load_32(false, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_DW:
|
||||
emit_load_64(false, rd, off, rs, ctx);
|
||||
break;
|
||||
}
|
||||
emit_fence_r_rw(ctx);
|
||||
|
||||
/* If our next insn is a redundant zext, return 1 to tell
|
||||
* build_body() to skip it.
|
||||
*/
|
||||
if (BPF_SIZE(code) != BPF_DW && insn_is_zext(&insn[1]))
|
||||
return 1;
|
||||
break;
|
||||
/* store_release(dst_reg + off16, src_reg) */
|
||||
case BPF_STORE_REL:
|
||||
emit_fence_rw_w(ctx);
|
||||
switch (BPF_SIZE(code)) {
|
||||
case BPF_B:
|
||||
emit_store_8(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_H:
|
||||
emit_store_16(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_W:
|
||||
emit_store_32(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_DW:
|
||||
emit_store_64(rd, off, rs, ctx);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err_once("bpf-jit: invalid atomic load/store opcode %02x\n", imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emit_atomic_rmw(u8 rd, u8 rs, const struct bpf_insn *insn,
|
||||
struct rv_jit_context *ctx)
|
||||
{
|
||||
u8 r0, code = insn->code;
|
||||
s16 off = insn->off;
|
||||
s32 imm = insn->imm;
|
||||
int jmp_offset;
|
||||
bool is64;
|
||||
|
||||
if (BPF_SIZE(code) != BPF_W && BPF_SIZE(code) != BPF_DW) {
|
||||
pr_err_once("bpf-jit: 1- and 2-byte RMW atomics are not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
is64 = BPF_SIZE(code) == BPF_DW;
|
||||
|
||||
if (off) {
|
||||
if (is_12b_int(off)) {
|
||||
@@ -554,9 +755,14 @@ static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
|
||||
rv_sc_w(RV_REG_T3, rs, rd, 0, 1), ctx);
|
||||
jmp_offset = ninsns_rvoff(-6);
|
||||
emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx);
|
||||
emit(rv_fence(0x3, 0x3), ctx);
|
||||
emit_fence_rw_rw(ctx);
|
||||
break;
|
||||
default:
|
||||
pr_err_once("bpf-jit: invalid atomic RMW opcode %02x\n", imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0)
|
||||
@@ -1650,8 +1856,8 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_W:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW:
|
||||
{
|
||||
int insn_len, insns_start;
|
||||
bool sign_ext;
|
||||
int insn_len;
|
||||
|
||||
sign_ext = BPF_MODE(insn->code) == BPF_MEMSX ||
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX;
|
||||
@@ -1663,78 +1869,16 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
|
||||
switch (BPF_SIZE(code)) {
|
||||
case BPF_B:
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lb(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lbu(rd, off, rs), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lb(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lbu(rd, 0, RV_REG_T1), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
insn_len = emit_load_8(sign_ext, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_H:
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lh(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lhu(rd, off, rs), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lh(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lhu(rd, 0, RV_REG_T1), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
insn_len = emit_load_16(sign_ext, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_W:
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lw(rd, off, rs), ctx);
|
||||
else
|
||||
emit(rv_lwu(rd, off, rs), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
if (sign_ext)
|
||||
emit(rv_lw(rd, 0, RV_REG_T1), ctx);
|
||||
else
|
||||
emit(rv_lwu(rd, 0, RV_REG_T1), ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
insn_len = emit_load_32(sign_ext, rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_DW:
|
||||
if (is_12b_int(off)) {
|
||||
insns_start = ctx->ninsns;
|
||||
emit_ld(rd, off, rs, ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rs, ctx);
|
||||
insns_start = ctx->ninsns;
|
||||
emit_ld(rd, 0, RV_REG_T1, ctx);
|
||||
insn_len = ctx->ninsns - insns_start;
|
||||
insn_len = emit_load_64(sign_ext, rd, off, rs, ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1879,49 +2023,27 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||
|
||||
/* STX: *(size *)(dst + off) = src */
|
||||
case BPF_STX | BPF_MEM | BPF_B:
|
||||
if (is_12b_int(off)) {
|
||||
emit(rv_sb(rd, off, rs), ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit(rv_sb(RV_REG_T1, 0, rs), ctx);
|
||||
emit_store_8(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_STX | BPF_MEM | BPF_H:
|
||||
if (is_12b_int(off)) {
|
||||
emit(rv_sh(rd, off, rs), ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit(rv_sh(RV_REG_T1, 0, rs), ctx);
|
||||
emit_store_16(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_STX | BPF_MEM | BPF_W:
|
||||
if (is_12b_int(off)) {
|
||||
emit_sw(rd, off, rs, ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit_sw(RV_REG_T1, 0, rs, ctx);
|
||||
emit_store_32(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_STX | BPF_MEM | BPF_DW:
|
||||
if (is_12b_int(off)) {
|
||||
emit_sd(rd, off, rs, ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
emit_sd(RV_REG_T1, 0, rs, ctx);
|
||||
emit_store_64(rd, off, rs, ctx);
|
||||
break;
|
||||
case BPF_STX | BPF_ATOMIC | BPF_B:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_H:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
emit_atomic(rd, rs, off, imm,
|
||||
BPF_SIZE(code) == BPF_DW, ctx);
|
||||
if (bpf_atomic_is_load_store(insn))
|
||||
ret = emit_atomic_ld_st(rd, rs, insn, ctx);
|
||||
else
|
||||
ret = emit_atomic_rmw(rd, rs, insn, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case BPF_STX | BPF_PROBE_MEM32 | BPF_B:
|
||||
|
||||
@@ -26,9 +26,8 @@ static int build_body(struct rv_jit_context *ctx, bool extra_pass, int *offset)
|
||||
int ret;
|
||||
|
||||
ret = bpf_jit_emit_insn(insn, ctx, extra_pass);
|
||||
/* BPF_LD | BPF_IMM | BPF_DW: skip the next instruction. */
|
||||
if (ret > 0)
|
||||
i++;
|
||||
i++; /* skip the next instruction */
|
||||
if (offset)
|
||||
offset[i] = ctx->ninsns;
|
||||
if (ret < 0)
|
||||
|
||||
@@ -26,8 +26,6 @@ static inline bool nospec_uses_trampoline(void)
|
||||
return __is_defined(CC_USING_EXPOLINE) && !nospec_disable;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXPOLINE_EXTERN
|
||||
|
||||
void __s390_indirect_jump_r1(void);
|
||||
void __s390_indirect_jump_r2(void);
|
||||
void __s390_indirect_jump_r3(void);
|
||||
@@ -44,8 +42,6 @@ void __s390_indirect_jump_r13(void);
|
||||
void __s390_indirect_jump_r14(void);
|
||||
void __s390_indirect_jump_r15(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ASM_S390_EXPOLINE_H */
|
||||
|
||||
@@ -48,8 +48,6 @@ struct bpf_jit {
|
||||
int lit64; /* Current position in 64-bit literal pool */
|
||||
int base_ip; /* Base address for literal pool */
|
||||
int exit_ip; /* Address of exit */
|
||||
int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */
|
||||
int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */
|
||||
int tail_call_start; /* Tail call start offset */
|
||||
int excnt; /* Number of exception table entries */
|
||||
int prologue_plt_ret; /* Return address for prologue hotpatch PLT */
|
||||
@@ -127,6 +125,18 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
||||
jit->seen_regs |= (1 << r1);
|
||||
}
|
||||
|
||||
static s32 off_to_pcrel(struct bpf_jit *jit, u32 off)
|
||||
{
|
||||
return off - jit->prg;
|
||||
}
|
||||
|
||||
static s64 ptr_to_pcrel(struct bpf_jit *jit, const void *ptr)
|
||||
{
|
||||
if (jit->prg_buf)
|
||||
return (const u8 *)ptr - ((const u8 *)jit->prg_buf + jit->prg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define REG_SET_SEEN(b1) \
|
||||
({ \
|
||||
reg_set_seen(jit, b1); \
|
||||
@@ -201,7 +211,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
||||
|
||||
#define EMIT4_PCREL_RIC(op, mask, target) \
|
||||
({ \
|
||||
int __rel = ((target) - jit->prg) / 2; \
|
||||
int __rel = off_to_pcrel(jit, target) / 2; \
|
||||
_EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \
|
||||
})
|
||||
|
||||
@@ -239,7 +249,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
||||
|
||||
#define EMIT6_PCREL_RIEB(op1, op2, b1, b2, mask, target) \
|
||||
({ \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
unsigned int rel = off_to_pcrel(jit, target) / 2; \
|
||||
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \
|
||||
(op2) | (mask) << 12); \
|
||||
REG_SET_SEEN(b1); \
|
||||
@@ -248,7 +258,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
||||
|
||||
#define EMIT6_PCREL_RIEC(op1, op2, b1, imm, mask, target) \
|
||||
({ \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
unsigned int rel = off_to_pcrel(jit, target) / 2; \
|
||||
_EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \
|
||||
(rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \
|
||||
REG_SET_SEEN(b1); \
|
||||
@@ -257,29 +267,41 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)
|
||||
|
||||
#define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \
|
||||
({ \
|
||||
int rel = (addrs[(i) + (off) + 1] - jit->prg) / 2; \
|
||||
int rel = off_to_pcrel(jit, addrs[(i) + (off) + 1]) / 2;\
|
||||
_EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\
|
||||
REG_SET_SEEN(b1); \
|
||||
REG_SET_SEEN(b2); \
|
||||
})
|
||||
|
||||
#define EMIT6_PCREL_RILB(op, b, target) \
|
||||
({ \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
_EMIT6((op) | reg_high(b) << 16 | rel >> 16, rel & 0xffff);\
|
||||
REG_SET_SEEN(b); \
|
||||
})
|
||||
static void emit6_pcrel_ril(struct bpf_jit *jit, u32 op, s64 pcrel)
|
||||
{
|
||||
u32 pc32dbl = (s32)(pcrel / 2);
|
||||
|
||||
#define EMIT6_PCREL_RIL(op, target) \
|
||||
({ \
|
||||
unsigned int rel = (int)((target) - jit->prg) / 2; \
|
||||
_EMIT6((op) | rel >> 16, rel & 0xffff); \
|
||||
})
|
||||
_EMIT6(op | pc32dbl >> 16, pc32dbl & 0xffff);
|
||||
}
|
||||
|
||||
static void emit6_pcrel_rilb(struct bpf_jit *jit, u32 op, u8 b, s64 pcrel)
|
||||
{
|
||||
emit6_pcrel_ril(jit, op | reg_high(b) << 16, pcrel);
|
||||
REG_SET_SEEN(b);
|
||||
}
|
||||
|
||||
#define EMIT6_PCREL_RILB(op, b, target) \
|
||||
emit6_pcrel_rilb(jit, op, b, off_to_pcrel(jit, target))
|
||||
|
||||
#define EMIT6_PCREL_RILB_PTR(op, b, target_ptr) \
|
||||
emit6_pcrel_rilb(jit, op, b, ptr_to_pcrel(jit, target_ptr))
|
||||
|
||||
static void emit6_pcrel_rilc(struct bpf_jit *jit, u32 op, u8 mask, s64 pcrel)
|
||||
{
|
||||
emit6_pcrel_ril(jit, op | mask << 20, pcrel);
|
||||
}
|
||||
|
||||
#define EMIT6_PCREL_RILC(op, mask, target) \
|
||||
({ \
|
||||
EMIT6_PCREL_RIL((op) | (mask) << 20, (target)); \
|
||||
})
|
||||
emit6_pcrel_rilc(jit, op, mask, off_to_pcrel(jit, target))
|
||||
|
||||
#define EMIT6_PCREL_RILC_PTR(op, mask, target_ptr) \
|
||||
emit6_pcrel_rilc(jit, op, mask, ptr_to_pcrel(jit, target_ptr))
|
||||
|
||||
#define _EMIT6_IMM(op, imm) \
|
||||
({ \
|
||||
@@ -503,7 +525,7 @@ static void bpf_skip(struct bpf_jit *jit, int size)
|
||||
{
|
||||
if (size >= 6 && !is_valid_rel(size)) {
|
||||
/* brcl 0xf,size */
|
||||
EMIT6_PCREL_RIL(0xc0f4000000, size);
|
||||
EMIT6_PCREL_RILC(0xc0040000, 0xf, size);
|
||||
size -= 6;
|
||||
} else if (size >= 4 && is_valid_rel(size)) {
|
||||
/* brc 0xf,size */
|
||||
@@ -605,43 +627,30 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
}
|
||||
/* Setup stack and backchain */
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
|
||||
/* lgr %w1,%r15 (backchain) */
|
||||
EMIT4(0xb9040000, REG_W1, REG_15);
|
||||
/* lgr %w1,%r15 (backchain) */
|
||||
EMIT4(0xb9040000, REG_W1, REG_15);
|
||||
/* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
|
||||
EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
|
||||
/* aghi %r15,-STK_OFF */
|
||||
EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth));
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
|
||||
/* stg %w1,152(%r15) (backchain) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
|
||||
REG_15, 152);
|
||||
/* stg %w1,152(%r15) (backchain) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
|
||||
REG_15, 152);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit an expoline for a jump that follows
|
||||
* Jump using a register either directly or via an expoline thunk
|
||||
*/
|
||||
static void emit_expoline(struct bpf_jit *jit)
|
||||
{
|
||||
/* exrl %r0,.+10 */
|
||||
EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10);
|
||||
/* j . */
|
||||
EMIT4_PCREL(0xa7f40000, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit __s390_indirect_jump_r1 thunk if necessary
|
||||
*/
|
||||
static void emit_r1_thunk(struct bpf_jit *jit)
|
||||
{
|
||||
if (nospec_uses_trampoline()) {
|
||||
jit->r1_thunk_ip = jit->prg;
|
||||
emit_expoline(jit);
|
||||
/* br %r1 */
|
||||
_EMIT2(0x07f1);
|
||||
}
|
||||
}
|
||||
#define EMIT_JUMP_REG(reg) do { \
|
||||
if (nospec_uses_trampoline()) \
|
||||
/* brcl 0xf,__s390_indirect_jump_rN */ \
|
||||
EMIT6_PCREL_RILC_PTR(0xc0040000, 0x0f, \
|
||||
__s390_indirect_jump_r ## reg); \
|
||||
else \
|
||||
/* br %rN */ \
|
||||
_EMIT2(0x07f0 | reg); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Call r1 either directly or via __s390_indirect_jump_r1 thunk
|
||||
@@ -650,7 +659,8 @@ static void call_r1(struct bpf_jit *jit)
|
||||
{
|
||||
if (nospec_uses_trampoline())
|
||||
/* brasl %r14,__s390_indirect_jump_r1 */
|
||||
EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip);
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14,
|
||||
__s390_indirect_jump_r1);
|
||||
else
|
||||
/* basr %r14,%r1 */
|
||||
EMIT2(0x0d00, REG_14, REG_1);
|
||||
@@ -666,16 +676,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)
|
||||
EMIT4(0xb9040000, REG_2, BPF_REG_0);
|
||||
/* Restore registers */
|
||||
save_restore_regs(jit, REGS_RESTORE, stack_depth, 0);
|
||||
if (nospec_uses_trampoline()) {
|
||||
jit->r14_thunk_ip = jit->prg;
|
||||
/* Generate __s390_indirect_jump_r14 thunk */
|
||||
emit_expoline(jit);
|
||||
}
|
||||
/* br %r14 */
|
||||
_EMIT2(0x07fe);
|
||||
|
||||
if (is_first_pass(jit) || (jit->seen & SEEN_FUNC))
|
||||
emit_r1_thunk(jit);
|
||||
EMIT_JUMP_REG(14);
|
||||
|
||||
jit->prg = ALIGN(jit->prg, 8);
|
||||
jit->prologue_plt = jit->prg;
|
||||
@@ -1877,7 +1878,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
/* aghi %r1,tail_call_start */
|
||||
EMIT4_IMM(0xa70b0000, REG_1, jit->tail_call_start);
|
||||
/* brcl 0xf,__s390_indirect_jump_r1 */
|
||||
EMIT6_PCREL_RILC(0xc0040000, 0xf, jit->r1_thunk_ip);
|
||||
EMIT6_PCREL_RILC_PTR(0xc0040000, 0xf,
|
||||
__s390_indirect_jump_r1);
|
||||
} else {
|
||||
/* bc 0xf,tail_call_start(%r1) */
|
||||
_EMIT4(0x47f01000 + jit->tail_call_start);
|
||||
@@ -2585,9 +2587,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
if (nr_stack_args > MAX_NR_STACK_ARGS)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Return to %r14, since func_addr and %r0 are not available. */
|
||||
if ((!func_addr && !(flags & BPF_TRAMP_F_ORIG_STACK)) ||
|
||||
(flags & BPF_TRAMP_F_INDIRECT))
|
||||
/* Return to %r14 in the struct_ops case. */
|
||||
if (flags & BPF_TRAMP_F_INDIRECT)
|
||||
flags |= BPF_TRAMP_F_SKIP_FRAME;
|
||||
|
||||
/*
|
||||
@@ -2847,17 +2848,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
0xf000 | tjit->tccnt_off);
|
||||
/* aghi %r15,stack_size */
|
||||
EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size);
|
||||
/* Emit an expoline for the following indirect jump. */
|
||||
if (nospec_uses_trampoline())
|
||||
emit_expoline(jit);
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
/* br %r14 */
|
||||
_EMIT2(0x07fe);
|
||||
EMIT_JUMP_REG(14);
|
||||
else
|
||||
/* br %r1 */
|
||||
_EMIT2(0x07f1);
|
||||
|
||||
emit_r1_thunk(jit);
|
||||
EMIT_JUMP_REG(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+80
-22
@@ -19,7 +19,9 @@
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sync_file.h>
|
||||
#include <linux/poll.h>
|
||||
@@ -35,35 +37,91 @@
|
||||
|
||||
static inline int is_dma_buf_file(struct file *);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
static DEFINE_MUTEX(debugfs_list_mutex);
|
||||
static LIST_HEAD(debugfs_list);
|
||||
static DEFINE_MUTEX(dmabuf_list_mutex);
|
||||
static LIST_HEAD(dmabuf_list);
|
||||
|
||||
static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
|
||||
static void __dma_buf_list_add(struct dma_buf *dmabuf)
|
||||
{
|
||||
mutex_lock(&debugfs_list_mutex);
|
||||
list_add(&dmabuf->list_node, &debugfs_list);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
mutex_lock(&dmabuf_list_mutex);
|
||||
list_add(&dmabuf->list_node, &dmabuf_list);
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
}
|
||||
|
||||
static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf)
|
||||
static void __dma_buf_list_del(struct dma_buf *dmabuf)
|
||||
{
|
||||
if (!dmabuf)
|
||||
return;
|
||||
|
||||
mutex_lock(&debugfs_list_mutex);
|
||||
mutex_lock(&dmabuf_list_mutex);
|
||||
list_del(&dmabuf->list_node);
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
}
|
||||
#else
|
||||
static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf)
|
||||
{
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
}
|
||||
|
||||
static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf)
|
||||
/**
|
||||
* dma_buf_iter_begin - begin iteration through global list of all DMA buffers
|
||||
*
|
||||
* Returns the first buffer in the global list of DMA-bufs that's not in the
|
||||
* process of being destroyed. Increments that buffer's reference count to
|
||||
* prevent buffer destruction. Callers must release the reference, either by
|
||||
* continuing iteration with dma_buf_iter_next(), or with dma_buf_put().
|
||||
*
|
||||
* Return:
|
||||
* * First buffer from global list, with refcount elevated
|
||||
* * NULL if no active buffers are present
|
||||
*/
|
||||
struct dma_buf *dma_buf_iter_begin(void)
|
||||
{
|
||||
struct dma_buf *ret = NULL, *dmabuf;
|
||||
|
||||
/*
|
||||
* The list mutex does not protect a dmabuf's refcount, so it can be
|
||||
* zeroed while we are iterating. We cannot call get_dma_buf() since the
|
||||
* caller may not already own a reference to the buffer.
|
||||
*/
|
||||
mutex_lock(&dmabuf_list_mutex);
|
||||
list_for_each_entry(dmabuf, &dmabuf_list, list_node) {
|
||||
if (file_ref_get(&dmabuf->file->f_ref)) {
|
||||
ret = dmabuf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_buf_iter_next - continue iteration through global list of all DMA buffers
|
||||
* @dmabuf: [in] pointer to dma_buf
|
||||
*
|
||||
* Decrements the reference count on the provided buffer. Returns the next
|
||||
* buffer from the remainder of the global list of DMA-bufs with its reference
|
||||
* count incremented. Callers must release the reference, either by continuing
|
||||
* iteration with dma_buf_iter_next(), or with dma_buf_put().
|
||||
*
|
||||
* Return:
|
||||
* * Next buffer from global list, with refcount elevated
|
||||
* * NULL if no additional active buffers are present
|
||||
*/
|
||||
struct dma_buf *dma_buf_iter_next(struct dma_buf *dmabuf)
|
||||
{
|
||||
struct dma_buf *ret = NULL;
|
||||
|
||||
/*
|
||||
* The list mutex does not protect a dmabuf's refcount, so it can be
|
||||
* zeroed while we are iterating. We cannot call get_dma_buf() since the
|
||||
* caller may not already own a reference to the buffer.
|
||||
*/
|
||||
mutex_lock(&dmabuf_list_mutex);
|
||||
dma_buf_put(dmabuf);
|
||||
list_for_each_entry_continue(dmabuf, &dmabuf_list, list_node) {
|
||||
if (file_ref_get(&dmabuf->file->f_ref)) {
|
||||
ret = dmabuf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
@@ -115,7 +173,7 @@ static int dma_buf_file_release(struct inode *inode, struct file *file)
|
||||
if (!is_dma_buf_file(file))
|
||||
return -EINVAL;
|
||||
|
||||
__dma_buf_debugfs_list_del(file->private_data);
|
||||
__dma_buf_list_del(file->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -685,7 +743,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
file->f_path.dentry->d_fsdata = dmabuf;
|
||||
dmabuf->file = file;
|
||||
|
||||
__dma_buf_debugfs_list_add(dmabuf);
|
||||
__dma_buf_list_add(dmabuf);
|
||||
|
||||
return dmabuf;
|
||||
|
||||
@@ -1563,7 +1621,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
size_t size = 0;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&debugfs_list_mutex);
|
||||
ret = mutex_lock_interruptible(&dmabuf_list_mutex);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1572,7 +1630,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n",
|
||||
"size", "flags", "mode", "count", "ino");
|
||||
|
||||
list_for_each_entry(buf_obj, &debugfs_list, list_node) {
|
||||
list_for_each_entry(buf_obj, &dmabuf_list, list_node) {
|
||||
|
||||
ret = dma_resv_lock_interruptible(buf_obj->resv, NULL);
|
||||
if (ret)
|
||||
@@ -1609,11 +1667,11 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
|
||||
|
||||
seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size);
|
||||
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
return 0;
|
||||
|
||||
error_unlock:
|
||||
mutex_unlock(&debugfs_list_mutex);
|
||||
mutex_unlock(&dmabuf_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -667,10 +667,11 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)
|
||||
*/
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF
|
||||
#define BTF \
|
||||
. = ALIGN(PAGE_SIZE); \
|
||||
.BTF : AT(ADDR(.BTF) - LOAD_OFFSET) { \
|
||||
BOUNDED_SECTION_BY(.BTF, _BTF) \
|
||||
} \
|
||||
. = ALIGN(4); \
|
||||
. = ALIGN(PAGE_SIZE); \
|
||||
.BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) { \
|
||||
*(.BTF_ids) \
|
||||
}
|
||||
|
||||
@@ -426,8 +426,6 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
|
||||
|
||||
const struct bpf_func_proto *
|
||||
cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||
const struct bpf_func_proto *
|
||||
cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||
#else
|
||||
|
||||
static inline void cgroup_bpf_lifetime_notifier_init(void)
|
||||
@@ -466,12 +464,6 @@ cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline const struct bpf_func_proto *
|
||||
cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux,
|
||||
struct bpf_map *map) { return 0; }
|
||||
static inline struct bpf_cgroup_storage *bpf_cgroup_storage_alloc(
|
||||
|
||||
@@ -346,6 +346,12 @@ static inline const char *btf_field_type_name(enum btf_field_type type)
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_KERNEL)
|
||||
#define BPF_WARN_ONCE(cond, format...) WARN_ONCE(cond, format)
|
||||
#else
|
||||
#define BPF_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond)
|
||||
#endif
|
||||
|
||||
static inline u32 btf_field_type_size(enum btf_field_type type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -1349,6 +1355,20 @@ u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
|
||||
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len);
|
||||
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
|
||||
bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr);
|
||||
int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset,
|
||||
void *src, u32 len, u64 flags);
|
||||
void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u32 offset,
|
||||
void *buffer__opt, u32 buffer__szk);
|
||||
|
||||
static inline int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len)
|
||||
{
|
||||
u32 size = __bpf_dynptr_size(ptr);
|
||||
|
||||
if (len > size || offset > size - len)
|
||||
return -E2BIG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_JIT
|
||||
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
|
||||
|
||||
@@ -356,7 +356,11 @@ enum {
|
||||
INSN_F_SPI_MASK = 0x3f, /* 6 bits */
|
||||
INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */
|
||||
|
||||
INSN_F_STACK_ACCESS = BIT(9), /* we need 10 bits total */
|
||||
INSN_F_STACK_ACCESS = BIT(9),
|
||||
|
||||
INSN_F_DST_REG_STACK = BIT(10), /* dst_reg is PTR_TO_STACK */
|
||||
INSN_F_SRC_REG_STACK = BIT(11), /* src_reg is PTR_TO_STACK */
|
||||
/* total 12 bits are used now. */
|
||||
};
|
||||
|
||||
static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES);
|
||||
@@ -365,9 +369,9 @@ static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8);
|
||||
struct bpf_insn_hist_entry {
|
||||
u32 idx;
|
||||
/* insn idx can't be bigger than 1 million */
|
||||
u32 prev_idx : 22;
|
||||
/* special flags, e.g., whether insn is doing register stack spill/load */
|
||||
u32 flags : 10;
|
||||
u32 prev_idx : 20;
|
||||
/* special INSN_F_xxx flags */
|
||||
u32 flags : 12;
|
||||
/* additional registers that need precision tracking when this
|
||||
* jump is backtracked, vector of six 10-bit records
|
||||
*/
|
||||
@@ -591,6 +595,7 @@ struct bpf_insn_aux_data {
|
||||
* bpf_fastcall pattern.
|
||||
*/
|
||||
u8 fastcall_spills_num:3;
|
||||
u8 arg_prog:4;
|
||||
|
||||
/* below fields are initialized once */
|
||||
unsigned int orig_idx; /* original instruction index */
|
||||
@@ -838,6 +843,17 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
|
||||
u32 insn_off,
|
||||
const char *prefix_fmt, ...);
|
||||
|
||||
#define verifier_bug_if(cond, env, fmt, args...) \
|
||||
({ \
|
||||
bool __cond = (cond); \
|
||||
if (unlikely(__cond)) { \
|
||||
BPF_WARN_ONCE(1, "verifier bug: " fmt "(" #cond ")\n", ##args); \
|
||||
bpf_log(&env->log, "verifier bug: " fmt "(" #cond ")\n", ##args); \
|
||||
} \
|
||||
(__cond); \
|
||||
})
|
||||
#define verifier_bug(env, fmt, args...) verifier_bug_if(1, env, fmt, ##args)
|
||||
|
||||
static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_verifier_state *cur = env->cur_state;
|
||||
|
||||
@@ -361,10 +361,8 @@ struct dma_buf {
|
||||
*/
|
||||
struct module *owner;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
/** @list_node: node for dma_buf accounting and debugging. */
|
||||
struct list_head list_node;
|
||||
#endif
|
||||
|
||||
/** @priv: exporter specific private data for this buffer object. */
|
||||
void *priv;
|
||||
@@ -609,4 +607,6 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map);
|
||||
void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map);
|
||||
int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map);
|
||||
void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map);
|
||||
struct dma_buf *dma_buf_iter_begin(void);
|
||||
struct dma_buf *dma_buf_iter_next(struct dma_buf *dmbuf);
|
||||
#endif /* __DMA_BUF_H__ */
|
||||
|
||||
@@ -1506,7 +1506,7 @@ union bpf_attr {
|
||||
__s32 map_token_fd;
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */
|
||||
__u32 map_fd;
|
||||
__aligned_u64 key;
|
||||
union {
|
||||
@@ -1995,11 +1995,15 @@ union bpf_attr {
|
||||
* long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags)
|
||||
* Description
|
||||
* Store *len* bytes from address *from* into the packet
|
||||
* associated to *skb*, at *offset*. *flags* are a combination of
|
||||
* **BPF_F_RECOMPUTE_CSUM** (automatically recompute the
|
||||
* checksum for the packet after storing the bytes) and
|
||||
* **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\
|
||||
* **->swhash** and *skb*\ **->l4hash** to 0).
|
||||
* associated to *skb*, at *offset*. The *flags* are a combination
|
||||
* of the following values:
|
||||
*
|
||||
* **BPF_F_RECOMPUTE_CSUM**
|
||||
* Automatically update *skb*\ **->csum** after storing the
|
||||
* bytes.
|
||||
* **BPF_F_INVALIDATE_HASH**
|
||||
* Set *skb*\ **->hash**, *skb*\ **->swhash** and *skb*\
|
||||
* **->l4hash** to 0.
|
||||
*
|
||||
* A call to this helper is susceptible to change the underlying
|
||||
* packet buffer. Therefore, at load time, all checks on pointers
|
||||
@@ -2051,7 +2055,7 @@ union bpf_attr {
|
||||
* untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
|
||||
* for updates resulting in a null checksum the value is set to
|
||||
* **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
|
||||
* the checksum is to be computed against a pseudo-header.
|
||||
* that the modified header field is part of the pseudo-header.
|
||||
*
|
||||
* This helper works in combination with **bpf_csum_diff**\ (),
|
||||
* which does not update the checksum in-place, but offers more
|
||||
@@ -6723,6 +6727,7 @@ struct bpf_link_info {
|
||||
__u32 name_len;
|
||||
__u32 offset; /* offset from file_name */
|
||||
__u64 cookie;
|
||||
__u64 ref_ctr_offset;
|
||||
} uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */
|
||||
struct {
|
||||
__aligned_u64 func_name; /* in/out */
|
||||
|
||||
@@ -53,6 +53,9 @@ obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += btf_iter.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
|
||||
ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
|
||||
endif
|
||||
|
||||
CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
|
||||
|
||||
@@ -601,7 +601,7 @@ int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
|
||||
if (model->ret_size > 0)
|
||||
flags |= BPF_TRAMP_F_RET_FENTRY_RET;
|
||||
|
||||
size = arch_bpf_trampoline_size(model, flags, tlinks, NULL);
|
||||
size = arch_bpf_trampoline_size(model, flags, tlinks, stub_func);
|
||||
if (size <= 0)
|
||||
return size ? : -EFAULT;
|
||||
|
||||
|
||||
+20
-25
@@ -26,6 +26,7 @@
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include <net/netfilter/nf_bpf_link.h>
|
||||
|
||||
@@ -3957,7 +3958,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
|
||||
/* This needs to be kzalloc to zero out padding and unused fields, see
|
||||
* comment in btf_record_equal.
|
||||
*/
|
||||
rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN);
|
||||
rec = kzalloc(struct_size(rec, fields, cnt), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!rec)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -5583,7 +5584,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
|
||||
if (id < 0)
|
||||
continue;
|
||||
|
||||
new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]),
|
||||
new_aof = krealloc(aof, struct_size(new_aof, ids, aof->cnt + 1),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!new_aof) {
|
||||
ret = -ENOMEM;
|
||||
@@ -5610,7 +5611,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
|
||||
if (ret != BTF_FIELD_FOUND)
|
||||
continue;
|
||||
|
||||
new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]),
|
||||
new_aof = krealloc(aof, struct_size(new_aof, ids, aof->cnt + 1),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!new_aof) {
|
||||
ret = -ENOMEM;
|
||||
@@ -5647,7 +5648,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf)
|
||||
continue;
|
||||
parse:
|
||||
tab_cnt = tab ? tab->cnt : 0;
|
||||
new_tab = krealloc(tab, offsetof(struct btf_struct_metas, types[tab_cnt + 1]),
|
||||
new_tab = krealloc(tab, struct_size(new_tab, types, tab_cnt + 1),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!new_tab) {
|
||||
ret = -ENOMEM;
|
||||
@@ -6383,12 +6384,11 @@ struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog)
|
||||
return prog->aux->attach_btf;
|
||||
}
|
||||
|
||||
static bool is_int_ptr(struct btf *btf, const struct btf_type *t)
|
||||
static bool is_void_or_int_ptr(struct btf *btf, const struct btf_type *t)
|
||||
{
|
||||
/* skip modifiers */
|
||||
t = btf_type_skip_modifiers(btf, t->type, NULL);
|
||||
|
||||
return btf_type_is_int(t);
|
||||
return btf_type_is_void(t) || btf_type_is_int(t);
|
||||
}
|
||||
|
||||
u32 btf_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto,
|
||||
@@ -6777,14 +6777,11 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
}
|
||||
}
|
||||
|
||||
if (t->type == 0)
|
||||
/* This is a pointer to void.
|
||||
* It is the same as scalar from the verifier safety pov.
|
||||
* No further pointer walking is allowed.
|
||||
*/
|
||||
return true;
|
||||
|
||||
if (is_int_ptr(btf, t))
|
||||
/*
|
||||
* If it's a pointer to void, it's the same as scalar from the verifier
|
||||
* safety POV. Either way, no futher pointer walking is allowed.
|
||||
*/
|
||||
if (is_void_or_int_ptr(btf, t))
|
||||
return true;
|
||||
|
||||
/* this is a pointer to another type */
|
||||
@@ -6830,10 +6827,10 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
|
||||
/* Is this a func with potential NULL args? */
|
||||
if (strcmp(tname, raw_tp_null_args[i].func))
|
||||
continue;
|
||||
if (raw_tp_null_args[i].mask & (0x1 << (arg * 4)))
|
||||
if (raw_tp_null_args[i].mask & (0x1ULL << (arg * 4)))
|
||||
info->reg_type |= PTR_MAYBE_NULL;
|
||||
/* Is the current arg IS_ERR? */
|
||||
if (raw_tp_null_args[i].mask & (0x2 << (arg * 4)))
|
||||
if (raw_tp_null_args[i].mask & (0x2ULL << (arg * 4)))
|
||||
ptr_err_raw_tp = true;
|
||||
break;
|
||||
}
|
||||
@@ -7663,7 +7660,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
|
||||
return 0;
|
||||
|
||||
if (!prog->aux->func_info) {
|
||||
bpf_log(log, "Verifier bug\n");
|
||||
verifier_bug(env, "func_info undefined");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@@ -7687,7 +7684,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
|
||||
tname = btf_name_by_offset(btf, fn_t->name_off);
|
||||
|
||||
if (prog->aux->func_info_aux[subprog].unreliable) {
|
||||
bpf_log(log, "Verifier bug in function %s()\n", tname);
|
||||
verifier_bug(env, "unreliable BTF for function %s()", tname);
|
||||
return -EFAULT;
|
||||
}
|
||||
if (prog_type == BPF_PROG_TYPE_EXT)
|
||||
@@ -8564,7 +8561,7 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook,
|
||||
|
||||
/* Grow set */
|
||||
set = krealloc(tab->sets[hook],
|
||||
offsetof(struct btf_id_set8, pairs[set_cnt + add_set->cnt]),
|
||||
struct_size(set, pairs, set_cnt + add_set->cnt),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!set) {
|
||||
ret = -ENOMEM;
|
||||
@@ -8850,7 +8847,7 @@ int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_c
|
||||
}
|
||||
|
||||
tab = krealloc(btf->dtor_kfunc_tab,
|
||||
offsetof(struct btf_id_dtor_kfunc_tab, dtors[tab_cnt + add_cnt]),
|
||||
struct_size(tab, dtors, tab_cnt + add_cnt),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!tab) {
|
||||
ret = -ENOMEM;
|
||||
@@ -9408,8 +9405,7 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops,
|
||||
|
||||
tab = btf->struct_ops_tab;
|
||||
if (!tab) {
|
||||
tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]),
|
||||
GFP_KERNEL);
|
||||
tab = kzalloc(struct_size(tab, ops, 4), GFP_KERNEL);
|
||||
if (!tab)
|
||||
return -ENOMEM;
|
||||
tab->capacity = 4;
|
||||
@@ -9422,8 +9418,7 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops,
|
||||
|
||||
if (tab->cnt == tab->capacity) {
|
||||
new_tab = krealloc(tab,
|
||||
offsetof(struct btf_struct_ops_tab,
|
||||
ops[tab->capacity * 2]),
|
||||
struct_size(tab, ops, tab->capacity * 2),
|
||||
GFP_KERNEL);
|
||||
if (!new_tab)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1687,10 +1687,6 @@ cgroup_dev_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
func_proto = cgroup_current_func_proto(func_id, prog);
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_perf_event_output:
|
||||
return &bpf_event_output_data_proto;
|
||||
@@ -2238,10 +2234,6 @@ sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
func_proto = cgroup_current_func_proto(func_id, prog);
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_sysctl_get_name:
|
||||
return &bpf_sysctl_get_name_proto;
|
||||
@@ -2385,10 +2377,6 @@ cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
func_proto = cgroup_current_func_proto(func_id, prog);
|
||||
if (func_proto)
|
||||
return func_proto;
|
||||
|
||||
switch (func_id) {
|
||||
#ifdef CONFIG_NET
|
||||
case BPF_FUNC_get_netns_cookie:
|
||||
@@ -2635,23 +2623,3 @@ cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Common helpers for cgroup hooks with valid process context. */
|
||||
const struct bpf_func_proto *
|
||||
cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_get_current_uid_gid:
|
||||
return &bpf_get_current_uid_gid_proto;
|
||||
case BPF_FUNC_get_current_comm:
|
||||
return &bpf_get_current_comm_proto;
|
||||
#ifdef CONFIG_CGROUP_NET_CLASSID
|
||||
case BPF_FUNC_get_cgroup_classid:
|
||||
return &bpf_get_cgroup_classid_curr_proto;
|
||||
#endif
|
||||
case BPF_FUNC_current_task_under_cgroup:
|
||||
return &bpf_current_task_under_cgroup_proto;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-12
@@ -2358,8 +2358,8 @@ static unsigned int __bpf_prog_ret0_warn(const void *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bpf_prog_map_compatible(struct bpf_map *map,
|
||||
const struct bpf_prog *fp)
|
||||
static bool __bpf_prog_map_compatible(struct bpf_map *map,
|
||||
const struct bpf_prog *fp)
|
||||
{
|
||||
enum bpf_prog_type prog_type = resolve_prog_type(fp);
|
||||
bool ret;
|
||||
@@ -2368,14 +2368,6 @@ bool bpf_prog_map_compatible(struct bpf_map *map,
|
||||
if (fp->kprobe_override)
|
||||
return false;
|
||||
|
||||
/* XDP programs inserted into maps are not guaranteed to run on
|
||||
* a particular netdev (and can run outside driver context entirely
|
||||
* in the case of devmap and cpumap). Until device checks
|
||||
* are implemented, prohibit adding dev-bound programs to program maps.
|
||||
*/
|
||||
if (bpf_prog_is_dev_bound(aux))
|
||||
return false;
|
||||
|
||||
spin_lock(&map->owner.lock);
|
||||
if (!map->owner.type) {
|
||||
/* There's no owner yet where we could check for
|
||||
@@ -2409,6 +2401,19 @@ bool bpf_prog_map_compatible(struct bpf_map *map,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool bpf_prog_map_compatible(struct bpf_map *map, const struct bpf_prog *fp)
|
||||
{
|
||||
/* XDP programs inserted into maps are not guaranteed to run on
|
||||
* a particular netdev (and can run outside driver context entirely
|
||||
* in the case of devmap and cpumap). Until device checks
|
||||
* are implemented, prohibit adding dev-bound programs to program maps.
|
||||
*/
|
||||
if (bpf_prog_is_dev_bound(fp->aux))
|
||||
return false;
|
||||
|
||||
return __bpf_prog_map_compatible(map, fp);
|
||||
}
|
||||
|
||||
static int bpf_check_tail_call(const struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_prog_aux *aux = fp->aux;
|
||||
@@ -2421,7 +2426,7 @@ static int bpf_check_tail_call(const struct bpf_prog *fp)
|
||||
if (!map_type_contains_progs(map))
|
||||
continue;
|
||||
|
||||
if (!bpf_prog_map_compatible(map, fp)) {
|
||||
if (!__bpf_prog_map_compatible(map, fp)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -2469,7 +2474,7 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err)
|
||||
/* In case of BPF to BPF calls, verifier did all the prep
|
||||
* work with regards to JITing, etc.
|
||||
*/
|
||||
bool jit_needed = false;
|
||||
bool jit_needed = fp->jit_requested;
|
||||
|
||||
if (fp->bpf_func)
|
||||
goto finalize;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user