mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Daniel Borkmann says:
====================
pull-request: bpf-next 2022-02-09
We've added 126 non-merge commits during the last 16 day(s) which contain
a total of 201 files changed, 4049 insertions(+), 2215 deletions(-).
The main changes are:
1) Add custom BPF allocator for JITs that pack multiple programs into a huge
page to reduce iTLB pressure, from Song Liu.
2) Add __user tagging support in vmlinux BTF and utilize it from BPF
verifier when generating loads, from Yonghong Song.
3) Add per-socket fast path check guarding from cgroup/BPF overhead when
used by only some sockets, from Pavel Begunkov.
4) Continued libbpf deprecation work of APIs/features and removal of their
usage from samples, selftests, libbpf & bpftool, from Andrii Nakryiko
and various others.
5) Improve BPF instruction set documentation by adding byte swap
instructions and cleaning up load/store section, from Christoph Hellwig.
6) Switch BPF preload infra to light skeleton and remove libbpf dependency
from it, from Alexei Starovoitov.
7) Fix architecture-agnostic macros in libbpf for accessing syscall
arguments from BPF progs for non-x86 architectures,
from Ilya Leoshkevich.
8) Rework port members in struct bpf_sk_lookup and struct bpf_sock to be
of 16-bit field with anonymous zero padding, from Jakub Sitnicki.
9) Add new bpf_copy_from_user_task() helper to read memory from a different
task than current. Add ability to create sleepable BPF iterator progs,
from Kenny Yu.
10) Implement XSK batching for ice's zero-copy driver used by AF_XDP and
utilize TX batching API from XSK buffer pool, from Maciej Fijalkowski.
11) Generate temporary netns names for BPF selftests to avoid naming
collisions, from Hangbin Liu.
12) Implement bpf_core_types_are_compat() with limited recursion for
in-kernel usage, from Matteo Croce.
13) Simplify pahole version detection and finally enable CONFIG_DEBUG_INFO_DWARF5
to be selected with CONFIG_DEBUG_INFO_BTF, from Nathan Chancellor.
14) Misc minor fixes to libbpf and selftests from various folks.
* https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (126 commits)
selftests/bpf: Cover 4-byte load from remote_port in bpf_sk_lookup
bpf: Make remote_port field in struct bpf_sk_lookup 16-bit wide
libbpf: Fix compilation warning due to mismatched printf format
selftests/bpf: Test BPF_KPROBE_SYSCALL macro
libbpf: Add BPF_KPROBE_SYSCALL macro
libbpf: Fix accessing the first syscall argument on s390
libbpf: Fix accessing the first syscall argument on arm64
libbpf: Allow overriding PT_REGS_PARM1{_CORE}_SYSCALL
selftests/bpf: Skip test_bpf_syscall_macro's syscall_arg1 on arm64 and s390
libbpf: Fix accessing syscall arguments on riscv
libbpf: Fix riscv register names
libbpf: Fix accessing syscall arguments on powerpc
selftests/bpf: Use PT_REGS_SYSCALL_REGS in bpf_syscall_macro
libbpf: Add PT_REGS_SYSCALL_REGS macro
selftests/bpf: Fix an endianness issue in bpf_syscall_macro test
bpf: Fix bpf_prog_pack build HPAGE_PMD_SIZE
bpf: Fix leftover header->pages in sparc and powerpc code.
libbpf: Fix signedness bug in btf_dump_array_data()
selftests/bpf: Do not export subtest as standalone test
bpf, x86_64: Fail gracefully on bpf_jit_binary_pack_finalize failures
...
====================
Link: https://lore.kernel.org/r/20220209210050.8425-1-daniel@iogearbox.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -503,6 +503,19 @@ valid index (starting from 0) pointing to a member or an argument.
|
||||
* ``info.vlen``: 0
|
||||
* ``type``: the type with ``btf_type_tag`` attribute
|
||||
|
||||
Currently, ``BTF_KIND_TYPE_TAG`` is only emitted for pointer types.
|
||||
It has the following btf type chain:
|
||||
::
|
||||
|
||||
ptr -> [type_tag]*
|
||||
-> [const | volatile | restrict | typedef]*
|
||||
-> base_type
|
||||
|
||||
Basically, a pointer type points to zero or more
|
||||
type_tag, then zero or more const/volatile/restrict/typedef
|
||||
and finally the base type. The base type is one of
|
||||
int, ptr, array, struct, union, enum, func_proto and float types.
|
||||
|
||||
3. BTF Kernel API
|
||||
=================
|
||||
|
||||
|
||||
@@ -22,7 +22,13 @@ necessary across calls.
|
||||
Instruction encoding
|
||||
====================
|
||||
|
||||
eBPF uses 64-bit instructions with the following encoding:
|
||||
eBPF has two instruction encodings:
|
||||
|
||||
* the basic instruction encoding, which uses 64 bits to encode an instruction
|
||||
* the wide instruction encoding, which appends a second 64-bit immediate value
|
||||
(imm64) after the basic instruction for a total of 128 bits.
|
||||
|
||||
The basic instruction encoding looks as follows:
|
||||
|
||||
============= ======= =============== ==================== ============
|
||||
32 bits (MSB) 16 bits 4 bits 4 bits 8 bits (LSB)
|
||||
@@ -82,9 +88,9 @@ BPF_ALU uses 32-bit wide operands while BPF_ALU64 uses 64-bit wide operands for
|
||||
otherwise identical operations.
|
||||
The code field encodes the operation as below:
|
||||
|
||||
======== ===== ==========================
|
||||
======== ===== =================================================
|
||||
code value description
|
||||
======== ===== ==========================
|
||||
======== ===== =================================================
|
||||
BPF_ADD 0x00 dst += src
|
||||
BPF_SUB 0x10 dst -= src
|
||||
BPF_MUL 0x20 dst \*= src
|
||||
@@ -98,8 +104,8 @@ The code field encodes the operation as below:
|
||||
BPF_XOR 0xa0 dst ^= src
|
||||
BPF_MOV 0xb0 dst = src
|
||||
BPF_ARSH 0xc0 sign extending shift right
|
||||
BPF_END 0xd0 endianness conversion
|
||||
======== ===== ==========================
|
||||
BPF_END 0xd0 byte swap operations (see separate section below)
|
||||
======== ===== =================================================
|
||||
|
||||
BPF_ADD | BPF_X | BPF_ALU means::
|
||||
|
||||
@@ -118,6 +124,42 @@ BPF_XOR | BPF_K | BPF_ALU64 means::
|
||||
src_reg = src_reg ^ imm32
|
||||
|
||||
|
||||
Byte swap instructions
|
||||
----------------------
|
||||
|
||||
The byte swap instructions use an instruction class of ``BFP_ALU`` and a 4-bit
|
||||
code field of ``BPF_END``.
|
||||
|
||||
The byte swap instructions instructions operate on the destination register
|
||||
only and do not use a separate source register or immediate value.
|
||||
|
||||
The 1-bit source operand field in the opcode is used to to select what byte
|
||||
order the operation convert from or to:
|
||||
|
||||
========= ===== =================================================
|
||||
source value description
|
||||
========= ===== =================================================
|
||||
BPF_TO_LE 0x00 convert between host byte order and little endian
|
||||
BPF_TO_BE 0x08 convert between host byte order and big endian
|
||||
========= ===== =================================================
|
||||
|
||||
The imm field encodes the width of the swap operations. The following widths
|
||||
are supported: 16, 32 and 64.
|
||||
|
||||
Examples:
|
||||
|
||||
``BPF_ALU | BPF_TO_LE | BPF_END`` with imm = 16 means::
|
||||
|
||||
dst_reg = htole16(dst_reg)
|
||||
|
||||
``BPF_ALU | BPF_TO_BE | BPF_END`` with imm = 64 means::
|
||||
|
||||
dst_reg = htobe64(dst_reg)
|
||||
|
||||
``BPF_FROM_LE`` and ``BPF_FROM_BE`` exist as aliases for ``BPF_TO_LE`` and
|
||||
``BPF_TO_LE`` respetively.
|
||||
|
||||
|
||||
Jump instructions
|
||||
-----------------
|
||||
|
||||
@@ -176,63 +218,96 @@ The mode modifier is one of:
|
||||
============= ===== ====================================
|
||||
mode modifier value description
|
||||
============= ===== ====================================
|
||||
BPF_IMM 0x00 used for 64-bit mov
|
||||
BPF_ABS 0x20 legacy BPF packet access
|
||||
BPF_IND 0x40 legacy BPF packet access
|
||||
BPF_MEM 0x60 all normal load and store operations
|
||||
BPF_IMM 0x00 64-bit immediate instructions
|
||||
BPF_ABS 0x20 legacy BPF packet access (absolute)
|
||||
BPF_IND 0x40 legacy BPF packet access (indirect)
|
||||
BPF_MEM 0x60 regular load and store operations
|
||||
BPF_ATOMIC 0xc0 atomic operations
|
||||
============= ===== ====================================
|
||||
|
||||
BPF_MEM | <size> | BPF_STX means::
|
||||
|
||||
Regular load and store operations
|
||||
---------------------------------
|
||||
|
||||
The ``BPF_MEM`` mode modifier is used to encode regular load and store
|
||||
instructions that transfer data between a register and memory.
|
||||
|
||||
``BPF_MEM | <size> | BPF_STX`` means::
|
||||
|
||||
*(size *) (dst_reg + off) = src_reg
|
||||
|
||||
BPF_MEM | <size> | BPF_ST means::
|
||||
``BPF_MEM | <size> | BPF_ST`` means::
|
||||
|
||||
*(size *) (dst_reg + off) = imm32
|
||||
|
||||
BPF_MEM | <size> | BPF_LDX means::
|
||||
``BPF_MEM | <size> | BPF_LDX`` means::
|
||||
|
||||
dst_reg = *(size *) (src_reg + off)
|
||||
|
||||
Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW.
|
||||
Where size is one of: ``BPF_B``, ``BPF_H``, ``BPF_W``, or ``BPF_DW``.
|
||||
|
||||
Atomic operations
|
||||
-----------------
|
||||
|
||||
eBPF includes atomic operations, which use the immediate field for extra
|
||||
encoding::
|
||||
Atomic operations are operations that operate on memory and can not be
|
||||
interrupted or corrupted by other access to the same memory region
|
||||
by other eBPF programs or means outside of this specification.
|
||||
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_W | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
|
||||
All atomic operations supported by eBPF are encoded as store operations
|
||||
that use the ``BPF_ATOMIC`` mode modifier as follows:
|
||||
|
||||
The basic atomic operations supported are::
|
||||
* ``BPF_ATOMIC | BPF_W | BPF_STX`` for 32-bit operations
|
||||
* ``BPF_ATOMIC | BPF_DW | BPF_STX`` for 64-bit operations
|
||||
* 8-bit and 16-bit wide atomic operations are not supported.
|
||||
|
||||
BPF_ADD
|
||||
BPF_AND
|
||||
BPF_OR
|
||||
BPF_XOR
|
||||
The imm field is used to encode the actual atomic operation.
|
||||
Simple atomic operation use a subset of the values defined to encode
|
||||
arithmetic operations in the imm field to encode the atomic operation:
|
||||
|
||||
Each having equivalent semantics with the ``BPF_ADD`` example, that is: the
|
||||
memory location addresed by ``dst_reg + off`` is atomically modified, with
|
||||
``src_reg`` as the other operand. If the ``BPF_FETCH`` flag is set in the
|
||||
immediate, then these operations also overwrite ``src_reg`` with the
|
||||
value that was in memory before it was modified.
|
||||
======== ===== ===========
|
||||
imm value description
|
||||
======== ===== ===========
|
||||
BPF_ADD 0x00 atomic add
|
||||
BPF_OR 0x40 atomic or
|
||||
BPF_AND 0x50 atomic and
|
||||
BPF_XOR 0xa0 atomic xor
|
||||
======== ===== ===========
|
||||
|
||||
The more special operations are::
|
||||
|
||||
BPF_XCHG
|
||||
``BPF_ATOMIC | BPF_W | BPF_STX`` with imm = BPF_ADD means::
|
||||
|
||||
This atomically exchanges ``src_reg`` with the value addressed by ``dst_reg +
|
||||
off``. ::
|
||||
*(u32 *)(dst_reg + off16) += src_reg
|
||||
|
||||
BPF_CMPXCHG
|
||||
``BPF_ATOMIC | BPF_DW | BPF_STX`` with imm = BPF ADD means::
|
||||
|
||||
This atomically compares the value addressed by ``dst_reg + off`` with
|
||||
``R0``. If they match it is replaced with ``src_reg``. In either case, the
|
||||
value that was there before is zero-extended and loaded back to ``R0``.
|
||||
*(u64 *)(dst_reg + off16) += src_reg
|
||||
|
||||
Note that 1 and 2 byte atomic operations are not supported.
|
||||
``BPF_XADD`` is a deprecated name for ``BPF_ATOMIC | BPF_ADD``.
|
||||
|
||||
In addition to the simple atomic operations, there also is a modifier and
|
||||
two complex atomic operations:
|
||||
|
||||
=========== ================ ===========================
|
||||
imm value description
|
||||
=========== ================ ===========================
|
||||
BPF_FETCH 0x01 modifier: return old value
|
||||
BPF_XCHG 0xe0 | BPF_FETCH atomic exchange
|
||||
BPF_CMPXCHG 0xf0 | BPF_FETCH atomic compare and exchange
|
||||
=========== ================ ===========================
|
||||
|
||||
The ``BPF_FETCH`` modifier is optional for simple atomic operations, and
|
||||
always set for the complex atomic operations. If the ``BPF_FETCH`` flag
|
||||
is set, then the operation also overwrites ``src_reg`` with the value that
|
||||
was in memory before it was modified.
|
||||
|
||||
The ``BPF_XCHG`` operation atomically exchanges ``src_reg`` with the value
|
||||
addressed by ``dst_reg + off``.
|
||||
|
||||
The ``BPF_CMPXCHG`` operation atomically compares the value addressed by
|
||||
``dst_reg + off`` with ``R0``. If they match, the value addressed by
|
||||
``dst_reg + off`` is replaced with ``src_reg``. In either case, the
|
||||
value that was at ``dst_reg + off`` before the operation is zero-extended
|
||||
and loaded back to ``R0``.
|
||||
|
||||
Clang can generate atomic instructions by default when ``-mcpu=v3`` is
|
||||
enabled. If a lower version for ``-mcpu`` is set, the only atomic instruction
|
||||
@@ -240,40 +315,52 @@ Clang can generate is ``BPF_ADD`` *without* ``BPF_FETCH``. If you need to enable
|
||||
the atomics features, while keeping a lower ``-mcpu`` version, you can use
|
||||
``-Xclang -target-feature -Xclang +alu32``.
|
||||
|
||||
You may encounter ``BPF_XADD`` - this is a legacy name for ``BPF_ATOMIC``,
|
||||
referring to the exclusive-add operation encoded when the immediate field is
|
||||
zero.
|
||||
64-bit immediate instructions
|
||||
-----------------------------
|
||||
|
||||
16-byte instructions
|
||||
--------------------
|
||||
Instructions with the ``BPF_IMM`` mode modifier use the wide instruction
|
||||
encoding for an extra imm64 value.
|
||||
|
||||
eBPF has one 16-byte instruction: ``BPF_LD | BPF_DW | BPF_IMM`` which consists
|
||||
of two consecutive ``struct bpf_insn`` 8-byte blocks and interpreted as single
|
||||
instruction that loads 64-bit immediate value into a dst_reg.
|
||||
There is currently only one such instruction.
|
||||
|
||||
Packet access instructions
|
||||
--------------------------
|
||||
``BPF_LD | BPF_DW | BPF_IMM`` means::
|
||||
|
||||
eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
|
||||
(BPF_IND | <size> | BPF_LD) which are used to access packet data.
|
||||
dst_reg = imm64
|
||||
|
||||
They had to be carried over from classic BPF to have strong performance of
|
||||
socket filters running in eBPF interpreter. These instructions can only
|
||||
be used when interpreter context is a pointer to ``struct sk_buff`` and
|
||||
have seven implicit operands. Register R6 is an implicit input that must
|
||||
contain pointer to sk_buff. Register R0 is an implicit output which contains
|
||||
the data fetched from the packet. Registers R1-R5 are scratch registers
|
||||
and must not be used to store the data across BPF_ABS | BPF_LD or
|
||||
BPF_IND | BPF_LD instructions.
|
||||
|
||||
These instructions have implicit program exit condition as well. When
|
||||
eBPF program is trying to access the data beyond the packet boundary,
|
||||
the interpreter will abort the execution of the program. JIT compilers
|
||||
therefore must preserve this property. src_reg and imm32 fields are
|
||||
explicit inputs to these instructions.
|
||||
Legacy BPF Packet access instructions
|
||||
-------------------------------------
|
||||
|
||||
For example, BPF_IND | BPF_W | BPF_LD means::
|
||||
eBPF has special instructions for access to packet data that have been
|
||||
carried over from classic BPF to retain the performance of legacy socket
|
||||
filters running in the eBPF interpreter.
|
||||
|
||||
The instructions come in two forms: ``BPF_ABS | <size> | BPF_LD`` and
|
||||
``BPF_IND | <size> | BPF_LD``.
|
||||
|
||||
These instructions are used to access packet data and can only be used when
|
||||
the program context is a pointer to networking packet. ``BPF_ABS``
|
||||
accesses packet data at an absolute offset specified by the immediate data
|
||||
and ``BPF_IND`` access packet data at an offset that includes the value of
|
||||
a register in addition to the immediate data.
|
||||
|
||||
These instructions have seven implicit operands:
|
||||
|
||||
* Register R6 is an implicit input that must contain pointer to a
|
||||
struct sk_buff.
|
||||
* Register R0 is an implicit output which contains the data fetched from
|
||||
the packet.
|
||||
* Registers R1-R5 are scratch registers that are clobbered after a call to
|
||||
``BPF_ABS | BPF_LD`` or ``BPF_IND`` | BPF_LD instructions.
|
||||
|
||||
These instructions have an implicit program exit condition as well. When an
|
||||
eBPF program is trying to access the data beyond the packet boundary, the
|
||||
program execution will be aborted.
|
||||
|
||||
``BPF_ABS | BPF_W | BPF_LD`` means::
|
||||
|
||||
R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + imm32))
|
||||
|
||||
``BPF_IND | BPF_W | BPF_LD`` means::
|
||||
|
||||
R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
|
||||
|
||||
and R1 - R5 are clobbered.
|
||||
|
||||
@@ -3523,6 +3523,8 @@ F: net/sched/act_bpf.c
|
||||
F: net/sched/cls_bpf.c
|
||||
F: samples/bpf/
|
||||
F: scripts/bpf_doc.py
|
||||
F: scripts/pahole-flags.sh
|
||||
F: scripts/pahole-version.sh
|
||||
F: tools/bpf/
|
||||
F: tools/lib/bpf/
|
||||
F: tools/testing/selftests/bpf/
|
||||
|
||||
@@ -1143,6 +1143,11 @@ out:
|
||||
return prog;
|
||||
}
|
||||
|
||||
bool bpf_jit_supports_kfunc_call(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 bpf_jit_alloc_exec_limit(void)
|
||||
{
|
||||
return VMALLOC_END - VMALLOC_START;
|
||||
|
||||
@@ -264,7 +264,7 @@ skip_codegen_passes:
|
||||
fp->jited = 1;
|
||||
fp->jited_len = proglen + FUNCTION_DESCR_SIZE;
|
||||
|
||||
bpf_flush_icache(bpf_hdr, (u8 *)bpf_hdr + (bpf_hdr->pages * PAGE_SIZE));
|
||||
bpf_flush_icache(bpf_hdr, (u8 *)bpf_hdr + bpf_hdr->size);
|
||||
if (!fp->is_func || extra_pass) {
|
||||
bpf_jit_binary_lock_ro(bpf_hdr);
|
||||
bpf_prog_fill_jited_linfo(fp, addrs);
|
||||
|
||||
@@ -1599,7 +1599,7 @@ skip_init_ctx:
|
||||
if (bpf_jit_enable > 1)
|
||||
bpf_jit_dump(prog->len, image_size, pass, ctx.image);
|
||||
|
||||
bpf_flush_icache(header, (u8 *)header + (header->pages * PAGE_SIZE));
|
||||
bpf_flush_icache(header, (u8 *)header + header->size);
|
||||
|
||||
if (!prog->is_func || extra_pass) {
|
||||
bpf_jit_binary_lock_ro(header);
|
||||
|
||||
@@ -158,6 +158,7 @@ config X86
|
||||
select HAVE_ALIGNED_STRUCT_PAGE if SLUB
|
||||
select HAVE_ARCH_AUDITSYSCALL
|
||||
select HAVE_ARCH_HUGE_VMAP if X86_64 || X86_PAE
|
||||
select HAVE_ARCH_HUGE_VMALLOC if HAVE_ARCH_HUGE_VMAP
|
||||
select HAVE_ARCH_JUMP_LABEL
|
||||
select HAVE_ARCH_JUMP_LABEL_RELATIVE
|
||||
select HAVE_ARCH_KASAN if X86_64
|
||||
|
||||
@@ -44,6 +44,7 @@ extern void text_poke_early(void *addr, const void *opcode, size_t len);
|
||||
extern void *text_poke(void *addr, const void *opcode, size_t len);
|
||||
extern void text_poke_sync(void);
|
||||
extern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
|
||||
extern void *text_poke_copy(void *addr, const void *opcode, size_t len);
|
||||
extern int poke_int3_handler(struct pt_regs *regs);
|
||||
extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
|
||||
|
||||
|
||||
@@ -1102,6 +1102,40 @@ void *text_poke_kgdb(void *addr, const void *opcode, size_t len)
|
||||
return __text_poke(addr, opcode, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* text_poke_copy - Copy instructions into (an unused part of) RX memory
|
||||
* @addr: address to modify
|
||||
* @opcode: source of the copy
|
||||
* @len: length to copy, could be more than 2x PAGE_SIZE
|
||||
*
|
||||
* Not safe against concurrent execution; useful for JITs to dump
|
||||
* new code blocks into unused regions of RX memory. Can be used in
|
||||
* conjunction with synchronize_rcu_tasks() to wait for existing
|
||||
* execution to quiesce after having made sure no existing functions
|
||||
* pointers are live.
|
||||
*/
|
||||
void *text_poke_copy(void *addr, const void *opcode, size_t len)
|
||||
{
|
||||
unsigned long start = (unsigned long)addr;
|
||||
size_t patched = 0;
|
||||
|
||||
if (WARN_ON_ONCE(core_kernel_text(start)))
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
while (patched < len) {
|
||||
unsigned long ptr = start + patched;
|
||||
size_t s;
|
||||
|
||||
s = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(ptr), len - patched);
|
||||
|
||||
__text_poke((void *)ptr, opcode + patched, s);
|
||||
patched += s;
|
||||
}
|
||||
mutex_unlock(&text_mutex);
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void do_sync_core(void *info)
|
||||
{
|
||||
sync_core();
|
||||
|
||||
+42
-28
@@ -330,8 +330,7 @@ static int emit_jump(u8 **pprog, void *func, void *ip)
|
||||
}
|
||||
|
||||
static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
|
||||
void *old_addr, void *new_addr,
|
||||
const bool text_live)
|
||||
void *old_addr, void *new_addr)
|
||||
{
|
||||
const u8 *nop_insn = x86_nops[5];
|
||||
u8 old_insn[X86_PATCH_SIZE];
|
||||
@@ -365,10 +364,7 @@ static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
|
||||
goto out;
|
||||
ret = 1;
|
||||
if (memcmp(ip, new_insn, X86_PATCH_SIZE)) {
|
||||
if (text_live)
|
||||
text_poke_bp(ip, new_insn, X86_PATCH_SIZE, NULL);
|
||||
else
|
||||
memcpy(ip, new_insn, X86_PATCH_SIZE);
|
||||
text_poke_bp(ip, new_insn, X86_PATCH_SIZE, NULL);
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
@@ -384,7 +380,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
|
||||
/* BPF poking in modules is not supported */
|
||||
return -EINVAL;
|
||||
|
||||
return __bpf_arch_text_poke(ip, t, old_addr, new_addr, true);
|
||||
return __bpf_arch_text_poke(ip, t, old_addr, new_addr);
|
||||
}
|
||||
|
||||
#define EMIT_LFENCE() EMIT3(0x0F, 0xAE, 0xE8)
|
||||
@@ -558,24 +554,15 @@ static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)
|
||||
mutex_lock(&array->aux->poke_mutex);
|
||||
target = array->ptrs[poke->tail_call.key];
|
||||
if (target) {
|
||||
/* Plain memcpy is used when image is not live yet
|
||||
* and still not locked as read-only. Once poke
|
||||
* location is active (poke->tailcall_target_stable),
|
||||
* any parallel bpf_arch_text_poke() might occur
|
||||
* still on the read-write image until we finally
|
||||
* locked it as read-only. Both modifications on
|
||||
* the given image are under text_mutex to avoid
|
||||
* interference.
|
||||
*/
|
||||
ret = __bpf_arch_text_poke(poke->tailcall_target,
|
||||
BPF_MOD_JUMP, NULL,
|
||||
(u8 *)target->bpf_func +
|
||||
poke->adj_off, false);
|
||||
poke->adj_off);
|
||||
BUG_ON(ret < 0);
|
||||
ret = __bpf_arch_text_poke(poke->tailcall_bypass,
|
||||
BPF_MOD_JUMP,
|
||||
(u8 *)poke->tailcall_target +
|
||||
X86_PATCH_SIZE, NULL, false);
|
||||
X86_PATCH_SIZE, NULL);
|
||||
BUG_ON(ret < 0);
|
||||
}
|
||||
WRITE_ONCE(poke->tailcall_target_stable, true);
|
||||
@@ -787,7 +774,6 @@ static int emit_atomic(u8 **pprog, u8 atomic_op,
|
||||
/* emit opcode */
|
||||
switch (atomic_op) {
|
||||
case BPF_ADD:
|
||||
case BPF_SUB:
|
||||
case BPF_AND:
|
||||
case BPF_OR:
|
||||
case BPF_XOR:
|
||||
@@ -867,7 +853,7 @@ static void emit_nops(u8 **pprog, int len)
|
||||
|
||||
#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))
|
||||
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
|
||||
int oldproglen, struct jit_context *ctx, bool jmp_padding)
|
||||
{
|
||||
bool tail_call_reachable = bpf_prog->aux->tail_call_reachable;
|
||||
@@ -894,8 +880,8 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
||||
push_callee_regs(&prog, callee_regs_used);
|
||||
|
||||
ilen = prog - temp;
|
||||
if (image)
|
||||
memcpy(image + proglen, temp, ilen);
|
||||
if (rw_image)
|
||||
memcpy(rw_image + proglen, temp, ilen);
|
||||
proglen += ilen;
|
||||
addrs[0] = proglen;
|
||||
prog = temp;
|
||||
@@ -1324,6 +1310,9 @@ st: if (is_imm8(insn->off))
|
||||
pr_err("extable->insn doesn't fit into 32-bit\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
/* switch ex to rw buffer for writes */
|
||||
ex = (void *)rw_image + ((void *)ex - (void *)image);
|
||||
|
||||
ex->insn = delta;
|
||||
|
||||
ex->data = EX_TYPE_BPF;
|
||||
@@ -1706,7 +1695,7 @@ emit_jmp:
|
||||
pr_err("bpf_jit: fatal error\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
memcpy(image + proglen, temp, ilen);
|
||||
memcpy(rw_image + proglen, temp, ilen);
|
||||
}
|
||||
proglen += ilen;
|
||||
addrs[i] = proglen;
|
||||
@@ -2247,6 +2236,7 @@ int arch_prepare_bpf_dispatcher(void *image, s64 *funcs, int num_funcs)
|
||||
}
|
||||
|
||||
struct x64_jit_data {
|
||||
struct bpf_binary_header *rw_header;
|
||||
struct bpf_binary_header *header;
|
||||
int *addrs;
|
||||
u8 *image;
|
||||
@@ -2259,6 +2249,7 @@ struct x64_jit_data {
|
||||
|
||||
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
{
|
||||
struct bpf_binary_header *rw_header = NULL;
|
||||
struct bpf_binary_header *header = NULL;
|
||||
struct bpf_prog *tmp, *orig_prog = prog;
|
||||
struct x64_jit_data *jit_data;
|
||||
@@ -2267,6 +2258,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
bool tmp_blinded = false;
|
||||
bool extra_pass = false;
|
||||
bool padding = false;
|
||||
u8 *rw_image = NULL;
|
||||
u8 *image = NULL;
|
||||
int *addrs;
|
||||
int pass;
|
||||
@@ -2302,6 +2294,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
|
||||
oldproglen = jit_data->proglen;
|
||||
image = jit_data->image;
|
||||
header = jit_data->header;
|
||||
rw_header = jit_data->rw_header;
|
||||
rw_image = (void *)rw_header + ((void *)image - (void *)header);
|
||||
extra_pass = true;
|
||||
padding = true;
|
||||
goto skip_init_addrs;
|
||||
@@ -2332,12 +2326,12 @@ skip_init_addrs:
|
||||
for (pass = 0; pass < MAX_PASSES || image; pass++) {
|
||||
if (!padding && pass >= PADDING_PASSES)
|
||||
padding = true;
|
||||
proglen = do_jit(prog, addrs, image, oldproglen, &ctx, padding);
|
||||
proglen = do_jit(prog, addrs, image, rw_image, oldproglen, &ctx, padding);
|
||||
if (proglen <= 0) {
|
||||
out_image:
|
||||
image = NULL;
|
||||
if (header)
|
||||
bpf_jit_binary_free(header);
|
||||
bpf_jit_binary_pack_free(header, rw_header);
|
||||
prog = orig_prog;
|
||||
goto out_addrs;
|
||||
}
|
||||
@@ -2361,8 +2355,9 @@ out_image:
|
||||
sizeof(struct exception_table_entry);
|
||||
|
||||
/* allocate module memory for x86 insns and extable */
|
||||
header = bpf_jit_binary_alloc(roundup(proglen, align) + extable_size,
|
||||
&image, align, jit_fill_hole);
|
||||
header = bpf_jit_binary_pack_alloc(roundup(proglen, align) + extable_size,
|
||||
&image, align, &rw_header, &rw_image,
|
||||
jit_fill_hole);
|
||||
if (!header) {
|
||||
prog = orig_prog;
|
||||
goto out_addrs;
|
||||
@@ -2378,14 +2373,26 @@ out_image:
|
||||
|
||||
if (image) {
|
||||
if (!prog->is_func || extra_pass) {
|
||||
/*
|
||||
* bpf_jit_binary_pack_finalize fails in two scenarios:
|
||||
* 1) header is not pointing to proper module memory;
|
||||
* 2) the arch doesn't support bpf_arch_text_copy().
|
||||
*
|
||||
* Both cases are serious bugs and justify WARN_ON.
|
||||
*/
|
||||
if (WARN_ON(bpf_jit_binary_pack_finalize(prog, header, rw_header))) {
|
||||
prog = orig_prog;
|
||||
goto out_addrs;
|
||||
}
|
||||
|
||||
bpf_tail_call_direct_fixup(prog);
|
||||
bpf_jit_binary_lock_ro(header);
|
||||
} else {
|
||||
jit_data->addrs = addrs;
|
||||
jit_data->ctx = ctx;
|
||||
jit_data->proglen = proglen;
|
||||
jit_data->image = image;
|
||||
jit_data->header = header;
|
||||
jit_data->rw_header = rw_header;
|
||||
}
|
||||
prog->bpf_func = (void *)image;
|
||||
prog->jited = 1;
|
||||
@@ -2413,3 +2420,10 @@ bool bpf_jit_supports_kfunc_call(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void *bpf_arch_text_copy(void *dst, void *src, size_t len)
|
||||
{
|
||||
if (text_poke_copy(dst, src, len) == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -830,8 +830,6 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring)
|
||||
i40e_clean_tx_ring(tx_ring);
|
||||
kfree(tx_ring->tx_bi);
|
||||
tx_ring->tx_bi = NULL;
|
||||
kfree(tx_ring->xsk_descs);
|
||||
tx_ring->xsk_descs = NULL;
|
||||
|
||||
if (tx_ring->desc) {
|
||||
dma_free_coherent(tx_ring->dev, tx_ring->size,
|
||||
@@ -1431,13 +1429,6 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
|
||||
if (!tx_ring->tx_bi)
|
||||
goto err;
|
||||
|
||||
if (ring_is_xdp(tx_ring)) {
|
||||
tx_ring->xsk_descs = kcalloc(I40E_MAX_NUM_DESCRIPTORS, sizeof(*tx_ring->xsk_descs),
|
||||
GFP_KERNEL);
|
||||
if (!tx_ring->xsk_descs)
|
||||
goto err;
|
||||
}
|
||||
|
||||
u64_stats_init(&tx_ring->syncp);
|
||||
|
||||
/* round up to nearest 4K */
|
||||
@@ -1461,8 +1452,6 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(tx_ring->xsk_descs);
|
||||
tx_ring->xsk_descs = NULL;
|
||||
kfree(tx_ring->tx_bi);
|
||||
tx_ring->tx_bi = NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -392,7 +392,6 @@ struct i40e_ring {
|
||||
u16 rx_offset;
|
||||
struct xdp_rxq_info xdp_rxq;
|
||||
struct xsk_buff_pool *xsk_pool;
|
||||
struct xdp_desc *xsk_descs; /* For storing descriptors in the AF_XDP ZC path */
|
||||
} ____cacheline_internodealigned_in_smp;
|
||||
|
||||
static inline bool ring_uses_build_skb(struct i40e_ring *ring)
|
||||
|
||||
@@ -471,11 +471,11 @@ static void i40e_set_rs_bit(struct i40e_ring *xdp_ring)
|
||||
**/
|
||||
static bool i40e_xmit_zc(struct i40e_ring *xdp_ring, unsigned int budget)
|
||||
{
|
||||
struct xdp_desc *descs = xdp_ring->xsk_descs;
|
||||
struct xdp_desc *descs = xdp_ring->xsk_pool->tx_descs;
|
||||
u32 nb_pkts, nb_processed = 0;
|
||||
unsigned int total_bytes = 0;
|
||||
|
||||
nb_pkts = xsk_tx_peek_release_desc_batch(xdp_ring->xsk_pool, descs, budget);
|
||||
nb_pkts = xsk_tx_peek_release_desc_batch(xdp_ring->xsk_pool, budget);
|
||||
if (!nb_pkts)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -2803,6 +2803,8 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
|
||||
/* clone ring and setup updated count */
|
||||
xdp_rings[i] = *vsi->xdp_rings[i];
|
||||
xdp_rings[i].count = new_tx_cnt;
|
||||
xdp_rings[i].next_dd = ICE_RING_QUARTER(&xdp_rings[i]) - 1;
|
||||
xdp_rings[i].next_rs = ICE_RING_QUARTER(&xdp_rings[i]) - 1;
|
||||
xdp_rings[i].desc = NULL;
|
||||
xdp_rings[i].tx_buf = NULL;
|
||||
err = ice_setup_tx_ring(&xdp_rings[i]);
|
||||
|
||||
@@ -2495,10 +2495,10 @@ static int ice_xdp_alloc_setup_rings(struct ice_vsi *vsi)
|
||||
xdp_ring->reg_idx = vsi->txq_map[xdp_q_idx];
|
||||
xdp_ring->vsi = vsi;
|
||||
xdp_ring->netdev = NULL;
|
||||
xdp_ring->next_dd = ICE_TX_THRESH - 1;
|
||||
xdp_ring->next_rs = ICE_TX_THRESH - 1;
|
||||
xdp_ring->dev = dev;
|
||||
xdp_ring->count = vsi->num_tx_desc;
|
||||
xdp_ring->next_dd = ICE_RING_QUARTER(xdp_ring) - 1;
|
||||
xdp_ring->next_rs = ICE_RING_QUARTER(xdp_ring) - 1;
|
||||
WRITE_ONCE(vsi->xdp_rings[i], xdp_ring);
|
||||
if (ice_setup_tx_ring(xdp_ring))
|
||||
goto free_xdp_rings;
|
||||
|
||||
@@ -173,6 +173,8 @@ tx_skip_free:
|
||||
|
||||
tx_ring->next_to_use = 0;
|
||||
tx_ring->next_to_clean = 0;
|
||||
tx_ring->next_dd = ICE_RING_QUARTER(tx_ring) - 1;
|
||||
tx_ring->next_rs = ICE_RING_QUARTER(tx_ring) - 1;
|
||||
|
||||
if (!tx_ring->netdev)
|
||||
return;
|
||||
@@ -1467,7 +1469,7 @@ int ice_napi_poll(struct napi_struct *napi, int budget)
|
||||
bool wd;
|
||||
|
||||
if (tx_ring->xsk_pool)
|
||||
wd = ice_clean_tx_irq_zc(tx_ring, budget);
|
||||
wd = ice_xmit_zc(tx_ring, ICE_DESC_UNUSED(tx_ring), budget);
|
||||
else if (ice_ring_is_xdp(tx_ring))
|
||||
wd = true;
|
||||
else
|
||||
@@ -1520,7 +1522,7 @@ int ice_napi_poll(struct napi_struct *napi, int budget)
|
||||
/* Exit the polling mode, but don't re-enable interrupts if stack might
|
||||
* poll us due to busy-polling
|
||||
*/
|
||||
if (likely(napi_complete_done(napi, work_done))) {
|
||||
if (napi_complete_done(napi, work_done)) {
|
||||
ice_net_dim(q_vector);
|
||||
ice_enable_interrupt(q_vector);
|
||||
} else {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#define ICE_MAX_CHAINED_RX_BUFS 5
|
||||
#define ICE_MAX_BUF_TXD 8
|
||||
#define ICE_MIN_TX_LEN 17
|
||||
#define ICE_TX_THRESH 32
|
||||
|
||||
/* The size limit for a transmit buffer in a descriptor is (16K - 1).
|
||||
* In order to align with the read requests we will align the value to
|
||||
@@ -111,6 +110,8 @@ static inline int ice_skb_pad(void)
|
||||
(u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
|
||||
(R)->next_to_clean - (R)->next_to_use - 1)
|
||||
|
||||
#define ICE_RING_QUARTER(R) ((R)->count >> 2)
|
||||
|
||||
#define ICE_TX_FLAGS_TSO BIT(0)
|
||||
#define ICE_TX_FLAGS_HW_VLAN BIT(1)
|
||||
#define ICE_TX_FLAGS_SW_VLAN BIT(2)
|
||||
@@ -321,17 +322,18 @@ struct ice_tx_ring {
|
||||
u16 count; /* Number of descriptors */
|
||||
u16 q_index; /* Queue number of ring */
|
||||
/* stats structs */
|
||||
struct ice_txq_stats tx_stats;
|
||||
/* CL3 - 3rd cacheline starts here */
|
||||
struct ice_q_stats stats;
|
||||
struct u64_stats_sync syncp;
|
||||
struct ice_txq_stats tx_stats;
|
||||
|
||||
/* CL3 - 3rd cacheline starts here */
|
||||
struct rcu_head rcu; /* to avoid race on free */
|
||||
DECLARE_BITMAP(xps_state, ICE_TX_NBITS); /* XPS Config State */
|
||||
struct ice_channel *ch;
|
||||
struct ice_ptp_tx *tx_tstamps;
|
||||
spinlock_t tx_lock;
|
||||
u32 txq_teid; /* Added Tx queue TEID */
|
||||
/* CL4 - 4th cacheline starts here */
|
||||
u16 xdp_tx_active;
|
||||
#define ICE_TX_FLAGS_RING_XDP BIT(0)
|
||||
u8 flags;
|
||||
u8 dcb_tc; /* Traffic class of ring */
|
||||
|
||||
@@ -222,6 +222,7 @@ ice_receive_skb(struct ice_rx_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag)
|
||||
static void ice_clean_xdp_irq(struct ice_tx_ring *xdp_ring)
|
||||
{
|
||||
unsigned int total_bytes = 0, total_pkts = 0;
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
u16 ntc = xdp_ring->next_to_clean;
|
||||
struct ice_tx_desc *next_dd_desc;
|
||||
u16 next_dd = xdp_ring->next_dd;
|
||||
@@ -233,7 +234,7 @@ static void ice_clean_xdp_irq(struct ice_tx_ring *xdp_ring)
|
||||
cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE)))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ICE_TX_THRESH; i++) {
|
||||
for (i = 0; i < tx_thresh; i++) {
|
||||
tx_buf = &xdp_ring->tx_buf[ntc];
|
||||
|
||||
total_bytes += tx_buf->bytecount;
|
||||
@@ -254,9 +255,9 @@ static void ice_clean_xdp_irq(struct ice_tx_ring *xdp_ring)
|
||||
}
|
||||
|
||||
next_dd_desc->cmd_type_offset_bsz = 0;
|
||||
xdp_ring->next_dd = xdp_ring->next_dd + ICE_TX_THRESH;
|
||||
xdp_ring->next_dd = xdp_ring->next_dd + tx_thresh;
|
||||
if (xdp_ring->next_dd > xdp_ring->count)
|
||||
xdp_ring->next_dd = ICE_TX_THRESH - 1;
|
||||
xdp_ring->next_dd = tx_thresh - 1;
|
||||
xdp_ring->next_to_clean = ntc;
|
||||
ice_update_tx_ring_stats(xdp_ring, total_pkts, total_bytes);
|
||||
}
|
||||
@@ -269,12 +270,13 @@ static void ice_clean_xdp_irq(struct ice_tx_ring *xdp_ring)
|
||||
*/
|
||||
int ice_xmit_xdp_ring(void *data, u16 size, struct ice_tx_ring *xdp_ring)
|
||||
{
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
u16 i = xdp_ring->next_to_use;
|
||||
struct ice_tx_desc *tx_desc;
|
||||
struct ice_tx_buf *tx_buf;
|
||||
dma_addr_t dma;
|
||||
|
||||
if (ICE_DESC_UNUSED(xdp_ring) < ICE_TX_THRESH)
|
||||
if (ICE_DESC_UNUSED(xdp_ring) < tx_thresh)
|
||||
ice_clean_xdp_irq(xdp_ring);
|
||||
|
||||
if (!unlikely(ICE_DESC_UNUSED(xdp_ring))) {
|
||||
@@ -300,13 +302,14 @@ int ice_xmit_xdp_ring(void *data, u16 size, struct ice_tx_ring *xdp_ring)
|
||||
tx_desc->cmd_type_offset_bsz = ice_build_ctob(ICE_TX_DESC_CMD_EOP, 0,
|
||||
size, 0);
|
||||
|
||||
xdp_ring->xdp_tx_active++;
|
||||
i++;
|
||||
if (i == xdp_ring->count) {
|
||||
i = 0;
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_rs);
|
||||
tx_desc->cmd_type_offset_bsz |=
|
||||
cpu_to_le64(ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S);
|
||||
xdp_ring->next_rs = ICE_TX_THRESH - 1;
|
||||
xdp_ring->next_rs = tx_thresh - 1;
|
||||
}
|
||||
xdp_ring->next_to_use = i;
|
||||
|
||||
@@ -314,7 +317,7 @@ int ice_xmit_xdp_ring(void *data, u16 size, struct ice_tx_ring *xdp_ring)
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_rs);
|
||||
tx_desc->cmd_type_offset_bsz |=
|
||||
cpu_to_le64(ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S);
|
||||
xdp_ring->next_rs += ICE_TX_THRESH;
|
||||
xdp_ring->next_rs += tx_thresh;
|
||||
}
|
||||
|
||||
return ICE_XDP_TX;
|
||||
|
||||
@@ -327,6 +327,13 @@ int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid)
|
||||
bool if_running, pool_present = !!pool;
|
||||
int ret = 0, pool_failure = 0;
|
||||
|
||||
if (!is_power_of_2(vsi->rx_rings[qid]->count) ||
|
||||
!is_power_of_2(vsi->tx_rings[qid]->count)) {
|
||||
netdev_err(vsi->netdev, "Please align ring sizes to power of 2\n");
|
||||
pool_failure = -EINVAL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if_running = netif_running(vsi->netdev) && ice_is_xdp_ena_vsi(vsi);
|
||||
|
||||
if (if_running) {
|
||||
@@ -349,6 +356,7 @@ xsk_pool_if_up:
|
||||
netdev_err(vsi->netdev, "ice_qp_ena error = %d\n", ret);
|
||||
}
|
||||
|
||||
failure:
|
||||
if (pool_failure) {
|
||||
netdev_err(vsi->netdev, "Could not %sable buffer pool, error = %d\n",
|
||||
pool_present ? "en" : "dis", pool_failure);
|
||||
@@ -359,33 +367,28 @@ xsk_pool_if_up:
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_alloc_rx_bufs_zc - allocate a number of Rx buffers
|
||||
* @rx_ring: Rx ring
|
||||
* ice_fill_rx_descs - pick buffers from XSK buffer pool and use it
|
||||
* @pool: XSK Buffer pool to pull the buffers from
|
||||
* @xdp: SW ring of xdp_buff that will hold the buffers
|
||||
* @rx_desc: Pointer to Rx descriptors that will be filled
|
||||
* @count: The number of buffers to allocate
|
||||
*
|
||||
* This function allocates a number of Rx buffers from the fill ring
|
||||
* or the internal recycle mechanism and places them on the Rx ring.
|
||||
*
|
||||
* Returns true if all allocations were successful, false if any fail.
|
||||
* Note that ring wrap should be handled by caller of this function.
|
||||
*
|
||||
* Returns the amount of allocated Rx descriptors
|
||||
*/
|
||||
bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count)
|
||||
static u16 ice_fill_rx_descs(struct xsk_buff_pool *pool, struct xdp_buff **xdp,
|
||||
union ice_32b_rx_flex_desc *rx_desc, u16 count)
|
||||
{
|
||||
union ice_32b_rx_flex_desc *rx_desc;
|
||||
u16 ntu = rx_ring->next_to_use;
|
||||
struct xdp_buff **xdp;
|
||||
u32 nb_buffs, i;
|
||||
dma_addr_t dma;
|
||||
u16 buffs;
|
||||
int i;
|
||||
|
||||
rx_desc = ICE_RX_DESC(rx_ring, ntu);
|
||||
xdp = ice_xdp_buf(rx_ring, ntu);
|
||||
|
||||
nb_buffs = min_t(u16, count, rx_ring->count - ntu);
|
||||
nb_buffs = xsk_buff_alloc_batch(rx_ring->xsk_pool, xdp, nb_buffs);
|
||||
if (!nb_buffs)
|
||||
return false;
|
||||
|
||||
i = nb_buffs;
|
||||
while (i--) {
|
||||
buffs = xsk_buff_alloc_batch(pool, xdp, count);
|
||||
for (i = 0; i < buffs; i++) {
|
||||
dma = xsk_buff_xdp_get_dma(*xdp);
|
||||
rx_desc->read.pkt_addr = cpu_to_le64(dma);
|
||||
rx_desc->wb.status_error0 = 0;
|
||||
@@ -394,13 +397,77 @@ bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count)
|
||||
xdp++;
|
||||
}
|
||||
|
||||
return buffs;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ice_alloc_rx_bufs_zc - allocate a number of Rx buffers
|
||||
* @rx_ring: Rx ring
|
||||
* @count: The number of buffers to allocate
|
||||
*
|
||||
* Place the @count of descriptors onto Rx ring. Handle the ring wrap
|
||||
* for case where space from next_to_use up to the end of ring is less
|
||||
* than @count. Finally do a tail bump.
|
||||
*
|
||||
* Returns true if all allocations were successful, false if any fail.
|
||||
*/
|
||||
static bool __ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count)
|
||||
{
|
||||
union ice_32b_rx_flex_desc *rx_desc;
|
||||
u32 nb_buffs_extra = 0, nb_buffs;
|
||||
u16 ntu = rx_ring->next_to_use;
|
||||
u16 total_count = count;
|
||||
struct xdp_buff **xdp;
|
||||
|
||||
rx_desc = ICE_RX_DESC(rx_ring, ntu);
|
||||
xdp = ice_xdp_buf(rx_ring, ntu);
|
||||
|
||||
if (ntu + count >= rx_ring->count) {
|
||||
nb_buffs_extra = ice_fill_rx_descs(rx_ring->xsk_pool, xdp,
|
||||
rx_desc,
|
||||
rx_ring->count - ntu);
|
||||
rx_desc = ICE_RX_DESC(rx_ring, 0);
|
||||
xdp = ice_xdp_buf(rx_ring, 0);
|
||||
ntu = 0;
|
||||
count -= nb_buffs_extra;
|
||||
ice_release_rx_desc(rx_ring, 0);
|
||||
}
|
||||
|
||||
nb_buffs = ice_fill_rx_descs(rx_ring->xsk_pool, xdp, rx_desc, count);
|
||||
|
||||
ntu += nb_buffs;
|
||||
if (ntu == rx_ring->count)
|
||||
ntu = 0;
|
||||
|
||||
ice_release_rx_desc(rx_ring, ntu);
|
||||
if (rx_ring->next_to_use != ntu)
|
||||
ice_release_rx_desc(rx_ring, ntu);
|
||||
|
||||
return count == nb_buffs;
|
||||
return total_count == (nb_buffs_extra + nb_buffs);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_alloc_rx_bufs_zc - allocate a number of Rx buffers
|
||||
* @rx_ring: Rx ring
|
||||
* @count: The number of buffers to allocate
|
||||
*
|
||||
* Wrapper for internal allocation routine; figure out how many tail
|
||||
* bumps should take place based on the given threshold
|
||||
*
|
||||
* Returns true if all calls to internal alloc routine succeeded
|
||||
*/
|
||||
bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count)
|
||||
{
|
||||
u16 rx_thresh = ICE_RING_QUARTER(rx_ring);
|
||||
u16 batched, leftover, i, tail_bumps;
|
||||
|
||||
batched = ALIGN_DOWN(count, rx_thresh);
|
||||
tail_bumps = batched / rx_thresh;
|
||||
leftover = count & (rx_thresh - 1);
|
||||
|
||||
for (i = 0; i < tail_bumps; i++)
|
||||
if (!__ice_alloc_rx_bufs_zc(rx_ring, rx_thresh))
|
||||
return false;
|
||||
return __ice_alloc_rx_bufs_zc(rx_ring, leftover);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -615,58 +682,6 @@ construct_skb:
|
||||
return failure ? budget : (int)total_rx_packets;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_xmit_zc - Completes AF_XDP entries, and cleans XDP entries
|
||||
* @xdp_ring: XDP Tx ring
|
||||
* @budget: max number of frames to xmit
|
||||
*
|
||||
* Returns true if cleanup/transmission is done.
|
||||
*/
|
||||
static bool ice_xmit_zc(struct ice_tx_ring *xdp_ring, int budget)
|
||||
{
|
||||
struct ice_tx_desc *tx_desc = NULL;
|
||||
bool work_done = true;
|
||||
struct xdp_desc desc;
|
||||
dma_addr_t dma;
|
||||
|
||||
while (likely(budget-- > 0)) {
|
||||
struct ice_tx_buf *tx_buf;
|
||||
|
||||
if (unlikely(!ICE_DESC_UNUSED(xdp_ring))) {
|
||||
xdp_ring->tx_stats.tx_busy++;
|
||||
work_done = false;
|
||||
break;
|
||||
}
|
||||
|
||||
tx_buf = &xdp_ring->tx_buf[xdp_ring->next_to_use];
|
||||
|
||||
if (!xsk_tx_peek_desc(xdp_ring->xsk_pool, &desc))
|
||||
break;
|
||||
|
||||
dma = xsk_buff_raw_get_dma(xdp_ring->xsk_pool, desc.addr);
|
||||
xsk_buff_raw_dma_sync_for_device(xdp_ring->xsk_pool, dma,
|
||||
desc.len);
|
||||
|
||||
tx_buf->bytecount = desc.len;
|
||||
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_to_use);
|
||||
tx_desc->buf_addr = cpu_to_le64(dma);
|
||||
tx_desc->cmd_type_offset_bsz =
|
||||
ice_build_ctob(ICE_TXD_LAST_DESC_CMD, 0, desc.len, 0);
|
||||
|
||||
xdp_ring->next_to_use++;
|
||||
if (xdp_ring->next_to_use == xdp_ring->count)
|
||||
xdp_ring->next_to_use = 0;
|
||||
}
|
||||
|
||||
if (tx_desc) {
|
||||
ice_xdp_ring_update_tail(xdp_ring);
|
||||
xsk_tx_release(xdp_ring->xsk_pool);
|
||||
}
|
||||
|
||||
return budget > 0 && work_done;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_clean_xdp_tx_buf - Free and unmap XDP Tx buffer
|
||||
* @xdp_ring: XDP Tx ring
|
||||
@@ -676,74 +691,213 @@ static void
|
||||
ice_clean_xdp_tx_buf(struct ice_tx_ring *xdp_ring, struct ice_tx_buf *tx_buf)
|
||||
{
|
||||
xdp_return_frame((struct xdp_frame *)tx_buf->raw_buf);
|
||||
xdp_ring->xdp_tx_active--;
|
||||
dma_unmap_single(xdp_ring->dev, dma_unmap_addr(tx_buf, dma),
|
||||
dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
|
||||
dma_unmap_len_set(tx_buf, len, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_clean_tx_irq_zc - Completes AF_XDP entries, and cleans XDP entries
|
||||
* @xdp_ring: XDP Tx ring
|
||||
* @budget: NAPI budget
|
||||
* ice_clean_xdp_irq_zc - Reclaim resources after transmit completes on XDP ring
|
||||
* @xdp_ring: XDP ring to clean
|
||||
* @napi_budget: amount of descriptors that NAPI allows us to clean
|
||||
*
|
||||
* Returns true if cleanup/tranmission is done.
|
||||
* Returns count of cleaned descriptors
|
||||
*/
|
||||
bool ice_clean_tx_irq_zc(struct ice_tx_ring *xdp_ring, int budget)
|
||||
static u16 ice_clean_xdp_irq_zc(struct ice_tx_ring *xdp_ring, int napi_budget)
|
||||
{
|
||||
int total_packets = 0, total_bytes = 0;
|
||||
s16 ntc = xdp_ring->next_to_clean;
|
||||
struct ice_tx_desc *tx_desc;
|
||||
struct ice_tx_buf *tx_buf;
|
||||
u32 xsk_frames = 0;
|
||||
bool xmit_done;
|
||||
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, ntc);
|
||||
tx_buf = &xdp_ring->tx_buf[ntc];
|
||||
ntc -= xdp_ring->count;
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
int budget = napi_budget / tx_thresh;
|
||||
u16 next_dd = xdp_ring->next_dd;
|
||||
u16 ntc, cleared_dds = 0;
|
||||
|
||||
do {
|
||||
if (!(tx_desc->cmd_type_offset_bsz &
|
||||
cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE)))
|
||||
struct ice_tx_desc *next_dd_desc;
|
||||
u16 desc_cnt = xdp_ring->count;
|
||||
struct ice_tx_buf *tx_buf;
|
||||
u32 xsk_frames;
|
||||
u16 i;
|
||||
|
||||
next_dd_desc = ICE_TX_DESC(xdp_ring, next_dd);
|
||||
if (!(next_dd_desc->cmd_type_offset_bsz &
|
||||
cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE)))
|
||||
break;
|
||||
|
||||
total_bytes += tx_buf->bytecount;
|
||||
total_packets++;
|
||||
|
||||
if (tx_buf->raw_buf) {
|
||||
ice_clean_xdp_tx_buf(xdp_ring, tx_buf);
|
||||
tx_buf->raw_buf = NULL;
|
||||
} else {
|
||||
xsk_frames++;
|
||||
cleared_dds++;
|
||||
xsk_frames = 0;
|
||||
if (likely(!xdp_ring->xdp_tx_active)) {
|
||||
xsk_frames = tx_thresh;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
tx_desc->cmd_type_offset_bsz = 0;
|
||||
tx_buf++;
|
||||
tx_desc++;
|
||||
ntc++;
|
||||
ntc = xdp_ring->next_to_clean;
|
||||
|
||||
if (unlikely(!ntc)) {
|
||||
ntc -= xdp_ring->count;
|
||||
tx_buf = xdp_ring->tx_buf;
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, 0);
|
||||
for (i = 0; i < tx_thresh; i++) {
|
||||
tx_buf = &xdp_ring->tx_buf[ntc];
|
||||
|
||||
if (tx_buf->raw_buf) {
|
||||
ice_clean_xdp_tx_buf(xdp_ring, tx_buf);
|
||||
tx_buf->raw_buf = NULL;
|
||||
} else {
|
||||
xsk_frames++;
|
||||
}
|
||||
|
||||
ntc++;
|
||||
if (ntc >= xdp_ring->count)
|
||||
ntc = 0;
|
||||
}
|
||||
skip:
|
||||
xdp_ring->next_to_clean += tx_thresh;
|
||||
if (xdp_ring->next_to_clean >= desc_cnt)
|
||||
xdp_ring->next_to_clean -= desc_cnt;
|
||||
if (xsk_frames)
|
||||
xsk_tx_completed(xdp_ring->xsk_pool, xsk_frames);
|
||||
next_dd_desc->cmd_type_offset_bsz = 0;
|
||||
next_dd = next_dd + tx_thresh;
|
||||
if (next_dd >= desc_cnt)
|
||||
next_dd = tx_thresh - 1;
|
||||
} while (budget--);
|
||||
|
||||
prefetch(tx_desc);
|
||||
xdp_ring->next_dd = next_dd;
|
||||
|
||||
} while (likely(--budget));
|
||||
return cleared_dds * tx_thresh;
|
||||
}
|
||||
|
||||
ntc += xdp_ring->count;
|
||||
xdp_ring->next_to_clean = ntc;
|
||||
/**
|
||||
* ice_xmit_pkt - produce a single HW Tx descriptor out of AF_XDP descriptor
|
||||
* @xdp_ring: XDP ring to produce the HW Tx descriptor on
|
||||
* @desc: AF_XDP descriptor to pull the DMA address and length from
|
||||
* @total_bytes: bytes accumulator that will be used for stats update
|
||||
*/
|
||||
static void ice_xmit_pkt(struct ice_tx_ring *xdp_ring, struct xdp_desc *desc,
|
||||
unsigned int *total_bytes)
|
||||
{
|
||||
struct ice_tx_desc *tx_desc;
|
||||
dma_addr_t dma;
|
||||
|
||||
if (xsk_frames)
|
||||
xsk_tx_completed(xdp_ring->xsk_pool, xsk_frames);
|
||||
dma = xsk_buff_raw_get_dma(xdp_ring->xsk_pool, desc->addr);
|
||||
xsk_buff_raw_dma_sync_for_device(xdp_ring->xsk_pool, dma, desc->len);
|
||||
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_to_use++);
|
||||
tx_desc->buf_addr = cpu_to_le64(dma);
|
||||
tx_desc->cmd_type_offset_bsz = ice_build_ctob(ICE_TX_DESC_CMD_EOP,
|
||||
0, desc->len, 0);
|
||||
|
||||
*total_bytes += desc->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_xmit_pkt_batch - produce a batch of HW Tx descriptors out of AF_XDP descriptors
|
||||
* @xdp_ring: XDP ring to produce the HW Tx descriptors on
|
||||
* @descs: AF_XDP descriptors to pull the DMA addresses and lengths from
|
||||
* @total_bytes: bytes accumulator that will be used for stats update
|
||||
*/
|
||||
static void ice_xmit_pkt_batch(struct ice_tx_ring *xdp_ring, struct xdp_desc *descs,
|
||||
unsigned int *total_bytes)
|
||||
{
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
u16 ntu = xdp_ring->next_to_use;
|
||||
struct ice_tx_desc *tx_desc;
|
||||
u32 i;
|
||||
|
||||
loop_unrolled_for(i = 0; i < PKTS_PER_BATCH; i++) {
|
||||
dma_addr_t dma;
|
||||
|
||||
dma = xsk_buff_raw_get_dma(xdp_ring->xsk_pool, descs[i].addr);
|
||||
xsk_buff_raw_dma_sync_for_device(xdp_ring->xsk_pool, dma, descs[i].len);
|
||||
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, ntu++);
|
||||
tx_desc->buf_addr = cpu_to_le64(dma);
|
||||
tx_desc->cmd_type_offset_bsz = ice_build_ctob(ICE_TX_DESC_CMD_EOP,
|
||||
0, descs[i].len, 0);
|
||||
|
||||
*total_bytes += descs[i].len;
|
||||
}
|
||||
|
||||
xdp_ring->next_to_use = ntu;
|
||||
|
||||
if (xdp_ring->next_to_use > xdp_ring->next_rs) {
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_rs);
|
||||
tx_desc->cmd_type_offset_bsz |=
|
||||
cpu_to_le64(ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S);
|
||||
xdp_ring->next_rs += tx_thresh;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_fill_tx_hw_ring - produce the number of Tx descriptors onto ring
|
||||
* @xdp_ring: XDP ring to produce the HW Tx descriptors on
|
||||
* @descs: AF_XDP descriptors to pull the DMA addresses and lengths from
|
||||
* @nb_pkts: count of packets to be send
|
||||
* @total_bytes: bytes accumulator that will be used for stats update
|
||||
*/
|
||||
static void ice_fill_tx_hw_ring(struct ice_tx_ring *xdp_ring, struct xdp_desc *descs,
|
||||
u32 nb_pkts, unsigned int *total_bytes)
|
||||
{
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
u32 batched, leftover, i;
|
||||
|
||||
batched = ALIGN_DOWN(nb_pkts, PKTS_PER_BATCH);
|
||||
leftover = nb_pkts & (PKTS_PER_BATCH - 1);
|
||||
for (i = 0; i < batched; i += PKTS_PER_BATCH)
|
||||
ice_xmit_pkt_batch(xdp_ring, &descs[i], total_bytes);
|
||||
for (; i < batched + leftover; i++)
|
||||
ice_xmit_pkt(xdp_ring, &descs[i], total_bytes);
|
||||
|
||||
if (xdp_ring->next_to_use > xdp_ring->next_rs) {
|
||||
struct ice_tx_desc *tx_desc;
|
||||
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_rs);
|
||||
tx_desc->cmd_type_offset_bsz |=
|
||||
cpu_to_le64(ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S);
|
||||
xdp_ring->next_rs += tx_thresh;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ice_xmit_zc - take entries from XSK Tx ring and place them onto HW Tx ring
|
||||
* @xdp_ring: XDP ring to produce the HW Tx descriptors on
|
||||
* @budget: number of free descriptors on HW Tx ring that can be used
|
||||
* @napi_budget: amount of descriptors that NAPI allows us to clean
|
||||
*
|
||||
* Returns true if there is no more work that needs to be done, false otherwise
|
||||
*/
|
||||
bool ice_xmit_zc(struct ice_tx_ring *xdp_ring, u32 budget, int napi_budget)
|
||||
{
|
||||
struct xdp_desc *descs = xdp_ring->xsk_pool->tx_descs;
|
||||
u16 tx_thresh = ICE_RING_QUARTER(xdp_ring);
|
||||
u32 nb_pkts, nb_processed = 0;
|
||||
unsigned int total_bytes = 0;
|
||||
|
||||
if (budget < tx_thresh)
|
||||
budget += ice_clean_xdp_irq_zc(xdp_ring, napi_budget);
|
||||
|
||||
nb_pkts = xsk_tx_peek_release_desc_batch(xdp_ring->xsk_pool, budget);
|
||||
if (!nb_pkts)
|
||||
return true;
|
||||
|
||||
if (xdp_ring->next_to_use + nb_pkts >= xdp_ring->count) {
|
||||
struct ice_tx_desc *tx_desc;
|
||||
|
||||
nb_processed = xdp_ring->count - xdp_ring->next_to_use;
|
||||
ice_fill_tx_hw_ring(xdp_ring, descs, nb_processed, &total_bytes);
|
||||
tx_desc = ICE_TX_DESC(xdp_ring, xdp_ring->next_rs);
|
||||
tx_desc->cmd_type_offset_bsz |=
|
||||
cpu_to_le64(ICE_TX_DESC_CMD_RS << ICE_TXD_QW1_CMD_S);
|
||||
xdp_ring->next_rs = tx_thresh - 1;
|
||||
xdp_ring->next_to_use = 0;
|
||||
}
|
||||
|
||||
ice_fill_tx_hw_ring(xdp_ring, &descs[nb_processed], nb_pkts - nb_processed,
|
||||
&total_bytes);
|
||||
|
||||
ice_xdp_ring_update_tail(xdp_ring);
|
||||
ice_update_tx_ring_stats(xdp_ring, nb_pkts, total_bytes);
|
||||
|
||||
if (xsk_uses_need_wakeup(xdp_ring->xsk_pool))
|
||||
xsk_set_tx_need_wakeup(xdp_ring->xsk_pool);
|
||||
|
||||
ice_update_tx_ring_stats(xdp_ring, total_packets, total_bytes);
|
||||
xmit_done = ice_xmit_zc(xdp_ring, ICE_DFLT_IRQ_WORK);
|
||||
|
||||
return budget > 0 && xmit_done;
|
||||
return nb_pkts < budget;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,19 +6,37 @@
|
||||
#include "ice_txrx.h"
|
||||
#include "ice.h"
|
||||
|
||||
#define PKTS_PER_BATCH 8
|
||||
|
||||
#ifdef __clang__
|
||||
#define loop_unrolled_for _Pragma("clang loop unroll_count(8)") for
|
||||
#elif __GNUC__ >= 4
|
||||
#define loop_unrolled_for _Pragma("GCC unroll 8") for
|
||||
#else
|
||||
#define loop_unrolled_for for
|
||||
#endif
|
||||
|
||||
struct ice_vsi;
|
||||
|
||||
#ifdef CONFIG_XDP_SOCKETS
|
||||
int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool,
|
||||
u16 qid);
|
||||
int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget);
|
||||
bool ice_clean_tx_irq_zc(struct ice_tx_ring *xdp_ring, int budget);
|
||||
int ice_xsk_wakeup(struct net_device *netdev, u32 queue_id, u32 flags);
|
||||
bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring, u16 count);
|
||||
bool ice_xsk_any_rx_ring_ena(struct ice_vsi *vsi);
|
||||
void ice_xsk_clean_rx_ring(struct ice_rx_ring *rx_ring);
|
||||
void ice_xsk_clean_xdp_ring(struct ice_tx_ring *xdp_ring);
|
||||
bool ice_xmit_zc(struct ice_tx_ring *xdp_ring, u32 budget, int napi_budget);
|
||||
#else
|
||||
static inline bool
|
||||
ice_xmit_zc(struct ice_tx_ring __always_unused *xdp_ring,
|
||||
u32 __always_unused budget,
|
||||
int __always_unused napi_budget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int
|
||||
ice_xsk_pool_setup(struct ice_vsi __always_unused *vsi,
|
||||
struct xsk_buff_pool __always_unused *pool,
|
||||
@@ -34,13 +52,6 @@ ice_clean_rx_irq_zc(struct ice_rx_ring __always_unused *rx_ring,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ice_clean_tx_irq_zc(struct ice_tx_ring __always_unused *xdp_ring,
|
||||
int __always_unused budget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ice_alloc_rx_bufs_zc(struct ice_rx_ring __always_unused *rx_ring,
|
||||
u16 __always_unused count)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user